]> O.S.I.I.S - jp/crow.git/commitdiff
list item
authorJean-Philippe Bruyère <jp_bruyere@hotmail.com>
Mon, 6 Jul 2020 06:18:33 +0000 (08:18 +0200)
committerJean-Philippe Bruyère <jp_bruyere@hotmail.com>
Mon, 6 Jul 2020 06:18:33 +0000 (08:18 +0200)
57 files changed:
Crow.sln
Crow/Crow.csproj
Crow/Default.style
Crow/Templates/ContextMenu.template
Crow/Templates/DefaultItem.template
Crow/Templates/DirectoryView.template
Crow/Templates/FileItems.template
Crow/Templates/GroupBox.template
Crow/Templates/MenuItem.template
Crow/Templates/Tooltip.template
Crow/src/DebugUtils/DbgEvent.cs [new file with mode: 0644]
Crow/src/DebugUtils/DbgEventSource.cs [new file with mode: 0644]
Crow/src/DebugUtils/DbgEventTypeColors.cs [new file with mode: 0644]
Crow/src/DebugUtils/DbgEventWidget (copie).cs [new file with mode: 0644]
Crow/src/DebugUtils/DbgWidgetRecord.cs [new file with mode: 0644]
Crow/src/DebugUtils/DebugLogger.cs [new file with mode: 0644]
Crow/src/DebugUtils/Utils.cs [new file with mode: 0644]
Crow/src/EventArgs/SelectionChangeEventArgs.cs
Crow/src/EventArgs/TreeExpandEventArg.cs [new file with mode: 0644]
Crow/src/Fill/SolidColor.cs
Crow/src/IML/Instantiator.cs
Crow/src/Interface.cs
Crow/src/LayoutingQueueItem.cs
Crow/src/Widgets/Expandable.cs
Crow/src/Widgets/Group.cs
Crow/src/Widgets/ListItem.cs [new file with mode: 0644]
Crow/src/Widgets/PrivateContainer.cs
Crow/src/Widgets/TemplatedContainer.cs
Crow/src/Widgets/TemplatedControl.cs
Crow/src/Widgets/TemplatedGroup.cs
Crow/src/Widgets/TreeView.cs
Crow/src/Widgets/Widget.cs
Crow/src/debug/DbgEventTypeColors.cs [deleted file]
Crow/src/debug/DbgLogViewer.cs [deleted file]
Crow/src/debug/DebugLogger.cs [deleted file]
Samples/BasicTests/BasicTests.cs
Samples/DebugLogAnalyzer/DbgEventWidget.cs [new file with mode: 0644]
Samples/DebugLogAnalyzer/DbgLogViewer.cs [new file with mode: 0644]
Samples/DebugLogAnalyzer/DebugLogAnalyzer.csproj [new file with mode: 0644]
Samples/DebugLogAnalyzer/Program.cs [new file with mode: 0644]
Samples/DebugLogAnalyzer/Properties/AssemblyInfo.cs [new file with mode: 0644]
Samples/DebugLogAnalyzer/ui/DbgEventTreeItems.itemp [new file with mode: 0644]
Samples/DebugLogAnalyzer/ui/DbgEvtTooltip.crow [new file with mode: 0644]
Samples/DebugLogAnalyzer/ui/dbglog.crow [new file with mode: 0644]
Samples/HelloWorld/HelloWorld.csproj
Samples/HelloWorld/main.cs
Samples/ShowCase/ShowCase.cs
Samples/common/SampleBase.cs
Samples/common/samples.style
Samples/common/ui/Interfaces/CheckBox2.imlt
Samples/common/ui/Interfaces/Divers/classBindings.crow [new file with mode: 0644]
Samples/common/ui/Interfaces/Divers/dbglog.crow [deleted file]
Samples/common/ui/Interfaces/Divers/splitter0.crow [new file with mode: 0644]
Samples/common/ui/Interfaces/Splitter/1.crow [deleted file]
Samples/common/ui/Interfaces/Splitter/3.crow [deleted file]
Samples/common/ui/Interfaces/TabItem.template [deleted file]
Samples/common/ui/Interfaces/colorItem.crow

index a0113386a88b27ad44468e8ae3f927686a12ac86..ee47449616f2f86f82aa9ecdd57524878d848354 100644 (file)
--- a/Crow.sln
+++ b/Crow.sln
@@ -27,6 +27,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BasicTests", "Samples\Basic
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PerfTests", "Samples\PerfTests\PerfTests.csproj", "{18EBB41F-815E-4BF5-B80F-C9E2FAB2993A}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DebugLogAnalyzer", "Samples\DebugLogAnalyzer\DebugLogAnalyzer.csproj", "{7915538F-B2B1-414C-95A3-1FC58E3286B9}"
+EndProject
 Global
        GlobalSection(SolutionConfigurationPlatforms) = preSolution
                Debug|Any CPU = Debug|Any CPU
@@ -59,6 +61,10 @@ Global
                {18EBB41F-815E-4BF5-B80F-C9E2FAB2993A}.Debug|Any CPU.Build.0 = Debug|Any CPU
                {18EBB41F-815E-4BF5-B80F-C9E2FAB2993A}.Release|Any CPU.ActiveCfg = Release|Any CPU
                {18EBB41F-815E-4BF5-B80F-C9E2FAB2993A}.Release|Any CPU.Build.0 = Release|Any CPU
+               {7915538F-B2B1-414C-95A3-1FC58E3286B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {7915538F-B2B1-414C-95A3-1FC58E3286B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {7915538F-B2B1-414C-95A3-1FC58E3286B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {7915538F-B2B1-414C-95A3-1FC58E3286B9}.Release|Any CPU.Build.0 = Release|Any CPU
        EndGlobalSection
        GlobalSection(SolutionProperties) = preSolution
                HideSolutionNode = FALSE
@@ -69,6 +75,7 @@ Global
                {91F1CE07-EECE-4F1D-A3EE-7239B563654A} = {B2C7855A-2878-47FD-AD32-9A83DB4AB8C6}
                {7AEB6DD5-916E-4415-84E1-78EC6E5881CE} = {B2C7855A-2878-47FD-AD32-9A83DB4AB8C6}
                {18EBB41F-815E-4BF5-B80F-C9E2FAB2993A} = {B2C7855A-2878-47FD-AD32-9A83DB4AB8C6}
+               {7915538F-B2B1-414C-95A3-1FC58E3286B9} = {B2C7855A-2878-47FD-AD32-9A83DB4AB8C6}
        EndGlobalSection
        GlobalSection(ExtensibilityGlobals) = postSolution
                SolutionGuid = {00D4E149-7131-49F4-BAAD-559AA961A78E}
index 38082c372b3f6ac51a2e38dfd4bab88c9154544d..8a0b9434c5ec59c2a4c2dd7cd77d6baa091dc3de 100644 (file)
@@ -32,7 +32,7 @@
        </PropertyGroup>
        <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
                <DebugType>full</DebugType>
-               <DefineConstants>$(DefineConstants);DEBUG_LOG;DEBUG;TRACE;DEBUG_LAYOUTING;DEBUG_DISPOSE;_DEBUG_BINDING;_DEBUG_CLIP_RECTANGLE</DefineConstants>
+               <DefineConstants>$(DefineConstants);_DEBUG_LOG;DEBUG;TRACE;DEBUG_LAYOUTING;DEBUG_DISPOSE;_DEBUG_BINDING;_DEBUG_CLIP_RECTANGLE</DefineConstants>
                <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
        </PropertyGroup>
        <ItemGroup Condition="$(TargetFramework.StartsWith('netstandard'))">
index 8bc81ea6b70dac4333c006f132673f4e44d3db9b..cbc9dcbaf87130f119fbe004fe14ace137d9f04d 100644 (file)
@@ -10,6 +10,10 @@ IconSize = "11";
 IconMargin = "1";
 ToggleIconSize = "16";
 
+TooltipBackground = "Khaki";
+TooltipForeground = "DimGrey";
+TooltipMargin = "3";
+
 WindowBackgroundColor = "DarkGrey";
 WindowBorderColor = "Grey";
 WindowBorderWidth = "1";
@@ -84,8 +88,8 @@ Menu {
        Height = "Fit";
        Width = "Stretched";
        VerticalAlignment = "Top";
-       SelectionBackground = "${ControlHighlight}";
-       SelectionColoring = "false";
+       //SelectionBackground = "${ControlHighlight}";
+       //SelectionColoring = "false";
 }
 MenuItem {
        Caption = "MenuItem";
@@ -96,8 +100,13 @@ MenuItem {
        MouseEnter = "{Background=${ControlHighlight}}";
        MouseLeave = "{Background=${MenuBackground}}";
        //SelectionBackground = "${ControlHighlight}";
-       SelectionBackground = "Transparent";
-       SelectionColoring = "false";
+       //SelectionBackground = "Transparent";
+       //SelectionColoring = "false";
+}
+ListItem {
+       //Selected = "{Background=${ControlHighlight}}";
+       //Unselected = "{Background=Transparent}";
+       //Margin="1";
 }
 MessageBox {
        Background = "0.1,0.1,0.2,0.85";
index 7fc8dcc9cd836d53fa139127e6828f57344de893..fda09d1f444ce4eaae671c155f4432c57b901e11 100644 (file)
@@ -1,7 +1,6 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <MenuItem MinimumSize="20,20" Height="Fit" Width="160" Caption="Context Menu" Data="{ContextCommands}" Orientation="Vertical"
-       IsOpened ="true" Visible="{/IsOpened}" SelectionBackground="Transparent"
-       Background="Red">
+       IsOpened ="true" Visible="{/IsOpened}" Background="Red">
        <Template>
                <HorizontalStack>
                        <Border Background="DimGrey" Foreground="DimGrey" CornerRadius="5">
@@ -11,7 +10,7 @@
                </HorizontalStack>
        </Template>
        <ItemTemplate>
-               <MenuItem Command="{}" Width="150" PopWidth="120" SelectionBackground="Transparent" IsEnabled="{CanExecute}">
+               <MenuItem Command="{}" Width="150" PopWidth="120" IsEnabled="{CanExecute}">
                        <Template>
                                <Popper Font="{./Font}" Caption="{./Caption}"  Background="{./Background}" PopDirection="{./PopDirection}"
                                        Foreground = "{./Foreground}" CanPop="{./HasChildren}" MouseDown="./onMI_Click"
index 38840830fa68feeb6b6cf65f0da98bddbc656d4f..f2d7102d42d2b38b5d2772e48e1bf718b7053bba 100644 (file)
@@ -1,8 +1,7 @@
 <?xml version="1.0"?>
-<Container Height="Fit" Margin="0" Focusable="true"
-               HorizontalAlignment="Left"
-               MouseEnter="{Background=SteelBlue}"
-               MouseLeave="{Background=Transparent}">
+<ListItem Height="Fit" Margin="0" Focusable="true" HorizontalAlignment="Left" 
+                               Selected = "{Background=${ControlHighlight}}"
+                               Unselected = "{Background=Transparent}">
        <Label Text="{}" HorizontalAlignment="Left" />
-</Container>
+</ListItem>
 
index 36f042cfc847cdc94b2fe700f703c6b0db7d8f76..009f46d06224459bd111d5d4f7b03f6230dabf7a 100644 (file)
@@ -2,43 +2,46 @@
 <TreeView IsRoot="true" Name="treeView" Data="{./FileSystemEntries}" Background="{./Background}"
                SelectedItemChanged="./onSelectedItemChanged">
        <ItemTemplate DataType="System.IO.FileInfo">
-               <Border CornerRadius="2" Margin="0" Focusable="true"  Height="Fit" Width="Stretched" Foreground="Transparent"
-                               MouseEnter="{Foreground=DimGrey}"
-                               MouseLeave="{Foreground=Transparent}">
+               <ListItem CornerRadius="2" Margin="0" Height="Fit" Width="Stretched"
+                               Selected="{Background=${ControlHighlight}}"
+                               Unselected="{Background=Transparent}">
                        <HorizontalStack>
-                               <Image Margin="1" Width="14" Height="14"
-                                       Path="#Crow.Icons.file.svg"/>
+                               <Image Margin="1" Width="14" Height="14" Path="#Crow.Icons.file.svg"/>
                                <Label Text="{Name}" Width="Stretched"/>
                        </HorizontalStack>
-               </Border>
+               </ListItem>
        </ItemTemplate>
        <ItemTemplate DataType="System.IO.DirectoryInfo" Data="GetFileSystemInfosOrdered">
-               <Expandable Caption="{Name}" MouseDoubleClick="/onClickForExpand">
-                       <Template>
-                               <VerticalStack>
-                                       <Border CornerRadius="2" Margin="0" Height="Fit" MouseDoubleClick="./onClickForExpand"
-                                                       Foreground="Transparent"
-                                                       MouseEnter="{Foreground=DimGrey}"
-                                                       MouseLeave="{Foreground=Transparent}">
-                                               <HorizontalStack Spacing="1">
-                                                       <Image Margin="1" Width="9" Height="9" Focusable="true" MouseDown="./onClickForExpand"
-                                                               Path="{./Image}"
-                                                               Visible="{./IsExpandable}"
-                                                               SvgSub="{./IsExpanded}"
-                                                               MouseEnter="{Background=LightGrey}"
-                                                               MouseLeave="{Background=Transparent}"/>
-                                                       <Image Margin="1" Width="16" Height="16"
-                                                               Path="#Crow.Icons.folder.svg" SvgSub="{./IsExpanded}"/>
-                                                       <Label Text="{./Caption}"/>
-                                               </HorizontalStack>
-                                       </Border>
-                                       <Container Name="Content" Visible="false"/>
-                               </VerticalStack>
-                       </Template>
-                       <HorizontalStack Height="Fit">
-                               <Widget Width="12" Height="10"/>
-                               <VerticalStack Height="Fit" Name="ItemsContainer"/>
-                       </HorizontalStack>
-               </Expandable>
+               <ListItem
+                               Selected="{/exp.Background=${ControlHighlight}}"
+                               Unselected="{/exp.Background=Transparent}">
+                       <Expandable Name="exp" Caption="{Name}" MouseDoubleClick="/onClickForExpand">
+                               <Template>
+                                       <VerticalStack>
+                                               <Border CornerRadius="2" Margin="0" Height="Fit" MouseDoubleClick="./onClickForExpand"
+                                                               Foreground="Transparent"
+                                                               MouseEnter="{Foreground=DimGrey}"
+                                                               MouseLeave="{Foreground=Transparent}">
+                                                       <HorizontalStack Background="{./Background}" Spacing="1">
+                                                               <Image Margin="1" Width="9" Height="9" Focusable="true" MouseDown="./onClickForExpand"
+                                                                       Path="{./Image}"
+                                                                       Visible="{./IsExpandable}"
+                                                                       SvgSub="{./IsExpanded}"
+                                                                       MouseEnter="{Background=LightGrey}"
+                                                                       MouseLeave="{Background=Transparent}"/>
+                                                               <Image Margin="1" Width="16" Height="16"
+                                                                       Path="#Crow.Icons.folder.svg" SvgSub="{./IsExpanded}"/>
+                                                               <Label Text="{./Caption}"/>
+                                                       </HorizontalStack>
+                                               </Border>
+                                               <Container Name="Content" Visible="false"/>
+                                       </VerticalStack>
+                               </Template>
+                               <HorizontalStack Height="Fit">
+                                       <Widget Width="12" Height="10"/>
+                                       <VerticalStack Height="Fit" Name="ItemsContainer"/>
+                               </HorizontalStack>
+                       </Expandable>
+               </ListItem>
        </ItemTemplate>
 </TreeView>
index fb95e29b6dd4bf6e5e368a3179007f85379e23de..2fad338403e296149dc8733f4fc770e4fcf25553 100644 (file)
@@ -2,24 +2,24 @@
        <Widget Height="16" Background="Red"/>
 </ItemTemplate>
 <ItemTemplate DataType="System.IO.FileInfo">
-       <HorizontalStack Focusable="true"  Height="Fit">
-               <Image Margin="2" Width="16" Height="16"
-                       Path="#Crow.Icons.file.svg"/>
-                       <Label Text="{Name}" Width="Stretched"
-                               MouseEnter="{Background=RoyalBlue}"
-                               MouseLeave="{Background=Transparent}"/>
-       </HorizontalStack>
+       <ListItem Height="Fit"
+                               Selected = "{Background=${ControlHighlight}}"
+                               Unselected = "{Background=Transparent}">
+               <HorizontalStack>
+                       <Image Margin="2" Width="16" Height="16" Path="#Crow.Icons.file.svg"/>
+                       <Label Text="{Name}" Width="Stretched"/>
+               </HorizontalStack>
+       </ListItem>
 </ItemTemplate>
 <ItemTemplate DataType="System.IO.DirectoryInfo">
-       <Border Foreground="Transparent" Focusable="true" Height="Fit">
-               <HorizontalStack
-                               MouseEnter="{Background=RoyalBlue}"
-                               MouseLeave="{Background=Transparent}">
-                       <Image Margin="2" Width="16" Height="16"
-                               Path="#Crow.Icons.folder.svg"/>
+       <ListItem Height="Fit"
+                               Selected = "{Background=${ControlHighlight}}"
+                               Unselected = "{Background=Transparent}">
+               <HorizontalStack>
+                       <Image Margin="2" Width="16" Height="16" Path="#Crow.Icons.folder.svg"/>
                        <Label Text="{Name}" Width="Stretched"/>
                        <Label Text="{LastAccessTime}" />
                </HorizontalStack>
-       </Border>
+       </ListItem>
 </ItemTemplate>
 
index cab9156471a54ec5646b474f2031a233c8c3c36a..cc4445676410636ce2e28f20f066c7470f2dd3d5 100644 (file)
@@ -1,12 +1,12 @@
 <?xml version="1.0"?>
-<Container Background="{./Background}">
-       <Group>
+<Container Background="{./Background}" >
+       <Group CacheEnabled="true">
                <VerticalStack Spacing="0">
                        <Widget Height="6"/>
                        <Border  CornerRadius="{./CornerRadius}" BorderWidth="1" Margin="10" Foreground="{./Foreground}">
                                <Container Name="Content" Margin="0" MinimumSize="70,10"/>
                        </Border>
                </VerticalStack>
-               <Label Text="{./Caption}" VerticalAlignment="Top" Left="8" Background="Clear" Margin="1"/>
+               <Label Text="{./Caption}" VerticalAlignment="Top" Left="8" Background="Clear" Margin="1" CacheEnabled="true"/>
        </Group>
 </Container>
\ No newline at end of file
index b4e119dced7c7c17c002a19adc1be07d7e866cfc..d5b39dd475a6125900f316ea5e1146546a1c10da 100644 (file)
@@ -1,23 +1,25 @@
 <?xml version="1.0"?>
-<Popper Font="{./Font}" Caption="{./Caption}"  Background="{./Background}" PopDirection="{./PopDirection}"
-       Foreground = "{./Foreground}" CanPop="{./HasChildren}"
-       IsPopped="{²./IsOpened}" PopWidth="{./PopWidth}" PopHeight="{./PopHeight}">
-       <Template>
-               <CheckBox IsChecked="{²./IsPopped}" Caption="{./Caption}" Background="{./Background}" Foreground="{./Foreground}">
-                       <Template>              
-                               <Border Name="border1"
-                                               MinimumSize = "60,0"
-                                               Foreground="Transparent"
-                                               Background="{./Background}">
-                                               <Label Text="{./Caption}"
-                                                       Foreground="{./Foreground}"
-                                                       Margin="2" HorizontalAlignment="Left"
-                                                       Font="{./Font}" />
-                               </Border>
-                       </Template>             
-               </CheckBox>
-       </Template>
-       <Border Foreground="DimGrey" Width="{../PopWidth}" Height="{../PopHeight}" Background="${MenuBackground}">
-               <VerticalStack Name="ItemsContainer"/>
-       </Border>
-</Popper>
\ No newline at end of file
+<ListItem>
+       <Popper Font="{./Font}" Caption="{./Caption}"  Background="{./Background}" PopDirection="{./PopDirection}"
+               Foreground = "{./Foreground}" CanPop="{./HasChildren}"
+               IsPopped="{²./IsOpened}" PopWidth="{./PopWidth}" PopHeight="{./PopHeight}">
+               <Template>
+                       <CheckBox IsChecked="{²./IsPopped}" Caption="{./Caption}" Background="{./Background}" Foreground="{./Foreground}">
+                               <Template>              
+                                       <Border Name="border1"
+                                                       MinimumSize = "60,0"
+                                                       Foreground="Transparent"
+                                                       Background="{./Background}">
+                                                       <Label Text="{./Caption}"
+                                                               Foreground="{./Foreground}"
+                                                               Margin="2" HorizontalAlignment="Left"
+                                                               Font="{./Font}" />
+                                       </Border>
+                               </Template>             
+                       </CheckBox>
+               </Template>
+               <Border Foreground="DimGrey" Width="{../PopWidth}" Height="{../PopHeight}" Background="${MenuBackground}">
+                       <VerticalStack Name="ItemsContainer" Width="Stretched" />
+               </Border>
+       </Popper>
+</ListItem>
\ No newline at end of file
index 277ac7cd0d9eba145d26795177bbabfb7eed67db..f05bca386422554da396daee2396092963207ace 100644 (file)
@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<Border Fit="true" Foreground="DimGrey" Background="Khaki">
-       <Label Margin="3" Foreground="DimGrey" Multiline="true" Text="{Tooltip}"/>
+<Border Fit="true" Foreground="${TooltipForeground}" Background="${TooltipBackground}">
+       <Label Margin="${TooltipMargin}" Foreground="${TooltipForeground}" Multiline="true" Text="{Tooltip}"/>
 </Border>
diff --git a/Crow/src/DebugUtils/DbgEvent.cs b/Crow/src/DebugUtils/DbgEvent.cs
new file mode 100644 (file)
index 0000000..00c0d9e
--- /dev/null
@@ -0,0 +1,237 @@
+// Copyright (c) 2013-2020  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System;
+using System.Collections.Generic;
+using System.Threading;
+
+namespace Crow
+{
+       public class DbgEvent : DbgEventSource
+       {
+               public long begin, end;
+               public int threadId;
+               public DbgEvtType type;
+               public DbgEvent parentEvent;
+               public bool HasChildEvents => Events != null && Events.Count > 0;
+               public override long Duration => end - begin;
+               public virtual bool IsWidgetEvent => false;
+               public virtual bool IsLayoutEvent => false;
+
+               bool isSelected;
+               bool isExpanded;
+               Widget listContainer;
+
+               public bool IsSelected {
+                       get => isSelected;
+                       set {
+                               if (isSelected == value)
+                                       return;
+                               isSelected = value;
+                               NotifyValueChangedAuto (isSelected);
+                       }
+               }
+               public bool IsExpanded {
+                       get => isExpanded;
+                       set {
+                               if (isExpanded == value)
+                                       return;
+                               isExpanded = value;
+                               if (isExpanded && parentEvent != null)
+                                       parentEvent.IsExpanded = true;
+                               NotifyValueChangedAuto (isExpanded);
+                       }
+               }
+               public Widget ListContainer {
+                       get => listContainer;
+                       set {
+                               if (listContainer == value)
+                                       return;
+                               listContainer = value;
+                               NotifyValueChangedAuto (listContainer);
+                       }
+               }
+
+               public virtual Color Color {
+                       get {
+                               switch (type) {
+                               case DbgEvtType.Layouting:
+                                       return Colors.Yellow;
+                               case DbgEvtType.Clipping:
+                                       return Colors.DarkTurquoise;
+                               case DbgEvtType.Drawing:
+                                       return Colors.MidnightBlue;
+                               case DbgEvtType.Update:
+                                       return Colors.Grey;
+                               case DbgEvtType.IFaceLoad:
+                                       return Colors.Teal;
+                               default:
+                                       return Colors.White;
+                               }
+
+                       }
+               }
+
+               public void AddEvent (DbgEvent evt)
+               {
+                       if (Events == null)
+                               Events = new List<DbgEvent> () { evt };
+                       else
+                               Events.Add (evt);
+                       evt.parentEvent = this;
+               }
+
+               public DbgEvent () { }
+               public DbgEvent (long timeStamp, DbgEvtType evt)
+               {
+                       type = evt;
+                       begin = timeStamp;
+                       end = timeStamp;
+                       threadId = Thread.CurrentThread.ManagedThreadId;
+               }
+
+               public static DbgEvent Parse (string str)
+               {
+                       if (str == null)
+                               return null;
+                       string [] tmp = str.Trim ().Split (';');
+
+                       DbgEvtType evtType = (DbgEvtType)Enum.Parse (typeof (DbgEvtType), tmp [3]);
+
+                       if (evtType.HasFlag (DbgEvtType.Widget)) {
+                               if (evtType.HasFlag (DbgEvtType.Layouting))
+                                       return new DbgLayoutEvent () {
+                                               begin = long.Parse (tmp [0]),
+                                               end = long.Parse (tmp [1]),
+                                               threadId = int.Parse (tmp [2]),
+                                               type = evtType,
+                                               InstanceIndex = int.Parse (tmp [4]),
+                                               layouting = (LayoutingType)Enum.Parse (typeof (LayoutingType), tmp [5]),
+                                               result = evtType == DbgEvtType.GOProcessLayouting ?
+                                                       (LayoutingQueueItem.Result)Enum.Parse (typeof (LayoutingQueueItem.Result), tmp [6])
+                                                       : LayoutingQueueItem.Result.Unknown,
+                                               OldSlot = Rectangle.Parse (tmp [7]),
+                                               NewSlot = Rectangle.Parse (tmp [8]),
+                                       };
+                               return new DbgWidgetEvent () {
+                                       begin = long.Parse (tmp [0]),
+                                       end = long.Parse (tmp [1]),
+                                       threadId = int.Parse (tmp [2]),
+                                       type = evtType,
+                                       InstanceIndex = int.Parse (tmp [4]),
+                               };
+                       }
+                       return new DbgEvent () {
+                               begin = long.Parse (tmp [0]),
+                               end = long.Parse (tmp [1]),
+                               threadId = int.Parse (tmp [2]),
+                               type = evtType,
+                       };
+               }
+               public override string ToString ()
+                       => $"{begin};{end};{threadId};{type}";
+                       
+       }
+       public class DbgWidgetEvent : DbgEvent
+       {
+               public int InstanceIndex;
+               public override Color Color {
+                       get {
+                               switch (type) {
+                               case DbgEvtType.GOClassCreation:
+                                       return Colors.DarkSlateGrey;
+                               case DbgEvtType.GOInitialization:
+                                       return Colors.DarkOliveGreen;
+                               case DbgEvtType.GOClippingRegistration:
+                                       return Colors.MediumTurquoise;
+                               case DbgEvtType.GORegisterClip:
+                                       return Colors.Turquoise;
+                               case DbgEvtType.GORegisterForGraphicUpdate:
+                                       return Colors.LightPink;
+                               case DbgEvtType.GOEnqueueForRepaint:
+                                       return Colors.LightSalmon;
+                               case DbgEvtType.GONewDataSource:
+                                       return Colors.MediumVioletRed;
+                               case DbgEvtType.GODraw:
+                                       return Colors.SteelBlue;
+                               case DbgEvtType.GORecreateCache:
+                                       return Colors.CornflowerBlue;
+                               case DbgEvtType.GOUpdateCache:
+                                       return Colors.SteelBlue;
+                               case DbgEvtType.GOPaint:
+                                       return Colors.RoyalBlue;
+                               case DbgEvtType.GOLockUpdate:
+                                       return Colors.SaddleBrown;
+                               case DbgEvtType.GOLockClipping:
+                                       return Colors.Sienna;
+                               case DbgEvtType.GOLockRender:
+                                       return Colors.BurlyWood;
+                               case DbgEvtType.GOLockLayouting:
+                                       return Colors.GoldenRod;
+                               case DbgEvtType.TGCancelLoadingThread:
+                                       return Colors.Maroon;
+                               default:
+                                       return Colors.Crimson;
+                               }
+                               if (type.HasFlag (DbgEvtType.Lock))
+                                       return Colors.DarkMagenta;
+                               return Colors.White;
+                       }
+               }
+               public override bool IsWidgetEvent => true;
+               public DbgWidgetEvent () { }
+#if DEBUG_LOG
+               public DbgWidgetEvent (long timeStamp, DbgEvtType evt, Widget w) : base (timeStamp, evt)
+               {
+                       InstanceIndex = w.instanceIndex;
+               }
+#endif
+               public override string ToString ()
+                       => $"{base.ToString ()};{InstanceIndex}";
+       }
+       public class DbgLayoutEvent : DbgWidgetEvent
+       {
+               public LayoutingType layouting;
+               public LayoutingQueueItem.Result result;
+               public Rectangle OldSlot, NewSlot;
+               public override Color Color {
+                       get {
+                               if (type == DbgEvtType.GORegisterLayouting)
+                                       return Colors.GreenYellow;
+                               if (type == DbgEvtType.GOProcessLayoutingWithNoParent)
+                                       return Colors.DarkRed;
+                               switch (result) {
+                               case LayoutingQueueItem.Result.Success:
+                                       return Colors.Green;
+                               case LayoutingQueueItem.Result.Deleted:
+                                       return Colors.Red;
+                               case LayoutingQueueItem.Result.Discarded:
+                                       return Colors.OrangeRed;
+                               default:
+                                       return Colors.Orange;
+                               }
+                       }
+               }
+               public override bool IsLayoutEvent => true;
+               public DbgLayoutEvent () { }
+#if DEBUG_LOG
+               public DbgLayoutEvent (long timeStamp, DbgEvtType evt, LayoutingQueueItem lqi) :
+                       base (timeStamp, evt, lqi.graphicObject)
+               {
+                       layouting = lqi.LayoutType;
+                       result = lqi.result;
+                       OldSlot = lqi.Slot;
+                       NewSlot = lqi.NewSlot;
+               }
+               public void SetLQI (LayoutingQueueItem lqi)
+               {
+                       layouting = lqi.LayoutType;
+                       result = lqi.result;
+                       OldSlot = lqi.Slot;
+                       NewSlot = lqi.NewSlot;
+               }
+#endif
+               public override string ToString ()
+                       => $"{base.ToString ()};{layouting};{result};{OldSlot};{NewSlot}";
+       }
+}
\ No newline at end of file
diff --git a/Crow/src/DebugUtils/DbgEventSource.cs b/Crow/src/DebugUtils/DbgEventSource.cs
new file mode 100644 (file)
index 0000000..409f214
--- /dev/null
@@ -0,0 +1,34 @@
+// Copyright (c) 2013-2020  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+
+namespace Crow
+{
+       //base class for both events and widgetRecord having an event list
+       public abstract class DbgEventSource : IValueChange
+       {
+               public event EventHandler<ValueChangeEventArgs> ValueChanged;
+               public virtual void NotifyValueChanged (string MemberName, object _value)
+                       => ValueChanged.Raise (this, new ValueChangeEventArgs (MemberName, _value));
+               public void NotifyValueChangedAuto (object _value, [CallerMemberName] string caller = null)
+                       => NotifyValueChanged (caller, _value);
+
+               //flattened event list of this widget
+               public List<DbgEvent> Events;
+               public virtual long Duration {
+                       get {
+                               if (Events == null)
+                                       return 0;
+                               long tot = 0;
+                               foreach (DbgEvent e in Events) 
+                                       tot += e.Duration;
+                               return tot;
+                       }
+               }
+
+
+       }
+}
\ No newline at end of file
diff --git a/Crow/src/DebugUtils/DbgEventTypeColors.cs b/Crow/src/DebugUtils/DbgEventTypeColors.cs
new file mode 100644 (file)
index 0000000..d287190
--- /dev/null
@@ -0,0 +1,54 @@
+// Copyright (c) 2013-2020  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+
+using System;
+using Crow.Cairo;
+using System.Linq;
+
+namespace Crow
+{
+       /*public class DbgEventTypeColors : Widget
+       {
+               protected override void onDraw (Context gr)
+               {
+                       base.onDraw (gr);
+
+                       gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight);
+                       gr.SetFontSize (Font.Size);
+                       gr.FontOptions = Interface.FontRenderingOptions;
+                       gr.Antialias = Interface.Antialias;
+
+                       DbgEvtType[] types = DbgLogViewer.colors.Keys.ToArray ();
+                       Color[] colors = DbgLogViewer.colors.Values.ToArray ();
+
+                       Rectangle r = ClientRectangle;
+                       FontExtents fe = gr.FontExtents;
+
+                       double penY = fe.Height + r.Top;
+                       double penX = (double)r.Left;
+
+
+                       for (int i = 0; i < types.Length; i++) {
+                               string n = types [i].ToString();
+                               Color c = colors[i];
+                               Foreground.SetAsSource (gr);
+
+                               gr.MoveTo (penX + 25.0, penY - fe.Descent);
+                               gr.ShowText (n);
+
+                               Rectangle rc = new Rectangle((int)penX, (int)(penY - fe.Height), 20, (int)fe.Height);
+                               rc.Inflate (-2);
+                               gr.Rectangle (rc);
+                               gr.StrokePreserve ();
+
+                               gr.SetSource (c);
+                               gr.Fill ();
+
+                               penY += fe.Height;
+
+                       }
+               }
+       }*/
+}
+
diff --git a/Crow/src/DebugUtils/DbgEventWidget (copie).cs b/Crow/src/DebugUtils/DbgEventWidget (copie).cs
new file mode 100644 (file)
index 0000000..c7892a9
--- /dev/null
@@ -0,0 +1,87 @@
+// Copyright (c) 2013-2020  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System;
+using System.Collections.Generic;
+using Crow.Cairo;
+#if DEBUG_LOG
+namespace Crow
+{
+       /*
+       /// <summary>
+       /// Compressed event list without gaps
+       /// </summary>
+       public class DbgEventListWidget : Widget
+       {
+               public DbgEventListWidget (){}
+
+               DbgWidgetRecord wRec;
+               public DbgWidgetRecord WidgetRecord {
+                       get => wRec;
+                       set {
+                               if (wRec == value)
+                                       return;
+                               wRec = value;
+                               pixelPerTick = WidgetRecord == null ? 0 : (double)ClientRectangle.Width / WidgetRecord.Duration;
+                               NotifyValueChangedAuto (wRec);
+                               RegisterForRedraw ();
+                       }
+               }
+
+               double pixelPerTick;
+
+               public override void OnLayoutChanges (LayoutingType layoutType)
+               {
+                       if (layoutType == LayoutingType.Width)
+                               pixelPerTick = WidgetRecord == null ? 0 : (double)ClientRectangle.Width / WidgetRecord.Duration;
+                       base.OnLayoutChanges (layoutType);
+               }
+
+               protected override void onDraw (Context gr)
+               {
+                       if (WidgetRecord == null || WidgetRecord.Duration == 0) {
+                               base.onDraw (gr);
+                               return;
+                       }
+
+                       Rectangle cb = ClientRectangle;
+
+                       drawEvent (gr, cb.Height, WidgetRecord);
+               }
+               void drawEvent (Context ctx, int h, DbgEvent dbge)
+               {
+                       double w = dbge.Duration == 0 ? 1.0 : dbge.Duration * pixelPerTick;
+                       double x = (dbge.begin - WidgetRecord.begin) * pixelPerTick;
+
+                       ctx.Rectangle (x, Margin, w, h);
+                       ctx.SetSource (dbge.Color);
+                       ctx.Fill ();
+                       if (dbge.Events == null)
+                               return;
+                       foreach (DbgEvent e in dbge.Events)
+                               drawEvent (ctx, h, e);
+               }
+
+               public override void onMouseMove (object sender, MouseMoveEventArgs e)
+               {
+                       if (WidgetRecord != null) {
+                               Point m = ScreenPointToLocal (e.Position);
+                               long curTick = (long)(m.X / pixelPerTick) + WidgetRecord.begin;
+                               NotifyValueChanged ("HoverEvent", hoverEvent (WidgetRecord, curTick));
+                               e.Handled = true;
+                       }
+                       base.onMouseMove (sender, e);
+               }
+
+               DbgEvent hoverEvent (DbgEvent hevt, long curTick){
+                       if (hevt.Events != null) {
+                               foreach (DbgEvent e in hevt.Events) {
+                                       if (curTick >= e.begin && curTick <= e.end)
+                                               return hoverEvent (e, curTick);
+                               }
+                       }
+                       return hevt;
+               }
+       }*/
+}
+#endif
\ No newline at end of file
diff --git a/Crow/src/DebugUtils/DbgWidgetRecord.cs b/Crow/src/DebugUtils/DbgWidgetRecord.cs
new file mode 100644 (file)
index 0000000..8aaf1c4
--- /dev/null
@@ -0,0 +1,50 @@
+// Copyright (c) 2013-2020  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Crow
+{
+       /// <summary>
+       /// Recorded Widget instance data.
+       /// </summary>
+       public class DbgWidgetRecord : DbgEventSource
+       {
+               public DbgWidgetRecord ()
+               {
+                        Events = new List<DbgEvent> ();
+               }
+               public int listIndex;//prevent doing an IndexOf on list for each event to know y pos on screen
+                                                        //public int instanceNum;//class instantiation order, used to bind events to objs
+               public string name;
+               //0 is the main graphic tree, for other obj tree not added to main tree, it range from 1->n
+               //useful to track events for obj shown later, not on start
+               public int treeIndex;
+               public int yIndex;//index in parenting, the whole main graphic tree is one continuous suite
+               public int xLevel;//depth
+               public String Width;
+               public String Height;
+
+               public static DbgWidgetRecord Parse (string str)
+               {
+                       DbgWidgetRecord g = new DbgWidgetRecord ();
+                       if (str == null)
+                               return null;
+                       string [] tmp = str.Trim ().Split (';');
+                       g.name = tmp [0];
+                       g.yIndex = int.Parse (tmp [1]);
+                       g.xLevel = int.Parse (tmp [2]);
+                       g.Width = tmp [3];
+                       g.Height = tmp [4];
+                       return g;
+               }
+
+               public List<DbgWidgetEvent> RootEvents => Events.OfType<DbgWidgetEvent> ().Where (
+                       e => (e.parentEvent != null && !e.parentEvent.type.HasFlag (DbgEvtType.Widget)) ||
+                       (e.parentEvent is DbgWidgetEvent w && w.InstanceIndex != e.InstanceIndex)).ToList();
+
+               public override string ToString () => $"{name}";
+       }
+}
\ No newline at end of file
diff --git a/Crow/src/DebugUtils/DebugLogger.cs b/Crow/src/DebugUtils/DebugLogger.cs
new file mode 100644 (file)
index 0000000..ecb9dbd
--- /dev/null
@@ -0,0 +1,263 @@
+// Copyright (c) 2013-2020  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+
+using System;
+using Crow.Cairo;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Threading;
+
+
+namespace Crow
+{
+       [Flags]
+       public enum DbgEvtType {
+               ////9 nth bit set for iface event
+               IFace                                                   = 0x10000,
+               Focus                                                   = 0x20000,
+               Override                                                = 0x40000,
+               Widget                                                  = 0x00100,
+               //GOLayouting                                   = 0x00200,
+               //Drawing                                               = 0x00400,
+               Lock                                                    = 0x00800,
+               Layouting                                               = IFace | 0x01000,
+               Clipping                                                = IFace | 0x02000,
+               Drawing                                                 = IFace | 0x04000,
+               Update                                                  = IFace | 0x08000,
+               IFaceLoad                                               = IFace | 0x05,
+               IFaceInit                                               = IFace | 0x06,
+               CreateITor                                              = IFace | 0x07,
+
+               HoverWidget                                             = Focus | 0x01,
+               FocusedWidget                                   = Focus | 0x02,
+               ActiveWidget                                    = Focus | 0x03,
+
+               //10 nth bit set for graphic obj
+               TemplatedGroup                                  = 0x1000,
+               Dispose                                                 = 0x2000,
+               Warning                                                 = 0x4000,
+               Error                                                   = 0x8000,
+               GOClassCreation                                 = Widget | 0x01,
+               GOInitialization                                = Widget | 0x02,
+               GORegisterForGraphicUpdate              = Widget | 0x03,
+               GOEnqueueForRepaint                             = Widget | 0x04,
+               GONewDataSource                                 = Widget | 0x05,
+               GONewParent                                             = Widget | 0x06,
+               GONewLogicalParent                              = Widget | 0x07,
+               GOAddChild                                              = Widget | 0x08,
+
+               GOSearchLargestChild                    = Widget | 0x09,
+               GOSearchTallestChild                    = Widget | 0x10,
+               GORegisterForRedraw                             = Widget | 0x11,
+
+               AlreadyDisposed                                 = Dispose | Widget | Error | 0x01,
+               DisposedByGC                                    = Dispose | Widget | Error | 0x02,
+               Disposing                                               = Dispose | Widget | 0x01,
+
+               GOClippingRegistration                  = Clipping | Widget | 0x01,
+               GORegisterClip                                  = Clipping | Widget | 0x02,
+               GORegisterLayouting                     = Layouting | Widget | 0x01,
+               GOProcessLayouting                              = Layouting | Widget | 0x02,
+               GOProcessLayoutingWithNoParent  = Layouting | Widget | Warning | 0x01,
+               GOMeasure                                               = Widget | 0x03,
+               GODraw                                                  = Drawing | Widget | 0x01,
+               GORecreateCache                                 = Drawing | Widget | 0x02,
+               GOUpdateCache                                   = Drawing | Widget | 0x03,
+               GOPaint                                                 = Drawing | Widget | 0x04,
+
+               GOLockUpdate                                    = Widget | Lock | 0x01,
+               GOLockClipping                                  = Widget | Lock | 0x02,
+               GOLockRender                                    = Widget | Lock | 0x03,
+               GOLockLayouting                                 = Widget | Lock | 0x04,
+
+               TGLoadingThread                                 = Widget | TemplatedGroup | 0x01,
+               TGCancelLoadingThread                   = Widget | TemplatedGroup | 0x02,
+
+               All = 0x0FFFFFFF
+       }
+#if DEBUG_LOG
+       public static class DbgLogger
+       {
+               public static DbgEvtType IncludeEvents = DbgEvtType.All;
+               public static DbgEvtType DiscardEvents = DbgEvtType.Focus;
+
+               static bool logevt (DbgEvtType evtType)
+                       => (evtType & DiscardEvents) == 0 && (evtType & IncludeEvents) != 0;
+
+
+               static object logMutex = new object ();
+               static Stopwatch chrono = Stopwatch.StartNew ();
+               static List<DbgEvent> events = new List<DbgEvent> ();
+               //started events per thread
+               static Dictionary<int, Stack<DbgEvent>> startedEvents = new Dictionary<int, Stack<DbgEvent>> ();
+               //helper for fetching current event list to add next event to while recording
+               static List<DbgEvent> curEventList {
+                       get {
+                               if (startedEvents.ContainsKey (Thread.CurrentThread.ManagedThreadId)) {
+                                       if (startedEvents [Thread.CurrentThread.ManagedThreadId].Count == 0)
+                                               return events;
+                                       DbgEvent e = startedEvents [Thread.CurrentThread.ManagedThreadId].Peek ();
+                                       if (e.Events == null) 
+                                               e.Events = new List<DbgEvent> ();
+                                       return e.Events;
+                               }
+                               return events;
+                       }
+               }
+
+               public static DbgEvent StartEvent (DbgEvtType evtType, params object[] data)
+               {
+                       if (!logevt (evtType))
+                               return null;
+                       lock (logMutex) {
+                               chrono.Stop ();
+                               DbgEvent evt = addEventInternal (evtType, data);
+                               if (!startedEvents.ContainsKey (Thread.CurrentThread.ManagedThreadId))
+                                       startedEvents [Thread.CurrentThread.ManagedThreadId] = new Stack<DbgEvent> ();
+                               startedEvents [Thread.CurrentThread.ManagedThreadId].Push (evt);
+                               chrono.Start ();
+                               return evt;
+                       }
+               }
+               public static DbgEvent EndEvent (DbgEvtType evtType, bool discardIfNoChildEvents = false)
+               {
+                       if (!logevt (evtType))
+                               return null;
+
+                       lock (logMutex) {
+                               chrono.Stop ();
+                               if (!startedEvents.ContainsKey (Thread.CurrentThread.ManagedThreadId))
+                                       throw new Exception ("Current thread has no event started");
+                               DbgEvent e = startedEvents [Thread.CurrentThread.ManagedThreadId].Pop ();
+                               if (e.type != evtType)
+                                       throw new Exception ($"Begin/end event logging mismatch: {e.type}/{evtType}");
+                               if (discardIfNoChildEvents && (e.Events == null || e.Events.Count == 0))
+                                       curEventList.Remove (e);
+                               else
+                                       e.end = chrono.ElapsedTicks;
+                               chrono.Start ();
+                               return e;
+                       }
+               }
+               /// <summary>
+               /// End event by reference to cancel unended events on failure
+               /// </summary>
+               public static DbgEvent EndEvent (DbgEvtType evtType, DbgEvent evt)
+               {
+                       if (!logevt (evtType))
+                               return null;
+
+                       lock (logMutex) {
+                               chrono.Stop ();
+                               if (!startedEvents.ContainsKey (Thread.CurrentThread.ManagedThreadId))
+                                       throw new Exception ("Current thread has no event started");
+                               DbgEvent e = startedEvents [Thread.CurrentThread.ManagedThreadId].Pop ();
+                               while (e != evt)
+                                       e = startedEvents [Thread.CurrentThread.ManagedThreadId].Pop ();
+                               e.end = chrono.ElapsedTicks;
+                               chrono.Start ();
+                               return e;
+                       }
+               }
+
+               public static DbgEvent AddEvent (DbgEvtType evtType, params object [] data) { 
+                       if (!logevt (evtType))
+                               return null;
+
+                       lock (logMutex) {
+                               chrono.Stop ();
+                               DbgEvent evt = addEventInternal (evtType, data);
+                               chrono.Start ();
+                               return evt;
+                       }
+               }
+
+               static DbgEvent addEventInternal (DbgEvtType evtType, params object [] data)
+               {
+                       DbgEvent evt = null;
+                       if (data == null || data.Length == 0)
+                               evt = new DbgEvent (chrono.ElapsedTicks, evtType);
+                       else if (data [0] is Widget w)
+                               evt = new DbgWidgetEvent (chrono.ElapsedTicks, evtType, w);
+                       else if (data [0] is LayoutingQueueItem lqi)
+                               evt = new DbgLayoutEvent (chrono.ElapsedTicks, evtType, lqi);
+                       else
+                               evt = new DbgEvent (chrono.ElapsedTicks, evtType);
+
+                       curEventList.Add (evt);
+                       return evt;
+               }
+
+               static void parseTree (Widget go, int level = 0, int y = 1) {
+                       if (go == null)
+                               return;
+
+                       go.yIndex = y;
+                       go.xLevel = level;
+
+                       Group gr = go as Group;
+                       if (gr != null) {
+                               foreach (Widget g in gr.Children) 
+                                       parseTree (g, level + 1, y + 1);
+
+                       } else {
+                               PrivateContainer pc = go as PrivateContainer;
+                               if (pc != null)
+                                       parseTree (pc.getTemplateRoot, level + 1, y + 1);                               
+                       }
+               }
+               /// <summary>
+               /// Clear all recorded events from logger.
+               /// </summary>
+               public static void Reset ()
+               {
+                       lock (logMutex) {
+                               startedEvents.Clear ();
+                               events.Clear ();
+                               chrono.Restart ();
+                       }
+               }
+               /// <summary>
+               /// Save recorded events to disk
+               /// </summary>
+               /// <param name="iface">Iface.</param>
+               public static void save(Interface iface, string dbgLogFilePath = "debug.log") {
+                       lock (logMutex) {
+
+                               foreach (Widget go in iface.GraphicTree)
+                                       parseTree (go);
+
+                               using (StreamWriter s = new StreamWriter (dbgLogFilePath)) {
+                                       s.WriteLine ("[GraphicObjects]");
+                                       lock (Widget.GraphicObjects) {
+                                               //Widget.GraphicObjects = Widget.GraphicObjects.OrderBy (o => o.yIndex).ToList ();
+                                               for (int i = 0; i < Widget.GraphicObjects.Count; i++) {
+                                                       Widget g = Widget.GraphicObjects [i];
+                                                       s.WriteLine ($"{g.GetType ().Name};{g.yIndex};{g.xLevel};{g.Width};{g.Height}");
+                                               }
+                                       }
+                                       s.WriteLine ("[Events]");
+                                       saveEventList (s, events);
+                               }
+                       }
+               }
+
+               static void saveEventList (StreamWriter s, List<DbgEvent> evts, int level = 0)
+               {
+                       foreach (DbgEvent e in evts) {
+                               if (e == null)
+                                       continue;
+                               s.WriteLine (new string ('\t', level) + e);
+                               if (e.Events != null)
+                                       saveEventList (s, e.Events, level + 1);
+                       }
+               }
+
+       }
+#endif
+}
+
+
diff --git a/Crow/src/DebugUtils/Utils.cs b/Crow/src/DebugUtils/Utils.cs
new file mode 100644 (file)
index 0000000..ce3d5c3
--- /dev/null
@@ -0,0 +1,29 @@
+// Copyright (c) 2013-2020  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+
+namespace Crow
+{
+       public static class Logger
+       {
+               [Flags]
+               public enum LogType
+               {
+                       Info            = 1,
+                       Warning         = 2,
+                       Error           = 4,
+               }
+
+               public static LogType CurrentLogLevel = LogType.Error;
+
+               static Stopwatch timer = Stopwatch.StartNew ();
+
+
+               public static void LOG(string message = null, [CallerMemberName] string caller = null) {
+                       Console.WriteLine ($"{timer.ElapsedMilliseconds, 10} {caller}: {message}");
+               }
+       }
+}
index 988a69757064aa4999a93e7dee2509a27482002b..a2097f124755997991f87c8a55dc288ff145d70e 100644 (file)
@@ -1,28 +1,6 @@
-//
-// SelectionChangeEventArgs.cs
+// Copyright (c) 2013-2020  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
 //
-// Author:
-//       Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
-//
-// Copyright (c) 2013-2017 Jean-Philippe Bruyère
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
 
 using System;
 
@@ -32,7 +10,6 @@ namespace Crow
        {               
                public object NewValue;
 
-
                public SelectionChangeEventArgs (object _newValue) : base()
                {
                        NewValue = _newValue;
diff --git a/Crow/src/EventArgs/TreeExpandEventArg.cs b/Crow/src/EventArgs/TreeExpandEventArg.cs
new file mode 100644 (file)
index 0000000..7641aa5
--- /dev/null
@@ -0,0 +1,18 @@
+// Copyright (c) 2013-2020  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System;
+namespace Crow
+{
+       public class TreeExpandEventArg : EventArgs
+       {
+               /// <summary>
+               /// Source of the expand/collapse event
+               /// </summary>
+               public Expandable SourceWidget;
+               public TreeExpandEventArg (Expandable sourceWidget)
+               {
+
+               }
+       }
+}
index 3fb1d452c38614653cf0f06798b0e43365e663da..efccd399adb88dbc10edfb520ef96bb97d51c39c 100644 (file)
@@ -1,28 +1,6 @@
-//
-// SolidColor.cs
+// Copyright (c) 2013-2020  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
 //
-// Author:
-//       Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
-//
-// Copyright (c) 2013-2017 Jean-Philippe Bruyère
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
 
 using System;
 using System.Collections.Generic;
index 2f9aa94a0a3d3d576eb7d04128596d91f8377a87..7bf27b285d844a9e1bb5a7a3e205643fee8c0fd9 100644 (file)
@@ -1563,7 +1563,15 @@ namespace Crow.IML {
                        if (t != null) {
                                knownGOTypes.Add (typeName, t);
                                return t;
-                       }                       
+                       }
+
+                       foreach (Type expT in Assembly.GetEntryAssembly ().GetExportedTypes ()) {
+                               if (expT.Name != typeName)
+                                       continue;
+                               knownGOTypes.Add (typeName, expT);
+                               return expT;
+                       }
+
                        foreach (Assembly a in Interface.crowAssemblies) {
                                foreach (Type expT in a.GetExportedTypes ()) {
                                        if (expT.Name != typeName)
index 87e8a1f1d0dee776b0214416d0c5e637dde27b3b..520899fd931ada34b53cbc7abf2b665fae5fe105 100644 (file)
@@ -1293,7 +1293,6 @@ namespace Crow
 
                protected void initTooltip ()
                {
-                       ToolTipContainer = CreateInstance ("#Crow.Tooltip.template");
                        Thread t = new Thread (toolTipThreadFunc);
                        t.IsBackground = true;
                        t.Start ();
@@ -1306,6 +1305,12 @@ namespace Crow
                                                Widget g = _hoverWidget;
                                                while (g != null) {
                                                        if (!string.IsNullOrEmpty (g.Tooltip)) {
+                                                               if (g.Tooltip.StartsWith("#", StringComparison.Ordinal)) {
+                                                                       //custom tooltip container
+                                                                       ToolTipContainer = CreateInstance (g.Tooltip);
+                                                               } else
+                                                                       ToolTipContainer = CreateInstance ("#Crow.Tooltip.template");
+                                                               ToolTipContainer.LayoutChanged += ToolTipContainer_LayoutChanged;
                                                                AddWidget (ToolTipContainer);
                                                                ToolTipContainer.DataSource = g;
                                                                ToolTipContainer.Top = MousePosition.Y + 10;
@@ -1324,12 +1329,42 @@ namespace Crow
                void resetTooltip ()
                {
                        if (tooltipVisible) {
-                               //ToolTipContainer.DataSource = null;
+                               ToolTipContainer.LayoutChanged -= ToolTipContainer_LayoutChanged;
+                               ToolTipContainer.DataSource = null;
                                RemoveWidget (ToolTipContainer);
                                tooltipVisible = false;
+                               ToolTipContainer.Dispose ();
+                               ToolTipContainer = null;
                        }
                        tooltipTimer.Restart ();
                }
+               void ToolTipContainer_LayoutChanged (object sender, LayoutingEventArgs e)
+               {
+                       Widget ttc = sender as Widget;
+                       //tooltip container datasource is the widget triggering the tooltip
+                       Rectangle r = ScreenCoordinates ((ttc.DataSource as Widget).Slot);
+
+                       if (e.LayoutType == LayoutingType.X) {
+                                       if (ttc.Slot.Right > clientRectangle.Right)
+                                               ttc.Left = clientRectangle.Right - ttc.Slot.Width;
+                       }/* else if (e.LayoutType == LayoutingType.Y) {
+                               if (ttc.Slot.Height < tc.ClientRectangle.Height) {
+                                       if (PopDirection.HasFlag (Alignment.Bottom)) {
+                                               if (r.Bottom + ttc.Slot.Height > tc.ClientRectangle.Bottom)
+                                                       ttc.Top = r.Top - ttc.Slot.Height;
+                                               else
+                                                       ttc.Top = r.Bottom;
+                                       } else if (PopDirection.HasFlag (Alignment.Top)) {
+                                               if (r.Top - ttc.Slot.Height < tc.ClientRectangle.Top)
+                                                       ttc.Top = r.Bottom;
+                                               else
+                                                       ttc.Top = r.Top - ttc.Slot.Height;
+                                       } else
+                                               ttc.Top = r.Top;
+                               } else
+                                       ttc.Top = 0;
+                       }*/
+               }
                #endregion
 
                #region Contextual menu
index 4fc22d1a2621f046a40a2aee14920f9b0e8d9098..86d3e9e70b90430548980716e20cdf0a00d9f624 100644 (file)
@@ -103,6 +103,7 @@ namespace Crow
                        }
                        #if DEBUG_LOG
                        DbgLogger.StartEvent (DbgEvtType.GOProcessLayouting, this);
+                       Slot = graphicObject.Slot;
                        #endif
                        LayoutingTries++;
                        if (!Layoutable.UpdateLayout (LayoutType)) {
@@ -131,7 +132,6 @@ namespace Crow
                        else{
                                result = Result.Success;
                        }
-                       Slot = graphicObject.LastSlots;
                        NewSlot = graphicObject.Slot;
                        (DbgLogger.EndEvent (DbgEvtType.GOProcessLayouting) as DbgLayoutEvent).SetLQI (this);
                        #endif
index e0b3c72a6d8b09f79cef8c64e2474439ec4404f5..4cb0a2b4c5c714692b80579b0f1b059550d69402 100644 (file)
@@ -1,32 +1,9 @@
-//
-// Expandable.cs
+// Copyright (c) 2013-2020  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
 //
-// Author:
-//       Jean-Philippe Bruyère <jp.bruyere@hotmail.com>
-//
-// Copyright (c) 2013-2017 Jean-Philippe Bruyère
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
 
 using System;
 using System.ComponentModel;
-using System.Xml.Serialization;
 
 namespace Crow
 {
index 419479b579393f1b35a34fbbab887281a5098236..df452bee84cdebb6ee347a7ab11556fc0b3983e2 100644 (file)
@@ -81,7 +81,7 @@ namespace Crow
 
                        childrenRWLock.ExitWriteLock();
 
-                       g.RegisteredLayoutings = LayoutingType.None;
+                       //g.RegisteredLayoutings = LayoutingType.None;
                        g.LayoutChanged += OnChildLayoutChanges;
                        g.RegisterForLayouting (LayoutingType.Sizing | LayoutingType.ArrangeChildren);
                }
@@ -214,6 +214,24 @@ namespace Crow
 
                        return tmp;
                }
+               public override Widget FindByType<T> ()
+               {
+                       if (this is T)
+                               return this;
+                       Widget tmp = null;
+
+                       childrenRWLock.EnterReadLock ();
+
+                       foreach (Widget w in Children) {
+                               tmp = w.FindByType<T> ();
+                               if (tmp != null)
+                                       break;
+                       }
+
+                       childrenRWLock.ExitReadLock ();
+
+                       return tmp;
+               }
                public override bool Contains (Widget goToFind)
                {
                        foreach (Widget w in Children) {
diff --git a/Crow/src/Widgets/ListItem.cs b/Crow/src/Widgets/ListItem.cs
new file mode 100644 (file)
index 0000000..2ed4929
--- /dev/null
@@ -0,0 +1,42 @@
+// Copyright (c) 2013-2020  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System;
+using System.ComponentModel;
+
+namespace Crow
+{
+       /// <summary>
+       /// Top container to use as ItemTemplate's root for TemplatedGroups (lists, treeviews, ...) that add selection
+       /// status and events
+       /// </summary>
+       public class ListItem : Container
+       {
+               #region CTOR
+               public ListItem (){}
+               public ListItem (Interface iface, string style = null) : base (iface, style) { }
+               #endregion
+
+               bool isSelected;
+
+               public event EventHandler Selected;
+               public event EventHandler Unselected;
+
+               [DefaultValue (false)]
+               public virtual bool IsSelected {
+                       get { return isSelected; }
+                       set {
+                               if (isSelected == value)
+                                       return;
+                               isSelected = value;
+
+                               if (isSelected)
+                                       Selected.Raise (this, null);
+                               else
+                                       Unselected.Raise (this, null);
+
+                               NotifyValueChangedAuto (isSelected);
+                       }
+               }
+       }
+}
index 4972ab962dd5022ecc490682f3aae6602a2eb8b8..c8e3dfdb1b010c2c6749347b22496252b09a8666 100644 (file)
@@ -81,6 +81,13 @@ namespace Crow
 
                        return child == null ? null : child.FindByName (nameToFind);
                }
+               public override Widget FindByType<T> ()
+               {
+                       if (this is T)
+                               return this;
+
+                       return child == null ? null : child.FindByType<T> ();
+               }
                public override bool Contains (Widget goToFind)
                {
                        return child == goToFind ? true : 
index 31039f45e571b27f60633f2226223bd688b87c21..b607ff918f8820c3808492322ceb6a83225fa12b 100644 (file)
@@ -60,6 +60,13 @@ namespace Crow
 
                        return Content == null ? null : Content.FindByName (nameToFind);
                }
+               public override Widget FindByType<T> ()
+               {
+                       if (this is T)
+                               return this;
+
+                       return Content == null ? null : Content.FindByType<T> ();
+               }
                public override bool Contains (Widget goToFind)
                {
                        if (Content == goToFind)
index b20813126ffbdc505ff7e6246704e8c4480741b5..e700513e4963367ec40c62d5c578f1c6297a89bf 100644 (file)
@@ -79,6 +79,7 @@ namespace Crow
                /// <returns>widget identified by name, or null if not found</returns>
                /// <param name="nameToFind">widget's name to find</param>
                public override Widget FindByName (string nameToFind) => nameToFind == this.Name ? this : null;
+               public override Widget FindByType<T> () => this is TemplatedControl ? this : null;
                public Widget FindByNameInTemplate (string nameToFind) => child?.FindByName (nameToFind);
                /// <summary>
                ///onDraw is overrided to prevent default drawing of background, template top container
index 416be3115459bd307b88dff50cfc9e7286589078..f4e63f4379187ede19007438d1bbf601ae1fc02d 100644 (file)
@@ -8,6 +8,7 @@ using System.Collections.Generic;
 using System.ComponentModel;
 using System.Linq;
 using System.Threading;
+using Crow.Cairo;
 using Crow.IML;
 
 namespace Crow {
@@ -41,13 +42,14 @@ namespace Crow {
 
                #region events
                public event EventHandler<SelectionChangeEventArgs> SelectedItemChanged;
+               /// <summary>
+               /// raised when root widget of item template is a 'ListItem' and this item is selected.
+               /// </summary>
+               public event EventHandler<SelectionChangeEventArgs> SelectedItemContainerChanged;
                public event EventHandler Loaded;
                #endregion
 
                IEnumerable data;
-               int _selectedIndex = -1;
-               Color selBackground, selForeground;
-               bool selColoring;
 
                int itemPerPage = 50;
                CrowThread loadingThread = null;
@@ -121,56 +123,23 @@ namespace Crow {
                                : items.Children;
                        }
                }
-               /// <summary>
-               /// Enable SelectionBackground and SelectionForeground color for selected item
-               /// </summary>
-               [DefaultValue (false)]
-               public bool SelectionColoring {
-                       get => selColoring;
-                       set {
-                               if (selColoring == value)
-                                       return;
-                               selColoring = value;
-                               NotifyValueChangedAuto (selColoring);
-                       }
-               }
-               [DefaultValue(-1)]public virtual int SelectedIndex{
-                       get { return _selectedIndex; }
-                       set {
-                               if (value == _selectedIndex)
-                                       return;
 
-                               if (selColoring && _selectedIndex >= 0 && Items.Count > _selectedIndex) {
-                                       Items[_selectedIndex].Foreground = Colors.Transparent;
-                                       Items[_selectedIndex].Background = Colors.Transparent;
-                               }
-
-                               _selectedIndex = value;
+               object selectedItem;
+               Widget selectedItemContainer = null;
 
-                               if (selColoring && _selectedIndex >= 0 && Items.Count > _selectedIndex) {
-                                       Items[_selectedIndex].Foreground = SelectionForeground;
-                                       Items[_selectedIndex].Background = SelectionBackground;
-                               }
-
-                               NotifyValueChangedAuto (_selectedIndex);
-                               NotifyValueChanged ("SelectedItem", SelectedItem);
-                               SelectedItemChanged.Raise (this, new SelectionChangeEventArgs (SelectedItem));
-                       }
-               }
                [XmlIgnore]public virtual object SelectedItem{
-                       get { return data == null ? null : _selectedIndex < 0 ? data.GetDefaultValue() : data is IList tmp ? tmp[_selectedIndex] : null; }
+                       get => selectedItem;
                        set {
-                               if (data == null) {
-                                       SelectedIndex = -1;
-                                       return;
-                               }
-                               //TODO:double check if value type will be notified to binding sys
-                               if (value == SelectedItem)
+                               if (SelectedItem == value)
                                        return;
-                               
-                               SelectedIndex = (int)((IList)data)?.IndexOf (value);
+                                       
+                               selectedItem = value;
+
+                               NotifyValueChanged ("SelectedItem", SelectedItem);
+                               SelectedItemChanged.Raise (this, new SelectionChangeEventArgs (SelectedItem));
                        }
                }
+
                [XmlIgnore]public bool HasItems {
                        get { return Items.Count > 0; }
                }
@@ -210,9 +179,8 @@ namespace Crow {
                                loadingThread.Finished += (object sender, EventArgs e) => (sender as TemplatedGroup).Loaded.Raise (sender, e);
                                loadingThread.Start ();
 
-                               NotifyValueChanged ("SelectedIndex", _selectedIndex);
-                               NotifyValueChanged ("SelectedItem", SelectedItem);
-                               SelectedItemChanged.Raise (this, new SelectionChangeEventArgs (SelectedItem));
+                               //NotifyValueChanged ("SelectedIndex", _selectedIndex);
+                               //NotifyValueChanged ("SelectedItem", SelectedItem);
                                NotifyValueChanged ("HasItems", HasItems);
                        }
                }
@@ -245,28 +213,6 @@ namespace Crow {
 
                }
 
-               [DefaultValue("SteelBlue")]
-               public virtual Color SelectionBackground {
-                       get { return selBackground; }
-                       set {
-                               if (value == selBackground)
-                                       return;
-                               selBackground = value;
-                               NotifyValueChangedAuto (selBackground);
-                               RegisterForRedraw ();
-                       }
-               }
-               [DefaultValue("White")]
-               public virtual Color SelectionForeground {
-                       get { return selForeground; }
-                       set {
-                               if (value == selForeground)
-                                       return;
-                               selForeground = value;
-                               NotifyValueChangedAuto (selForeground);
-                               RegisterForRedraw ();
-                       }
-               }
 
                protected void raiseSelectedItemChanged(){
                        SelectedItemChanged.Raise (this, new SelectionChangeEventArgs (SelectedItem));
@@ -288,9 +234,8 @@ namespace Crow {
 
                public virtual void ClearItems()
                {
-                       _selectedIndex = -1;
-                       NotifyValueChanged ("SelectedIndex", _selectedIndex);
-                       NotifyValueChanged ("SelectedItem", null);
+                       selectedItemContainer = null;
+                       SelectedItem = null;
 
                        items.ClearChildren ();
                        NotifyValueChanged ("HasChildren", false);
@@ -310,6 +255,18 @@ namespace Crow {
                        }
                        return null;
                }
+               public override Widget FindByType<T> ()
+               {
+                       if (this is T)
+                               return this;
+
+                       foreach (Widget w in Items) {
+                               Widget r = w.FindByType<T> ();
+                               if (r != null)
+                                       return r;
+                       }
+                       return null;
+               }
                public override bool Contains (Widget goToFind)
                {
                        foreach (Widget w in Items) {
@@ -487,18 +444,32 @@ namespace Crow {
                                g.MouseClick += itemClick;
                        Monitor.Exit (IFace.LayoutMutex);
 
-                       if (iTemp.Expand != null && g is Expandable) {
+                       if (iTemp.Expand != null) {
                                Expandable e = g as Expandable;
-                               e.Expand += iTemp.Expand;
-                               if ((o as ICollection) == null)
-                                       e.GetIsExpandable = new BooleanTestOnInstance((instance) => true);
-                               else
-                                       e.GetIsExpandable = iTemp.HasSubItems;
+                               if (e == null)
+                                       e = g.FindByType<Expandable> () as Expandable;
+                                       
+                               if (e != null) { 
+                                       e.Expand += iTemp.Expand;
+                                       if ((o as ICollection) == null)
+                                               e.GetIsExpandable = new BooleanTestOnInstance ((instance) => true);
+                                       else
+                                               e.GetIsExpandable = iTemp.HasSubItems;
+                               }
+                       }
+
+                       if (g is ListItem li) {
+                               li.Selected += Li_Selected;
                        }
 
                        g.DataSource = o;
                }
 
+               //void expandable_expandevent (object sender, EventHandler )
+               void Li_Selected (object sender, EventArgs e)
+               {
+                       SelectedItemContainerChanged.Raise (this, new SelectionChangeEventArgs (sender));
+               }
 
                //              protected void _list_LayoutChanged (object sender, LayoutingEventArgs e)
                //              {
@@ -531,7 +502,15 @@ namespace Crow {
                }
                internal virtual void itemClick(object sender, MouseButtonEventArgs e){
                        //SelectedIndex = (int)((IList)data)?.IndexOf((sender as Widget).DataSource);
-                       SelectedIndex = items.Children.IndexOf(sender as Widget);
+                       if (selectedItemContainer is ListItem li)
+                               li.IsSelected = false;
+                       selectedItemContainer = sender as Widget;
+                       if (selectedItemContainer is ListItem nli)
+                               nli.IsSelected = true;
+                       if (selectedItemContainer == null)
+                               return;
+                       SelectedItem = selectedItemContainer.DataSource;
+                       //SelectedIndex = items.Children.IndexOf(sender as Widget);
                }
 
                bool emitHelperIsAlreadyExpanded (Widget go){
index e150ef8671893f94b0d83bdeb439f43f2720b84d..2bcdfd8a6b1b6b5b746428547740e73bd0382c74 100644 (file)
@@ -11,7 +11,6 @@ namespace Crow
        //if their are expandable, some functions and events are added
        public class TreeView : TemplatedGroup
        {
-               Widget selectedItemContainer = null;
                bool isRoot;
 
                #region CTOR
@@ -29,28 +28,7 @@ namespace Crow
                                NotifyValueChangedAuto (isRoot);
                        }
                }
-               [XmlIgnore]public override object SelectedItem {
-                       get {
-                               return selectedItemContainer == null ?
-                                       "" : selectedItemContainer.DataSource;
-                       }
-               }
 
-               internal override void itemClick (object sender, MouseButtonEventArgs e)
-               {
-                       Widget tmp = sender as Widget;
-                       //if (!tmp.HasFocus)
-                       //      return;
-                       /*if (selectedItemContainer != null) {
-                               selectedItemContainer.Foreground = Color.Transparent;
-                               selectedItemContainer.Background = Color.Transparent;
-                       }*/
-                       selectedItemContainer = tmp;
-                       //selectedItemContainer.Foreground = SelectionForeground;
-                       //selectedItemContainer.Background = SelectionBackground;
-                       NotifyValueChanged ("SelectedItem", SelectedItem);
-                       raiseSelectedItemChanged ();
-               }
 
                void onExpandAll_MouseClick (object sender, MouseButtonEventArgs e)
                {
index 0ca6e92a54e4ac600100564aaa8e0e6c68c677f2..b93e98689e44a6cc9548c19244dc7c7729b4e563 100644 (file)
@@ -262,7 +262,7 @@ namespace Crow
                        loadDefaultValues ();
                }
                #region private fields
-               LayoutingType registeredLayoutings = LayoutingType.All;
+               LayoutingType registeredLayoutings;// = LayoutingType.All;
                ILayoutable logicalParent;
                ILayoutable parent;
                string name;
@@ -379,6 +379,12 @@ namespace Crow
                                return cb;
                        }
                }
+               /// <summary>
+               /// Compute rectangle position on surface of the context. It ma be the first cached surface in parenting chain,
+               /// or the top backend surface if no cached widget is part of the current widget tree.
+               /// </summary>
+               /// <returns>A new rectangle with same dimension as the input one with x and y relative to the context surface</returns>
+               /// <param name="r">A rectangle to compute the coordinate for.</param>
                public virtual Rectangle ContextCoordinates(Rectangle r){
                        Widget go = Parent as Widget;
                        if (go == null)
@@ -387,6 +393,15 @@ namespace Crow
                                r + Parent.ClientRectangle.Position :
                                Parent.ContextCoordinates (r);
                }
+
+               public virtual Rectangle RelativeSlot (Widget target)
+               {
+                       if (this == target)
+                               return Slot;
+                       if (Parent is Widget p)
+                               return Slot + p.RelativeSlot (target).Position + Margin;
+                       return Slot + new Point(Margin, Margin);
+               }
                public virtual Rectangle ScreenCoordinates (Rectangle r){
                        try {
                                return
@@ -465,6 +480,7 @@ namespace Crow
                public event EventHandler<DataSourceChangeEventArgs> ParentChanged;
                /// <summary>Occurs when the logical parent has changed</summary>
                public event EventHandler<DataSourceChangeEventArgs> LogicalParentChanged;
+               public event EventHandler Painted;
                #endregion
 
                internal bool hasDoubleClick => MouseDoubleClick != null;
@@ -973,12 +989,12 @@ namespace Crow
                        get {
                                return rootDataLevel ? dataSource : dataSource == null ?
                                        LogicalParent == null ? null :
-                                       LogicalParent is Widget ? (LogicalParent as Widget).DataSource : null :
+                                       LogicalParent is Widget w ? w.DataSource : null :
                                        dataSource;
                        }
                }
                /// <summary>
-               /// If true, lock datasource seeking upward in logic or graphic tree to this widget
+               /// If true, lock datasource seeking upward in logic or graphic tree to this widget.
                /// </summary>
                [DesignCategory ("Data")][DefaultValue(false)]
                public virtual bool RootDataLevel {
@@ -1023,6 +1039,17 @@ namespace Crow
                                NotifyValueChangedAuto (style);
                        }
                }
+               /// <summary>
+               /// Gets or sets a tooltip to show when mouse stay still over the control.
+               /// </summary>
+               /// <remarks>
+               /// By default, the tooltip container widget that will be show is defined in '#Crow.Tooltip.template' and the widget
+               /// tooltip string is interpreted as a single string helper message that may be a binding expression.
+               /// If the widget Tooltip property start with a '#', the tooltip string will be interpreted as a resource path of
+               /// a custom IML template to show, which will have its datasource set to the widget triggering the tooltip.
+               /// </remarks>
+               /// <value>A single helpt string that may comes from a binding expression, or by starting with a '#',
+               /// You may provide a custom tooltip template resource path.</value>
                [DesignCategory ("Divers")]
                public virtual string Tooltip {
                        get { return tooltip; }
@@ -1250,6 +1277,10 @@ namespace Crow
                public virtual Widget FindByName(string nameToFind){
                        return string.Equals(nameToFind, name, StringComparison.Ordinal) ? this : null;
                }
+               public virtual Widget FindByType<T> ()
+               {
+                       return this is T ? this : null;
+               }
                public virtual bool Contains(Widget goToFind){
                        return false;
                }
@@ -1442,7 +1473,7 @@ namespace Crow
                [MethodImpl(MethodImplOptions.AggressiveInlining)]
                public void RegisterForRedraw ()
                {
-#if DEBUG
+#if DEBUG_LOG
                        if (disposed) {
                                DbgLogger.AddEvent (DbgEvtType.GORegisterForRedraw | DbgEvtType.AlreadyDisposed, this);
                                return;
@@ -1458,6 +1489,9 @@ namespace Crow
 
                /// <summary> return size of content + margins </summary>
                public virtual int measureRawSize (LayoutingType lt) {
+#if DEBUG_LOG
+                       DbgLogger.AddEvent(DbgEvtType.GOMeasure, this);
+#endif
                        return lt == LayoutingType.Width ?
                                contentSize.Width + 2 * margin: contentSize.Height + 2 * margin;
                }
@@ -1766,11 +1800,10 @@ namespace Crow
 
                        Rectangle rb = Slot + Parent.ClientRectangle.Position;
                        if (clearBackground) {
-                                       ctx.Save ();
-                                       ctx.Operator = Operator.Clear;
-                                       ctx.Rectangle (rb);
-                                       ctx.Fill ();
-                                       ctx.Restore ();
+                               ctx.Operator = Operator.Clear;
+                               ctx.Rectangle (rb);
+                               ctx.Fill ();
+                               ctx.Operator = Operator.Over;
                        }
 
                        ctx.SetSourceSurface (bmp, rb.X, rb.Y);
@@ -1835,6 +1868,7 @@ namespace Crow
                                }
                                LastPaintedSlot = Slot;
                        }
+                       Painted.Raise (this, null);
                }
                void paintDisabled(Context gr, Rectangle rb){
                        gr.Operator = Operator.Xor;
diff --git a/Crow/src/debug/DbgEventTypeColors.cs b/Crow/src/debug/DbgEventTypeColors.cs
deleted file mode 100644 (file)
index 3be2c20..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright (c) 2013-2020  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
-//
-// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
-
-using System;
-using Crow.Cairo;
-using System.Linq;
-
-namespace Crow
-{
-       public class DbgEventTypeColors : Widget
-       {
-               protected override void onDraw (Context gr)
-               {
-                       base.onDraw (gr);
-
-                       gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight);
-                       gr.SetFontSize (Font.Size);
-                       gr.FontOptions = Interface.FontRenderingOptions;
-                       gr.Antialias = Interface.Antialias;
-
-                       DbgEvtType[] types = DbgLogViewer.colors.Keys.ToArray ();
-                       Color[] colors = DbgLogViewer.colors.Values.ToArray ();
-
-                       Rectangle r = ClientRectangle;
-                       FontExtents fe = gr.FontExtents;
-
-                       double penY = fe.Height + r.Top;
-                       double penX = (double)r.Left;
-
-
-                       for (int i = 0; i < types.Length; i++) {
-                               string n = types [i].ToString();
-                               Color c = colors[i];
-                               Foreground.SetAsSource (gr);
-
-                               gr.MoveTo (penX + 25.0, penY - fe.Descent);
-                               gr.ShowText (n);
-
-                               Rectangle rc = new Rectangle((int)penX, (int)(penY - fe.Height), 20, (int)fe.Height);
-                               rc.Inflate (-2);
-                               gr.Rectangle (rc);
-                               gr.StrokePreserve ();
-
-                               gr.SetSource (c);
-                               gr.Fill ();
-
-                               penY += fe.Height;
-
-                       }
-               }
-       }
-}
-
diff --git a/Crow/src/debug/DbgLogViewer.cs b/Crow/src/debug/DbgLogViewer.cs
deleted file mode 100644 (file)
index 57bd8e3..0000000
+++ /dev/null
@@ -1,624 +0,0 @@
-// Copyright (c) 2013-2020  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
-//
-// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
-
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
-using Crow.Cairo;
-
-namespace Crow
-{
-       public class DbgLogViewer : ScrollingObject
-       {
-               public static Dictionary<DbgEvtType, Color> colors;
-
-               public static Configuration colorsConf = new Configuration ("dbgcolor.conf");//, Interface.GetStreamFromPath("#Crow.dbgcolor.conf"));
-
-               public static void reloadColors () {
-                       colors = new Dictionary<DbgEvtType, Color>();
-                       foreach (string n in colorsConf.Names) {
-                               DbgEvtType t = (DbgEvtType)Enum.Parse (typeof(DbgEvtType), n);
-                               Color c = colorsConf.Get<Color> (n);
-                               colors.Add (t, c);
-                       }
-
-               }
-               #region CTOR
-               static DbgLogViewer() {
-                       reloadColors ();
-               }
-               protected DbgLogViewer () : base(){}
-               public DbgLogViewer (Interface iface, string style = null) : base(iface, style){}
-               #endregion
-
-               FontExtents fe;
-
-               double xScale = 1.0/512.0, yScale = 1.0, leftMargin, topMargin = 0.0;
-               string logFile;
-               DbgWidgetRecord curWidget;
-               DbgEvent curEvent;
-
-               List<DbgEvent> events;
-               List<DbgWidgetRecord> objs;
-
-               public List<DbgEvent> Events => events;
-               public List<DbgWidgetRecord> Widgets => objs;
-
-
-               long currentTick = 0, selStart = -1, selEnd = -1, minTicks = 0, maxTicks = 0, visibleTicks = 0;
-               int currentLine = -1;
-               int visibleLines = 1;
-               Point mousePos;
-
-               public string LogFile {
-                       get { return logFile; }
-                       set {
-                               if (logFile == value)
-                                       return;
-                               logFile = value;
-
-                               Console.WriteLine ("before load");
-                               loadDebugFile ();
-                               Console.WriteLine ("after load");
-
-                               NotifyValueChanged ("LogFile", logFile);
-                               RegisterForGraphicUpdate ();
-                       }
-               }
-               public double XScale {
-                       get { return xScale; }
-                       set {
-                               if (xScale == value)
-                                       return;
-                               xScale = value;
-                               NotifyValueChanged ("XScale", xScale);
-                               updateVisibleTicks ();
-                               RegisterForGraphicUpdate ();
-                       }
-               }
-               public double YScale {
-                       get => yScale;
-                       set {
-                               if (yScale == value)
-                                       return;
-                               yScale = value;
-                               NotifyValueChanged ("YScale", yScale);
-                               RegisterForGraphicUpdate ();
-                       }
-               }
-               public override Font Font {
-                       get { return base.Font; }
-                       set {
-                               base.Font = value;
-                               using (Context gr = new Context (IFace.surf)) {
-                                       gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight);
-                                       gr.SetFontSize (Font.Size);
-
-                                       fe = gr.FontExtents;
-                               }
-                               loadDebugFile ();
-                       }
-               }
-               public override int ScrollY {
-                       get => base.ScrollY;
-                       set {
-                               base.ScrollY = value;
-
-                               if (objs == null)
-                                       return;
-
-                               Rectangle cb = ClientRectangle;
-                               cb.Left += (int)leftMargin;
-                               cb.Width -= (int)leftMargin;
-                               cb.Y += (int)topMargin;
-                               cb.Height -= (int)topMargin;
-
-                               if (mousePos.Y < cb.Top || mousePos.Y > cb.Bottom)
-                                       currentLine = -1;
-                               else
-                                       currentLine = (int)((double)(mousePos.Y - cb.Top) / fe.Height) + ScrollY;
-
-                               NotifyValueChanged ("CurrentLine", currentLine);
-                       }
-               }
-
-               public DbgWidgetRecord CurrentWidget {
-                       get => curWidget;
-                       internal set {
-                               if (curWidget == value)
-                                       return;
-                               curWidget = value;
-                               NotifyValueChanged (nameof (CurrentWidget), curWidget);
-                       }
-               }
-               public DbgEvent CurrentEvent {
-                       get => curEvent;
-                       internal set {
-                               if (curEvent == value)
-                                       return;
-
-                               if (curEvent != null)
-                                       curEvent.IsSelected = false;
-                               curEvent = value;
-                               if (curEvent != null) {
-                                       curEvent.IsSelected = true;
-                                       curEvent.IsExpanded = true;
-                               }
-
-                               NotifyValueChanged (nameof (CurrentEvent), curEvent);
-                       }
-               }
-
-
-               void drawEvents (Context ctx, List<DbgEvent> evts)
-               {
-                       if (evts == null || evts.Count == 0)
-                               return;
-                       Rectangle cb = ClientRectangle;
-
-                       foreach (DbgEvent evt in evts) {
-                               if (evt.end - minTicks <= ScrollX)
-                                       continue;
-                               if (evt.begin - minTicks > ScrollX + visibleTicks)
-                                       break;
-                               double penY = topMargin + ClientRectangle.Top;
-                               if (evt.type.HasFlag (DbgEvtType.Widget)) {
-                                       DbgWidgetEvent eW = evt as DbgWidgetEvent;
-                                       int lIdx = eW.InstanceIndex - ScrollY;
-                                       if (lIdx < 0 || lIdx > visibleLines)
-                                               continue;
-                                       penY += (lIdx) * fe.Height; 
-                               
-                                       ctx.SetSource (evt.Color);
-
-                                       double x = xScale * (evt.begin - minTicks - ScrollX);
-                                       double w = Math.Max (Math.Max (2.0, 2.0 * xScale), (double)(evt.end - evt.begin) * xScale);
-                                       if (x < 0.0) {
-                                               w += x;
-                                               x = 0.0;
-                                       }
-                                       x += leftMargin + cb.Left;
-                                       double rightDiff = x + w - cb.Right;
-                                       if (rightDiff > 0)
-                                               w -= rightDiff;
-
-                                       ctx.Rectangle (x, penY, w, fe.Height);
-                                       ctx.Fill ();
-                               } else {
-                                       double x = xScale * (evt.begin - minTicks - ScrollX);
-                                       x += leftMargin + cb.Left;
-
-                                       double trunc = Math.Truncate (x);
-                                       if (x - trunc > 0.5)
-                                               x = trunc + 0.5;
-                                       else
-                                               x = trunc - 0.5;
-
-
-                                       ctx.SetSource (Colors.Yellow);
-                                       ctx.MoveTo (x, penY);
-                                       ctx.LineTo (x, cb.Bottom);
-                                       ctx.Stroke ();
-                                       string s = evt.type.ToString () [5].ToString ();
-                                       TextExtents te = ctx.TextExtents (s);
-                                       ctx.Rectangle (x - 0.5 * te.Width, penY - te.Height, te.Width, te.Height);
-                                       ctx.Fill ();
-                                       ctx.MoveTo (x - 0.5 * te.Width, penY - ctx.FontExtents.Descent);
-                                       ctx.SetSource (Colors.Jet);
-                                       ctx.ShowText (s);
-
-                               }
-                               drawEvents (ctx, evt.Events);
-                       }
-               }
-
-               protected override void onDraw (Cairo.Context gr)
-               {
-                       base.onDraw (gr);
-
-                       gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight);
-                       gr.SetFontSize (Font.Size);
-                       gr.FontOptions = Interface.FontRenderingOptions;
-                       gr.Antialias = Cairo.Antialias.None;
-
-                       if (objs == null)
-                               return;
-
-                       gr.LineWidth = 1.0;
-
-                       Rectangle cb = ClientRectangle;
-
-                       double penY = topMargin + ClientRectangle.Top;
-
-                       for (int i = 0; i < visibleLines; i++) {
-                               if (i + ScrollY >= objs.Count)
-                                       break;
-                               int gIdx = i + ScrollY;
-                               DbgWidgetRecord g = objs [gIdx];
-
-                               penY += fe.Height;
-
-                               gr.SetSource (Crow.Colors.Jet);
-                               gr.MoveTo (cb.X, penY - 0.5);
-                               gr.LineTo (cb.Right, penY - 0.5);
-                               gr.Stroke ();
-
-                               double penX = 5.0 * g.xLevel + cb.Left;
-
-                               if (g.yIndex == 0)
-                                       gr.SetSource (Crow.Colors.LightSalmon);
-                               else
-                                       Foreground.SetAsSource (gr);
-
-                               gr.MoveTo (penX, penY - gr.FontExtents.Descent);
-                               gr.ShowText (g.name + gIdx);
-
-                               gr.SetSource (Crow.Colors.White);
-                               gr.MoveTo (cb.X, penY - gr.FontExtents.Descent);
-                               gr.ShowText ((i + ScrollY).ToString ());
-                       }
-
-                       drawEvents (gr, events);
-                       /*
-                       for (int i = 0; i < visibleLines; i++) { 
-                               foreach (DbgEvent evt in events) {
-                                       if (evt.end - minTicks <= ScrollX)
-                                               continue;
-                                       if (evt.begin - minTicks > ScrollX + visibleTicks)
-                                               break;
-                                       
-                                       
-                               }
-
-                               
-                       }
-                       */
-
-                       gr.MoveTo (cb.Left, topMargin - 0.5 + cb.Top);
-                       gr.LineTo (cb.Right, topMargin - 0.5 + cb.Top);
-
-                       gr.MoveTo (leftMargin + cb.Left, cb.Top);
-                       gr.LineTo (leftMargin + cb.Left, cb.Bottom);
-                       gr.SetSource (Crow.Colors.Grey);
-
-                       penY = topMargin + ClientRectangle.Top;
-
-                       //graduation
-                       int largeGrad = int.Parse ("1" + new string ('0', visibleTicks.ToString ().Length - 1));
-                       int smallGrad = Math.Max (1, largeGrad / 10);
-
-                       long firstVisibleTicks = minTicks + ScrollX;
-                       long curGrad = firstVisibleTicks - firstVisibleTicks % smallGrad + smallGrad;
-
-                       long gg = curGrad - ScrollX - minTicks;
-                       while (gg < visibleTicks ) {
-                               double x = (double)gg * xScale + leftMargin + cb.Left;
-
-                               gr.MoveTo (x, penY - 0.5);
-                               if (curGrad % largeGrad == 0) { 
-                                       gr.LineTo (x, penY - 8.5);
-                                       string str = curGrad.ToString ();
-                                       TextExtents te = gr.TextExtents (str);
-                                       gr.RelMoveTo (-0.5 * te.Width, -2.0);
-                                       gr.ShowText (str);
-                               }else
-                                       gr.LineTo (x, penY - 2.5);
-
-                               curGrad += smallGrad;
-                               gg = curGrad - ScrollX - minTicks;
-                       }
-
-                       gr.Stroke ();
-
-                       //global events
-/*                     foreach (DbgEvent evt in events) {
-                               if (evt.begin - minTicks <= ScrollX)
-                                       continue;
-                               double x = xScale * (evt.begin - minTicks - ScrollX) ;
-                               x += leftMargin + cb.Left;
-
-
-                       }*/
-
-               }
-               public override void Paint (ref Cairo.Context ctx)
-               {
-                       base.Paint (ref ctx);
-
-                       Rectangle r = new Rectangle(mousePos.X, 0, 1, Slot.Height);
-                       Rectangle ctxR = ContextCoordinates (r);
-                       Rectangle cb = ClientRectangle;
-
-                       ctx.Rectangle (ctxR);
-                       ctx.SetSource (Colors.CornflowerBlue);
-                       ctx.Fill();
-
-                       ctx.SelectFontFace (Font.Name, Font.Slant, Font.Wheight);
-                       ctx.SetFontSize (Font.Size);
-                       ctx.FontOptions = Interface.FontRenderingOptions;
-                       ctx.Antialias = Interface.Antialias;
-
-                       ctx.MoveTo (ctxR.X, ctxR.Y + fe.Height);
-                       ctx.ShowText (currentTick.ToString ());
-
-                       ctx.Operator = Cairo.Operator.Add;
-
-                       if (currentLine >= 0) {
-                               double y = fe.Height * (currentLine - ScrollY) + topMargin + cb.Top;
-                               r = new Rectangle (cb.Left,  (int)y, cb.Width, (int)fe.Height);
-
-                               ctx.Operator = Cairo.Operator.Add;
-                               ctx.SetSourceRGBA (0.1, 0.1, 0.1, 0.4);
-                               ctx.Rectangle (ContextCoordinates (r));
-                               ctx.Fill ();
-                       }
-
-                       if (selStart < 0 || selEnd < 0) {
-                               ctx.Operator = Cairo.Operator.Over;
-                               return;
-                       }
-                       double selStartX = (double)(selStart - ScrollX - minTicks) * xScale + leftMargin + cb.Left;
-                       double selEndX = (double)(selEnd - ScrollX - minTicks) * xScale + leftMargin + cb.Left;
-
-                       if (selStartX < selEndX) {
-                               ctxR.X = (int)selStartX;
-                               ctxR.Width = (int)(selEndX - selStartX);
-                       } else {
-                               ctxR.X = (int)selEndX;
-                               ctxR.Width = (int)(selStartX - selEndX);
-                       }
-
-                       ctxR.Width = Math.Max (1, ctxR.Width);
-                       ctx.Rectangle (ctxR);
-                       //ctx.SetSourceColor (Color.LightYellow);
-                       ctx.SetSource (Colors.Jet);
-                       ctx.Fill();
-                       ctx.Operator = Cairo.Operator.Over;
-
-               }
-               public override void OnLayoutChanges (LayoutingType layoutType)
-               {
-                       base.OnLayoutChanges (layoutType);
-                       switch (layoutType) {
-                       case LayoutingType.Width:
-                               updateVisibleTicks ();
-                               break;
-                       case LayoutingType.Height:
-                               updateVisibleLines ();
-                               break;
-                       }
-               }
-
-               public override void onMouseLeave (object sender, MouseMoveEventArgs e)
-               {
-                       base.onMouseLeave (sender, e);
-                       currentLine = -1;
-                       currentTick = 0;
-               }
-               public override void onMouseMove (object sender, MouseMoveEventArgs e)
-               {
-                       base.onMouseMove (sender, e);
-
-                       long lastTick = currentTick;
-                       updateMouseLocalPos (e.Position);
-
-                       if (IFace.IsDown (Glfw.MouseButton.Left) && selStart >= 0)
-                               selEnd = currentTick;
-                       else if (IFace.IsDown(Glfw.MouseButton.Right)) {
-                               ScrollX += (int)(lastTick - currentTick);
-                               updateMouseLocalPos (e.Position);
-                       }
-
-                       if (RegisteredLayoutings == LayoutingType.None && !IsDirty)
-                               IFace.EnqueueForRepaint (this);
-                       
-               }
-               public override void onMouseDown (object sender, MouseButtonEventArgs e)
-               {
-                       base.onMouseDown (sender, e);
-
-                       if (e.Button == Glfw.MouseButton.Left) {
-                               CurrentWidget = (currentLine < 0 || currentLine >= objs.Count) ? null : objs [currentLine];
-                               CurrentEvent = curWidget?.Events.FirstOrDefault (ev => ev.begin <= currentTick && ev.end >= currentTick);
-                               selStart = currentTick;
-                               selEnd = -1;
-                       }
-
-                       RegisterForRedraw ();
-               }
-               public override void onMouseUp (object sender, MouseButtonEventArgs e)
-               {
-                       base.onMouseUp (sender, e);
-
-                       selStart = -1;
-                       selEnd = -1;
-
-                       RegisterForRedraw ();
-               }
-
-               /// <summary> Process scrolling vertically, or if shift is down, vertically </summary>
-               public override void onMouseWheel (object sender, MouseWheelEventArgs e)
-               {                       
-                       //base.onMouseWheel (sender, e);
-
-                       if (IFace.Shift)
-                               ScrollX -= (int)((double)(e.Delta * MouseWheelSpeed) / xScale);
-                       else if (IFace.Ctrl) {
-                               if (e.Delta > 0) {
-                                       XScale *= 2.0;
-                               } else {
-                                       if (MaxScrollX > 0)
-                                               XScale *= 0.5;
-                               }
-                               ScrollX = (int)(currentTick - (int)((double)Math.Max(0, mousePos.X - (int)leftMargin) / xScale) - minTicks);
-                       }else
-                               ScrollY -= e.Delta * MouseWheelSpeed;
-               }
-
-               public override void onKeyDown (object sender, KeyEventArgs e)
-               {
-                       base.onKeyDown (sender, e);
-
-                       if (e.Key == Glfw.Key.F3) {
-                               if (selEnd < 0)
-                                       return;
-                               if (selEnd < selStart)
-                                       zoom (selEnd, selStart);
-                               else
-                                       zoom (selStart, selEnd);
-                               selEnd = selStart = -1;
-                       }
-               }
-               async void loadDebugFile ()
-               {
-                       await loadDebugFileAsync ();
-               }
-
-
-
-               async Task loadDebugFileAsync ()
-               {
-                       if (!File.Exists (logFile))
-                               return;
-
-                       events = new List<DbgEvent> ();
-                       objs = new List<DbgWidgetRecord> ();
-                       minTicks = maxTicks = 0;
-                       leftMargin = topMargin = 0.0;
-
-                       using (Context gr = new Context (IFace.surf)) {
-                               double maxNameWidth = 0.0;
-
-                               gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight);
-                               gr.SetFontSize (Font.Size);
-
-                               using (StreamReader s = new StreamReader (logFile)) {
-                                       if (s.ReadLine () != "[GraphicObjects]")
-                                               return;
-                                       while (!s.EndOfStream) {
-                                               string l = s.ReadLine ();
-                                               if (l == "[Events]")
-                                                       break;
-                                               DbgWidgetRecord o = DbgWidgetRecord.Parse (l);
-                                               objs.Add (o);
-                                               double nameWidth = gr.TextExtents (o.name).Width + 5.0 * o.xLevel;
-                                               if (nameWidth > maxNameWidth)
-                                                       maxNameWidth = nameWidth;
-                                       }
-
-                                       Stack<DbgEvent> startedEvents = new Stack<DbgEvent> ();
-
-                                       if (!s.EndOfStream) {
-                                               while (!s.EndOfStream) {
-                                                       int level = 0;
-                                                       while (s.Peek () == (int)'\t') {
-                                                               s.Read ();
-                                                               level++;
-                                                       }
-                                                       DbgEvent evt = DbgEvent.Parse (s.ReadLine ());
-                                                       if (evt.end > maxTicks)
-                                                               maxTicks = evt.end;
-                                                       if (level == 0) {
-                                                               startedEvents.Clear ();
-                                                               events.Add (evt);
-                                                       } else {
-                                                               int levelDiff = level - startedEvents.Count + 1;
-                                                               if (levelDiff > 0) {
-                                                                       if (levelDiff > 1)
-                                                                               System.Diagnostics.Debugger.Break ();
-                                                                       startedEvents.Peek ().AddEvent (evt);
-                                                               } else {
-                                                                       startedEvents.Pop ();
-                                                                       if (-levelDiff > startedEvents.Count)
-                                                                               System.Diagnostics.Debugger.Break ();
-                                                                       while (startedEvents.Count > level)
-                                                                               startedEvents.Pop ();
-                                                                       startedEvents.Peek ().AddEvent (evt);
-                                                               }
-                                                       }
-                                                       startedEvents.Push (evt);
-                                                       if (evt.type.HasFlag(DbgEvtType.Widget))
-                                                               objs [(evt as DbgWidgetEvent).InstanceIndex].Events.Add (evt);
-                                               }
-                                               if (events.Count > 0)
-                                                       minTicks = events [0].begin;
-                                       }
-                               }
-
-                               leftMargin = 2.5 + maxNameWidth;
-                               topMargin = 2.0 * fe.Height;
-
-                               updateVisibleLines ();
-                               updateVisibleTicks ();
-                       }
-                       NotifyValueChanged ("Widgets", Widgets);
-                       NotifyValueChanged ("Events", Events);
-               }
-
-               void updateVisibleLines ()
-               {
-                       visibleLines = fe.Height < 1 ? 1 : (int)Math.Floor (((double)ClientRectangle.Height - topMargin) / fe.Height);
-                       NotifyValueChanged ("VisibleLines", visibleLines);
-                       updateMaxScrollY ();
-               }
-               void updateVisibleTicks ()
-               {
-                       visibleTicks = Math.Max (0, (long)((double)(ClientRectangle.Width - leftMargin) / XScale));
-                       NotifyValueChanged ("VisibleTicks", visibleTicks);
-                       updateMaxScrollX ();
-               }
-
-               void updateMaxScrollX ()
-               {
-                       if (objs == null)
-                               MaxScrollX = 0;
-                       else
-                               MaxScrollX = (int)Math.Max (0L, maxTicks - minTicks - visibleTicks);
-               }
-               void updateMaxScrollY ()
-               {
-                       if (objs == null)
-                               MaxScrollY = 0;
-                       else
-                               MaxScrollY = Math.Max (0, objs.Count - visibleLines);
-               }
-
-               void updateMouseLocalPos (Point mPos)
-               {
-                       Rectangle r = ScreenCoordinates (Slot);
-                       Rectangle cb = ClientRectangle;
-                       cb.Left += (int)leftMargin;
-                       cb.Width -= (int)leftMargin;
-                       cb.Y += (int)topMargin;
-                       cb.Height -= (int)topMargin;
-
-                       mousePos = mPos - r.Position;
-
-                       mousePos.X = Math.Max (cb.X, mousePos.X);
-                       mousePos.X = Math.Min (cb.Right, mousePos.X);
-
-                       if (mousePos.Y < cb.Top || mousePos.Y > cb.Bottom)
-                               currentLine = -1;
-                       else
-                               currentLine = (int)((double)(mousePos.Y - cb.Top) / fe.Height) + ScrollY;
-
-                       NotifyValueChanged ("CurrentLine", currentLine);
-
-                       mousePos.Y = Math.Max (cb.Y, mousePos.Y);
-                       mousePos.Y = Math.Min (cb.Bottom, mousePos.Y);
-
-                       currentTick = (int)((double)(mousePos.X - cb.X) / xScale) + minTicks + ScrollX;
-               }
-               void zoom (long start, long end) {                                              
-                       //Rectangle cb = ClientRectangle;
-                       //cb.X += (int)leftMargin;
-                       XScale = ((double)ClientRectangle.Width - leftMargin)/(end - start);
-                       ScrollX = (int)(start - minTicks);
-               }
-       }
-}
-
-
diff --git a/Crow/src/debug/DebugLogger.cs b/Crow/src/debug/DebugLogger.cs
deleted file mode 100644 (file)
index 1c18873..0000000
+++ /dev/null
@@ -1,262 +0,0 @@
-// Copyright (c) 2013-2020  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
-//
-// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
-
-using System;
-using Crow.Cairo;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
-using System.Linq;
-using System.Threading;
-
-
-namespace Crow
-{
-       [Flags]
-       public enum DbgEvtType {
-               ////9 nth bit set for iface event
-               IFace                                                   = 0x10000,
-               Focus                                                   = 0x20000,
-               Override                                                = 0x40000,
-               Widget                                                  = 0x00100,
-               //GOLayouting                                   = 0x00200,
-               //Drawing                                               = 0x00400,
-               Lock                                                    = 0x00800,
-               Layouting                                               = IFace | 0x01000,
-               Clipping                                                = IFace | 0x02000,
-               Drawing                                                 = IFace | 0x04000,
-               Update                                                  = IFace | 0x08000,
-               IFaceLoad                                               = IFace | 0x05,
-               IFaceInit                                               = IFace | 0x06,
-               CreateITor                                              = IFace | 0x07,
-
-               HoverWidget                                             = Focus | 0x01,
-               FocusedWidget                                   = Focus | 0x02,
-               ActiveWidget                                    = Focus | 0x03,
-
-               //10 nth bit set for graphic obj
-               TemplatedGroup                                  = 0x1000,
-               Dispose                                                 = 0x2000,
-               Warning                                                 = 0x4000,
-               Error                                                   = 0x8000,
-               GOClassCreation                                 = Widget | 0x01,
-               GOInitialization                                = Widget | 0x02,
-               GORegisterForGraphicUpdate              = Widget | 0x03,
-               GOEnqueueForRepaint                             = Widget | 0x04,
-               GONewDataSource                                 = Widget | 0x05,
-               GONewParent                                             = Widget | 0x06,
-               GONewLogicalParent                              = Widget | 0x07,
-               GOAddChild                                              = Widget | 0x08,
-
-               GOSearchLargestChild                    = Widget | 0x09,
-               GOSearchTallestChild                    = Widget | 0x10,
-               GORegisterForRedraw                             = Widget | 0x11,
-
-               AlreadyDisposed                                 = Dispose | Widget | Error | 0x01,
-               DisposedByGC                                    = Dispose | Widget | Error | 0x02,
-               Disposing                                               = Dispose | Widget | 0x01,
-
-               GOClippingRegistration                  = Clipping | Widget | 0x01,
-               GORegisterClip                                  = Clipping | Widget | 0x02,
-               GORegisterLayouting                     = Layouting | Widget | 0x01,
-               GOProcessLayouting                              = Layouting | Widget | 0x02,
-               GOProcessLayoutingWithNoParent  = Layouting | Widget | Warning | 0x01,
-               GODraw                                                  = Drawing | Widget | 0x01,
-               GORecreateCache                                 = Drawing | Widget | 0x02,
-               GOUpdateCache                                   = Drawing | Widget | 0x03,
-               GOPaint                                                 = Drawing | Widget | 0x04,
-
-               GOLockUpdate                                    = Widget | Lock | 0x01,
-               GOLockClipping                                  = Widget | Lock | 0x02,
-               GOLockRender                                    = Widget | Lock | 0x03,
-               GOLockLayouting                                 = Widget | Lock | 0x04,
-
-               TGLoadingThread                                 = Widget | TemplatedGroup | 0x01,
-               TGCancelLoadingThread                   = Widget | TemplatedGroup | 0x02,
-
-               All = 0x0FFFFFFF
-       }
-#if DEBUG_LOG
-       public static class DbgLogger
-       {
-               public static DbgEvtType IncludeEvents = DbgEvtType.All;
-               public static DbgEvtType DiscardEvents = DbgEvtType.Focus;
-
-               static bool logevt (DbgEvtType evtType)
-                       => (evtType & DiscardEvents) == 0 && (evtType & IncludeEvents) != 0;
-
-
-               static object logMutex = new object ();
-               static Stopwatch chrono = Stopwatch.StartNew ();
-               static List<DbgEvent> events = new List<DbgEvent> ();
-               //started events per thread
-               static Dictionary<int, Stack<DbgEvent>> startedEvents = new Dictionary<int, Stack<DbgEvent>> ();
-               //helper for fetching current event list to add next event to while recording
-               static List<DbgEvent> curEventList {
-                       get {
-                               if (startedEvents.ContainsKey (Thread.CurrentThread.ManagedThreadId)) {
-                                       if (startedEvents [Thread.CurrentThread.ManagedThreadId].Count == 0)
-                                               return events;
-                                       DbgEvent e = startedEvents [Thread.CurrentThread.ManagedThreadId].Peek ();
-                                       if (e.Events == null) 
-                                               e.Events = new List<DbgEvent> ();
-                                       return e.Events;
-                               }
-                               return events;
-                       }
-               }
-
-               public static DbgEvent StartEvent (DbgEvtType evtType, params object[] data)
-               {
-                       if (!logevt (evtType))
-                               return null;
-                       lock (logMutex) {
-                               chrono.Stop ();
-                               DbgEvent evt = addEventInternal (evtType, data);
-                               if (!startedEvents.ContainsKey (Thread.CurrentThread.ManagedThreadId))
-                                       startedEvents [Thread.CurrentThread.ManagedThreadId] = new Stack<DbgEvent> ();
-                               startedEvents [Thread.CurrentThread.ManagedThreadId].Push (evt);
-                               chrono.Start ();
-                               return evt;
-                       }
-               }
-               public static DbgEvent EndEvent (DbgEvtType evtType, bool discardIfNoChildEvents = false)
-               {
-                       if (!logevt (evtType))
-                               return null;
-
-                       lock (logMutex) {
-                               chrono.Stop ();
-                               if (!startedEvents.ContainsKey (Thread.CurrentThread.ManagedThreadId))
-                                       throw new Exception ("Current thread has no event started");
-                               DbgEvent e = startedEvents [Thread.CurrentThread.ManagedThreadId].Pop ();
-                               if (e.type != evtType)
-                                       throw new Exception ($"Begin/end event logging mismatch: {e.type}/{evtType}");
-                               if (discardIfNoChildEvents && (e.Events == null || e.Events.Count == 0))
-                                       curEventList.Remove (e);
-                               else
-                                       e.end = chrono.ElapsedTicks;
-                               chrono.Start ();
-                               return e;
-                       }
-               }
-               /// <summary>
-               /// End event by reference to cancel unended events on failure
-               /// </summary>
-               public static DbgEvent EndEvent (DbgEvtType evtType, DbgEvent evt)
-               {
-                       if (!logevt (evtType))
-                               return null;
-
-                       lock (logMutex) {
-                               chrono.Stop ();
-                               if (!startedEvents.ContainsKey (Thread.CurrentThread.ManagedThreadId))
-                                       throw new Exception ("Current thread has no event started");
-                               DbgEvent e = startedEvents [Thread.CurrentThread.ManagedThreadId].Pop ();
-                               while (e != evt)
-                                       e = startedEvents [Thread.CurrentThread.ManagedThreadId].Pop ();
-                               e.end = chrono.ElapsedTicks;
-                               chrono.Start ();
-                               return e;
-                       }
-               }
-
-               public static DbgEvent AddEvent (DbgEvtType evtType, params object [] data) { 
-                       if (!logevt (evtType))
-                               return null;
-
-                       lock (logMutex) {
-                               chrono.Stop ();
-                               DbgEvent evt = addEventInternal (evtType, data);
-                               chrono.Start ();
-                               return evt;
-                       }
-               }
-
-               static DbgEvent addEventInternal (DbgEvtType evtType, params object [] data)
-               {
-                       DbgEvent evt = null;
-                       if (data == null || data.Length == 0)
-                               evt = new DbgEvent (chrono.ElapsedTicks, evtType);
-                       else if (data [0] is Widget w)
-                               evt = new DbgWidgetEvent (chrono.ElapsedTicks, evtType, w);
-                       else if (data [0] is LayoutingQueueItem lqi)
-                               evt = new DbgLayoutEvent (chrono.ElapsedTicks, evtType, lqi);
-                       else
-                               evt = new DbgEvent (chrono.ElapsedTicks, evtType);
-
-                       curEventList.Add (evt);
-                       return evt;
-               }
-
-               static void parseTree (Widget go, int level = 0, int y = 1) {
-                       if (go == null)
-                               return;
-
-                       go.yIndex = y;
-                       go.xLevel = level;
-
-                       Group gr = go as Group;
-                       if (gr != null) {
-                               foreach (Widget g in gr.Children) 
-                                       parseTree (g, level + 1, y + 1);
-
-                       } else {
-                               PrivateContainer pc = go as PrivateContainer;
-                               if (pc != null)
-                                       parseTree (pc.getTemplateRoot, level + 1, y + 1);                               
-                       }
-               }
-               /// <summary>
-               /// Clear all recorded events from logger.
-               /// </summary>
-               public static void Reset ()
-               {
-                       lock (logMutex) {
-                               startedEvents.Clear ();
-                               events.Clear ();
-                               chrono.Restart ();
-                       }
-               }
-               /// <summary>
-               /// Save recorded events to disk
-               /// </summary>
-               /// <param name="iface">Iface.</param>
-               public static void save(Interface iface, string dbgLogFilePath = "debug.log") {
-                       lock (logMutex) {
-
-                               foreach (Widget go in iface.GraphicTree)
-                                       parseTree (go);
-
-                               using (StreamWriter s = new StreamWriter (dbgLogFilePath)) {
-                                       s.WriteLine ("[GraphicObjects]");
-                                       lock (Widget.GraphicObjects) {
-                                               //Widget.GraphicObjects = Widget.GraphicObjects.OrderBy (o => o.yIndex).ToList ();
-                                               for (int i = 0; i < Widget.GraphicObjects.Count; i++) {
-                                                       Widget g = Widget.GraphicObjects [i];
-                                                       s.WriteLine ($"{g.GetType ().Name};{g.yIndex};{g.xLevel}");
-                                               }
-                                       }
-                                       s.WriteLine ("[Events]");
-                                       saveEventList (s, events);
-                               }
-                       }
-               }
-
-               static void saveEventList (StreamWriter s, List<DbgEvent> evts, int level = 0)
-               {
-                       foreach (DbgEvent e in evts) {
-                               if (e == null)
-                                       continue;
-                               s.WriteLine (new string ('\t', level) + e);
-                               if (e.Events != null)
-                                       saveEventList (s, e.Events, level + 1);
-                       }
-               }
-
-       }
-#endif
-}
-
-
index bfebafcd65a2e65b166f297ef64f0a54c5e0c8c4..2e42052a665c90692acfa743dcab2018905bf4d3 100644 (file)
@@ -39,7 +39,8 @@ namespace tests
                        //testFiles = new string [] { @"Interfaces/TemplatedControl/testEnumSelector.crow" };
                        //testFiles = new string [] { @"Interfaces/Divers/all.crow" };
                        //testFiles = new string [] { @"Interfaces/Divers/gauge.crow" };
-                       testFiles = new string [] { @"Interfaces/Divers/testSlider.crow" };
+                       //testFiles = new string [] { @"Interfaces/Stack/StretchedInFit4.crow" };
+                       testFiles = new string [] { @"Interfaces/TemplatedGroup/1.crow" };
                        //testFiles = new string [] { @"Interfaces/Divers/colorPicker2.crow" };
                        testFiles = testFiles.Concat (Directory.GetFiles (@"Interfaces/GraphicObject", "*.crow")).ToArray ();
                        testFiles = testFiles.Concat (Directory.GetFiles (@"Interfaces/Container", "*.crow")).ToArray ();
@@ -69,18 +70,20 @@ namespace tests
                                        Quit ();
                                        break;
                                case Key.F2:
+                                       //if (IsKeyDown (Key.LeftShift))
+                                               //DbgLogger.Reset ();
+                                       //DbgLogger.save (this);
+                                       return false;
+                               case Key.F3:
                                        idx--;
                                        break;
-                               case Key.F3:
+                               case Key.F4:
                                        idx++;
                                        break;
                                case Key.F1:
                                        //TestList.Add ("new string");
                                        NotifyValueChanged ("TestList", TestList);
                                        break;
-                               case Key.F4:
-                                       Load ("Interfaces/TemplatedContainer/testWindow.goml").DataSource = this;
-                                       return false;
                                case Key.F5:
                                        Load ("Interfaces/Divers/testFileDialog.crow").DataSource = this;
                                        return false;
diff --git a/Samples/DebugLogAnalyzer/DbgEventWidget.cs b/Samples/DebugLogAnalyzer/DbgEventWidget.cs
new file mode 100644 (file)
index 0000000..1528e20
--- /dev/null
@@ -0,0 +1,143 @@
+// Copyright (c) 2013-2020  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using Crow.Cairo;
+
+namespace Crow
+{
+       public class DbgEventWidget : Widget
+       {
+               public DbgEventWidget (){}
+
+               DbgEvent evt, hoverEvt;
+               long ticksPerPixel;
+               double pixelPerTick;
+
+               public DbgEvent Event {
+                       get => evt;
+                       set {
+                               if (evt == value)
+                                       return;
+                               evt = value;
+                               updatePixelPerTicks ();
+                               NotifyValueChangedAuto (evt);
+                               RegisterForRedraw ();
+                       }
+               }
+               public DbgEvent HoverEvent {
+                       get => hoverEvt;
+                       private set {
+                               if (hoverEvt == value)
+                                       return;
+                               hoverEvt = value;
+                               NotifyValueChangedAuto (hoverEvt);
+                       }
+               }
+
+               [DefaultValue ("1000")]
+               public long TicksPerPixel {
+                       get => ticksPerPixel;
+                       set {
+                               if (ticksPerPixel == value)
+                                       return;
+                               ticksPerPixel = value;
+                               NotifyValueChangedAuto (ticksPerPixel);
+                               if (Width == Measure.Fit)
+                                       RegisterForLayouting (LayoutingType.Width);
+                       }
+               }
+
+               public override int measureRawSize (LayoutingType lt)
+               {
+                       updatePixelPerTicks ();
+                       if (lt == LayoutingType.Width)
+                               contentSize.Width = Event == null ? 0 : (int)Math.Max(pixelPerTick * Event.Duration, 2);
+                       
+                       return base.measureRawSize (lt);
+               }
+
+               public override void OnLayoutChanges (LayoutingType layoutType)
+               {
+                       if (layoutType == LayoutingType.Width)
+                               updatePixelPerTicks ();
+
+                       base.OnLayoutChanges (layoutType);
+               }
+
+               protected override void onDraw (Context gr)
+               {
+                       if (Event == null) {
+                               base.onDraw (gr);
+                               return;
+                       }
+
+                       gr.LineWidth = 1;
+                       gr.SetDash (new double [] { 1.0, 3.0 }, 0);
+
+                       Rectangle cb = ClientRectangle;
+
+                       if (Event.Duration == 0) {
+                               gr.SetSource (Event.Color);
+                               gr.Rectangle (cb);
+                               gr.Fill ();
+                               return;
+                       }
+
+                       drawEvent (gr, cb.Height, Event);
+               }
+               void drawEvent (Context ctx, int h, DbgEvent dbge)
+               {
+                       double w = Math.Max(dbge.Duration * pixelPerTick, 2.0);
+                       double x = (dbge.begin - Event.begin) * pixelPerTick;
+
+                       ctx.Rectangle (x, Margin, w, h);
+                       ctx.SetSource (dbge.Color);
+                       if (dbge.IsSelected) {
+                               ctx.FillPreserve ();
+                               ctx.SetSourceRGB (1, 1, 1);
+                               ctx.Stroke ();
+                       }else
+                               ctx.Fill ();
+
+                       if (dbge.Events == null)
+                               return;
+                       foreach (DbgEvent e in dbge.Events)
+                               drawEvent (ctx, h, e);
+               }
+
+               public override void onMouseMove (object sender, MouseMoveEventArgs e)
+               {
+                       if (Event != null) {
+                               Point m = ScreenPointToLocal (e.Position);
+                               long curTick = (long)(m.X / pixelPerTick) + Event.begin;
+                               /*if (Width == Measure.Fit) 
+                                       NotifyValueChanged ("HoverEvent", Event);
+                               else*/
+                               HoverEvent = hoverEvent (Event, curTick);
+
+                               e.Handled = true;
+                       }
+                       base.onMouseMove (sender, e);
+               }
+
+               DbgEvent hoverEvent (DbgEvent hevt, long curTick){
+                       if (hevt.Events != null) {
+                               foreach (DbgEvent e in hevt.Events) {
+                                       if (curTick >= e.begin && curTick <= e.end)
+                                               return hoverEvent (e, curTick);
+                               }
+                       }
+                       return hevt;
+               }
+               void updatePixelPerTicks ()
+               {
+                       if (Width == Measure.Fit)
+                               pixelPerTick = 1.0 / ticksPerPixel;
+                       else
+                               pixelPerTick = Event == null ? 0 : (double)ClientRectangle.Width / Event.Duration;
+               }
+       }
+}
\ No newline at end of file
diff --git a/Samples/DebugLogAnalyzer/DbgLogViewer.cs b/Samples/DebugLogAnalyzer/DbgLogViewer.cs
new file mode 100644 (file)
index 0000000..4b7a108
--- /dev/null
@@ -0,0 +1,637 @@
+// Copyright (c) 2013-2020  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using Crow.Cairo;
+
+namespace Crow
+{
+       public class DbgLogViewer : ScrollingObject
+       {
+               public static Dictionary<DbgEvtType, Color> colors;
+
+               public static Configuration colorsConf = new Configuration ("dbgcolor.conf");//, Interface.GetStreamFromPath("#Crow.dbgcolor.conf"));
+
+               public static void reloadColors () {
+                       colors = new Dictionary<DbgEvtType, Color>();
+                       foreach (string n in colorsConf.Names) {
+                               DbgEvtType t = (DbgEvtType)Enum.Parse (typeof(DbgEvtType), n);
+                               Color c = colorsConf.Get<Color> (n);
+                               colors.Add (t, c);
+                       }
+
+               }
+               #region CTOR
+               static DbgLogViewer() {
+                       reloadColors ();
+               }
+               protected DbgLogViewer () : base(){}
+               public DbgLogViewer (Interface iface, string style = null) : base(iface, style){}
+               #endregion
+
+               FontExtents fe;
+
+               double xScale = 1.0/1024.0, yScale = 1.0, leftMargin, topMargin = 0.0;
+               DbgWidgetRecord curWidget, hoverWidget;
+               DbgEvent curEvent, hoverEvent;
+
+               List<DbgEvent> events = new List<DbgEvent> ();
+               List<DbgWidgetRecord> widgets = new List<DbgWidgetRecord> ();
+
+               public List<DbgEvent> Events {
+                       get => events;
+                       set {
+                               if (events == value)
+                                       return;
+                               events = value;
+                               NotifyValueChanged (nameof (Events), events);
+                               if (events == null)
+                                       return;
+
+                               maxTicks = 0;
+                               minTicks = long.MaxValue;
+                               foreach (DbgEvent e in events) {
+                                       if (e.begin < minTicks)
+                                               minTicks = e.begin;
+                                       if (e.end > maxTicks)
+                                               maxTicks = e.end;
+                               }
+
+                               visibleTicks = maxTicks - minTicks;
+                               XScale = (ClientRectangle.Width - leftMargin)/visibleTicks;
+                               ScrollX = 0;
+                               ScrollY = 0;
+
+                               RegisterForGraphicUpdate ();
+                       }
+               }
+               public List<DbgWidgetRecord> Widgets {
+                       get => widgets;
+                       set {
+                               if (widgets == value)
+                                       return;
+                               widgets = value;
+                               NotifyValueChanged (nameof (Widgets), widgets);
+                               updateMargins ();
+                               updateMaxScrollX ();
+                               updateMaxScrollY ();
+                       }
+               }
+               public DbgWidgetRecord CurrentWidget {
+                       get => curWidget;
+                       set {
+                               if (curWidget == value)
+                                       return;
+                               curWidget = value;
+                               NotifyValueChanged (nameof (CurrentWidget), curWidget);
+                       }
+               }
+               public DbgEvent CurrentEvent {
+                       get => curEvent;
+                       set {
+                               if (curEvent == value)
+                                       return;
+                               if (curEvent != null)
+                                       curEvent.IsSelected = false;
+                               curEvent = value;
+                               if (curEvent != null) {
+                                       curEvent.IsSelected = true;
+                                       if (curEvent is DbgWidgetEvent we) {
+                                               //CurrentWidget = Widgets [we.InstanceIndex];
+                                               currentLine = we.InstanceIndex;
+                                       }
+                                       currentTick = curEvent.begin;
+                                       if (curEvent.begin > minTicks + ScrollX + visibleTicks ||
+                                               curEvent.end < minTicks + ScrollX) {
+                                               ScrollX = (int)(currentTick - visibleTicks / 2);
+                                       }
+                               }
+                               NotifyValueChanged (nameof (CurrentEvent), curEvent);
+                               RegisterForRedraw ();
+                       }
+               }
+               public DbgWidgetRecord HoverWidget {
+                       get => hoverWidget;
+                       internal set {
+                               if (hoverWidget == value)
+                                       return;
+                               hoverWidget = value;
+                               NotifyValueChanged (nameof (HoverWidget), hoverWidget);
+                       }
+               }
+
+               public DbgEvent HoverEvent {
+                       get => hoverEvent;
+                       set {
+                               if (hoverEvent == value)
+                                       return;
+                               hoverEvent = value;
+                               NotifyValueChanged (nameof (HoverEvent), hoverEvent);
+                       }
+               }
+
+               long currentTick = 0, selStart = -1, selEnd = -1, minTicks = 0, maxTicks = 0, visibleTicks = 0;
+               int currentLine = -1;
+               int visibleLines = 1;
+               Point mousePos;
+
+               public double XScale {
+                       get { return xScale; }
+                       set {
+                               if (xScale == value)
+                                       return;
+                               xScale = value;
+                               NotifyValueChanged ("XScale", xScale);
+                               updateVisibleTicks ();
+                               RegisterForGraphicUpdate ();
+                       }
+               }
+               public double YScale {
+                       get => yScale;
+                       set {
+                               if (yScale == value)
+                                       return;
+                               yScale = value;
+                               NotifyValueChanged ("YScale", yScale);
+                               RegisterForGraphicUpdate ();
+                       }
+               }
+               public override Font Font {
+                       get { return base.Font; }
+                       set {
+                               base.Font = value;
+                               using (Context gr = new Context (IFace.surf)) {
+                                       gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight);
+                                       gr.SetFontSize (Font.Size);
+
+                                       fe = gr.FontExtents;
+                               }
+                               updateMargins ();
+                       }
+               }
+               public override int ScrollY {
+                       get => base.ScrollY;
+                       set {
+                               base.ScrollY = value;
+
+                               if (widgets == null)
+                                       return;
+
+                               Rectangle cb = ClientRectangle;
+                               cb.Left += (int)leftMargin;
+                               cb.Width -= (int)leftMargin;
+                               cb.Y += (int)topMargin;
+                               cb.Height -= (int)topMargin;
+
+                               if (mousePos.Y < cb.Top || mousePos.Y > cb.Bottom)
+                                       currentLine = -1;
+                               else
+                                       currentLine = (int)((double)(mousePos.Y - cb.Top) / fe.Height) + ScrollY;
+
+                               NotifyValueChanged ("CurrentLine", currentLine);
+                       }
+               }
+
+               void drawEvents (Context ctx, List<DbgEvent> evts)
+               {
+                       if (evts == null || evts.Count == 0)
+                               return;
+                       Rectangle cb = ClientRectangle;
+
+                       foreach (DbgEvent evt in evts) {
+                               if (evt.end - minTicks <= ScrollX)
+                                       continue;
+                               if (evt.begin - minTicks > ScrollX + visibleTicks)
+                                       break;
+                               double penY = topMargin + ClientRectangle.Top;
+                               if (evt.type.HasFlag (DbgEvtType.Widget)) {
+                                       DbgWidgetEvent eW = evt as DbgWidgetEvent;
+                                       int lIdx = eW.InstanceIndex - ScrollY;
+                                       if (lIdx < 0 || lIdx > visibleLines)
+                                               continue;
+                                       penY += (lIdx) * fe.Height; 
+                               
+                                       ctx.SetSource (evt.Color);
+
+                                       double x = xScale * (evt.begin - minTicks - ScrollX);
+                                       double w = Math.Max (Math.Max (2.0, 2.0 * xScale), (double)(evt.end - evt.begin) * xScale);
+                                       if (x < 0.0) {
+                                               w += x;
+                                               x = 0.0;
+                                       }
+                                       x += leftMargin + cb.Left;
+                                       double rightDiff = x + w - cb.Right;
+                                       if (rightDiff > 0)
+                                               w -= rightDiff;
+
+                                       ctx.Rectangle (x, penY, w, fe.Height);
+                                       ctx.Fill ();
+                               } else {
+                                       /*double x = xScale * (evt.begin - minTicks - ScrollX);
+                                       x += leftMargin + cb.Left;
+
+                                       double trunc = Math.Truncate (x);
+                                       if (x - trunc > 0.5)
+                                               x = trunc + 0.5;
+                                       else
+                                               x = trunc - 0.5;
+
+
+                                       ctx.SetSource (Colors.Yellow);
+                                       ctx.MoveTo (x, penY);
+                                       ctx.LineTo (x, cb.Bottom);
+                                       ctx.Stroke ();
+                                       string s = evt.type.ToString () [5].ToString ();
+                                       TextExtents te = ctx.TextExtents (s);
+                                       ctx.Rectangle (x - 0.5 * te.Width, penY - te.Height, te.Width, te.Height);
+                                       ctx.Fill ();
+                                       ctx.MoveTo (x - 0.5 * te.Width, penY - ctx.FontExtents.Descent);
+                                       ctx.SetSource (Colors.Jet);
+                                       ctx.ShowText (s);*/
+
+                               }
+                               drawEvents (ctx, evt.Events);
+                       }
+               }
+
+               protected override void onDraw (Cairo.Context gr)
+               {
+                       base.onDraw (gr);
+
+                       gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight);
+                       gr.SetFontSize (Font.Size);
+                       gr.FontOptions = Interface.FontRenderingOptions;
+                       gr.Antialias = Cairo.Antialias.None;
+
+                       if (widgets == null)
+                               return;
+
+                       gr.LineWidth = 1.0;
+
+                       Rectangle cb = ClientRectangle;
+
+                       double penY = topMargin + ClientRectangle.Top;
+
+                       for (int i = 0; i < visibleLines; i++) {
+                               if (i + ScrollY >= widgets.Count)
+                                       break;
+                               int gIdx = i + ScrollY;
+                               DbgWidgetRecord g = widgets [gIdx];
+
+                               penY += fe.Height;
+
+                               gr.SetSource (Crow.Colors.Jet);
+                               gr.MoveTo (cb.X, penY - 0.5);
+                               gr.LineTo (cb.Right, penY - 0.5);
+                               gr.Stroke ();
+
+                               double penX = 5.0 * g.xLevel + cb.Left;
+
+                               if (g.yIndex == 0)
+                                       gr.SetSource (Crow.Colors.LightSalmon);
+                               else
+                                       Foreground.SetAsSource (gr);
+
+                               gr.MoveTo (penX, penY - gr.FontExtents.Descent);
+                               gr.ShowText (g.name + gIdx);
+                       }
+
+                       drawEvents (gr, events);
+                       /*
+                       for (int i = 0; i < visibleLines; i++) { 
+                               foreach (DbgEvent evt in events) {
+                                       if (evt.end - minTicks <= ScrollX)
+                                               continue;
+                                       if (evt.begin - minTicks > ScrollX + visibleTicks)
+                                               break;
+                                       
+                                       
+                               }
+
+                               
+                       }
+                       */
+
+                       gr.MoveTo (cb.Left, topMargin - 0.5 + cb.Top);
+                       gr.LineTo (cb.Right, topMargin - 0.5 + cb.Top);
+
+                       gr.MoveTo (leftMargin + cb.Left, cb.Top);
+                       gr.LineTo (leftMargin + cb.Left, cb.Bottom);
+                       gr.SetSource (Crow.Colors.Grey);
+
+                       penY = topMargin + ClientRectangle.Top;
+
+                       //graduation
+                       int largeGrad = int.Parse ("1" + new string ('0', visibleTicks.ToString ().Length - 1));
+                       int smallGrad = Math.Max (1, largeGrad / 10);
+
+                       long firstVisibleTicks = minTicks + ScrollX;
+                       long curGrad = firstVisibleTicks - firstVisibleTicks % smallGrad + smallGrad;
+
+                       long gg = curGrad - ScrollX - minTicks;
+                       while (gg < visibleTicks ) {
+                               double x = (double)gg * xScale + leftMargin + cb.Left;
+
+                               gr.MoveTo (x, penY - 0.5);
+                               if (curGrad % largeGrad == 0) { 
+                                       gr.LineTo (x, penY - 8.5);
+                                       string str = curGrad.ToString ();
+                                       TextExtents te = gr.TextExtents (str);
+                                       gr.RelMoveTo (-0.5 * te.Width, -2.0);
+                                       gr.ShowText (str);
+                               }else
+                                       gr.LineTo (x, penY - 2.5);
+
+                               curGrad += smallGrad;
+                               gg = curGrad - ScrollX - minTicks;
+                       }
+
+                       gr.Stroke ();
+
+                       //global events
+/*                     foreach (DbgEvent evt in events) {
+                               if (evt.begin - minTicks <= ScrollX)
+                                       continue;
+                               double x = xScale * (evt.begin - minTicks - ScrollX) ;
+                               x += leftMargin + cb.Left;
+
+
+                       }*/
+
+               }
+               public override void Paint (ref Cairo.Context ctx)
+               {
+                       base.Paint (ref ctx);
+
+                       Rectangle r = new Rectangle(mousePos.X, 0, 1, Slot.Height);
+                       Rectangle ctxR = ContextCoordinates (r);
+                       Rectangle cb = ClientRectangle;
+                       ctx.LineWidth = 1.0;
+                       double x = xScale * (currentTick - minTicks - ScrollX) + leftMargin;
+                       if (x - Math.Truncate (x) > 0.5)
+                               x = Math.Truncate (x) + 0.5;
+                       else
+                               x = Math.Truncate (x) - 0.5;
+                       ctx.MoveTo (x, cb.Top + topMargin - 4.0);
+                       ctx.LineTo (x, cb.Bottom);
+
+                       //ctx.Rectangle (ctxR);
+                       ctx.SetSource (Colors.CornflowerBlue);
+                       ctx.Stroke();
+
+                       ctx.SelectFontFace (Font.Name, Font.Slant, Font.Wheight);
+                       ctx.SetFontSize (Font.Size);
+                       ctx.FontOptions = Interface.FontRenderingOptions;
+                       ctx.Antialias = Interface.Antialias;
+
+                       ctx.MoveTo (ctxR.X - ctx.TextExtents (currentTick.ToString ()).Width / 2, ctxR.Y + fe.Height);
+                       ctx.ShowText (currentTick.ToString ());
+
+                       ctx.Operator = Cairo.Operator.Add;
+
+                       if (currentLine >= 0) {
+                               double y = fe.Height * (currentLine - ScrollY) + topMargin + cb.Top;
+                               r = new Rectangle (cb.Left,  (int)y, cb.Width, (int)fe.Height);
+
+                               ctx.Operator = Cairo.Operator.Add;
+                               ctx.SetSourceRGBA (0.1, 0.1, 0.1, 0.4);
+                               ctx.Rectangle (ContextCoordinates (r));
+                               ctx.Fill ();
+                       }
+
+                       if (CurrentWidget != null) {
+
+                       }
+
+                       if (selStart < 0 || selEnd < 0) {
+                               ctx.Operator = Cairo.Operator.Over;
+                               return;
+                       }
+                       double selStartX = (double)(selStart - ScrollX - minTicks) * xScale + leftMargin + cb.Left;
+                       double selEndX = (double)(selEnd - ScrollX - minTicks) * xScale + leftMargin + cb.Left;
+
+                       if (selStartX < selEndX) {
+                               ctxR.X = (int)selStartX;
+                               ctxR.Width = (int)(selEndX - selStartX);
+                       } else {
+                               ctxR.X = (int)selEndX;
+                               ctxR.Width = (int)(selStartX - selEndX);
+                       }
+
+                       ctxR.Width = Math.Max (1, ctxR.Width);
+                       ctx.Rectangle (ctxR);
+                       //ctx.SetSourceColor (Color.LightYellow);
+                       ctx.SetSource (Colors.Jet);
+                       ctx.Fill();
+                       ctx.Operator = Cairo.Operator.Over;
+
+               }
+               public override void OnLayoutChanges (LayoutingType layoutType)
+               {
+                       base.OnLayoutChanges (layoutType);
+                       switch (layoutType) {
+                       case LayoutingType.Width:
+                               if (xScale < 0) {
+                                       visibleTicks = maxTicks - minTicks;
+                                       XScale = (ClientRectangle.Width - leftMargin) / visibleTicks;
+                               }
+                               updateVisibleTicks ();
+                               break;
+                       case LayoutingType.Height:
+                               updateVisibleLines ();
+                               break;
+                       }
+               }
+
+               public override void onMouseLeave (object sender, MouseMoveEventArgs e)
+               {
+                       base.onMouseLeave (sender, e);
+                       currentLine = -1;
+                       currentTick = 0;
+               }
+               public override void onMouseMove (object sender, MouseMoveEventArgs e)
+               {
+                       base.onMouseMove (sender, e);
+
+                       long lastTick = currentTick;
+                       updateMouseLocalPos (e.Position);
+
+                       if (IFace.IsDown (Glfw.MouseButton.Left) && selStart >= 0)
+                               selEnd = currentTick;
+                       else if (IFace.IsDown(Glfw.MouseButton.Right)) {
+                               ScrollX += (int)(lastTick - currentTick);
+                               updateMouseLocalPos (e.Position);
+                       } else {
+                               HoverWidget = (currentLine < 0 || currentLine >= widgets.Count) ? null : widgets [currentLine];
+                               HoverEvent = hoverWidget?.Events.FirstOrDefault (ev => ev.begin <= currentTick && ev.end >= currentTick);
+                       }
+
+                       if (RegisteredLayoutings == LayoutingType.None && !IsDirty)
+                               IFace.EnqueueForRepaint (this);
+                       
+               }
+               public override void onMouseDown (object sender, MouseButtonEventArgs e)
+               {
+                       base.onMouseDown (sender, e);
+
+                       if (e.Button == Glfw.MouseButton.Left) {
+                               CurrentWidget = hoverWidget;
+                               CurrentEvent = hoverEvent;
+                               selStart = currentTick;
+                               selEnd = -1;
+                       }
+
+                       RegisterForRedraw ();
+               }
+               public override void onMouseUp (object sender, MouseButtonEventArgs e)
+               {
+                       base.onMouseUp (sender, e);
+
+                       if (e.Button == Glfw.MouseButton.Left && selEnd > 0 && selEnd != selStart) {
+                               long scrX = 0;
+                               if (selStart < selEnd) {
+                                       visibleTicks = selEnd - selStart;
+                                       scrX = selStart - minTicks;
+                               } else {
+                                       visibleTicks = selStart - selEnd;
+                                       scrX = selEnd - minTicks;
+                               }
+                               XScale = (ClientRectangle.Width - leftMargin) / visibleTicks;
+                               ScrollX = (int)scrX;
+                       }
+                       selStart = -1;
+                       selEnd = -1;
+
+                       RegisterForRedraw ();
+               }
+
+               /// <summary> Process scrolling vertically, or if shift is down, vertically </summary>
+               public override void onMouseWheel (object sender, MouseWheelEventArgs e)
+               {                       
+                       base.onMouseWheel (sender, e);
+
+                       if (IFace.Shift)
+                               ScrollX -= (int)((double)(e.Delta * MouseWheelSpeed) / xScale);
+                       else if (IFace.Ctrl) {
+                               if (e.Delta > 0) {
+                                       XScale *= 2.0;
+                               } else {
+                                       if (MaxScrollX > 0)
+                                               XScale *= 0.5;
+                               }
+                               ScrollX = (int)(currentTick - (int)((double)Math.Max(0, mousePos.X - (int)leftMargin) / xScale) - minTicks);
+                       }else
+                               ScrollY -= e.Delta * MouseWheelSpeed;
+               }
+
+               public override void onKeyDown (object sender, KeyEventArgs e)
+               {
+                       base.onKeyDown (sender, e);
+
+                       if (e.Key == Glfw.Key.F3) {
+                               if (selEnd < 0)
+                                       return;
+                               if (selEnd < selStart)
+                                       zoom (selEnd, selStart);
+                               else
+                                       zoom (selStart, selEnd);
+                               selEnd = selStart = -1;
+                       }
+               }
+
+               void updateMargins ()
+               {
+                       leftMargin = topMargin = 0.0;
+
+                       if (widgets == null)
+                               return;
+
+                       using (Context gr = new Context (IFace.surf)) {
+                               double maxNameWidth = 0.0;
+
+                               gr.SelectFontFace (Font.Name, Font.Slant, Font.Wheight);
+                               gr.SetFontSize (Font.Size);
+
+                               foreach (DbgWidgetRecord o in widgets) {
+                                       double nameWidth = gr.TextExtents (o.name).Width + 5.0 * o.xLevel;
+                                       if (nameWidth > maxNameWidth)
+                                               maxNameWidth = nameWidth;
+                               }
+
+                               leftMargin = 10.5 + maxNameWidth;
+                               topMargin = 2.0 * fe.Height;
+
+                               RegisterForGraphicUpdate ();
+                       }
+               }
+
+               void updateVisibleLines ()
+               {
+                       visibleLines = fe.Height < 1 ? 1 : (int)Math.Floor (((double)ClientRectangle.Height - topMargin) / fe.Height);
+                       NotifyValueChanged ("VisibleLines", visibleLines);
+                       updateMaxScrollY ();
+               }
+               void updateVisibleTicks ()
+               {
+                       visibleTicks = Math.Max (0, (long)((double)(ClientRectangle.Width - leftMargin) / XScale));
+                       NotifyValueChanged ("VisibleTicks", visibleTicks);
+                       updateMaxScrollX ();
+               }
+
+               void updateMaxScrollX ()
+               {
+                       if (widgets == null)
+                               MaxScrollX = 0;
+                       else
+                               MaxScrollX = (int)Math.Max (0L, maxTicks - minTicks - visibleTicks);
+               }
+               void updateMaxScrollY ()
+               {
+                       if (widgets == null)
+                               MaxScrollY = 0;
+                       else
+                               MaxScrollY = Math.Max (0, widgets.Count - visibleLines);
+               }
+
+               void updateMouseLocalPos (Point mPos)
+               {
+                       Rectangle r = ScreenCoordinates (Slot);
+                       Rectangle cb = ClientRectangle;
+                       cb.Left += (int)leftMargin;
+                       cb.Width -= (int)leftMargin;
+                       cb.Y += (int)topMargin;
+                       cb.Height -= (int)topMargin;
+
+                       mousePos = mPos - r.Position;
+
+                       mousePos.X = Math.Max (cb.X, mousePos.X);
+                       mousePos.X = Math.Min (cb.Right, mousePos.X);
+
+                       if (mousePos.Y < cb.Top || mousePos.Y > cb.Bottom)
+                               currentLine = -1;
+                       else
+                               currentLine = (int)((double)(mousePos.Y - cb.Top) / fe.Height) + ScrollY;
+
+                       NotifyValueChanged ("CurrentLine", currentLine);
+
+                       mousePos.Y = Math.Max (cb.Y, mousePos.Y);
+                       mousePos.Y = Math.Min (cb.Bottom, mousePos.Y);
+
+                       currentTick = (int)((double)(mousePos.X - cb.X) / xScale) + minTicks + ScrollX;
+                       RegisterForRedraw ();
+               }
+               void zoom (long start, long end) {                                              
+                       //Rectangle cb = ClientRectangle;
+                       //cb.X += (int)leftMargin;
+                       XScale = ((double)ClientRectangle.Width - leftMargin)/(end - start);
+                       ScrollX = (int)(start - minTicks);
+               }
+       }
+}
+
+
diff --git a/Samples/DebugLogAnalyzer/DebugLogAnalyzer.csproj b/Samples/DebugLogAnalyzer/DebugLogAnalyzer.csproj
new file mode 100644 (file)
index 0000000..df93e68
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project Sdk="Microsoft.NET.Sdk">
+       <ItemGroup>
+               <EmbeddedResource Include="ui\**\*.*">
+                       <LogicalName>Dbg.%(Filename)%(Extension)</LogicalName>
+               </EmbeddedResource>
+</ItemGroup>
+
+</Project>
\ No newline at end of file
diff --git a/Samples/DebugLogAnalyzer/Program.cs b/Samples/DebugLogAnalyzer/Program.cs
new file mode 100644 (file)
index 0000000..6be26ce
--- /dev/null
@@ -0,0 +1,178 @@
+// Copyright (c) 2013-2020  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System;
+using System.Collections.Generic;
+using System.IO;
+using Crow;
+using Crow.Cairo;
+
+namespace DebugLogAnalyzer
+{
+       public class Program : SampleBase
+       {
+               static void Main (string [] args)
+               {
+#if NETCOREAPP3_1
+                       DllMapCore.Resolve.Enable (true);
+#endif
+                       using (Program app = new Program ()) 
+                               app.Run ();
+               }
+
+               List<DbgEvent> events = new List<DbgEvent>();
+               List<DbgWidgetRecord> widgets = new List<DbgWidgetRecord>();
+               DbgEvent curEvent = new DbgEvent();
+               DbgWidgetRecord curWidget = new DbgWidgetRecord();
+
+               public List<DbgEvent> Events {
+                       get => events;
+                       set {
+                               if (events == value)
+                                       return;
+                               events = value;
+                               NotifyValueChanged (nameof (Events), events);
+                       }
+               }
+               public List<DbgWidgetRecord> Widgets {
+                       get => widgets;
+                       set {
+                               if (widgets == value)
+                                       return;
+                               widgets = value;
+                               NotifyValueChanged (nameof (Widgets), widgets);
+                       }
+               }
+               public DbgEvent CurrentEvent {
+                       get => curEvent;
+                       set {
+                               if (curEvent == value)
+                                       return;
+
+                               if (curEvent != null)
+                                       curEvent.IsSelected = false;
+                               curEvent = value;
+                               if (curEvent != null) {
+                                       curEvent.IsSelected = true;
+                                       if (curEvent.parentEvent != null)
+                                               curEvent.parentEvent.IsExpanded = true;
+                               }
+
+                               NotifyValueChanged (nameof (CurrentEvent), curEvent);
+                       }
+               }
+               public DbgWidgetRecord CurrentWidget {
+                       get => curWidget;
+                       set {
+                               if (curWidget == value)
+                                       return;
+                               curWidget = value;
+                               NotifyValueChanged (nameof (CurrentWidget), curWidget);
+                               NotifyValueChanged ("CurWidgetRootEvents", CurWidgetRootEvents);
+                       }
+               }
+               public List<DbgWidgetEvent> CurWidgetRootEvents => curWidget == null? new List<DbgWidgetEvent>() : curWidget.RootEvents;
+
+               Scroller dbgTreeViewScroller;
+
+               protected override void OnInitialized ()
+               {
+                       Load ("#Dbg.dbglog.crow").DataSource = this;
+
+                       TreeView tv = FindByName("dbgTV") as TreeView;
+                       dbgTreeViewScroller = tv.FindByNameInTemplate ("scroller1") as Scroller;
+
+                       loadDebugFile ("/var/tmp/debug.log");
+               }
+
+               void loadDebugFile (string logFile)
+               {
+                       if (!File.Exists (logFile))
+                               return;
+
+                       List<DbgEvent> evts = new List<DbgEvent> ();
+                       List<DbgWidgetRecord> objs = new List<DbgWidgetRecord> ();
+
+                       using (StreamReader s = new StreamReader (logFile)) {
+                               if (s.ReadLine () != "[GraphicObjects]")
+                                       return;
+                               while (!s.EndOfStream) {
+                                       string l = s.ReadLine ();
+                                       if (l == "[Events]")
+                                               break;
+                                       DbgWidgetRecord o = DbgWidgetRecord.Parse (l);
+                                       objs.Add (o);
+                               }
+
+                               Stack<DbgEvent> startedEvents = new Stack<DbgEvent> ();
+
+                               if (!s.EndOfStream) {
+                                       while (!s.EndOfStream) {
+                                               int level = 0;
+                                               while (s.Peek () == (int)'\t') {
+                                                       s.Read ();
+                                                       level++;
+                                               }
+                                               DbgEvent evt = DbgEvent.Parse (s.ReadLine ());                                                  
+                                               if (level == 0) {
+                                                       startedEvents.Clear ();
+                                                       evts.Add (evt);
+                                               } else {
+                                                       int levelDiff = level - startedEvents.Count + 1;
+                                                       if (levelDiff > 0) {
+                                                               if (levelDiff > 1)
+                                                                       System.Diagnostics.Debugger.Break ();
+                                                               startedEvents.Peek ().AddEvent (evt);
+                                                       } else {
+                                                               startedEvents.Pop ();
+                                                               if (-levelDiff > startedEvents.Count)
+                                                                       System.Diagnostics.Debugger.Break ();
+                                                               while (startedEvents.Count > level)
+                                                                       startedEvents.Pop ();
+                                                               startedEvents.Peek ().AddEvent (evt);
+                                                       }
+                                               }
+                                               startedEvents.Push (evt);
+                                               if (evt.type.HasFlag (DbgEvtType.Widget))
+                                                       objs [(evt as DbgWidgetEvent).InstanceIndex].Events.Add (evt);
+                                       }
+                               }
+                       }
+                       Widgets = objs;
+                       Events = evts;
+               }
+
+               int targetTvScroll = -1;
+
+               void onTvPainted (object sender, EventArgs e)
+               {
+                       if (targetTvScroll < 0 || targetTvScroll > dbgTreeViewScroller.MaxScrollY + dbgTreeViewScroller.Slot.Height)
+                               return;
+                       dbgTreeViewScroller.MaxScrollY = targetTvScroll;
+                       targetTvScroll = -1;
+               }
+
+               void onSelectedItemContainerChanged (object sender, SelectionChangeEventArgs e)
+               {
+                       TreeView tv = sender as TreeView;
+                       Group it = tv.FindByNameInTemplate ("ItemsContainer") as Group;
+
+                       ListItem li = e.NewValue as ListItem;
+                       Rectangle selRect = li.RelativeSlot (it);
+
+                       if (selRect.Y > dbgTreeViewScroller.ScrollY && selRect.Y < dbgTreeViewScroller.Slot.Height + dbgTreeViewScroller.ScrollY)
+                               return;
+
+                       Console.WriteLine ($"Scroll={dbgTreeViewScroller.ScrollY} selRectY={selRect.Y} MaxScrollY={dbgTreeViewScroller.MaxScrollY} ScrollerH={dbgTreeViewScroller.Slot.Height}");
+                       targetTvScroll = selRect.Y;
+                       if (selRect.Y > dbgTreeViewScroller.MaxScrollY + dbgTreeViewScroller.Slot.Height)
+                               targetTvScroll = selRect.Y;
+                       else {
+                               targetTvScroll = -1;
+                               dbgTreeViewScroller.ScrollY = selRect.Y;
+                       }
+               }
+
+       }
+}
+
diff --git a/Samples/DebugLogAnalyzer/Properties/AssemblyInfo.cs b/Samples/DebugLogAnalyzer/Properties/AssemblyInfo.cs
new file mode 100644 (file)
index 0000000..d864652
--- /dev/null
@@ -0,0 +1,5 @@
+// Copyright (c) 2013-2020  Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+[assembly: Crow.Crow ]
+
diff --git a/Samples/DebugLogAnalyzer/ui/DbgEventTreeItems.itemp b/Samples/DebugLogAnalyzer/ui/DbgEventTreeItems.itemp
new file mode 100644 (file)
index 0000000..2f6fbfb
--- /dev/null
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<ItemTemplate Data="Events">
+       <ListItem IsSelected="{²IsSelected}" Height="Fit"
+                                               Selected="{/exp.Background=${ControlHighlight}}"
+                                               Unselected="{/exp.Background=Transparent}">
+               <Expandable Name="exp" Caption="{type}" MouseDoubleClick="/onClickForExpand" CacheEnabled="true" IsExpanded="{²IsExpanded}">
+                       <Template>
+                                       <VerticalStack>
+                                               <Border CornerRadius="2" Margin="0" Height="Fit" MouseDoubleClick="./onClickForExpand"
+                                                               Foreground="Transparent"
+                                                               Background="{./Background}"
+                                                               MouseEnter="{Foreground=DimGrey}"
+                                                               MouseLeave="{Foreground=Transparent}">
+                                                       <HorizontalStack Spacing="2" >
+                                                               <Image Margin="1" Width="9" Height="9" Focusable="true" MouseDown="./onClickForExpand"
+                                                                       Path="{./Image}"
+                                                                       Visible="{HasChildEvents}"
+                                                                       SvgSub="{./IsExpanded}"
+                                                                       MouseEnter="{Background=LightGrey}"
+                                                                       MouseLeave="{Background=Transparent}"/>
+                                                               <Label Text="{./Caption}" Width="80" Font="mono, 8" />
+                                                               <Label Text="{Duration}" Width="40" Font="mono, 8" TextAlignment="Center" Background="DimGrey"/>
+                                                               <DbgEventWidget Event="{}" Tooltip="#Dbg.DbgEvtTooltip.crow" Width="Stretched" Height="5"/>
+                                                       </HorizontalStack>
+                                               </Border>
+                                               <Container Name="Content" Visible="false"/>
+                                       </VerticalStack>
+                       </Template>
+                       <HorizontalStack Height="Fit">
+                               <Widget Width="12" Height="10"/>
+                               <VerticalStack Height="Fit" Name="ItemsContainer"/>
+                       </HorizontalStack>
+               </Expandable>
+       </ListItem>
+</ItemTemplate>                
+<ItemTemplate Data="Events" DataType="DbgWidgetEvent">
+       <ListItem IsSelected="{²IsSelected}" Height="Fit"
+                                               Selected="{/exp.Background=${ControlHighlight}}"
+                                               Unselected="{/exp.Background=Transparent}">
+               <Expandable Name="exp" Caption="{type}" MouseDoubleClick="/onClickForExpand" CacheEnabled="true" IsExpanded="{²IsExpanded}">
+                       <Template>
+                               <VerticalStack>
+                                       <Border CornerRadius="2" Margin="0" Height="Fit" MouseDoubleClick="./onClickForExpand"
+                                                       Foreground="Transparent"
+                                                       Background="{./Background}"
+                                                       MouseEnter="{Foreground=DimGrey}"
+                                                       MouseLeave="{Foreground=Transparent}">
+                                               <HorizontalStack Spacing="2" >
+                                                       <Image Margin="1" Width="9" Height="9" Focusable="true" MouseDown="./onClickForExpand"
+                                                               Path="{./Image}"
+                                                               Visible="{HasChildEvents}"
+                                                               SvgSub="{./IsExpanded}"
+                                                               MouseEnter="{Background=LightGrey}"
+                                                               MouseLeave="{Background=Transparent}"/>
+                                                       <Label Text="{./Caption}" Width="80" Font="mono, 8" />
+                                                       <Label Text="{Duration}" Width="40" Font="mono, 8" TextAlignment="Center" Background="DimGrey"/>
+                                                       <Label Text="{InstanceIndex}" Width="40" Font="mono, 8" TextAlignment="Center" Background="DimGrey"/>
+                                                       <DbgEventWidget Event="{}" Tooltip="#Dbg.DbgEvtTooltip.crow" Width="Stretched" Height="5"/>
+                                               </HorizontalStack>
+                                       </Border>
+                                       <Container Name="Content" Visible="false"/>
+                               </VerticalStack>
+                       </Template>
+                       <HorizontalStack Height="Fit">
+                               <Widget Width="12" Height="10"/>
+                               <VerticalStack Height="Fit" Name="ItemsContainer"/>
+                       </HorizontalStack>
+               </Expandable>
+       </ListItem>
+</ItemTemplate>                
\ No newline at end of file
diff --git a/Samples/DebugLogAnalyzer/ui/DbgEvtTooltip.crow b/Samples/DebugLogAnalyzer/ui/DbgEvtTooltip.crow
new file mode 100644 (file)
index 0000000..9d40f45
--- /dev/null
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<Border Fit="true" Foreground="${TooltipForeground}" Background="${TooltipBackground}">
+       <VerticalStack Fit="true" Margin="${TooltipMargin}" DataSource="{HoverEvent}">
+               <Label Text="{type}" Background="{Color}" Foreground="Black" Width="200" TextAlignment="Center" Margin="6"/>
+               <HorizontalStack Height="Fit" Width="Stretched" Spacing="2" Visible="{IsWidgetEvent}" Background="DimGrey" Margin="2">
+                       <Label Text="Instance:" Foreground="White" Width="50%" />       
+                       <Label Text="{InstanceIndex}" Foreground="White" Background="Onyx" Width="Stretched" TextAlignment="Center"/>                           
+               </HorizontalStack>
+               <HorizontalStack Height="Fit" Width="Stretched" Spacing="2" Background="DimGrey" Margin="2">
+                       <Label Text="Duration:" Foreground="White" Width="50%" />       
+                       <Label Text="{Duration}" Foreground="White" Background="Onyx" Width="Stretched" TextAlignment="Right"/>                         
+               </HorizontalStack>
+               <HorizontalStack Height="Fit" Width="Stretched" Spacing="2">
+                       <Label Text="Begin:" Foreground="${TooltipForeground}" Width="50%"/>    
+                       <Label Text="{begin}" Foreground="${TooltipForeground}" Width="Stretched" TextAlignment="Right"/>                               
+               </HorizontalStack>
+               <HorizontalStack Height="Fit" Width="Stretched" Spacing="2">
+                       <Label Text="End:" Foreground="${TooltipForeground}" Width="50%"/>      
+                       <Label Text="{end}" Foreground="${TooltipForeground}" Width="Stretched" TextAlignment="Right"/>                         
+               </HorizontalStack>
+               <VerticalStack Height="Fit" Width="Stretched" >
+                       <HorizontalStack Height="Fit" Width="Stretched" Spacing="2" Background="DimGrey" Margin="2">
+                               <Label Text="Result:" Foreground="White" Width="50%" /> 
+                               <Label Text="{result}" Foreground="White" Background="Onyx" Width="Stretched" TextAlignment="Center"/>                          
+                       </HorizontalStack>
+                       <HorizontalStack Height="Fit" Width="Stretched" Spacing="2" Background="DimGrey" Margin="2">
+                               <Label Text="Old Slot:" Foreground="White" Width="50%" />       
+                               <Label Text="{OldSlot}" Foreground="White" Background="Onyx" Width="Stretched" TextAlignment="Center"/>                         
+                       </HorizontalStack>
+                       <HorizontalStack Height="Fit" Width="Stretched" Spacing="2" Background="DimGrey" Margin="2">
+                               <Label Text="New Slot:" Foreground="White" Width="50%" />       
+                               <Label Text="{NewSlot}" Foreground="White" Background="Onyx" Width="Stretched" TextAlignment="Center"/>                         
+                       </HorizontalStack>
+               </VerticalStack>                
+               <!--<Label Text="{}" Foreground="${TooltipForeground}"/>-->
+       </VerticalStack>
+</Border>
\ No newline at end of file
diff --git a/Samples/DebugLogAnalyzer/ui/dbglog.crow b/Samples/DebugLogAnalyzer/ui/dbglog.crow
new file mode 100644 (file)
index 0000000..72d96bf
--- /dev/null
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<VerticalStack>
+       <VerticalStack Height="40%">
+               <DbgLogViewer Visible="true" Name="dbv" Events="{Events}" Widgets="{Widgets}" MouseWheelSpeed="10" Font="mono, 8"                                                                       
+                                                                       CurrentEvent="{²CurrentEvent}" CurrentWidget="{²CurrentWidget}" Background="Onyx"/>
+               <ScrollBar Style="HScrollBar" Maximum="{../dbv.MaxScrollX}" Value="{²../dbv.ScrollX}"/>
+               <HorizontalStack Height="Fit" DataSource="{CurrentWidget}">
+                       <Label Text="{name}"/>
+                       <Label Text="{listIndex}"/>
+                       <Label Text="{treeIndex}"/>
+                       <Label Text="{yIndex}"/>
+                       <Label Text="{xLevel}"/>
+                       <Label Text="Width:"/>
+                       <Label Text="{Width}"/>
+                       <Label Text="Height:"/>
+                       <Label Text="{Height}"/>
+                       <Label Text="ScrollX"/>
+                       <Label Text="{../../dbv.ScrollX}"/>
+               </HorizontalStack>
+       </VerticalStack>
+       <Splitter/>
+       <HorizontalStack Height="40%">
+               <VerticalStack Width="Stretched">
+                       <TreeView Height="Stretched" Name="dbgTV" Data="{Events}" SelectedItem="{²CurrentEvent}" Background="DarkGrey"
+                                               SelectedItemContainerChanged="onSelectedItemContainerChanged" Painted="onTvPainted"
+                                               ItemTemplate="#Dbg.DbgEventTreeItems.itemp"/> 
+                       <ListBox Data="{CurWidgetRootEvents}" Height="100" SelectedItem="{²CurrentEvent}">
+                               <ItemTemplate>
+                                       <ListItem Margin="0" Height="16" IsSelected="{²IsSelected}" Width="Fit"
+                                                               Selected="{Background=${ControlHighlight}}"
+                                                               Unselected="{Background=Transparent}">
+                                               <DbgEventWidget Height="10" Width="Fit" Event="{}" Tooltip="#Dbg.DbgEvtTooltip.crow" TicksPerPixel="20" VerticalAlignment="Center"/>
+                                       </ListItem>
+                               </ItemTemplate>
+                               <Template>
+                                       <Wrapper Name="ItemsContainer" Spacing="0" Background="DarkGrey"/>
+                               </Template>
+                       </ListBox>                              
+               </VerticalStack>
+               <Splitter/>
+               <VerticalStack Height="Stretched" Width="20%" Margin="${TooltipMargin}" DataSource="{CurrentEvent}">
+                               <Label Text="{type}" Background="{Color}" Foreground="Black" Width="200" TextAlignment="Center" Margin="6"/>
+                               <HorizontalStack Height="Fit" Width="Stretched" Spacing="2" Visible="{IsWidgetEvent}" Background="DimGrey" Margin="2">
+                                       <Label Text="Instance:" Foreground="White" Width="50%" />       
+                                       <Label Text="{InstanceIndex}" Foreground="White" Background="Onyx" Width="Stretched" TextAlignment="Center"/>                           
+                               </HorizontalStack>
+                               <HorizontalStack Height="Fit" Width="Stretched" Spacing="2" Background="DimGrey" Margin="2">
+                                       <Label Text="Duration:" Foreground="White" Width="50%" />       
+                                       <Label Text="{Duration}" Foreground="White" Background="Onyx" Width="Stretched" TextAlignment="Right"/>                         
+                               </HorizontalStack>
+                               <HorizontalStack Height="Fit" Width="Stretched" Spacing="2">
+                                       <Label Text="Begin:" Foreground="${TooltipForeground}" Width="50%"/>    
+                                       <Label Text="{begin}" Foreground="${TooltipForeground}" Width="Stretched" TextAlignment="Right"/>                               
+                               </HorizontalStack>
+                               <HorizontalStack Height="Fit" Width="Stretched" Spacing="2">
+                                       <Label Text="End:" Foreground="${TooltipForeground}" Width="50%"/>      
+                                       <Label Text="{end}" Foreground="${TooltipForeground}" Width="Stretched" TextAlignment="Right"/>                         
+                               </HorizontalStack>
+                               <VerticalStack Height="Fit" Width="Stretched" >
+                                       <HorizontalStack Height="Fit" Width="Stretched" Spacing="2" Background="DimGrey" Margin="2">
+                                               <Label Text="Layout:" Foreground="White" Width="50%" /> 
+                                               <Label Text="{layouting}" Foreground="White" Background="Onyx" Width="Stretched" TextAlignment="Center"/>                               
+                                       </HorizontalStack>
+                                       <HorizontalStack Height="Fit" Width="Stretched" Spacing="2" Background="DimGrey" Margin="2">
+                                               <Label Text="Result:" Foreground="White" Width="50%" /> 
+                                               <Label Text="{result}" Foreground="White" Background="Onyx" Width="Stretched" TextAlignment="Center"/>                          
+                                       </HorizontalStack>
+                                       <HorizontalStack Height="Fit" Width="Stretched" Spacing="2" Background="DimGrey" Margin="2">
+                                               <Label Text="Old Slot:" Foreground="White" Width="50%" />       
+                                               <Label Text="{OldSlot}" Foreground="White" Background="Onyx" Width="Stretched" TextAlignment="Center"/>                         
+                                       </HorizontalStack>
+                                       <HorizontalStack Height="Fit" Width="Stretched" Spacing="2" Background="DimGrey" Margin="2">
+                                               <Label Text="New Slot:" Foreground="White" Width="50%" />       
+                                               <Label Text="{NewSlot}" Foreground="White" Background="Onyx" Width="Stretched" TextAlignment="Center"/>                         
+                                       </HorizontalStack>
+                               </VerticalStack>                
+                               <!--<Label Text="{}" Foreground="${TooltipForeground}"/>-->
+                       </VerticalStack>                
+       </HorizontalStack>
+       <Splitter/>
+       <VerticalStack Name="vs">
+                       
+               <VerticalStack DataSource="{CurrentEvent}">
+                       <ListBox Data="{Events}" Height="100" SelectedItem="{²CurrentEvent}">
+                               <ItemTemplate>
+                                       <ListItem Margin="0" IsSelected="{²IsSelected}" Height="12"
+                                                               Selected="{Background=${ControlHighlight}}"
+                                                               Unselected="{Background=Transparent}">
+                                               <HorizontalStack>
+                                                       <Label Text="{type}" Width="80"/>
+                                                       <DbgEventWidget Height="8" Width="Stretched" Event="{}"/>
+                                               </HorizontalStack>
+                                       </ListItem>
+                               </ItemTemplate>
+                       </ListBox>
+               </VerticalStack>
+       </VerticalStack>
+</VerticalStack>
\ No newline at end of file
index 253e0eb1c5bb46b2cdfde7cd05cbe21fa6f08835..b19bb1c6ae1a1d73f7b536b59bde978228b05f0e 100644 (file)
@@ -1,7 +1,8 @@
-<Project Sdk="Microsoft.NET.Sdk">    
-  <ItemGroup>
-    <EmbeddedResource Include="ui\**\*.*">
-      <LogicalName>HelloWorld.%(Filename)%(Extension)</LogicalName>
-    </EmbeddedResource>                
-  </ItemGroup>
-</Project>
+<?xml version="1.0" encoding="UTF-8"?>
+<Project Sdk="Microsoft.NET.Sdk">
+       <ItemGroup>
+               <EmbeddedResource Include="ui\**\*.*">
+                       <LogicalName>HelloWorld.%(Filename)%(Extension)</LogicalName>
+               </EmbeddedResource>
+       </ItemGroup>
+</Project>
\ No newline at end of file
index 743419ee01f9ce021e46780c097fc997f70d3871..1aa90bbec7128e5f6e0e99861293e2bbe66839d9 100644 (file)
@@ -11,6 +11,8 @@ namespace HelloWorld
                        using (Interface app = new Interface ()) {
                                app.Initialized += (sender, e) => (sender as Interface).Load ("#HelloWorld.helloworld.crow");
                                app.Run ();
+
+                               DbgLogger.save (app);
                        }
                }
        }
index dd0b77c9b8298cb8127bc471ce4ae16b47992dfa..f3aba2b505221fbe51910c3911e3dc45e8fd5a24 100644 (file)
@@ -8,6 +8,7 @@ using System.IO;
 using System.Text;
 using Crow.IML;
 using System.Runtime.CompilerServices;
+using Glfw;
 
 namespace ShowCase
 {
@@ -175,18 +176,17 @@ namespace ShowCase
 
                        reloadFromFile ();
                }
-
-               static void App_KeyboardKeyDown (object sender, KeyEventArgs e)
+               public override bool OnKeyDown (Key key)
                {
-#if DEBUG_LOG
-                       switch (e.Key) {
+                       switch (key) {
                        case Key.F2:
-                               DebugLog.save (sender as Interface);
-                               break;
+                               //DbgLogger.save (this);
+                               return true;
                        }
-#endif
+                       return base.OnKeyDown (key);
                }
 
+
                public void goUpDirClick (object sender, MouseButtonEventArgs e)
                {
                        string root = Directory.GetDirectoryRoot (CurrentDir);
index aea139c36e9c680ad7d0dff03e984e8e145e70fd..93aec9074374ad00ce7cd3b72b4a10f67595e65f 100644 (file)
@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using System.IO;
 using System.Linq;
 using System.Reflection;
+using System.Runtime.CompilerServices;
 using System.Threading;
 using Crow;
 
@@ -81,6 +82,79 @@ namespace Crow
                                => $"{Prop1}, {Prop2}";
 
                }
+               public class TestClassVC : IValueChange
+               {
+                       public event EventHandler<ValueChangeEventArgs> ValueChanged;
+                       public void NotifyValueChanged (object _value, [CallerMemberName] string caller = null)
+                               => ValueChanged.Raise (this, new ValueChangeEventArgs (caller, _value));
+                       string prop1, prop2;
+                       public string Prop1 {
+                               get => prop1;
+                               set {
+                                       if (prop1 == value)
+                                               return;
+                                       prop1 = value;
+                                       NotifyValueChanged (prop1);
+                               }
+                       }
+                       public string Prop2 {
+                               get => prop2;
+                               set {
+                                       if (prop2 == value)
+                                               return;
+                                       prop2 = value;
+                                       NotifyValueChanged (prop2);
+                               }
+
+
+
+               }
+
+               public override string ToString ()
+                               => $"{Prop1}, {Prop2}";
+
+               }
+               TestClass tcInstance;// = new TestClass () { Prop1 = "instance 0 prop1 value", Prop2 = "instance 0 prop2 value" };
+               TestClassVC tcVCInstance;// = new TestClassVC () { Prop1 = "instance 0 prop1 value", Prop2 = "instance 0 prop2 value" };
+               TestClass tcInstance1 = new TestClass () { Prop1 = "instance 1 prop1 value", Prop2 = "instance 1 prop2 value" };
+               TestClassVC tcVCInstance1 = new TestClassVC () { Prop1 = "instance 1 prop1 value", Prop2 = "instance 1 prop2 value" };
+               TestClass tcInstance2 = new TestClass () { Prop1 = "instance 2 prop1 value", Prop2 = "instance 2 prop2 value" };
+               TestClassVC tcVCInstance2 = new TestClassVC () { Prop1 = "instance 2 prop1 value", Prop2 = "instance 2 prop2 value" };
+
+               public TestClass TcInstance {
+                       get => tcInstance;
+                       set {
+                               if (tcInstance == value)
+                                       return;
+                               tcInstance = value;
+                               NotifyValueChanged (tcInstance);
+                       }
+               }
+               public TestClassVC TcVCInstance {
+                       get => tcVCInstance;
+                       set {
+                               if (tcVCInstance == value)
+                                       return;
+                               tcVCInstance = value;
+                               NotifyValueChanged (tcVCInstance);
+                       }
+               }
+
+               void tcInstance_ChangeProperties_MouseClick (object sender, MouseButtonEventArgs e)
+               {
+               }
+               public void tcInstance_ChangeInstance_MouseClick (object sender, MouseButtonEventArgs e)
+               {
+                       if (TcInstance == tcInstance1)
+                               TcInstance = tcInstance2;
+                       else
+                               TcInstance = tcInstance1;
+               }
+               void tcVCInstance_ChangeInstance_MouseClick (object sender, MouseButtonEventArgs e)
+               {
+                       TcVCInstance = new TestClassVC () { Prop1 = "prop1 value changed", Prop2 = "prop2 value changed" };
+               }
+
                public IEnumerable<TestClass> List3 = new List<TestClass> (new TestClass[]
                        {
                                new TestClass { Prop1 = "string1", Prop2="prop2-1" },
index 9c382e6a20330fba0f6accabd89ea6883bddaf63..124cfcf45331cd9a770691f12c2aa6c422325092 100644 (file)
@@ -24,7 +24,7 @@ CheckBox2 {
        Template= "Interfaces/CheckBox2.imlt";
        Background = "Jet";
        Checked="{Background=MediumSeaGreen}";
-       Unchecked = "{Background=Jet}";
+       Unchecked = "{Background=Jet}"; 
 }
 CheckBox3 {
        Template= "Interfaces/CheckBox2.imlt";
@@ -44,7 +44,7 @@ labPerfVal{
        Width = "50%";
 }
 DbgLogViewer{
-       Background = "Onyx";
+       //Background = "Onyx";
        Font = "mono, 8";
        Foreground = "LightGrey";
        Focusable = "true";
index c2bfe8da7edaaf7d60eb2133aad5fa277652d96d..84ec22b39f6e12e5dd69eb9226cfe43f511bf222 100644 (file)
@@ -1,5 +1,5 @@
-<Label Font="{./Font}" Text="{./Caption}" Height="{./HeightPolicy}" Width="{./WidthPolicy}"
-       Margin="3"
+<Label Font="{./Font}" Text="{./Caption}" Width="Stretched"
+       Margin="5"
        Background="{./Background}"
        Foreground="DimGrey"
        TextAlignment="Center"
diff --git a/Samples/common/ui/Interfaces/Divers/classBindings.crow b/Samples/common/ui/Interfaces/Divers/classBindings.crow
new file mode 100644 (file)
index 0000000..72144e8
--- /dev/null
@@ -0,0 +1,9 @@
+<VerticalStack Fit="true">
+       <VerticalStack DataSourceType="Crow.TestClass" DataSource="{TcInstance}" Margin="10" Background="Onyx" Fit="true">
+               <Label Text="{Prop1}"/>
+               <Label Text="{Prop2}"/>
+       </VerticalStack>
+       <HorizontalStack>
+               <Button Caption="Change Instance" MouseClick="tcInstance_ChangeInstance_MouseClick"/>
+       </HorizontalStack>
+</VerticalStack>
\ No newline at end of file
diff --git a/Samples/common/ui/Interfaces/Divers/dbglog.crow b/Samples/common/ui/Interfaces/Divers/dbglog.crow
deleted file mode 100644 (file)
index 16f6c35..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<HorizontalStack>
-       <TreeView Width="20%" Data="{../dbv.Events}" SelectedItemChanged="onSelectedItemChanged"> 
-               <ItemTemplate Data="Events">
-                       <Expandable Caption="{type}" MouseDoubleClick="/onClickForExpand" CacheEnabled="true" IsExpanded="{²IsExpanded}">
-                               <Template>
-                                       <VerticalStack>
-                                               <Border CornerRadius="2" Margin="0" Height="Fit" MouseDoubleClick="./onClickForExpand"
-                                                               Foreground="Transparent"
-                                                               MouseEnter="{Foreground=DimGrey}"
-                                                               MouseLeave="{Foreground=Transparent}">
-                                                       <HorizontalStack Spacing="2" Background="Onyx">
-                                                               <Image Margin="1" Width="9" Height="9" Focusable="true" MouseDown="./onClickForExpand"
-                                                                       Path="{./Image}"
-                                                                       Visible="{HasChildEvents}"
-                                                                       SvgSub="{./IsExpanded}"
-                                                                       MouseEnter="{Background=LightGrey}"
-                                                                       MouseLeave="{Background=Transparent}"/>
-                                                               <Label Text="{./Caption}" Width="80" Font="mono, 8" />
-                                                               <Label Text="{Duration}" Width="40" Font="mono, 8" TextAlignment="Center" Background="DimGrey"/>
-                                                               <DbgEventWidget Event="{}" Tooltip="{/HoverEvent}" TicksPerPixel="200" Width="Fit" Height="5"/>
-                                                       </HorizontalStack>
-                                               </Border>
-                                               <Container Name="Content" Visible="false"/>
-                                       </VerticalStack>
-                               </Template>
-                               <HorizontalStack Height="Fit">
-                                       <Widget Width="12" Height="10"/>
-                                       <VerticalStack Height="Fit" Name="ItemsContainer"/>
-                               </HorizontalStack>
-                       </Expandable>
-               </ItemTemplate>         
-               <ItemTemplate Data="Events" DataType="DbgWidgetEvent">
-                       <Expandable Caption="{type}" MouseDoubleClick="/onClickForExpand" CacheEnabled="true" IsExpanded="{²IsExpanded}">
-                               <Template>
-                                       <VerticalStack>
-                                               <Border CornerRadius="2" Margin="0" Height="Fit" MouseDoubleClick="./onClickForExpand"
-                                                               Foreground="Transparent"
-                                                               MouseEnter="{Foreground=DimGrey}"
-                                                               MouseLeave="{Foreground=Transparent}">
-                                                       <HorizontalStack Spacing="2" Background="Onyx">
-                                                               <Image Margin="1" Width="9" Height="9" Focusable="true" MouseDown="./onClickForExpand"
-                                                                       Path="{./Image}"
-                                                                       Visible="{HasChildEvents}"
-                                                                       SvgSub="{./IsExpanded}"
-                                                                       MouseEnter="{Background=LightGrey}"
-                                                                       MouseLeave="{Background=Transparent}"/>
-                                                               <Label Text="{./Caption}" Width="80" Font="mono, 8" />
-                                                               <Label Text="{Duration}" Width="40" Font="mono, 8" TextAlignment="Center" Background="DimGrey"/>
-                                                               <Label Text="{InstanceIndex}" Width="40" Font="mono, 8" TextAlignment="Center" Background="DimGrey"/>
-                                                               <DbgEventWidget Event="{}" Tooltip="{/HoverEvent}" TicksPerPixel="200" Width="Fit" Height="5"/>
-                                                       </HorizontalStack>
-                                               </Border>
-                                               <Container Name="Content" Visible="false"/>
-                                       </VerticalStack>
-                               </Template>
-                               <HorizontalStack Height="Fit">
-                                       <Widget Width="12" Height="10"/>
-                                       <VerticalStack Height="Fit" Name="ItemsContainer"/>
-                               </HorizontalStack>
-                       </Expandable>
-               </ItemTemplate>         
-       </TreeView>
-       <Splitter/>
-       <VerticalStack>
-               <DbgLogViewer Visible="true" Name="dbv" LogFile="debug.log" MouseWheelSpeed="10" Font="mono, 8"/>
-               <ScrollBar Style="HScrollBar" Maximum="{../dbv.MaxScrollX}" Value="{²../dbv.ScrollX}"/>
-               <HorizontalStack Height="Fit" DataSource="{../../dbv.CurrentWidget}">
-                       <Label Text="{name}"/>
-                       <Label Text="{listIndex}"/>
-                       <Label Text="{instanceNum}"/>
-                       <Label Text="{treeIndex}"/>
-                       <Label Text="{yIndex}"/>
-                       <Label Text="{xLevel}"/>
-               </HorizontalStack>
-               <ListBox Data="{RootEvents}" Height="100" DataSource="{../../dbv.CurrentWidget}">
-                       <ItemTemplate>
-                               <DbgEventWidget Height="10" Width="Fit" Event="{}" Tooltip="{/HoverEvent}" TicksPerPixel="100"/>
-                       </ItemTemplate>
-                       <Template>
-                               <Wrapper Name="ItemsContainer" Spacing="2" Background="DarkGrey"/>
-                       </Template>
-               </ListBox>      
-               <!--<DbgEventWidget Height="20" Width="Stretched" Event="{../../dbv.CurrentEvent}"/>-->
-               <VerticalStack DataSource="{../../dbv.CurrentEvent}">
-                       <HorizontalStack>
-                               <Label Text="{type}"/>
-                               <Label Text="{begin}"/>
-                               <Label Text="{end}"/>
-                               <Label Text="{Duration}"/>
-                       </HorizontalStack>
-                       <ListBox Data="{events}" Height="100">
-                               <ItemTemplate>
-                                       <DbgEventWidget Height="20" Width="Stretched" Event="{}"/>
-                               </ItemTemplate>
-                       </ListBox>
-               </VerticalStack>
-       </VerticalStack>
-</HorizontalStack>
\ No newline at end of file
diff --git a/Samples/common/ui/Interfaces/Divers/splitter0.crow b/Samples/common/ui/Interfaces/Divers/splitter0.crow
new file mode 100644 (file)
index 0000000..218ba62
--- /dev/null
@@ -0,0 +1,7 @@
+<HorizontalStack Margin="50">
+       <Widget Background="SeaGreen" Width="33%"/>
+       <Splitter/>
+       <Widget Background="SeaGreen" Width="33%"/>
+       <Splitter/>
+       <Widget Background="SeaGreen"/>
+</HorizontalStack>
diff --git a/Samples/common/ui/Interfaces/Splitter/1.crow b/Samples/common/ui/Interfaces/Splitter/1.crow
deleted file mode 100644 (file)
index 48c1ea1..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0"?>
-<HorizontalStack Width="80%" Height="50%">
-       <Border Width="45%" CornerRadius="10" BorderWidth="1">
-               <VerticalStack>
-                       <VerticalStack Height="45%">
-                               <CheckBox Height="25%" IsChecked="false" Caption="Check 1" Background="hgradient|0:LimeGreen|1:Transparent"/>
-                               <CheckBox Height="25%" IsChecked="false" Caption="Check 2"  Background="hgradient|0:LimeGreen|1:Transparent"/>
-                               <CheckBox Height="25%" IsChecked="true" Caption="Check 3"  Background="hgradient|0:LimeGreen|1:Transparent"/>
-                               <CheckBox Height="25%" IsChecked="false" Caption="Check 4" Background="hgradient|0:LimeGreen|1:Transparent"/>
-                       </VerticalStack>
-                       <Splitter Thickness="3"/>
-                       <VerticalStack Height="45%">
-                               <CheckBox Height="25%" IsChecked="false" Caption="Check 1" Background="hgradient|0:LimeGreen|1:Transparent"/>
-                               <CheckBox Height="25%" IsChecked="false" Caption="Check 2"  Background="hgradient|0:LimeGreen|1:Transparent"/>
-                               <CheckBox Height="25%" IsChecked="true" Caption="Check 3"  Background="hgradient|0:LimeGreen|1:Transparent"/>
-                               <CheckBox Height="25%" IsChecked="false" Caption="Check 4" Background="hgradient|0:LimeGreen|1:Transparent"/>
-                       </VerticalStack>
-               </VerticalStack>
-       </Border>
-       <Splitter Thickness="3"/>
-       <Border Width="45%" CornerRadius="10" BorderWidth="1">
-               <VerticalStack Width="50%" Height="50%" Background="DimGrey">
-                       <RadioButton IsChecked="false" Caption="Choice 1" Background="hgradient|0:DarkRed|1:Transparent"/>
-                       <RadioButton IsChecked="false" Caption="Choice 2"  Background="hgradient|0:DarkRed|1:Transparent"/>
-                       <RadioButton IsChecked="true" Caption="Choice 3"  Background="hgradient|0:DarkRed|1:Transparent"/>
-                       <RadioButton IsChecked="false" Caption="Choice 4" Background="hgradient|0:DarkRed|1:Transparent"/>
-               </VerticalStack>
-       </Border>
-       <Splitter Thickness="3"/>
-</HorizontalStack>
diff --git a/Samples/common/ui/Interfaces/Splitter/3.crow b/Samples/common/ui/Interfaces/Splitter/3.crow
deleted file mode 100644 (file)
index 9a28896..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0"?>
-<VerticalStack Fit="true">
-       <Widget CornerRadius="5" Height="10" Width="600" Background="vgradient|0:Transparent|0.5:White|1:Transparent"/>
-       <HorizontalStack Fit="true" HorizontalAlignment="Left">
-               <Border Fit="true" CornerRadius="10" BorderWidth="1">
-                       <VerticalStack Margin="10">
-                               <CheckBox IsChecked="false" Caption="Check 1" Background="hgradient|0:LimeGreen|1:Transparent"/>
-                               <CheckBox IsChecked="false" Caption="Check 2"  Background="hgradient|0:LimeGreen|1:Transparent"/>
-                               <CheckBox IsChecked="true" Caption="Check 3"  Background="hgradient|0:LimeGreen|1:Transparent"/>
-                               <CheckBox IsChecked="false" Caption="Check 4" Background="hgradient|0:LimeGreen|1:Transparent"/>
-                       </VerticalStack>
-               </Border>
-               <Splitter/>
-               <Border Fit="true" CornerRadius="10" BorderWidth="1">
-                       <VerticalStack Margin="10">
-                               <RadioButton IsChecked="false" Caption="Choice 1" Background="hgradient|0:DarkRed|1:Transparent"/>
-                               <Splitter/>
-                               <RadioButton Width="Stretched" IsChecked="false" Caption="Choice 2"  Background="hgradient|0:DarkRed|1:Transparent"/>
-                               <RadioButton IsChecked="true" Caption="Choice 3"  Background="hgradient|0:DarkRed|1:Transparent"/>
-                               <RadioButton IsChecked="false" Caption="Choice 4" Background="hgradient|0:DarkRed|1:Transparent"/>
-                       </VerticalStack>
-               </Border>
-               <Splitter Thickness="3" />
-               <Border Fit="true" CornerRadius="10" BorderWidth="1" MaximumSize="150,150" MinimumSize="50,50">
-                       <VerticalStack Margin="10">
-                               <Label Text="label 1" Background="hgradient|0:Blue|1:Transparent"/>
-                               <Label Text="label 2" Background="hgradient|0:Blue|1:Transparent"/>
-                               <Label Text="label 3" Background="hgradient|0:Blue|1:Transparent"/>
-                               <Label Text="label 4" Width="Stretched" Background="hgradient|0:Blue|1:Transparent"/>
-                       </VerticalStack>
-               </Border>
-               <Splitter/>
-               <Border Fit="true" CornerRadius="10" BorderWidth="1">
-                       <VerticalStack Margin="10">
-                               <Button Caption="Button 1" Background="hgradient|0:Red|1:DarkRed" CornerRadius="5"/>
-                               <Button Caption="Button 2"  Background="hgradient|0:Green|1:Blue" CornerRadius="3"/>
-                               <Button Caption="Button 3"  Background="hgradient|0:DarkRed|1:Transparent" CornerRadius="0"/>
-                               <Button Caption="Button 4" Background="hgradient|0:DarkRed|1:Transparent" CornerRadius="8"/>
-                       </VerticalStack>
-               </Border>
-               <Splitter/>
-       </HorizontalStack>
-       <Widget CornerRadius="5" Height="10" Width="600" Background="vgradient|0:Transparent|0.5:White|1:Transparent"/>
-</VerticalStack>
\ No newline at end of file
diff --git a/Samples/common/ui/Interfaces/TabItem.template b/Samples/common/ui/Interfaces/TabItem.template
deleted file mode 100644 (file)
index fd0f310..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0"?>
-<GenericStack Orientation="Vertical" Spacing="0"
-               Background="{./Background}"
-               MouseEnter="{/caption.Foreground=White}"
-               MouseLeave="{/caption.Foreground=Grey}">
-       <HorizontalStack Left="{./TabOffset}"
-               Name="TabTitle"
-               HorizontalAlignment="Left"
-               Height="{./TabHeight}"
-               Width="{./TabWidth}">
-               <Label Name="caption" Text="{./Caption}" Foreground="Grey" Width="Stretched"/>
-               <Label Text="{./ViewIndex}" Foreground="Green"/>
-               <Label Text="{./TabOffset}" Foreground="Red"/>
-               <Border CornerRadius="5" BorderWidth="1" Foreground="Transparent"  Height="12" Width="12"
-                                       MouseEnter="{Foreground=White}" MouseLeave="{Foreground=Transparent}">
-                       <Image Focusable="true" Name="Image" Margin="0" Width="Stretched" Height="Stretched" Path="#Crow.Icons.exit2.svg"
-                                MouseClick="./butCloseTabClick"/>
-               </Border>
-       </HorizontalStack>
-       <Container Margin="20">
-               <Container Background="DimGrey" Name="Content"/>
-       </Container>
-</GenericStack>
-
index 06169f2b46dd1a6ce22619fea757603906d1b0e1..1def381178d10ae309714ae05c24a9f5c373b81a 100644 (file)
@@ -1,10 +1,10 @@
 <?xml version="1.0"?>
-<Border Foreground="Transparent" Focusable="true" HorizontalAlignment="Left" Height="Fit">
-       <HorizontalStack Margin="1" Spacing="3"
-                               MouseEnter="{Background=LightBlue}"
-                               MouseLeave="{Background=Transparent}">
+<ListItem HorizontalAlignment="Left" Height="Fit"
+               Selected = "{Background=${ControlHighlight}}"
+               Unselected = "{Background=Transparent}">
+       <HorizontalStack Margin="1" Spacing="3">
                <Widget Height="12" Width="20" Background="{}" Margin="0" CornerRadius="5"/>
                <Label  Text="{}" Margin="0" Width="Stretched"/>
        </HorizontalStack>
-</Border>
+</ListItem>