From: Jean-Philippe Bruyère Date: Fri, 2 Apr 2021 05:54:52 +0000 (+0200) Subject: DebugLog, event bindings resolutions, enumSelector bitfields, wip X-Git-Tag: v0.9.5-beta~36 X-Git-Url: https://git.osiis.dedyn.io/?a=commitdiff_plain;h=8c0f9d6b02eac97b7a45780f82f5683771745cf0;p=jp%2Fcrow.git DebugLog, event bindings resolutions, enumSelector bitfields, wip --- diff --git a/Crow.sln b/Crow.sln index afc71387..7db03c15 100644 --- a/Crow.sln +++ b/Crow.sln @@ -1,4 +1,4 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 +Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30104.148 MinimumVisualStudioVersion = 10.0.40219.1 @@ -10,6 +10,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "global", "global", "{728545 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Crow", "Crow\Crow.csproj", "{C2980F9B-4798-4C05-99E2-E174810F7C7B}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CrowDbgShared", "CrowDbgShared\CrowDbgShared.csproj", "{91F1CE07-EECE-4F1D-A3EE-7239B563654A}" +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{B2C7855A-2878-47FD-AD32-9A83DB4AB8C6}" ProjectSection(SolutionItems) = preProject Samples\Directory.Build.props = Samples\Directory.Build.props @@ -19,8 +21,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HelloWorld", "Samples\Hello EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ShowCase", "Samples\ShowCase\ShowCase.csproj", "{56329D48-D382-4850-93DE-59C453894E8A}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlLib", "Samples\ControlLib\ControlLib.csproj", "{91F1CE07-EECE-4F1D-A3EE-7239B563654A}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "unitTests", "unitTests\unitTests.csproj", "{0CC6DFAB-2E4A-4786-976C-89053D5EA6A2}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BasicTests", "Samples\BasicTests\BasicTests.csproj", "{7AEB6DD5-916E-4415-84E1-78EC6E5881CE}" @@ -37,6 +37,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BindingTest", "Samples\Bind EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PathPainter", "Samples\PathPainter\PathPainter.csproj", "{4066FE1E-3508-4361-ABAA-21601F632ED8}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrowDebugger", "Samples\CrowDebugger\CrowDebugger.csproj", "{F86711E7-CACE-4050-82BF-0229CCEFDBA3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -89,6 +91,10 @@ Global {4066FE1E-3508-4361-ABAA-21601F632ED8}.Debug|Any CPU.Build.0 = Debug|Any CPU {4066FE1E-3508-4361-ABAA-21601F632ED8}.Release|Any CPU.ActiveCfg = Release|Any CPU {4066FE1E-3508-4361-ABAA-21601F632ED8}.Release|Any CPU.Build.0 = Release|Any CPU + {F86711E7-CACE-4050-82BF-0229CCEFDBA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F86711E7-CACE-4050-82BF-0229CCEFDBA3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F86711E7-CACE-4050-82BF-0229CCEFDBA3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F86711E7-CACE-4050-82BF-0229CCEFDBA3}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -96,7 +102,6 @@ Global GlobalSection(NestedProjects) = preSolution {F535A8AB-CD93-49AB-B1B0-FFF9AE51ED6A} = {B2C7855A-2878-47FD-AD32-9A83DB4AB8C6} {56329D48-D382-4850-93DE-59C453894E8A} = {B2C7855A-2878-47FD-AD32-9A83DB4AB8C6} - {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} @@ -104,6 +109,7 @@ Global {E19FD3DB-902A-4C99-8BF0-5ACAFFE35608} = {B2C7855A-2878-47FD-AD32-9A83DB4AB8C6} {242094B3-A1F1-44F8-B78D-D819B595DDBA} = {B2C7855A-2878-47FD-AD32-9A83DB4AB8C6} {4066FE1E-3508-4361-ABAA-21601F632ED8} = {B2C7855A-2878-47FD-AD32-9A83DB4AB8C6} + {F86711E7-CACE-4050-82BF-0229CCEFDBA3} = {B2C7855A-2878-47FD-AD32-9A83DB4AB8C6} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {00D4E149-7131-49F4-BAAD-559AA961A78E} diff --git a/Crow/Crow.csproj b/Crow/Crow.csproj index 9cff5e2d..86b42dab 100644 --- a/Crow/Crow.csproj +++ b/Crow/Crow.csproj @@ -31,7 +31,7 @@ full - $(DefineConstants);DEBUG;_DEBUG_LOG;TRACE;_DEBUG_DISPOSE;_DEBUG_BINDING;_DEBUG_CLIP_RECTANGLE + $(DefineConstants);DEBUG;TRACE;_DEBUG_DISPOSE;_DEBUG_BINDING;_DEBUG_CLIP_RECTANGLE true @@ -40,7 +40,7 @@ - + @@ -50,6 +50,13 @@ + + $(DefineConstants);DEBUG_LOG + + + + + diff --git a/Crow/Default.style b/Crow/Default.style index 9833be47..622f5b79 100644 --- a/Crow/Default.style +++ b/Crow/Default.style @@ -26,6 +26,7 @@ MenuBackground = "Jet"; Button, CheckBox, RadioButton, ComboBox, Expandable, MessageBox, Popper, Slider, Spinner, TextBox { //Focusable = "true"; + Foreground="${ControlForeground}"; Height = "Fit"; Background = "${ControlBackground}"; CornerRadius = "${ControlCornerRadius}"; @@ -40,7 +41,10 @@ GroupBox { Caption = "Group Box"; } Control { Margin="${ControlInsideMargin}"; - Spacing="3"; + Spacing="3"; + //Foreground = "${ControlForeground}"; + //MouseEnter = "{Foreground=${ControlCaptionHoverColor}}"; + //MouseLeave = "{Foreground=${ControlForeground}}"; } ControlBorder { BorderWidth = "${ControlBorderWidth}"; @@ -48,9 +52,9 @@ ControlBorder { Margin = "${ControlInsideMargin}"; } ControlCaption { - Foreground = "${ControlForeground}"; - MouseEnter = "{Foreground=${ControlCaptionHoverColor}}"; - MouseLeave = "{Foreground=${ControlForeground}}"; + //Foreground = "${ControlForeground}"; + //MouseEnter = "{Foreground=${ControlCaptionHoverColor}}"; + //MouseLeave = "{Foreground=${ControlForeground}}"; } ControlEditableText { Foreground = "${ControlForeground}"; diff --git a/Crow/Templates/TabView.template b/Crow/Templates/TabView.template index cfe1a701..a524bbf9 100644 --- a/Crow/Templates/TabView.template +++ b/Crow/Templates/TabView.template @@ -1,17 +1,17 @@  - - + + - + diff --git a/Crow/src/Colors.cs b/Crow/src/Colors.cs index ba2ad7e8..1318764c 100644 --- a/Crow/src/Colors.cs +++ b/Crow/src/Colors.cs @@ -317,7 +317,8 @@ namespace Crow /// normalized alpha component public Color AdjustAlpha(double _A) { - return new Color (this.R, this.G, this.B, _A); + float[] tmp = floatArray; + return new Color (tmp[0], tmp[1], tmp[2], _A); } public override bool Equals (object obj) diff --git a/Crow/src/Command.cs b/Crow/src/Command.cs index 34d48c4f..51f0bc0a 100644 --- a/Crow/src/Command.cs +++ b/Crow/src/Command.cs @@ -86,11 +86,17 @@ namespace Crow { #region CTOR public Command () {} /// - /// Initializes a new instance of Command with the action pass as argument. + /// Initializes a new instance of Command with the action passed as argument. /// /// action to excecute when command is triggered - public Command (Action _executeAction) - { + public Command (Action _executeAction) { + execute = _executeAction; + } + /// + /// Initializes a new instance of Command with the action passed as argument. + /// + /// action to excecute when command is triggered + public Command (Action _executeAction) { execute = _executeAction; } public Command (string caption, Action executeAction, string icon = null, bool _canExecute = true) @@ -99,10 +105,17 @@ namespace Crow { execute = executeAction; canExecute = _canExecute; } + public Command (string caption, Action executeAction, string icon = null, bool _canExecute = true) + :base (caption, icon) + { + execute = executeAction; + canExecute = _canExecute; + } #endregion - Action execute; + Delegate execute; + bool canExecute = true; /// @@ -122,12 +135,16 @@ namespace Crow { /// /// trigger the execution of the command /// - public virtual void Execute(){ + public virtual void Execute (object sender = null){ if (execute != null && CanExecute){ - Task task = new Task(execute); + Task task = (execute is Action a) ? + task = new Task(a) : + (execute is Action o) ? + task = new Task(o, sender) : throw new Exception("Invalid Delegate type in Crow.Command, expecting Action or Action"); task.Start(); } } + internal override void raiseAllValuesChanged() { base.raiseAllValuesChanged(); diff --git a/Crow/src/DebugUtils/DbgEvent.cs b/Crow/src/DebugUtils/DbgEvent.cs index fe056f79..a9969716 100644 --- a/Crow/src/DebugUtils/DbgEvent.cs +++ b/Crow/src/DebugUtils/DbgEvent.cs @@ -1,77 +1,28 @@ -// Copyright (c) 2013-2020 Jean-Philippe Bruyère +using System.Diagnostics; +// Copyright (c) 2013-2021 Jean-Philippe Bruyère // // 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 +namespace Crow.DebugLogger { public class DbgEvent : DbgEventSource { public long begin, end; public int threadId; public DbgEvtType type; + public DbgEvtType Category => type & DbgEvtType.All; public DbgEvent parentEvent; public bool HasChildEvents => Events != null && Events.Count > 0; public override long Duration => end - begin; + public double DurationMS => Math.Round ((double)Duration / Stopwatch.Frequency * 1000.0, 4); + public double BeginMS => Math.Round ((double)begin / Stopwatch.Frequency * 1000.0, 4); + public double EndMS => Math.Round ((double)end / Stopwatch.Frequency * 1000.0, 4); 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) @@ -113,13 +64,20 @@ namespace Crow 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 (tmp.Length < 5) ? + new DbgWidgetEvent () { + begin = long.Parse (tmp [0]), + end = long.Parse (tmp [1]), + threadId = int.Parse (tmp [2]), + type = evtType, + InstanceIndex = -1, + } : 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]), @@ -128,120 +86,27 @@ namespace Crow type = evtType, }; } - public virtual string Print() + public virtual string Print () => $"{begin,10}:{threadId,-2}:{type,-20}:"; public override string ToString () => $"{begin};{end};{threadId};{type}"; - - } - public class DbgWidgetEvent : DbgEvent - { - public int InstanceIndex; - public override Color Color { + public virtual 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; + case DbgEvtType.ProcessLayouting: + return Colors.Yellow; + case DbgEvtType.ClippingRegistration: + return Colors.DarkTurquoise; + case DbgEvtType.ProcessDrawing: + return Colors.MidnightBlue; + case DbgEvtType.Update: + return Colors.Grey; + case DbgEvtType.IFaceLoad: + return Colors.Teal; default: return Colors.White; } } - } - public override bool IsWidgetEvent => true; - public DbgWidgetEvent () { } - public DbgWidgetEvent (long timeStamp, DbgEvtType evt, Widget w) : base (timeStamp, evt) - { -#if DEBUG_LOG - InstanceIndex = w.instanceIndex; -#endif - } -#if DEBUG_LOG - public override string Print () - => $"{base.Print ()} {Widget.GraphicObjects[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; - } -#else - public DbgLayoutEvent (long timeStamp, DbgEvtType evt, LayoutingQueueItem lqi) {} - public void SetLQI (LayoutingQueueItem lqi) { } - -#endif - public override string Print () - => $"{layouting} {result} {OldSlot}->{NewSlot} {base.Print ()}"; - - 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 index 409f2147..133b5ede 100644 --- a/Crow/src/DebugUtils/DbgEventSource.cs +++ b/Crow/src/DebugUtils/DbgEventSource.cs @@ -5,17 +5,11 @@ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; -namespace Crow +namespace Crow.DebugLogger { //base class for both events and widgetRecord having an event list - public abstract class DbgEventSource : IValueChange + public abstract class DbgEventSource { - public event EventHandler 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 Events; public virtual long Duration { diff --git a/Crow/src/DebugUtils/DbgEventTypeColors.cs b/Crow/src/DebugUtils/DbgEventTypeColors.cs deleted file mode 100644 index d2871904..00000000 --- a/Crow/src/DebugUtils/DbgEventTypeColors.cs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) 2013-2020 Jean-Philippe Bruyère -// -// 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 deleted file mode 100644 index f6b4dc6b..00000000 --- a/Crow/src/DebugUtils/DbgEventWidget (copie).cs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) 2013-2020 Jean-Philippe Bruyère -// -// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) -using System; -using System.Collections.Generic; -using Crow.Cairo; - -namespace Crow -{ - /* - /// - /// Compressed event list without gaps - /// - 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; - } - }*/ -} diff --git a/Crow/src/DebugUtils/DbgEvtType.cs b/Crow/src/DebugUtils/DbgEvtType.cs new file mode 100644 index 00000000..24fd99fa --- /dev/null +++ b/Crow/src/DebugUtils/DbgEvtType.cs @@ -0,0 +1,84 @@ +// Copyright (c) 2013-2021 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; + +namespace Crow +{ + [Flags] + public enum DbgEvtType : Int32 { + None = 0, + IFace = 0x40000000, + Widget = 0x20000000, + + Warning = 0x10000000, + Error = 0x08000000, + + Binding = 0x800000, + Lock = 0x400000, + Layouting = 0x200000, + Clipping = 0x100000, + Drawing = 0x080000, + + Focus = 0x040000, + Override = 0x020000, + TemplatedGroup = 0x010000, + Dispose = 0x008000, + + Update = IFace | 0x004000, + ProcessLayouting = IFace | Update | Lock | Layouting, + ClippingRegistration = IFace | Update | Lock | Clipping, + ProcessDrawing = IFace | Update | Lock | Drawing, + IFaceLoad = IFace | 0x01, + IFaceInit = IFace | 0x02, + CreateITor = IFace | 0x04, + IFaceReloadTheme = IFace | 0x08, + + HoverWidget = Focus | Widget | 0x01, + FocusedWidget = Focus | Widget | 0x02, + ActiveWidget = Focus | Widget | 0x04, + UnfocusedWidget = Focus | Widget | 0x08, + + //10 nth bit set for graphic obj + GOClassCreation = Widget | 0x01, + GOInitialization = Widget | 0x02, + GORegisterForGraphicUpdate = Widget | 0x04, + GOEnqueueForRepaint = Widget | 0x08, + GONewDataSource = Widget | 0x10, + GONewParent = Widget | 0x20, + GONewLogicalParent = Widget | 0x40, + GOAddChild = Widget | 0x80, + + GOSearchLargestChild = Widget | 0x09, + GOSearchTallestChild = Widget | 0x0A, + GORegisterForRedraw = Widget | 0x0B, + GOComputeChildrenPositions = Widget | 0x0C, + GOOnChildLayoutChange = Widget | 0x0D, + + 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 = 0x7FFFFF00 + } +} \ No newline at end of file diff --git a/Crow/src/DebugUtils/DbgLayoutEvent.cs b/Crow/src/DebugUtils/DbgLayoutEvent.cs new file mode 100644 index 00000000..4444fe3d --- /dev/null +++ b/Crow/src/DebugUtils/DbgLayoutEvent.cs @@ -0,0 +1,53 @@ +// Copyright (c) 2013-2021 Jean-Philippe Bruyère +// +// 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.DebugLogger +{ + public class DbgLayoutEvent : DbgWidgetEvent + { + public LayoutingType layouting; + public LayoutingQueueItem.Result result; + public Rectangle OldSlot, NewSlot; + public override bool IsLayoutEvent => true; + public DbgLayoutEvent () { } + public DbgLayoutEvent (long timeStamp, DbgEvtType evt, int widgetInstanceIndex, + LayoutingType layouting, LayoutingQueueItem.Result result, Rectangle oldSlot, Rectangle newSlot) : + base (timeStamp, evt, widgetInstanceIndex) + { + SetLQI (layouting, result, oldSlot, newSlot); + } + public void SetLQI (LayoutingType layouting, LayoutingQueueItem.Result result, Rectangle oldSlot, Rectangle newSlot) + { + this.layouting = layouting; + this.result = result; + OldSlot = oldSlot; + NewSlot = newSlot; + } + public override string Print() + => $"{base.Print()} {layouting} {result} {OldSlot}->{NewSlot}"; + + public override string ToString () + => $"{base.ToString ()};{layouting};{result};{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; + } } + } + } +} \ No newline at end of file diff --git a/Crow/src/DebugUtils/DbgWidgetEvent.cs b/Crow/src/DebugUtils/DbgWidgetEvent.cs new file mode 100644 index 00000000..3ec58df7 --- /dev/null +++ b/Crow/src/DebugUtils/DbgWidgetEvent.cs @@ -0,0 +1,64 @@ +// Copyright (c) 2013-2021 Jean-Philippe Bruyère +// +// 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.DebugLogger +{ + public class DbgWidgetEvent : DbgEvent + { + public int InstanceIndex; + public override bool IsWidgetEvent => true; + public DbgWidgetEvent () { } + public DbgWidgetEvent (long timeStamp, DbgEvtType evt, int widgetInstanceIndex) : base (timeStamp, evt) + { + InstanceIndex = widgetInstanceIndex; + } + //public override string Print() => $"{base.Print()}:{InstanceIndex}" + + public override string ToString () + => $"{base.ToString ()};{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.White; + } + } + } + } +} \ No newline at end of file diff --git a/Crow/src/DebugUtils/DbgWidgetRecord.cs b/Crow/src/DebugUtils/DbgWidgetRecord.cs index 8aaf1c47..0dd676f7 100644 --- a/Crow/src/DebugUtils/DbgWidgetRecord.cs +++ b/Crow/src/DebugUtils/DbgWidgetRecord.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; -namespace Crow +namespace Crow.DebugLogger { /// /// Recorded Widget instance data. diff --git a/Crow/src/DebugUtils/DebugLogger.cs b/Crow/src/DebugUtils/DebugLogger.cs index 207ae217..eb096f80 100644 --- a/Crow/src/DebugUtils/DebugLogger.cs +++ b/Crow/src/DebugUtils/DebugLogger.cs @@ -1,95 +1,33 @@ -// Copyright (c) 2013-2020 Jean-Philippe Bruyère +using System.Text; +// Copyright (c) 2013-2021 Jean-Philippe Bruyère // // 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; +using Crow.DebugLogger; namespace Crow -{ - [Flags] - public enum DbgEvtType { - ////9 nth bit set for iface event - IFace = 0x10000, - Focus = 0x20000, - Override = 0x40000, - Binding = 0x80000, - 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, - IFaceReleadTheme = IFace | 0x08, - - HoverWidget = IFace | Focus | Widget | 0x01, - FocusedWidget = IFace | Focus | Widget | 0x02, - ActiveWidget = IFace | Focus | Widget | 0x03, - UnfocusedWidget = IFace | Focus | Widget | 0x04, - - //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 | 0x0A, - GORegisterForRedraw = Widget | 0x0B, - - 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 - } - +{ public static class DbgLogger { public static DbgEvtType IncludeEvents = DbgEvtType.All; public static DbgEvtType DiscardEvents = DbgEvtType.Focus; public static bool ConsoleOutput = true; + +#if DEBUG_LOG static bool logevt (DbgEvtType evtType) - => (evtType & DiscardEvents) == 0 && (evtType & IncludeEvents) == IncludeEvents; + //=> IncludeEvents != DbgEvtType.None && (evtType & DiscardEvents) == 0 && (evtType & IncludeEvents) == IncludeEvents; + /*=> IncludeEvents == DbgEvtType.All || + IncludeEvents != DbgEvtType.None && (evtType & DiscardEvents) == 0 && (evtType & IncludeEvents) == IncludeEvents;*/ + => IncludeEvents == DbgEvtType.All || (IncludeEvents != DbgEvtType.None && (evtType & IncludeEvents) == IncludeEvents); + static object logMutex = new object (); @@ -111,10 +49,15 @@ namespace Crow return events; } } + public static readonly bool IsEnabled = true; +#else + public static readonly bool IsEnabled = false; +#endif [Conditional ("DEBUG_LOG")] public static void StartEvent (DbgEvtType evtType, params object[] data) { +#if DEBUG_LOG if (!logevt (evtType)) return; lock (logMutex) { @@ -125,11 +68,13 @@ namespace Crow startedEvents [Thread.CurrentThread.ManagedThreadId].Push (evt); chrono.Start (); } +#endif } [Conditional ("DEBUG_LOG")] public static void EndEvent (DbgEvtType evtType, bool discardIfNoChildEvents = false) { +#if DEBUG_LOG if (!logevt (evtType)) return; @@ -146,11 +91,13 @@ namespace Crow e.end = chrono.ElapsedTicks; chrono.Start (); } +#endif } // End layouting queue event and set the corresponding lqi [Conditional ("DEBUG_LOG")] public static void EndEvent (DbgEvtType evtType, LayoutingQueueItem lqi) { +#if DEBUG_LOG if (!logevt (evtType)) return; @@ -162,10 +109,10 @@ namespace Crow if (e?.type != evtType) throw new Exception ($"Begin/end event logging mismatch: {e.type}/{evtType}"); e.end = chrono.ElapsedTicks; - e.SetLQI (lqi); + e.SetLQI (lqi.LayoutType, lqi.result, lqi.Slot, lqi.NewSlot); chrono.Start (); } - +#endif } /// /// End event by reference to cancel unended events on failure @@ -190,7 +137,8 @@ namespace Crow }*/ [Conditional("DEBUG_LOG")] - public static void AddEvent (DbgEvtType evtType, params object [] data) { + public static void AddEvent (DbgEvtType evtType, params object [] data) { +#if DEBUG_LOG if (!logevt (evtType)) return; @@ -199,33 +147,36 @@ namespace Crow DbgEvent evt = addEventInternal (evtType, data); chrono.Start (); } +#endif } +#if DEBUG_LOG static DbgEvent addEventInternal (DbgEvtType evtType, params object [] data) { DbgEvent evt = null; -#if DEBUG_LOG 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); + evt = new DbgWidgetEvent (chrono.ElapsedTicks, evtType, w.instanceIndex); else if (data [0] is LayoutingQueueItem lqi) - evt = new DbgLayoutEvent (chrono.ElapsedTicks, evtType, lqi); + evt = new DbgLayoutEvent (chrono.ElapsedTicks, evtType, lqi.graphicObject.instanceIndex, lqi.LayoutType, lqi.result, lqi.Slot, lqi.NewSlot); else evt = new DbgEvent (chrono.ElapsedTicks, evtType); if (ConsoleOutput) { if (evt.type.HasFlag (DbgEvtType.Error)) { Console.ForegroundColor = ConsoleColor.Red; - } - Console.WriteLine (evt.Print()); + } + if (evt is DbgWidgetEvent we) + Console.WriteLine ($"{evt.Print()} {Widget.GraphicObjects[we.InstanceIndex]}"); + else + Console.WriteLine ($"{evt.Print()}"); Console.ResetColor (); } else curEventList.Add (evt); -#endif return evt; } -#if DEBUG_LOG + static void parseTree (Widget go, int level = 0, int y = 1) { if (go == null) return; @@ -244,61 +195,126 @@ namespace Crow parseTree (pc.getTemplateRoot, level + 1, y + 1); } } + static void saveEventList (StreamWriter s, List 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 /// /// Clear all recorded events from logger. /// + [Conditional("DEBUG_LOG")] public static void Reset () { #if DEBUG_LOG lock (logMutex) { - startedEvents.Clear (); + startedEvents.Clear (); events.Clear (); + /*lock (Widget.GraphicObjects) + Widget.GraphicObjects.Clear();*/ chrono.Restart (); } Console.WriteLine ($"Crow Debug Log reseted"); -#else - Console.WriteLine ($"Logging disabled, compile Crow with DEBUG and DEBUG_LOG defined to enable logging."); #endif } /// /// Save recorded events to disk /// - /// Iface. + /// Iface. public static void Save(Interface iface, string dbgLogFilePath = "debug.log") { #if 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}"); - } + using (Stream stream = new FileStream (dbgLogFilePath, FileMode.Create, FileAccess.Write)) + Save (iface, stream); + Console.WriteLine ($"Crow Debug Log saved to: {System.IO.Path.GetFullPath(dbgLogFilePath)}"); +#endif + } + [Conditional("DEBUG_LOG")] + public static void Save(Interface iface, Stream stream) { +#if DEBUG_LOG + using (StreamWriter writer = new StreamWriter (stream, Encoding.UTF8, 1024, true)) { + lock (logMutex) + lock (iface.UpdateMutex) { + chrono.Stop(); + foreach (Widget go in iface.GraphicTree) + parseTree (go); + + writer.WriteLine ("[GraphicObjects]"); + for (int i = 0; i < Widget.GraphicObjects.Count; i++) { + Widget g = Widget.GraphicObjects [i]; + writer.WriteLine ($"{g.GetType ().Name};{g.yIndex};{g.xLevel};{g.Width};{g.Height}"); } - s.WriteLine ("[Events]"); - saveEventList (s, events); + + writer.WriteLine ("[Events]"); + saveEventList (writer, events); + chrono.Start(); } } - Console.WriteLine ($"Crow Debug Log saved to: {dbgLogFilePath}"); -#else - Console.WriteLine ($"Compile Crow with DEBUG and DEBUG_LOG defined to enable logging. No log saved."); #endif } - - static void saveEventList (StreamWriter s, List evts, int level = 0) + public static void Load (string logFile, List events, List widgets) { - 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); + if (!File.Exists (logFile)) + return; + using (Stream stream = new FileStream (logFile, FileMode.Open, FileAccess.Read)) + Load (stream, events, widgets); + } + public static void Load (Stream stream, List events, List widgets) + { + using (StreamReader reader = new StreamReader (stream)) { + + if (reader.ReadLine () != "[GraphicObjects]") + return; + while (!reader.EndOfStream) { + string l = reader.ReadLine (); + if (l == "[Events]") + break; + DbgWidgetRecord o = DbgWidgetRecord.Parse (l); + o.listIndex = widgets.Count; + widgets.Add (o); + } + + Stack startedEvents = new Stack (); + + if (!reader.EndOfStream) { + while (!reader.EndOfStream) { + int level = 0; + while (reader.Peek () == (int)'\t') { + reader.Read (); + level++; + } + DbgEvent evt = DbgEvent.Parse (reader.ReadLine ()); + 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)) { + DbgWidgetEvent dwe = evt as DbgWidgetEvent; + if (dwe.InstanceIndex >= 0) + widgets [dwe.InstanceIndex].Events.Add (evt); + } + } + } } } } diff --git a/Crow/src/IML/Instantiator.cs b/Crow/src/IML/Instantiator.cs index f43e700d..b29912bc 100644 --- a/Crow/src/IML/Instantiator.cs +++ b/Crow/src/IML/Instantiator.cs @@ -710,6 +710,11 @@ namespace Crow.IML { } else if (rop.IsSingleName && rop.Tokens [0] == "this") { il.Emit (OpCodes.Ldarg_0); //load sender ref onto the stack, the current node lop.emitSetProperty (il); + } else if (rop.IsCurrentNodeProperty || rop.IsTemplateBinding || rop.LevelsUp > 0) { + il.Emit (OpCodes.Ldarg_0); //load sender ref onto the stack, the current nod + rop.emitGetTarget (il, cancelFinalSet); + rop.emitGetProperty (il, cancelFinalSet); + lop.emitSetProperty (il); } else if (rop.LevelsUp == 0 && !string.IsNullOrEmpty (rop.Tokens [0])) {//parsable constant depending on lop type //if left operand is member of current node, it's easy to fetch type, else we should use reflexion in msil if (lopPI == null) {//accept GraphicObj members, but it's restricive diff --git a/Crow/src/Interface.cs b/Crow/src/Interface.cs index 98df8cee..f1a3da1a 100644 --- a/Crow/src/Interface.cs +++ b/Crow/src/Interface.cs @@ -132,6 +132,8 @@ namespace Crow Cursor currentCursor; bool ownWindow; + public IntPtr WindowHandle => hWin; + protected void registerGlfwCallbacks () { windows.Add (hWin, this); @@ -426,7 +428,7 @@ namespace Crow /// Sync mutex between host and Crow for rendering operations (bmp, dirtyBmp,...) public object RenderMutex = new object(); /// Global lock of the update cycle - public object UpdateMutex = new object(); + public object UpdateMutex = new object(); /// Global lock of the clipping queue public object ClippingMutex = new object(); //TODO:share resource instances @@ -553,13 +555,13 @@ namespace Crow if (StylingConstants == null)//iFace not yet initialized return; lock (UpdateMutex) { - DbgLogger.StartEvent (DbgEvtType.IFaceReleadTheme); + DbgLogger.StartEvent (DbgEvtType.IFaceReloadTheme); disposeContextMenus (); initDictionaries (); loadStyling (); loadThemeFiles (); initContextMenus (); - DbgLogger.EndEvent (DbgEvtType.IFaceReleadTheme); + DbgLogger.EndEvent (DbgEvtType.IFaceReloadTheme); } } } @@ -915,7 +917,7 @@ namespace Crow Monitor.Exit (LayoutMutex); return; } - DbgLogger.StartEvent (DbgEvtType.Layouting); + DbgLogger.StartEvent (DbgEvtType.ProcessLayouting); PerformanceMeasure.Begin (PerformanceMeasure.Kind.Layouting); DiscardQueue = new Queue (LayoutingQueue.Count); @@ -928,7 +930,7 @@ namespace Crow LayoutingQueue = DiscardQueue; PerformanceMeasure.End (PerformanceMeasure.Kind.Layouting); - DbgLogger.EndEvent (DbgEvtType.Layouting, true); + DbgLogger.EndEvent (DbgEvtType.ProcessLayouting, true); Monitor.Exit (LayoutMutex); DiscardQueue = null; @@ -941,7 +943,7 @@ namespace Crow if (ClippingQueue.Count == 0) return; - DbgLogger.StartEvent (DbgEvtType.Clipping); + DbgLogger.StartEvent (DbgEvtType.ClippingRegistration); PerformanceMeasure.Begin (PerformanceMeasure.Kind.Clipping); Widget g = null; @@ -954,26 +956,25 @@ namespace Crow } PerformanceMeasure.End (PerformanceMeasure.Kind.Clipping); - DbgLogger.EndEvent (DbgEvtType.Clipping, true); + DbgLogger.EndEvent (DbgEvtType.ClippingRegistration, true); } /// Clipping Rectangles drive the drawing process. For compositing, each object under a clip rectangle should be /// repainted. If it contains also clip rectangles, its cache will be update, or if not cached a full redraw will take place - void processDrawing(Context ctx){ + protected virtual void processDrawing(Context ctx){ - DbgLogger.StartEvent (DbgEvtType.Drawing); + DbgLogger.StartEvent (DbgEvtType.ProcessDrawing); if (DragImage != null) clipping.UnionRectangle(new Rectangle (DragImageX, DragImageY, DragImageWidth, DragImageHeight)); if (!clipping.IsEmpty) { - PerformanceMeasure.Begin (PerformanceMeasure.Kind.Drawing); - IsDirty = true; + PerformanceMeasure.Begin (PerformanceMeasure.Kind.Drawing); ctx.PushGroup (); for (int i = GraphicTree.Count -1; i >= 0 ; i--){ Widget p = GraphicTree[i]; - if (!p.Visible) + if (!p.IsVisible) continue; if (clipping.Contains (p.Slot) == RegionOverlap.Out) continue; @@ -1025,13 +1026,14 @@ namespace Crow clipping = new Region (); PerformanceMeasure.End (PerformanceMeasure.Kind.Drawing); + IsDirty = true; } drawTextCursor (ctx); debugHighlightFocus (ctx); - DbgLogger.EndEvent (DbgEvtType.Drawing, true); + DbgLogger.EndEvent (DbgEvtType.ProcessDrawing, true); } #endregion @@ -1613,7 +1615,7 @@ namespace Crow } void toolTipThreadFunc () { - while (true) { + while (true) { if (tooltipTimer.ElapsedMilliseconds > TOOLTIP_DELAY) { if (!tooltipVisible) { Widget g = _hoverWidget; @@ -1654,14 +1656,18 @@ namespace Crow } void ToolTipContainer_LayoutChanged (object sender, LayoutingEventArgs e) { - Widget ttc = sender as Widget; + 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.Right > clientRectangle.Right) + ttc.Left = clientRectangle.Right - ttc.Slot.Width; + }else if (e.LayoutType == LayoutingType.Y) { + if (ttc.Slot.Bottom > clientRectangle.Bottom) + ttc.Top = clientRectangle.Bottom - ttc.Slot.Height; + }/* if (ttc.Slot.Height < tc.ClientRectangle.Height) { if (PopDirection.HasFlag (Alignment.Bottom)) { if (r.Bottom + ttc.Slot.Height > tc.ClientRectangle.Bottom) @@ -1678,6 +1684,7 @@ namespace Crow } else ttc.Top = 0; }*/ + } #endregion diff --git a/Crow/src/Mono.Cairo/Context.cs b/Crow/src/Mono.Cairo/Context.cs index 9253424e..5cd60dc1 100644 --- a/Crow/src/Mono.Cairo/Context.cs +++ b/Crow/src/Mono.Cairo/Context.cs @@ -443,6 +443,10 @@ namespace Crow.Cairo { { Rectangle (rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height); } + public void Rectangle (Crow.RectangleD rectangle) + { + Rectangle (rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height); + } public void Rectangle (PointD p, double width, double height) { diff --git a/Crow/src/Widgets/Button.cs b/Crow/src/Widgets/Button.cs index 35dcee00..009aed78 100644 --- a/Crow/src/Widgets/Button.cs +++ b/Crow/src/Widgets/Button.cs @@ -105,7 +105,7 @@ namespace Crow public override void onMouseClick (object sender, MouseButtonEventArgs e) { - command?.Execute (); + command?.Execute (this); e.Handled = true; base.onMouseClick (sender, e); } diff --git a/Crow/src/Widgets/EnumSelector.cs b/Crow/src/Widgets/EnumSelector.cs index c30c79a4..aa77839e 100644 --- a/Crow/src/Widgets/EnumSelector.cs +++ b/Crow/src/Widgets/EnumSelector.cs @@ -37,6 +37,7 @@ namespace Crow #region private fields Enum enumValue; + UInt32 bitFieldExcludeMask; Type enumType; bool enumTypeIsBitsfield; string rbStyle, iconsPrefix, iconsExtension; @@ -102,19 +103,24 @@ namespace Crow if (enumTypeIsBitsfield) { IML.Instantiator iTor = IFace.CreateITorFromIMLFragment ($""); - UInt32 currentValue = Convert.ToUInt32 (EnumValue); + UInt32 currentValue = Convert.ToUInt32 (EnumValue); + currentValue &= ~bitFieldExcludeMask; + enumValue = (Enum)Enum.ToObject(enumType, currentValue); foreach (Enum en in enumType.GetEnumValues()) { + UInt32 eni = Convert.ToUInt32 (en); + if ((eni & bitFieldExcludeMask) != 0) + continue; + CheckBox rb = iTor.CreateInstance (); rb.Caption = en.ToString(); rb.LogicalParent = this; - rb.Tag = $"{iconsPrefix}{en}{IconsExtension}"; - - UInt32 eni = Convert.ToUInt32 (en); + rb.Tag = $"{iconsPrefix}{en}{IconsExtension}"; rb.Tooltip = $"0x{eni:x8}"; + if (eni == 0) { rb.IsChecked = currentValue == 0; - rb.Checked += (sender, e) => EnumValue = (Enum)Enum.ToObject(enumType, 0); + rb.Checked += (sender, e) => EnumValue = (Enum)Enum.ToObject(enumType, 0); } else { rb.IsChecked = currentValue == 0 ? false : EnumValue.HasFlag (en); rb.Checked += onChecked; @@ -145,7 +151,10 @@ namespace Crow } else if (enumTypeIsBitsfield) { - UInt32 currentValue = Convert.ToUInt32 (EnumValue); + UInt32 currentValue = Convert.ToUInt32 (EnumValue); + currentValue &= ~bitFieldExcludeMask; + enumValue = (Enum)Enum.ToObject(enumType, currentValue); + if (currentValue == 0) { foreach (CheckBox rb in enumValueContainer.Children) { Enum en = (Enum)Enum.Parse(enumType, rb.Caption); @@ -181,7 +190,20 @@ namespace Crow } } #endregion - + /// + /// Include mask for bitfields. Used to ignore enum values in display. + /// + /// UInt32 bitfield mask + public UInt32 BitFieldExcludeMask { + get => bitFieldExcludeMask; + set { + if (bitFieldExcludeMask == value) + return; + bitFieldExcludeMask = value; + NotifyValueChangedAuto(bitFieldExcludeMask); + forceRefresh(); + } + } void onChecked (object sender, EventArgs e) { Enum en =(Enum)Enum.Parse (enumType, (sender as CheckBox).Caption); UInt32 newVal = Convert.ToUInt32 (en); diff --git a/Crow/src/Widgets/GenericStack.cs b/Crow/src/Widgets/GenericStack.cs index b2fbe650..d0b94f37 100644 --- a/Crow/src/Widgets/GenericStack.cs +++ b/Crow/src/Widgets/GenericStack.cs @@ -66,6 +66,7 @@ namespace Crow { return base.measureRawSize (lt); } public virtual void ComputeChildrenPositions () { + DbgLogger.StartEvent(DbgEvtType.GOComputeChildrenPositions, this); int d = 0; if (Orientation == Orientation.Horizontal) { foreach (Widget c in Children) { @@ -83,6 +84,7 @@ namespace Crow { } } IsDirty = true; + DbgLogger.EndEvent(DbgEvtType.GOComputeChildrenPositions); } Widget stretchedGO = null; public override bool UpdateLayout (LayoutingType layoutType) { @@ -145,6 +147,7 @@ namespace Crow { } public override void OnChildLayoutChanges (object sender, LayoutingEventArgs arg) { + DbgLogger.StartEvent(DbgEvtType.GOOnChildLayoutChange, this); Widget go = sender as Widget; //Debug.WriteLine ("child layout change: " + go.LastSlots.ToString() + " => " + go.Slot.ToString()); switch (arg.LayoutType) { @@ -157,6 +160,7 @@ namespace Crow { } else if (stretchedGO != go) { go.Slot.Width = 0; go.Width = Measure.Fit; + DbgLogger.EndEvent(DbgEvtType.GOOnChildLayoutChange); return; } } else if (stretchedGO == go) { @@ -172,6 +176,7 @@ namespace Crow { this.RegisterForLayouting (LayoutingType.Width); this.RegisterForLayouting (LayoutingType.ArrangeChildren); + DbgLogger.EndEvent(DbgEvtType.GOOnChildLayoutChange); return; } break; @@ -184,6 +189,7 @@ namespace Crow { } else if (stretchedGO != go) { go.Slot.Height = 0; go.Height = Measure.Fit; + DbgLogger.EndEvent(DbgEvtType.GOOnChildLayoutChange); return; } } else if (stretchedGO == go) { @@ -198,11 +204,13 @@ namespace Crow { this.RegisterForLayouting (LayoutingType.Height); this.RegisterForLayouting (LayoutingType.ArrangeChildren); + DbgLogger.EndEvent(DbgEvtType.GOOnChildLayoutChange); return; } break; } base.OnChildLayoutChanges (sender, arg); + DbgLogger.EndEvent(DbgEvtType.GOOnChildLayoutChange); } #endregion diff --git a/Crow/src/Widgets/Group.cs b/Crow/src/Widgets/Group.cs index 480ecaaf..0500d129 100644 --- a/Crow/src/Widgets/Group.cs +++ b/Crow/src/Widgets/Group.cs @@ -326,7 +326,7 @@ namespace Crow childrenRWLock.EnterReadLock (); foreach (Widget c in Children) { - if (!c.Visible) + if (!c.IsVisible) continue; if (Clipping.Contains (c.Slot + ClientRectangle.Position) == RegionOverlap.Out) continue; @@ -352,12 +352,16 @@ namespace Crow public virtual void OnChildLayoutChanges (object sender, LayoutingEventArgs arg) { + DbgLogger.StartEvent(DbgEvtType.GOOnChildLayoutChange, this); + Widget g = sender as Widget; switch (arg.LayoutType) { case LayoutingType.Width: - if (Width != Measure.Fit) + if (Width != Measure.Fit) { + DbgLogger.EndEvent(DbgEvtType.GOOnChildLayoutChange); return; + } if (g.Slot.Width > contentSize.Width) { largestChild = g; contentSize.Width = g.Slot.Width; @@ -367,8 +371,10 @@ namespace Crow this.RegisterForLayouting (LayoutingType.Width); break; case LayoutingType.Height: - if (Height != Measure.Fit) + if (Height != Measure.Fit) { + DbgLogger.EndEvent(DbgEvtType.GOOnChildLayoutChange); return; + } if (g.Slot.Height > contentSize.Height) { tallestChild = g; contentSize.Height = g.Slot.Height; @@ -378,6 +384,7 @@ namespace Crow this.RegisterForLayouting (LayoutingType.Height); break; } + DbgLogger.EndEvent(DbgEvtType.GOOnChildLayoutChange); } //TODO: x,y position should be taken in account for computation of width and height void resetChildrenMaxSize(){ @@ -392,7 +399,7 @@ namespace Crow largestChild = null; contentSize.Width = 0; for (int i = 0; i < Children.Count; i++) { - if (!Children [i].Visible) + if (!Children [i].IsVisible) continue; int cw = 0; if (forceMeasure) @@ -416,7 +423,7 @@ namespace Crow tallestChild = null; contentSize.Height = 0; for (int i = 0; i < Children.Count; i++) { - if (!Children [i].Visible) + if (!Children [i].IsVisible) continue; int ch = 0; if (forceMeasure) diff --git a/Crow/src/Widgets/MenuItem.cs b/Crow/src/Widgets/MenuItem.cs index b9b9d1f3..d56f5213 100644 --- a/Crow/src/Widgets/MenuItem.cs +++ b/Crow/src/Widgets/MenuItem.cs @@ -145,7 +145,7 @@ namespace Crow public override void onMouseClick (object sender, MouseButtonEventArgs e) { if (command != null) { - command.Execute (); + command.Execute (this); closeMenu (); } if (hasClick) diff --git a/Crow/src/Widgets/ScrollBar.cs b/Crow/src/Widgets/ScrollBar.cs index fd3dd244..8ef519f1 100644 --- a/Crow/src/Widgets/ScrollBar.cs +++ b/Crow/src/Widgets/ScrollBar.cs @@ -34,6 +34,9 @@ namespace Crow void updateCursor () { if (cursorRatio < 0) return; + ILayoutable l = cursor?.Parent; + if (l == null) + return; Rectangle r = cursor.Parent.ClientRectangle; if (Orientation == Orientation.Horizontal) CursorSize = (int)(cursorRatio * r.Width); diff --git a/Crow/src/Widgets/ScrollingObject.cs b/Crow/src/Widgets/ScrollingObject.cs index 2545ffc7..3173996d 100644 --- a/Crow/src/Widgets/ScrollingObject.cs +++ b/Crow/src/Widgets/ScrollingObject.cs @@ -123,11 +123,12 @@ namespace Crow /// Process scrolling vertically, or if shift is down, vertically public override void onMouseWheel (object sender, MouseWheelEventArgs e) { - base.onMouseWheel (sender, e); if (IFace.Shift) ScrollX += e.Delta * MouseWheelSpeed; else ScrollY -= e.Delta * MouseWheelSpeed; + e.Handled = true; + base.onMouseWheel (sender, e); } /// Process scrolling with arrow keys, home and end keys. public override void onKeyDown (object sender, KeyEventArgs e) diff --git a/Crow/src/Widgets/Slider.cs b/Crow/src/Widgets/Slider.cs index b99fadfd..ca4089ec 100644 --- a/Crow/src/Widgets/Slider.cs +++ b/Crow/src/Widgets/Slider.cs @@ -26,12 +26,12 @@ namespace Crow cursor = child.FindByName ("Cursor"); if (cursor == null) return; - (cursor.Parent as Widget).LayoutChanged += HandleCursorContainerLayoutChanged; + (cursor.Parent as Widget).LayoutChanged += HandleCursorContainerLayoutChanged;//too difficult to unregister updateCursorWidgetProps (); } protected virtual void HandleCursorContainerLayoutChanged (object sender, LayoutingEventArgs e) - { + { computeCursorPosition (); } #endregion @@ -206,5 +206,11 @@ namespace Crow { Value += SmallIncrement; } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + } } } diff --git a/Crow/src/Widgets/TabView.cs b/Crow/src/Widgets/TabView.cs index 440322fd..68538e8d 100644 --- a/Crow/src/Widgets/TabView.cs +++ b/Crow/src/Widgets/TabView.cs @@ -31,6 +31,10 @@ namespace Crow } public Orientation OppositeOrientation => orientation == Orientation.Vertical ? Orientation.Horizontal : Orientation.Vertical; + public Measure TabWidth + => orientation == Orientation.Vertical ? Measure.Stretched : Measure.Fit; + public Measure TabHeight + => orientation == Orientation.Horizontal ? Measure.Stretched : Measure.Fit; } } diff --git a/Crow/src/Widgets/Widget.cs b/Crow/src/Widgets/Widget.cs index 81f08525..47ac67f5 100644 --- a/Crow/src/Widgets/Widget.cs +++ b/Crow/src/Widgets/Widget.cs @@ -290,7 +290,7 @@ namespace Crow bool allowDrop; string allowedDropTypes; string tooltip; - IList contextCommands; + CommandGroup contextCommands; #endregion #region public fields @@ -325,7 +325,7 @@ namespace Crow #endregion #region ILayoutable - [XmlIgnore]public LayoutingType RegisteredLayoutings { get { return registeredLayoutings; } set { registeredLayoutings = value; } } + [XmlIgnore]public LayoutingType RegisteredLayoutings { get => registeredLayoutings; set => registeredLayoutings = value; } //TODO: it would save the recurent cost of a cast in event bubbling if parent type was GraphicObject // or we could add to the interface the mouse events /// @@ -499,7 +499,7 @@ namespace Crow /// [DesignCategory ("Behavior")][DefaultValue(true)] public virtual bool CacheEnabled { - get { return cacheEnabled; } + get => cacheEnabled; set { if (cacheEnabled == value) return; @@ -510,9 +510,9 @@ namespace Crow /// /// If true, rendering of GraphicObject is clipped inside client rectangle /// - [DesignCategory ("Appearance")][DefaultValue(false)] + [DesignCategory ("Appearance")][DefaultValue(true)] public virtual bool ClipToClientRect { - get { return clipToClientRect; } + get => clipToClientRect; set { if (clipToClientRect == value) return; @@ -521,11 +521,9 @@ namespace Crow this.RegisterForRedraw (); } } - #if DEBUG_LOG - [XmlIgnore]public string TreePath { - get { return this.GetType().Name + GraphicObjects.IndexOf(this).ToString (); } - } - #endif +#if DEBUG_LOG + [XmlIgnore]public string TreePath => this.GetType().Name + GraphicObjects.IndexOf(this).ToString (); +#endif /// /// Name is used in binding to reference other GraphicObjects inside the graphic tree /// and by template controls to find special element in their template implementation such @@ -914,10 +912,18 @@ namespace Crow return; isVisible = value; - + if (!isVisible) unshownPostActions (); RegisterForLayouting (LayoutingType.Sizing); + + /*if (isVisible){ + IsDirty = true; + RegisterForLayouting(LayoutingType.Sizing); + } else { + unshownPostActions (); + }*/ + NotifyValueChangedAuto (isVisible); } @@ -1089,8 +1095,8 @@ namespace Crow } } [DesignCategory ("Divers")] - public IList ContextCommands { - get { return contextCommands; } + public CommandGroup ContextCommands { + get => contextCommands; set { if (contextCommands == value) return; @@ -1467,7 +1473,7 @@ namespace Crow /// Clip rectangle public virtual void RegisterClip(Rectangle clip){ if (disposed) { - DbgLogger.AddEvent (DbgEvtType.AlreadyDisposed | DbgEvtType.GORegisterClip); + DbgLogger.AddEvent (DbgEvtType.AlreadyDisposed | DbgEvtType.GORegisterClip, this); return; } DbgLogger.StartEvent(DbgEvtType.GORegisterClip, this); @@ -1500,43 +1506,49 @@ namespace Crow [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RegisterForGraphicUpdate () { -#if DEBUG_LOG if (disposed) { DbgLogger.AddEvent (DbgEvtType.GORegisterForGraphicUpdate | DbgEvtType.AlreadyDisposed, this); return; } -#endif - IsDirty = true; + DbgLogger.StartEvent(DbgEvtType.GORegisterForGraphicUpdate, this); + + IsDirty = true; if (Width.IsFit || Height.IsFit) RegisterForLayouting (LayoutingType.Sizing); else if (RegisteredLayoutings == LayoutingType.None) IFace.EnqueueForRepaint (this); + + DbgLogger.EndEvent(DbgEvtType.GORegisterForGraphicUpdate); } /// query an update of the content without layouting changes [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RegisterForRedraw () { -#if DEBUG_LOG if (disposed) { DbgLogger.AddEvent (DbgEvtType.GORegisterForRedraw | DbgEvtType.AlreadyDisposed, this); return; } -#endif IsDirty = true; if (RegisteredLayoutings == LayoutingType.None) IFace.EnqueueForRepaint (this); } + public void RegisterForRepaint () { + if (RegisteredLayoutings == LayoutingType.None && !IsDirty) + IFace.EnqueueForRepaint (this); + } #endregion #region Layouting /// return size of content + margins public virtual int measureRawSize (LayoutingType lt) { -#if DEBUG_LOG - DbgLogger.AddEvent(DbgEvtType.GOMeasure, this); -#endif - return lt == LayoutingType.Width ? + DbgLogger.StartEvent(DbgEvtType.GOMeasure, this, lt); + + int tmp = lt == LayoutingType.Width ? contentSize.Width + 2 * margin: contentSize.Height + 2 * margin; + + DbgLogger.EndEvent(DbgEvtType.GOMeasure); + return tmp; } internal bool firstUnresolvedFitWidth (out Widget ancestorInUnresolvedFit) @@ -1564,12 +1576,11 @@ namespace Crow public virtual void ChildrenLayoutingConstraints(ILayoutable layoutable, ref LayoutingType layoutType){ } /// Query a layouting for the type pass as parameter, redraw only if layout changed. public virtual void RegisterForLayouting(LayoutingType layoutType){ -#if DEBUG_LOG if (disposed) { - DbgLogger.AddEvent (DbgEvtType.GORegisterLayouting | DbgEvtType.AlreadyDisposed, this); + DbgLogger.AddEvent (DbgEvtType.AlreadyDisposed, this); return; } -#endif + if (Parent == null) return; DbgLogger.StartEvent (DbgEvtType.GOLockLayouting, this); @@ -1843,6 +1854,9 @@ namespace Crow /// of the widget public virtual void Paint (Context ctx) { + if (!IsVisible) + return; + DbgLogger.StartEvent (DbgEvtType.GOPaint, this); //TODO:this test should not be necessary @@ -1888,10 +1902,12 @@ namespace Crow ctx.Translate (rb.X, rb.Y); onDraw (ctx); - if (!IsEnabled) - paintDisabled (ctx, Slot); ctx.Restore (); + + if (!IsEnabled) + paintDisabled (ctx, rb); + } LastPaintedSlot = Slot; } @@ -1900,11 +1916,11 @@ namespace Crow DbgLogger.EndEvent (DbgEvtType.GOPaint); } void paintDisabled(Context gr, Rectangle rb){ - gr.Operator = Operator.Xor; - gr.SetSource (0.1, 0.1, 0.1, 0.8); + //gr.Operator = Operator.Xor; + gr.SetSource (0.2, 0.2, 0.2, 0.8); gr.Rectangle (rb); gr.Fill (); - gr.Operator = Operator.Over; + //gr.Operator = Operator.Over; } #endregion @@ -2130,6 +2146,21 @@ namespace Crow /// Checks to handle when widget is removed from the visible graphic tree /// void unshownPostActions () { + //IsDirty = true; + + /*if (parent is Widget p) + p.RegisterForGraphicUpdate(); + else*/ + /*try + { + parent?.RegisterClip (ContextCoordinates(LastPaintedSlot)); + } + catch (System.Exception e) + { + Debug.WriteLine($"[ERR]:unshownPostActions:{e}"); + }*/ + + if (IFace.ActiveWidget != null) { if (IFace.ActiveWidget.IsOrIsInside (this)) IFace.ActiveWidget = null; @@ -2151,6 +2182,24 @@ namespace Crow IFace.OnMouseMove (IFace.MousePosition.X, IFace.MousePosition.Y); } } + + /*Slot = default; + try + { + if (LastSlots.Width > 0) + OnLayoutChanges (LayoutingType.Width); + if (LastSlots.Height > 0) + OnLayoutChanges (LayoutingType.Height); + OnLayoutChanges (LayoutingType.X); + OnLayoutChanges (LayoutingType.Y); + } + catch (System.Exception ex) + { + Console.WriteLine($"[ERROR]Unshown post actions: {ex}"); + } + LastSlots = default; + LastPaintedSlot = default;*/ + } } } diff --git a/Crow/src/Widgets/Wrapper.cs b/Crow/src/Widgets/Wrapper.cs index 39648139..a0fac536 100644 --- a/Crow/src/Widgets/Wrapper.cs +++ b/Crow/src/Widgets/Wrapper.cs @@ -28,7 +28,7 @@ namespace Crow if (Orientation == Orientation.Vertical) { int tallestChild = 0; foreach (Widget c in Children) { - if (!c.Visible) + if (!c.IsVisible) continue; if (dx + c.Slot.Width > ClientRectangle.Width) { dx = 0; @@ -47,7 +47,7 @@ namespace Crow } else { int largestChild = 0; foreach (Widget c in Children) { - if (!c.Visible) + if (!c.IsVisible) continue; if (dy + c.Slot.Height > ClientRectangle.Height) { dy = 0; diff --git a/CrowDbgShared/CrowDbgShared.csproj b/CrowDbgShared/CrowDbgShared.csproj new file mode 100644 index 00000000..0ae49780 --- /dev/null +++ b/CrowDbgShared/CrowDbgShared.csproj @@ -0,0 +1,13 @@ + + + Library + netstandard2.1 + false + + + + + + + + diff --git a/CrowDbgShared/src/SharedDelegates.cs b/CrowDbgShared/src/SharedDelegates.cs new file mode 100644 index 00000000..aee1ef1e --- /dev/null +++ b/CrowDbgShared/src/SharedDelegates.cs @@ -0,0 +1,20 @@ +// Copyright (c) 2013-2021 Bruyère Jean-Philippe jp_bruyere@hotmail.com +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; +using System.IO; +using System.Diagnostics; +using System.Collections.Generic; +using System.Linq; +using Glfw; + +namespace CrowDbgShared +{ + public delegate void InterfaceResizeDelegate(int a, int b); + public delegate bool InterfaceMouseMoveDelegate(int a, int b); + public delegate bool InterfaceMouseButtonDelegate(MouseButton button); + public delegate void VoidDelegate(); + public delegate IntPtr IntPtrGetterDelegate(); +} + diff --git a/Directory.Build.props b/Directory.Build.props index 650c49bd..41e78201 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -9,5 +9,7 @@ 0.9.4 $(CrowVersion)-beta true + false + 0.2.11-beta diff --git a/Samples/BasicTests/BasicTests.cs b/Samples/BasicTests/BasicTests.cs index 656c05a3..954d4879 100644 --- a/Samples/BasicTests/BasicTests.cs +++ b/Samples/BasicTests/BasicTests.cs @@ -18,12 +18,12 @@ namespace tests protected override void OnInitialized () { - Commands = new List (new Crow.Command [] { + Commands = new CommandGroup ( new Crow.Command("command1", new Action(() => Console.WriteLine ("command1 triggered"))), new Crow.Command("command2", new Action(() => Console.WriteLine ("command2 triggered"))), new Crow.Command("command3", new Action(() => Console.WriteLine ("command3 triggered"))), - new Crow.Command("command4", new Action(() => Console.WriteLine ("command4 triggered"))), - }); + new Crow.Command("command4", new Action(() => Console.WriteLine ("command4 triggered"))) + ); // += KeyboardKeyDown1; diff --git a/Samples/ControlLib/Class1.cs b/Samples/ControlLib/Class1.cs deleted file mode 100644 index 23bcb161..00000000 --- a/Samples/ControlLib/Class1.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System; - -namespace ControlLib -{ - public class Class1 - { - } -} diff --git a/Samples/ControlLib/ControlLib.csproj b/Samples/ControlLib/ControlLib.csproj deleted file mode 100644 index 2a6fdb43..00000000 --- a/Samples/ControlLib/ControlLib.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - netstandard2.1 - false - - - - TRACE;DEBUG - - - - - - - - - - - - ControlLib.%(Filename)%(Extension) - - - diff --git a/Samples/CrowDebugger/CrowDebugAssemblyLoadContext.cs b/Samples/CrowDebugger/CrowDebugAssemblyLoadContext.cs new file mode 100644 index 00000000..0c629fc7 --- /dev/null +++ b/Samples/CrowDebugger/CrowDebugAssemblyLoadContext.cs @@ -0,0 +1,15 @@ +using System.Threading; +using System; +using System.Runtime.Loader; +using System.Reflection; + +namespace CrowDebugger +{ + public class CrowDebugAssemblyLoadContext : AssemblyLoadContext + { + protected override Assembly Load(AssemblyName assemblyName) + { + return base.Load(assemblyName); + } + } +} \ No newline at end of file diff --git a/Samples/CrowDebugger/CrowDebugger.csproj b/Samples/CrowDebugger/CrowDebugger.csproj new file mode 100644 index 00000000..c73e0d16 --- /dev/null +++ b/Samples/CrowDebugger/CrowDebugger.csproj @@ -0,0 +1,8 @@ + + + + Exe + netcoreapp3.1 + + + diff --git a/Samples/CrowDebugger/Program.cs b/Samples/CrowDebugger/Program.cs new file mode 100644 index 00000000..b15e3b17 --- /dev/null +++ b/Samples/CrowDebugger/Program.cs @@ -0,0 +1,58 @@ +using System.Threading; +using System; +using System.IO; +using System.Reflection; +using System.Runtime.Loader; + +namespace CrowDebugger +{ + class Program + { + const int BUFF_MAX = 1024; + static void Main(string[] args) + { + AssemblyLoadContext crowLoadCtx = new AssemblyLoadContext("CrowDebuggerLoadContext"); + Console.WriteLine("Crow Debugger started."); + char[] buffer = new char[1024]; + int buffPtr = 0; + while (true) + { + if (Console.KeyAvailable) + { + ConsoleKeyInfo key = Console.ReadKey(true); + if (key.Key == ConsoleKey.Enter) { + Span cmd = buffer.AsSpan(0, buffPtr); + Console.WriteLine($"=> {cmd.ToString()}"); + if (cmd[0] == 'q' || cmd.SequenceEqual ("quit")) + break; + + if (cmd.StartsWith("load")) { + using(crowLoadCtx.EnterContextualReflection()) { + string str = cmd.Slice(5).ToString(); + Console.WriteLine($"[msg]:Trying to load crow dll from: '{str}'"); + if (!File.Exists (str)) { + Console.WriteLine ($"[error]:File not found: {str}"); + } else { + Assembly crowAssembly = crowLoadCtx.LoadFromAssemblyPath (str); + if (crowAssembly == null) { + Console.WriteLine($"[error]:Failed to load crow dll from: {str}"); + } else { + Console.WriteLine($"[ok]:Crow Assembly loaded:{crowAssembly.FullName}"); + + } + } + } + } else + Console.WriteLine($"=> {cmd.ToString()}"); + buffPtr = 0; + continue; + } + + buffer[buffPtr++] = key.KeyChar; + } + } + + Console.WriteLine("exited"); + } + } +} diff --git a/Samples/DebugLogAnalyzer/DbgEventWidget.cs b/Samples/DebugLogAnalyzer/DbgEventWidget.cs deleted file mode 100644 index b2af84dd..00000000 --- a/Samples/DebugLogAnalyzer/DbgEventWidget.cs +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright (c) 2013-2020 Jean-Philippe Bruyère -// -// 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.SetSource (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 deleted file mode 100644 index 49facb5a..00000000 --- a/Samples/DebugLogAnalyzer/DbgLogViewer.cs +++ /dev/null @@ -1,637 +0,0 @@ -// Copyright (c) 2013-2020 Jean-Philippe Bruyère -// -// 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 colors; - - public static Configuration colorsConf = new Configuration ("dbgcolor.conf");//, Interface.GetStreamFromPath("#Crow.dbgcolor.conf")); - - public static void reloadColors () { - colors = new Dictionary(); - foreach (string n in colorsConf.Names) { - DbgEvtType t = (DbgEvtType)Enum.Parse (typeof(DbgEvtType), n); - Color c = colorsConf.Get (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 events = new List (); - List widgets = new List (); - - public List 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 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 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 (IFace, 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 (Cairo.Context ctx) - { - base.Paint (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.SetSource (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 (); - } - - /// Process scrolling vertically, or if shift is down, vertically - 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 index df93e686..057ee24c 100644 --- a/Samples/DebugLogAnalyzer/DebugLogAnalyzer.csproj +++ b/Samples/DebugLogAnalyzer/DebugLogAnalyzer.csproj @@ -1,9 +1,18 @@ + + false + - + + + + Dbg.%(Filename)%(Extension) - - + + + + + \ No newline at end of file diff --git a/Samples/DebugLogAnalyzer/Program.cs b/Samples/DebugLogAnalyzer/Program.cs deleted file mode 100644 index 7493a842..00000000 --- a/Samples/DebugLogAnalyzer/Program.cs +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright (c) 2013-2020 Jean-Philippe Bruyère -// -// 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) - { - using (Program app = new Program ()) - app.Run (); - } - - List events = new List(); - List widgets = new List(); - DbgEvent curEvent = new DbgEvent(); - DbgWidgetRecord curWidget = new DbgWidgetRecord(); - - public List Events { - get => events; - set { - if (events == value) - return; - events = value; - NotifyValueChanged (nameof (Events), events); - } - } - public List 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 CurWidgetRootEvents => curWidget == null? new List() : 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 ("debug.log"); - } - - void loadDebugFile (string logFile) - { - if (!File.Exists (logFile)) - return; - - List evts = new List (); - List objs = new List (); - - 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 startedEvents = new Stack (); - - 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/src/DbgEventWidget.cs b/Samples/DebugLogAnalyzer/src/DbgEventWidget.cs new file mode 100644 index 00000000..19666ee2 --- /dev/null +++ b/Samples/DebugLogAnalyzer/src/DbgEventWidget.cs @@ -0,0 +1,142 @@ +// Copyright (c) 2013-2020 Jean-Philippe Bruyère +// +// 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; +using Crow.DebugLogger; +using DebugLogAnalyzer; + +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, 0, w, h); + ctx.SetSource (dbge.Color); + /*if (dbge.IsSelected) { + ctx.FillPreserve (); + ctx.SetSource (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; + 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/src/DbgLogViewer.cs b/Samples/DebugLogAnalyzer/src/DbgLogViewer.cs new file mode 100644 index 00000000..1273c9e6 --- /dev/null +++ b/Samples/DebugLogAnalyzer/src/DbgLogViewer.cs @@ -0,0 +1,805 @@ +using System.Diagnostics; +using System.Runtime.InteropServices.ComTypes; +// Copyright (c) 2013-2020 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Crow.Cairo; +using Crow.DebugLogger; +using DebugLogAnalyzer; + +namespace Crow +{ + public class DbgLogViewer : Widget + { + public static Dictionary colors; + + public static Configuration colorsConf = new Configuration ("dbgcolor.conf");//, Interface.GetStreamFromPath("#Crow.dbgcolor.conf")); + + public static void reloadColors () { + colors = new Dictionary(); + foreach (string n in colorsConf.Names) { + DbgEvtType t = (DbgEvtType)Enum.Parse (typeof(DbgEvtType), n); + Color c = colorsConf.Get (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 events = new List (); + List widgets = new List (); + + + public DbgEvtType Filter { + get => Configuration.Global.Get ("DbgLogViewFilter"); + set { + if (Filter == value) + return; + Configuration.Global.Set ("DbgLogViewFilter", value); + NotifyValueChangedAuto(Filter); + RegisterForGraphicUpdate(); + } + } + public List Events { + get => events; + set { + if (events == value) + return; + events = value; + NotifyValueChanged (nameof (Events), events); + + maxTicks = minTicks = 0; + if (events != null && events.Count > 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; + } else { + maxTicks = 1; + XScale = 1.0/1024.0; + } + + + RegisterForGraphicUpdate (); + } + } + public List 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); + if (CurrentWidget == null) + return; + if (CurrentWidget.listIndex < scrollY || CurrentWidget.listIndex > scrollY + visibleLines) + ScrollY = CurrentWidget.listIndex - (visibleLines / 2); + + currentLine = CurrentWidget.listIndex; + RegisterForRedraw(); + } + } + 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]; + hoverLine = we.InstanceIndex; + } + currentTick = curEvent.begin; + if (curEvent.begin > minTicks + ScrollX + visibleTicks || + curEvent.end < minTicks + ScrollX) + ScrollX = curEvent.begin - minTicks - 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 hoverTick = 0, currentTick, selStart = -1, selEnd = -1, minTicks = 0, maxTicks = 0, visibleTicks = 0; + int hoverLine = -1, currentLine = -1; + int visibleLines = 1; + Point mousePos; + + public double XScale { + get => 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 (); + } + } + + void drawEvents (Context ctx, List evts) + { + if (evts == null || evts.Count == 0) + return; + Rectangle cb = ClientRectangle; + + foreach (DbgEvent evt in evts) { + if ((evt.Category & currentFilter) == currentFilter) { + 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) { + + 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; + RectangleD r = new RectangleD(x, penY, w, fe.Height); + ctx.Rectangle (r); + ctx.Fill (); + /*if (evt == CurrentEvent) { + r.Inflate(2,2); + ctx.SetSource(Colors.White); + ctx.Rectangle(r); + ctx.Stroke(); + }*/ + } + } else if (evt.type.HasFlag (DbgEvtType.IFace)) { + 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.SetSource (0.9,0.9,0.0,0.1); + ctx.SetSource (evt.Color.AdjustAlpha(0.15)); + ctx.Rectangle (x, cb.Top + topMargin, w, cb.Height); + ctx.Fill (); + } + } + drawEvents (ctx, evt.Events); + } + } + + DbgEvtType currentFilter; + 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 if (currentLine == g.listIndex) + gr.SetSource(Colors.RoyalBlue); + else + Foreground.SetAsSource (IFace, gr); + + gr.MoveTo (penX, penY - gr.FontExtents.Descent); + gr.ShowText (g.name + gIdx); + } + + currentFilter = Filter; + drawEvents (gr, events); + + 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 + long largeGrad = long.Parse ("1" + new string ('0', visibleTicks.ToString ().Length - 1)); + long 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 = ticksToMS(curGrad); + 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 (); + + + + } + string ticksToMS(long ticks) => Math.Round ((double)ticks / Stopwatch.Frequency * 1000.0, 2).ToString(); + public override void Paint (Cairo.Context ctx) + { + base.Paint (ctx); + + Rectangle r = new Rectangle(mousePos.X, 0, 1, Slot.Height); + Rectangle ctxR = ContextCoordinates (r); + Rectangle cb = ClientRectangle; + ctx.LineWidth = 1.0; + if (hoverTick >= 0) { + double x = xScale * (hoverTick - 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.SetSource (0.7,0.7,0.7,0.5); + ctx.Stroke(); + } + if (currentTick >= 0) { + double x = xScale * (currentTick - minTicks - ScrollX) + leftMargin; + if (x > leftMargin && x < cb.Right) { + 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); + ctx.LineTo (x, cb.Bottom); + ctx.SetSource (0.2,0.7,1.0,0.6); + ctx.Stroke(); + } + } + + ctx.SelectFontFace (Font.Name, Font.Slant, Font.Wheight); + ctx.SetFontSize (Font.Size); + ctx.FontOptions = Interface.FontRenderingOptions; + ctx.Antialias = Interface.Antialias; + + string str = ticksToMS(hoverTick); + + ctx.MoveTo (ctxR.X - ctx.TextExtents (str).Width / 2, ctxR.Y + fe.Height); + ctx.ShowText (str); + + ctx.Operator = Cairo.Operator.Add; + + if (hoverLine >= 0) { + double y = fe.Height * (hoverLine - ScrollY) + topMargin + cb.Top; + r = new Rectangle (cb.Left, (int)y, cb.Width, (int)fe.Height); + + ctx.SetSource (0.1, 0.1, 0.1, 0.4); + ctx.Rectangle (ContextCoordinates (r)); + ctx.Fill (); + } + + if (currentLine >= ScrollY && currentLine < scrollY + visibleLines) { + double y = fe.Height * (currentLine - ScrollY) + topMargin + cb.Top; + r = new Rectangle (cb.Left, (int)y, cb.Width, (int)fe.Height); + + ctx.SetSource (0.1, 0.1, 0.7, 0.2); + 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: + 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); + hoverLine = -1; + hoverTick = 0; + } + public override void onMouseMove (object sender, MouseMoveEventArgs e) + { + long lastTick = hoverTick; + int lastLine = hoverLine; + updateMouseLocalPos (e.Position); + + if (IFace.IsDown (Glfw.MouseButton.Left) && selStart >= 0) + selEnd = hoverTick; + else if (IFace.IsDown(Glfw.MouseButton.Right)) { + if (lastTick >= 0 && hoverTick >= 0) + ScrollX += lastTick - hoverTick; + if (lastLine >= 0 && hoverLine >= 0) + ScrollY += lastLine - hoverLine; + updateMouseLocalPos (e.Position); + } else { + HoverWidget = (hoverLine < 0 || hoverLine >= widgets.Count) ? null : widgets [hoverLine]; + HoverEvent = hoverWidget?.Events.FirstOrDefault (ev => ev.begin <= hoverTick && ev.end >= hoverTick); + Task.Run (() => findHoverEvent (hoverWidget, hoverTick)); + } + + RegisterForRepaint(); + + e.Handled = true; + base.onMouseMove (sender, e); + } + void findHoverEvent (DbgWidgetRecord widget, long tick) { + DbgEvent tmp = widget?.Events.FirstOrDefault (ev => ev.begin <= tick && ev.end >= tick); + if (tmp == null) { + tmp = Events.Where(e=>e.type.HasFlag(DbgEvtType.IFace)).Where (ev => ev.begin <= tick && ev.end >= tick).FirstOrDefault(); + while(tmp != null) { + DbgEvent che = tmp.Events?.Where(e=>e.type.HasFlag(DbgEvtType.IFace)).Where (ev => ev.begin <= tick && ev.end >= tick).FirstOrDefault(); + if (che == null) + break; + tmp = che; + } + } else { + while(tmp != null) { + DbgEvent che = tmp.Events?.OfType()?.Where(ev=>ev.InstanceIndex == widget.listIndex && ev.begin <= tick && ev.end >= tick).FirstOrDefault(); + if (che == null) + break; + tmp = che; + } + } + HoverEvent = tmp; + } + public override void onMouseClick(object sender, MouseButtonEventArgs e) + { + if (e.Button == Glfw.MouseButton.Left && selEnd < 0) { + currentTick = hoverTick; + currentLine = hoverLine; + CurrentWidget = hoverWidget; + CurrentEvent = hoverEvent; + } + selStart = -1; + selEnd = -1; + + e.Handled = true; + base.onMouseClick(sender, e); + } + public override void onMouseDown (object sender, MouseButtonEventArgs e) + { + if (e.Button == Glfw.MouseButton.Left) { + selStart = hoverTick; + selEnd = -1; + } + + RegisterForRedraw (); + e.Handled = true; + base.onMouseDown (sender, e); + } + public override void onMouseUp (object sender, MouseButtonEventArgs 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 = scrX; + } + + RegisterForRedraw (); + e.Handled = true; + base.onMouseUp (sender, e); + } + + /// Process scrolling vertically, or if shift is down, vertically + 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 = (long)(hoverTick - (long)((double)Math.Max(0, mousePos.X - (long)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.Ceiling (((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 { + long tot = maxTicks - minTicks; + MaxScrollX = Math.Max (0L, tot - visibleTicks); + NotifyValueChanged ("ChildWidthRatio", (double)visibleTicks / tot); + } + } + void updateMaxScrollY () + { + if (widgets == null) + MaxScrollY = 0; + else { + MaxScrollY = Math.Max (0, widgets.Count + 1 - visibleLines); + NotifyValueChanged ("ChildHeightRatio", (double)visibleLines / (widgets.Count + 1)); + } + } + + 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) + hoverLine = -1; + else + hoverLine = (int)((double)(mousePos.Y - cb.Top) / fe.Height) + ScrollY; + + NotifyValueChanged ("CurrentLine", hoverLine); + + mousePos.Y = Math.Max (cb.Y, mousePos.Y); + mousePos.Y = Math.Min (cb.Bottom, mousePos.Y); + + hoverTick = (long)((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); + } + + + long scrollX, maxScrollX; + int scrollY, maxScrollY, mouseWheelSpeed; + + /// + /// if true, key stroke are handled in derrived class + /// + protected bool KeyEventsOverrides = false; + + /// Horizontal Scrolling Position + [DefaultValue(0)] + public virtual long ScrollX { + get => scrollX; + set { + if (scrollX == value) + return; + + long newS = value; + if (newS < 0) + newS = 0; + else if (newS > maxScrollX) + newS = maxScrollX; + + if (newS == scrollX) + return; + + scrollX = newS; + + NotifyValueChangedAuto (scrollX); + RegisterForGraphicUpdate (); + } + } + /// Vertical Scrolling Position + [DefaultValue(0)] + public virtual int ScrollY { + get => scrollY; + set { + if (scrollY == value) + return; + + int newS = value; + if (newS < 0) + newS = 0; + else if (newS > maxScrollY) + newS = maxScrollY; + + if (newS == scrollY) + return; + + scrollY = newS; + + NotifyValueChangedAuto (scrollY); + RegisterForGraphicUpdate (); + + 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) + hoverLine = -1; + else + hoverLine = (int)((double)(mousePos.Y - cb.Top) / fe.Height) + ScrollY; + + NotifyValueChanged ("CurrentLine", hoverLine); + } + } + /// Horizontal Scrolling maximum value + [DefaultValue(0)] + public virtual long MaxScrollX { + get => maxScrollX; + set { + if (maxScrollX == value) + return; + + maxScrollX = Math.Max(0, value); + + if (scrollX > maxScrollX) + ScrollX = maxScrollX; + + NotifyValueChangedAuto (maxScrollX); + RegisterForGraphicUpdate (); + } + } + /// Vertical Scrolling maximum value + [DefaultValue(0)] + public virtual int MaxScrollY { + get => maxScrollY; + set { + if (maxScrollY == value) + return; + + maxScrollY = Math.Max (0, value); + + if (scrollY > maxScrollY) + ScrollY = maxScrollY; + + NotifyValueChangedAuto (maxScrollY); + RegisterForGraphicUpdate (); + } + } + /// Mouse Wheel Scrolling multiplier + [DefaultValue(1)] + public virtual int MouseWheelSpeed { + get => mouseWheelSpeed; + set { + if (mouseWheelSpeed == value) + return; + + mouseWheelSpeed = value; + + NotifyValueChangedAuto (mouseWheelSpeed); + } + } + } +} + + diff --git a/Samples/DebugLogAnalyzer/src/DebugInterface.cs b/Samples/DebugLogAnalyzer/src/DebugInterface.cs new file mode 100644 index 00000000..c0615321 --- /dev/null +++ b/Samples/DebugLogAnalyzer/src/DebugInterface.cs @@ -0,0 +1,114 @@ +// Copyright (c) 2013-2019 Bruyère Jean-Philippe +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; +using Crow.Cairo; +using System.Threading; + +namespace Crow +{ + public class DebugInterface : SampleBase { + static DebugInterface() { + DbgLogger.IncludeEvents = DbgEvtType.None; + DbgLogger.DiscardEvents = DbgEvtType.None; + DbgLogger.ConsoleOutput = true; + } + public DebugInterface (IntPtr hWin) : base (hWin) + { + surf = new ImageSurface (Format.Argb32, 100, 100); + } + + public override void Run() + { + Init(); + + Thread t = new Thread (interfaceThread) { + IsBackground = true + }; + t.Start (); + } + public bool Terminate; + string source; + Action delRegisterForRepaint;//call RegisterForRepaint in the container widget (DebugInterfaceWidget) + Action delSetCurrentException; + + void interfaceThread () { + while (!Terminate) { + try + { + Update(); + } + catch (System.Exception ex) + { + if (Monitor.IsEntered(LayoutMutex)) + Monitor.Exit (LayoutMutex); + if (Monitor.IsEntered(UpdateMutex)) + Monitor.Exit (UpdateMutex); + if (Monitor.IsEntered(ClippingMutex)) + Monitor.Exit (ClippingMutex); + delSetCurrentException (ex); + ClearInterface(); + Thread.Sleep(1000); + } + + if (IsDirty) + delRegisterForRepaint(); + + Thread.Sleep (UPDATE_INTERVAL); + } + } + public IntPtr SurfacePointer { + get { + lock(UpdateMutex) + return surf.Handle; + } + } + public void RegisterDebugInterfaceCallback (object w){ + Type t = w.GetType(); + delRegisterForRepaint = (Action)Delegate.CreateDelegate(typeof(Action), w, t.GetMethod("RegisterForRepaint")); + delSetCurrentException = (Action)Delegate.CreateDelegate(typeof(Action), w, t.GetProperty("CurrentException").GetSetMethod()); + } + public void ResetDirtyState () { + IsDirty = false; + } + public string Source { + set { + if (source == value) + return; + source = value; + if (string.IsNullOrEmpty(source)) + return; + delSetCurrentException(null); + try + { + lock (UpdateMutex) { + Widget tmp = CreateITorFromIMLFragment (source).CreateInstance(); + ClearInterface(); + AddWidget (tmp); + tmp.DataSource = this; + } + } + catch (IML.InstantiatorException iTorEx) + { + delSetCurrentException(iTorEx.InnerException); + } + catch (System.Exception ex) + { + delSetCurrentException(ex); + } + } + } + public void Resize (int width, int height) { + + lock (UpdateMutex) { + clientRectangle = new Rectangle (0, 0, width, height); + surf?.Dispose(); + surf = new ImageSurface (Format.Argb32, width, height); + foreach (Widget g in GraphicTree) + g.RegisterForLayouting (LayoutingType.All); + RegisterClip (clientRectangle); + } + } + } +} \ No newline at end of file diff --git a/Samples/DebugLogAnalyzer/src/DebugInterfaceWidget.cs b/Samples/DebugLogAnalyzer/src/DebugInterfaceWidget.cs new file mode 100644 index 00000000..3f331cf2 --- /dev/null +++ b/Samples/DebugLogAnalyzer/src/DebugInterfaceWidget.cs @@ -0,0 +1,281 @@ +// Copyright (c) 2013-2019 Bruyère Jean-Philippe +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; +using Glfw; +using System.Reflection; +using System.Runtime.Loader; +using System.IO; +using Crow.Cairo; +using System.Diagnostics; +using CrowDbgShared; +using System.Collections.Generic; +using Crow.DebugLogger; + +namespace Crow +{ + public class DebugInterfaceWidget : Widget { + + string imlSource; + List events; + List widgets; + Exception currentException; + object dbgIFace; + AssemblyLoadContext crowLoadCtx; + Assembly crowAssembly, thisAssembly; + Type dbgIfaceType; + Action delResize; + Func delMouseMove; + Func delMouseDown, delMouseUp; + Action delResetDirtyState; + Action delResetDebugger; + Action delSaveDebugLog; + IntPtrGetterDelegate delGetSurfacePointer; + Action delSetSource; + + FieldInfo fiDbg_IncludeEvents, fiDbg_DiscardEvents, fiDbg_ConsoleOutput; + + bool initialized, recording; + string crowDbgAssemblyLocation; + DbgEvtType recordedEvents, discardedEvents; + + public CommandGroup LoggerCommands => + new CommandGroup( + new Command("Get logs", () => getLog ()), + new Command("Reset logs", () => delResetDebugger ()) + ); + public string IMLSource { + get => imlSource; + set { + if (imlSource == value) + return; + imlSource = value; + if (initialized) + delSetSource (imlSource); + NotifyValueChangedAuto(imlSource); + RegisterForRedraw(); + } + } + public Exception CurrentException { + get => currentException; + set { + if (currentException == value) + return; + currentException = value; + NotifyValueChangedAuto(currentException); + } + } + public string CrowDbgAssemblyLocation { + get => crowDbgAssemblyLocation; + set { + if (crowDbgAssemblyLocation == value) + return; + crowDbgAssemblyLocation = value; + NotifyValueChangedAuto(CrowDbgAssemblyLocation); + tryStartDebugInterface(); + } + } + + public bool Recording { + get => recording; + set { + if (recording == value) + return; + recording = value & initialized; + if (recording) { + fiDbg_DiscardEvents.SetValue (dbgIFace, DiscardedEvents); + fiDbg_IncludeEvents.SetValue (dbgIFace, RecordedEvents); + } else { + fiDbg_DiscardEvents.SetValue (dbgIFace, DiscardedEvents); + fiDbg_IncludeEvents.SetValue (dbgIFace, RecordedEvents); + } + NotifyValueChangedAuto(recording); + } + } + public DbgEvtType RecordedEvents { + get => recordedEvents; + set { + if (recordedEvents == value) + return; + recordedEvents = value; + if (Recording) + fiDbg_IncludeEvents.SetValue (dbgIFace, value); + NotifyValueChangedAuto (recordedEvents); + } + } + public DbgEvtType DiscardedEvents { + get => discardedEvents; + set { + if (discardedEvents == value) + return; + discardedEvents = value; + if (Recording) + fiDbg_DiscardEvents.SetValue (dbgIFace, value); + NotifyValueChangedAuto (discardedEvents); + } + } + public bool DebugLogToFile { + get => initialized ? !(bool)fiDbg_ConsoleOutput.GetValue (dbgIFace) : false; + set { + if (!initialized || DebugLogToFile == value) + return; + fiDbg_ConsoleOutput.SetValue (dbgIFace, !value); + NotifyValueChangedAuto (DebugLogToFile); + } + } + + + protected override void onInitialized(object sender, EventArgs e) + { + base.onInitialized(sender, e); + + + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + } + + public bool CrowDebuggerOK => initialized; + public bool CrowDebuggerNOK => !initialized; + void notifyCrowDebuggerState () { + NotifyValueChanged("CrowDebuggerOK", CrowDebuggerOK); + NotifyValueChanged("CrowDebuggerNOK", CrowDebuggerNOK); + } + + void tryStartDebugInterface () { + if (initialized) + return; + if (!File.Exists (crowDbgAssemblyLocation)) { + notifyCrowDebuggerState(); + return; + } + + crowLoadCtx = new AssemblyLoadContext("CrowDebuggerLoadContext"); + + //using (crowLoadCtx.EnterContextualReflection()) { + crowAssembly = crowLoadCtx.LoadFromAssemblyPath (crowDbgAssemblyLocation); + thisAssembly = crowLoadCtx.LoadFromAssemblyPath (new Uri(Assembly.GetEntryAssembly().CodeBase).LocalPath); + + Type debuggerType = crowAssembly.GetType("Crow.DbgLogger"); + if (!(bool)debuggerType.GetField("IsEnabled").GetValue(null)) { + notifyCrowDebuggerState(); + return; + } + + dbgIfaceType = thisAssembly.GetType("Crow.DebugInterface"); + + dbgIFace = Activator.CreateInstance (dbgIfaceType, new object[] {IFace.WindowHandle}); + + delResize = (Action)Delegate.CreateDelegate(typeof(Action), + dbgIFace, dbgIfaceType.GetMethod("Resize")); + + delMouseMove = (Func)Delegate.CreateDelegate(typeof(Func), + dbgIFace, dbgIfaceType.GetMethod("OnMouseMove")); + + delMouseDown = (Func)Delegate.CreateDelegate(typeof(Func), + dbgIFace, dbgIfaceType.GetMethod("OnMouseButtonDown")); + + delMouseUp = (Func)Delegate.CreateDelegate(typeof(Func), + dbgIFace, dbgIfaceType.GetMethod("OnMouseButtonUp")); + + delGetSurfacePointer = (IntPtrGetterDelegate)Delegate.CreateDelegate(typeof(IntPtrGetterDelegate), + dbgIFace, dbgIfaceType.GetProperty("SurfacePointer").GetGetMethod()); + delSetSource = (Action)Delegate.CreateDelegate(typeof(Action), + dbgIFace, dbgIfaceType.GetProperty("Source").GetSetMethod()); + + delResetDirtyState = (Action)Delegate.CreateDelegate(typeof(Action), + dbgIFace, dbgIfaceType.GetMethod("ResetDirtyState")); + + fiDbg_IncludeEvents = debuggerType.GetField("IncludeEvents"); + fiDbg_DiscardEvents = debuggerType.GetField("DiscardEvents"); + fiDbg_ConsoleOutput = debuggerType.GetField("ConsoleOutput"); + delResetDebugger = (Action)Delegate.CreateDelegate(typeof(Action), + null, debuggerType.GetMethod("Reset")); + /*delSaveDebugLog = (Action)Delegate.CreateDelegate(typeof(Action), + null, debuggerType.GetMethod("Save", new Type[] {dbgIfaceType, typeof(string)}));*/ + + dbgIfaceType.GetMethod("RegisterDebugInterfaceCallback").Invoke (dbgIFace, new object[] {this} ); + dbgIfaceType.GetMethod("Run").Invoke (dbgIFace, null); + + initialized = true; + + //Console.WriteLine($"DbgIFace: LoadCtx:{AssemblyLoadContext.GetLoadContext (dbgIFace.GetType().Assembly).Name})"); + //} + } + + protected override void onDraw(Context gr) + { + Console.WriteLine("onDraw"); + gr.SetSource(Colors.RoyalBlue); + gr.Paint(); + } + public override bool CacheEnabled { get => true; set => base.CacheEnabled = true; } + + public override void onMouseMove(object sender, MouseMoveEventArgs e) + { + if (initialized) { + Point m = ScreenPointToLocal (e.Position); + delMouseMove (m.X, m.Y); + e.Handled = true; + } + base.onMouseMove(sender, e); + } + public override void onMouseDown(object sender, MouseButtonEventArgs e) + { + if (initialized) { + delMouseDown (e.Button); + e.Handled=true; + } + base.onMouseDown (sender, e); + } + public override void onMouseUp(object sender, MouseButtonEventArgs e) + { + if (initialized) { + delMouseUp (e.Button); + e.Handled=true; + } + base.onMouseUp (sender, e); + } + + protected override void RecreateCache() + { + bmp?.Dispose (); + + if (initialized) { + delResize (Slot.Width, Slot.Height); + bmp = Crow.Cairo.Surface.Lookup (delGetSurfacePointer(), false); + } else + bmp = IFace.surf.CreateSimilar (Content.ColorAlpha, Slot.Width, Slot.Height); + + IsDirty = false; + } + protected override void UpdateCache(Context ctx) + { + if (initialized && bmp != null) { + paintCache (ctx, Slot + Parent.ClientRectangle.Position); + delResetDirtyState (); + } + + } + + void getLog () { + using (Stream stream = new MemoryStream (1024)) { + Type debuggerType = crowAssembly.GetType("Crow.DbgLogger"); + debuggerType.GetMethod("Save", new Type[] {dbgIfaceType, typeof(Stream)}).Invoke(null, new object[] {dbgIFace, stream}); + //debuggerType.GetMethod("Save", new Type[] {dbgIfaceType, typeof(string)}).Invoke(null, new object[] {dbgIFace, "debug.log"}); + //delSaveDebugLog(dbgIFace, "debug.log"); + stream.Seek(0, SeekOrigin.Begin); + events = new List(); + widgets = new List(); + DbgLogger.Load (stream, events, widgets); + //DbgLogger.Load ("debug.log", events, widgets); + DebugLogAnalyzer.Program dla = IFace as DebugLogAnalyzer.Program; + dla.Widgets = widgets; + dla.Events = events; + } + } + } +} \ No newline at end of file diff --git a/Samples/DebugLogAnalyzer/src/Editor.cs b/Samples/DebugLogAnalyzer/src/Editor.cs new file mode 100644 index 00000000..315a5a9d --- /dev/null +++ b/Samples/DebugLogAnalyzer/src/Editor.cs @@ -0,0 +1,44 @@ +// Copyright (c) 2013-2019 Bruyère Jean-Philippe +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; +using Glfw; +using Crow.Text; + +namespace Crow +{ + public class Editor : TextBox { + public override void onKeyDown(object sender, KeyEventArgs e) + { + TextSpan selection = Selection; + if (e.Key == Key.Tab && !selection.IsEmpty) { + int lineStart = lines.GetLocation (selection.Start).Line; + int lineEnd = lines.GetLocation (selection.End).Line; + + if (IFace.Shift) { + for (int l = lineStart; l <= lineEnd; l++) { + if (Text[lines[l].Start] == '\t') + update (new TextChange (lines[l].Start, 1, "")); + else if (Char.IsWhiteSpace (Text[lines[l].Start])) { + int i = 1; + while (i < lines[l].Length && i < Interface.TAB_SIZE && Char.IsWhiteSpace (Text[i])) + i++; + update (new TextChange (lines[l].Start, i, "")); + } + } + + }else{ + for (int l = lineStart; l <= lineEnd; l++) + update (new TextChange (lines[l].Start, 0, "\t")); + } + + selectionStart = new CharLocation (lineStart, 0); + CurrentLoc = new CharLocation (lineEnd, lines[lineEnd].Length); + + return; + } + base.onKeyDown(sender, e); + } + } +} \ No newline at end of file diff --git a/Samples/DebugLogAnalyzer/src/Extensions.cs b/Samples/DebugLogAnalyzer/src/Extensions.cs new file mode 100644 index 00000000..6086384c --- /dev/null +++ b/Samples/DebugLogAnalyzer/src/Extensions.cs @@ -0,0 +1,23 @@ + + +using Crow; + +namespace DebugLogAnalyzer { + public static class Extensions { + + public static CommandGroup GetCommands (this System.IO.DirectoryInfo di) => + new CommandGroup( + new Command ("Set as root", ()=> {Program.CurrentProgramInstance.CurrentDir = di.FullName;})); + public static CommandGroup GetCommands (this System.IO.FileInfo fi) => + new CommandGroup( + new Command ("Delete", (sender0) => { + MessageBox.ShowModal (Program.CurrentProgramInstance, MessageBox.Type.YesNo, $"Delete {fi.Name}?").Yes += (sender, e) => { + System.IO.File.Delete(fi.FullName); + Widget listContainer = ((sender0 as Widget).LogicalParent as Widget).DataSource as Widget; + (listContainer.Parent as Group).RemoveChild(listContainer); + }; + }) + ); + + } +} \ No newline at end of file diff --git a/Samples/DebugLogAnalyzer/src/Program.cs b/Samples/DebugLogAnalyzer/src/Program.cs new file mode 100644 index 00000000..7da9f5a1 --- /dev/null +++ b/Samples/DebugLogAnalyzer/src/Program.cs @@ -0,0 +1,466 @@ +using System.Reflection.PortableExecutable; +// Copyright (c) 2013-2021 Bruyère Jean-Philippe +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; +using Crow; +using System.IO; +using Glfw; +using Crow.Text; +using System.Collections.Generic; +using Encoding = System.Text.Encoding; +using Crow.DebugLogger; + +namespace DebugLogAnalyzer +{ + public class Program : SampleBase + { + public static Program CurrentProgramInstance; + static void Main (string [] args) + { + DbgLogger.IncludeEvents = DbgEvtType.None; + DbgLogger.DiscardEvents = DbgEvtType.All; + + using (Program app = new Program ()) { + CurrentProgramInstance = app; + app.Run (); + } + } + protected override void OnInitialized () { + initCommands (); + + base.OnInitialized (); + + if (string.IsNullOrEmpty (CurrentDir)) + CurrentDir = Path.Combine (Directory.GetCurrentDirectory (), "Interfaces"); + + Load ("#Dbg.main.crow").DataSource = this; + //crowContainer = FindByName ("CrowContainer") as Container; + editor = FindByName ("tb") as TextBox; + + /*TreeView tv = FindByName("dbgTV") as TreeView; + dbgTreeViewScroller = tv.FindByNameInTemplate ("scroller1") as Scroller;*/ + + + if (!File.Exists (CurrentFile)) + newFile (); + //I set an empty object as datasource at this level to force update when new + //widgets are added to the interface + + reloadFromFile (); + } + + List events = new List(); + List widgets = new List(); + DbgEvent curEvent = new DbgEvent(); + DbgWidgetRecord curWidget = new DbgWidgetRecord(); + bool debugLogRecording; + Scroller dbgTreeViewScroller; + int targetTvScroll = -1; + + + + + public Command CMDNew, CMDOpen, CMDSave, CMDSaveAs, CMDQuit, CMDShowLeftPane, + CMDUndo, CMDRedo, CMDCut, CMDCopy, CMDPaste, CMDHelp, CMDAbout, CMDOptions; + public CommandGroup EventCommands, DirectoryCommands; + public CommandGroup EditorCommands => new CommandGroup (CMDUndo, CMDRedo, CMDCut, CMDCopy, CMDPaste, CMDSave, CMDSaveAs); + void initCommands () + { + CMDNew = new Command ("New", new Action (onNewFile), "#Icons.blank-file.svg"); + CMDSave = new Command ("Save", new Action (onSave), "#Icons.save.svg", false); + CMDSaveAs = new Command ("Save As...", new Action (onSaveAs), "#Icons.save.svg"); + CMDQuit = new Command ("Quit", new Action (() => base.Quit ()), "#Icons.exit.svg"); + CMDUndo = new Command ("Undo", new Action (undo),"#Icons.undo.svg", false); + CMDRedo = new Command ("Redo", new Action (redo),"#Icons.redo.svg", false); + CMDCut = new Command ("Cut", new Action (() => cut ()), "#Icons.scissors.svg", false); + CMDCopy = new Command ("Copy", new Action (() => copy ()), "#Icons.copy-file.svg", false); + CMDPaste= new Command ("Paste", new Action (() => paste ()), "#Icons.paste-on-document.svg", false); + + EventCommands = new CommandGroup( + new Command("Goto parent event", ()=> { CurrentEvent = CurrentEvent?.parentEvent; }) + ); + DirectoryCommands = new CommandGroup( + new Command("Set as root directory", ()=> { CurrentEvent = CurrentEvent?.parentEvent; }) + ); + + } + + + + + + public string CrowDbgAssemblyLocation { + get => Configuration.Global.Get("CrowDbgAssemblyLocation"); + set { + if (CrowDbgAssemblyLocation == value) + return; + Configuration.Global.Set ("CrowDbgAssemblyLocation", value); + NotifyValueChanged(CrowDbgAssemblyLocation); + } + } + public List Events { + get => events; + set { + if (events == value) + return; + events = value; + NotifyValueChanged (nameof (Events), events); + } + } + public List Widgets { + get => widgets; + set { + if (widgets == value) + return; + widgets = value; + NotifyValueChanged (nameof (Widgets), widgets); + } + } + public DbgEvent CurrentEvent { + get => curEvent; + set { + if (curEvent == value) + return; + + curEvent = value; + NotifyValueChanged (nameof (CurrentEvent), curEvent); + NotifyValueChanged ("CurEventChildEvents", curEvent?.Events); + + } + } + public DbgWidgetRecord CurrentWidget { + get => curWidget; + set { + if (curWidget == value) + return; + curWidget = value; + NotifyValueChanged (nameof (CurrentWidget), curWidget); + NotifyValueChanged ("CurWidgetRootEvents", curWidget?.RootEvents); + NotifyValueChanged ("CurWidgetEvents", curWidget?.Events); + + } + } + public List CurWidgetRootEvents => curWidget == null? new List() : curWidget.RootEvents; + public DbgEvtType RecordedEvents { + get => Configuration.Global.Get (nameof (RecordedEvents)); + set { + if (RecordedEvents == value) + return; + Configuration.Global.Set (nameof (RecordedEvents), value); + if (DebugLogRecording) + DbgLogger.IncludeEvents = RecordedEvents; + NotifyValueChanged(RecordedEvents); + } + } + public DbgEvtType DiscardedEvents { + get => Configuration.Global.Get (nameof (DiscardedEvents)); + set { + if (DiscardedEvents == value) + return; + Configuration.Global.Set (nameof (DiscardedEvents), value); + if (DebugLogRecording) + DbgLogger.DiscardEvents = DiscardedEvents; + NotifyValueChanged(DiscardedEvents); + } + } + public bool DebugLoggingEnabled => DbgLogger.IsEnabled; + public bool DebugLogToFile { + get => Configuration.Global.Get (nameof(DebugLogToFile)); + set { + if (DbgLogger.ConsoleOutput != value) + return; + Configuration.Global.Set (nameof(DebugLogToFile), value); + NotifyValueChanged(DebugLogToFile); + } + } + public string DebugLogFilePath { + get => Configuration.Global.Get (nameof (DebugLogFilePath)); + set { + if (CurrentFile == value) + return; + Configuration.Global.Set (nameof (DebugLogFilePath), value); + NotifyValueChanged (DebugLogFilePath); + } + } + public bool DebugLogRecording { + get => debugLogRecording; + set { + if (debugLogRecording == value) + return; + debugLogRecording = value; + NotifyValueChanged(debugLogRecording); + } + } + + + const string _defaultFileName = "unnamed.txt"; + string source = "", origSource; + TextBox editor; + Stack undoStack = new Stack (); + Stack redoStack = new Stack (); + TextSpan selection; + Exception currentException; + public string CurrentDir { + get => Configuration.Global.Get (nameof (CurrentDir)); + set { + if (CurrentDir == value) + return; + Configuration.Global.Set (nameof (CurrentDir), value); + NotifyValueChanged (CurrentDir); + } + } + public string CurrentFile { + get => Configuration.Global.Get (nameof (CurrentFile)); + set { + if (CurrentFile == value) + return; + Configuration.Global.Set (nameof (CurrentFile), value); + NotifyValueChanged (CurrentFile); + } + } + public new bool IsDirty => source != origSource; + public string Source { + get => source; + set { + if (source == value) + return; + source = value; + CMDSave.CanExecute = IsDirty; + NotifyValueChanged (source); + NotifyValueChanged ("IsDirty", IsDirty); + } + } + string SelectedText => + selection.IsEmpty ? "" : Source.AsSpan (selection.Start, selection.Length).ToString (); + public Exception CurrentException { + get => currentException; + set { + if (currentException == value) + return; + currentException = value; + NotifyValueChanged ("ShowError", ShowError); + NotifyValueChanged ("CurrentExceptionMSG", (object)CurrentExceptionMSG); + NotifyValueChanged (currentException); + } + } + public bool ShowError => currentException != null; + public string CurrentExceptionMSG => currentException == null ? "" : currentException.Message; + + + + void undo () { + if (undoStack.TryPop (out TextChange tch)) { + redoStack.Push (tch.Inverse (source)); + CMDRedo.CanExecute = true; + apply (tch); + editor.SetCursorPosition (tch.End + tch.ChangedText.Length); + } + if (undoStack.Count == 0) + CMDUndo.CanExecute = false; + } + void redo () { + if (redoStack.TryPop (out TextChange tch)) { + undoStack.Push (tch.Inverse (source)); + CMDUndo.CanExecute = true; + apply (tch); + editor.SetCursorPosition (tch.End + tch.ChangedText.Length); + } + if (redoStack.Count == 0) + CMDRedo.CanExecute = false; + } + void cut () { + copy (); + applyChange (new TextChange (selection.Start, selection.Length, "")); + } + void copy () { + Clipboard = SelectedText; + } + void paste () { + applyChange (new TextChange (selection.Start, selection.Length, Clipboard)); + } + bool disableTextChangedEvent = false; + void apply (TextChange change) { + Span tmp = stackalloc char[source.Length + (change.ChangedText.Length - change.Length)]; + ReadOnlySpan src = source.AsSpan (); + src.Slice (0, change.Start).CopyTo (tmp); + if (!string.IsNullOrEmpty (change.ChangedText)) + change.ChangedText.AsSpan ().CopyTo (tmp.Slice (change.Start)); + src.Slice (change.End).CopyTo (tmp.Slice (change.Start + change.ChangedText.Length)); + disableTextChangedEvent = true; + Source = tmp.ToString (); + disableTextChangedEvent = false; + } + void applyChange (TextChange change) { + undoStack.Push (change.Inverse (source)); + redoStack.Clear (); + CMDUndo.CanExecute = true; + CMDRedo.CanExecute = false; + apply (change); + } + + void resetUndoRedo () { + undoStack.Clear (); + redoStack.Clear (); + CMDUndo.CanExecute = false; + CMDRedo.CanExecute = false; + } + + void onNewFile () { + if (IsDirty) { + MessageBox.ShowModal (this, MessageBox.Type.YesNo, "Current file has unsaved changes, are you sure?").Yes += (sender, e) => newFile (); + } else + newFile (); + } + void onSave () + { + if (!File.Exists (CurrentFile)) { + onSaveAs (); + return; + } + save (); + } + void onSaveAs () + { + string dir = Path.GetDirectoryName (CurrentFile); + if (string.IsNullOrEmpty (dir)) + dir = Directory.GetCurrentDirectory (); + LoadIMLFragment (@"").DataSource = this; + } + void saveFileDialog_OkClicked (object sender, EventArgs e) + { + FileDialog fd = sender as FileDialog; + + if (string.IsNullOrEmpty (fd.SelectedFileFullPath)) + return; + + if (File.Exists(fd.SelectedFileFullPath)) { + MessageBox mb = MessageBox.ShowModal (this, MessageBox.Type.YesNo, "File exists, overwrite?"); + mb.Yes += (sender2, e2) => { + CurrentFile = fd.SelectedFileFullPath; + save (); + }; + return; + } + + CurrentFile = fd.SelectedFileFullPath; + save (); + } + + void newFile() + { + disableTextChangedEvent = true; + Source = @"