diff options
| author | Jean-Philippe Bruyère <jp_bruyere@hotmail.com> | 2021-04-02 07:54:52 +0200 |
|---|---|---|
| committer | Jean-Philippe Bruyère <jp_bruyere@hotmail.com> | 2021-04-02 07:54:52 +0200 |
| commit | 8c0f9d6b02eac97b7a45780f82f5683771745cf0 (patch) | |
| tree | 7c638b9ace3bf6b86669eebaa8b4c9638f941f17 /Samples/DebugLogAnalyzer | |
| parent | 89a58fbf483a4b08309c640dfd07ddf8a58e8bd1 (diff) | |
DebugLog, event bindings resolutions, enumSelector bitfields, wip
Diffstat (limited to 'Samples/DebugLogAnalyzer')
18 files changed, 1648 insertions, 367 deletions
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 @@ <?xml version="1.0" encoding="utf-8"?> <Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> + <EnableDefaultCompileItems>false</EnableDefaultCompileItems> + </PropertyGroup> <ItemGroup> - <EmbeddedResource Include="ui\**\*.*"> + <Compile Include="src\**\*.cs"/> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="ui\*.*"> <LogicalName>Dbg.%(Filename)%(Extension)</LogicalName> - </EmbeddedResource> -</ItemGroup> + </EmbeddedResource> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\..\CrowDbgShared\CrowDbgShared.csproj" /> + </ItemGroup> </Project>
\ 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 <jp_bruyere@hotmail.com> -// -// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) -using System; -using System.Collections.Generic; -using System.IO; -using Crow; -using Crow.Cairo; - -namespace DebugLogAnalyzer -{ - public class Program : SampleBase - { - static void Main (string [] args) - { - using (Program app = new Program ()) - app.Run (); - } - - List<DbgEvent> events = new List<DbgEvent>(); - List<DbgWidgetRecord> widgets = new List<DbgWidgetRecord>(); - DbgEvent curEvent = new DbgEvent(); - DbgWidgetRecord curWidget = new DbgWidgetRecord(); - - public List<DbgEvent> Events { - get => events; - set { - if (events == value) - return; - events = value; - NotifyValueChanged (nameof (Events), events); - } - } - public List<DbgWidgetRecord> Widgets { - get => widgets; - set { - if (widgets == value) - return; - widgets = value; - NotifyValueChanged (nameof (Widgets), widgets); - } - } - public DbgEvent CurrentEvent { - get => curEvent; - set { - if (curEvent == value) - return; - - if (curEvent != null) - curEvent.IsSelected = false; - curEvent = value; - if (curEvent != null) { - curEvent.IsSelected = true; - if (curEvent.parentEvent != null) - curEvent.parentEvent.IsExpanded = true; - } - - NotifyValueChanged (nameof (CurrentEvent), curEvent); - } - } - public DbgWidgetRecord CurrentWidget { - get => curWidget; - set { - if (curWidget == value) - return; - curWidget = value; - NotifyValueChanged (nameof (CurrentWidget), curWidget); - NotifyValueChanged ("CurWidgetRootEvents", CurWidgetRootEvents); - } - } - public List<DbgWidgetEvent> CurWidgetRootEvents => curWidget == null? new List<DbgWidgetEvent>() : curWidget.RootEvents; - - Scroller dbgTreeViewScroller; - - protected override void OnInitialized () - { - Load ("#Dbg.dbglog.crow").DataSource = this; - - TreeView tv = FindByName("dbgTV") as TreeView; - dbgTreeViewScroller = tv.FindByNameInTemplate ("scroller1") as Scroller; - - loadDebugFile ("debug.log"); - } - - void loadDebugFile (string logFile) - { - if (!File.Exists (logFile)) - return; - - List<DbgEvent> evts = new List<DbgEvent> (); - List<DbgWidgetRecord> objs = new List<DbgWidgetRecord> (); - - using (StreamReader s = new StreamReader (logFile)) { - if (s.ReadLine () != "[GraphicObjects]") - return; - while (!s.EndOfStream) { - string l = s.ReadLine (); - if (l == "[Events]") - break; - DbgWidgetRecord o = DbgWidgetRecord.Parse (l); - objs.Add (o); - } - - Stack<DbgEvent> startedEvents = new Stack<DbgEvent> (); - - if (!s.EndOfStream) { - while (!s.EndOfStream) { - int level = 0; - while (s.Peek () == (int)'\t') { - s.Read (); - level++; - } - DbgEvent evt = DbgEvent.Parse (s.ReadLine ()); - if (level == 0) { - startedEvents.Clear (); - evts.Add (evt); - } else { - int levelDiff = level - startedEvents.Count + 1; - if (levelDiff > 0) { - if (levelDiff > 1) - System.Diagnostics.Debugger.Break (); - startedEvents.Peek ().AddEvent (evt); - } else { - startedEvents.Pop (); - if (-levelDiff > startedEvents.Count) - System.Diagnostics.Debugger.Break (); - while (startedEvents.Count > level) - startedEvents.Pop (); - startedEvents.Peek ().AddEvent (evt); - } - } - startedEvents.Push (evt); - if (evt.type.HasFlag (DbgEvtType.Widget)) - objs [(evt as DbgWidgetEvent).InstanceIndex].Events.Add (evt); - } - } - } - Widgets = objs; - Events = evts; - } - - int targetTvScroll = -1; - - void onTvPainted (object sender, EventArgs e) - { - if (targetTvScroll < 0 || targetTvScroll > dbgTreeViewScroller.MaxScrollY + dbgTreeViewScroller.Slot.Height) - return; - dbgTreeViewScroller.MaxScrollY = targetTvScroll; - targetTvScroll = -1; - } - - void onSelectedItemContainerChanged (object sender, SelectionChangeEventArgs e) - { - TreeView tv = sender as TreeView; - Group it = tv.FindByNameInTemplate ("ItemsContainer") as Group; - - ListItem li = e.NewValue as ListItem; - Rectangle selRect = li.RelativeSlot (it); - - if (selRect.Y > dbgTreeViewScroller.ScrollY && selRect.Y < dbgTreeViewScroller.Slot.Height + dbgTreeViewScroller.ScrollY) - return; - - Console.WriteLine ($"Scroll={dbgTreeViewScroller.ScrollY} selRectY={selRect.Y} MaxScrollY={dbgTreeViewScroller.MaxScrollY} ScrollerH={dbgTreeViewScroller.Slot.Height}"); - targetTvScroll = selRect.Y; - if (selRect.Y > dbgTreeViewScroller.MaxScrollY + dbgTreeViewScroller.Slot.Height) - targetTvScroll = selRect.Y; - else { - targetTvScroll = -1; - dbgTreeViewScroller.ScrollY = selRect.Y; - } - } - - } -} - diff --git a/Samples/DebugLogAnalyzer/DbgEventWidget.cs b/Samples/DebugLogAnalyzer/src/DbgEventWidget.cs index b2af84dd..19666ee2 100644 --- a/Samples/DebugLogAnalyzer/DbgEventWidget.cs +++ b/Samples/DebugLogAnalyzer/src/DbgEventWidget.cs @@ -5,6 +5,8 @@ using System; using System.Collections.Generic; using System.ComponentModel; using Crow.Cairo; +using Crow.DebugLogger; +using DebugLogAnalyzer; namespace Crow { @@ -93,13 +95,13 @@ namespace Crow double w = Math.Max(dbge.Duration * pixelPerTick, 2.0); double x = (dbge.begin - Event.begin) * pixelPerTick; - ctx.Rectangle (x, Margin, w, h); + ctx.Rectangle (x, 0, w, h); ctx.SetSource (dbge.Color); - if (dbge.IsSelected) { + /*if (dbge.IsSelected) { ctx.FillPreserve (); ctx.SetSource (1, 1, 1); ctx.Stroke (); - }else + }else*/ ctx.Fill (); if (dbge.Events == null) @@ -113,9 +115,6 @@ namespace Crow 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; diff --git a/Samples/DebugLogAnalyzer/DbgLogViewer.cs b/Samples/DebugLogAnalyzer/src/DbgLogViewer.cs index 49facb5a..1273c9e6 100644 --- a/Samples/DebugLogAnalyzer/DbgLogViewer.cs +++ b/Samples/DebugLogAnalyzer/src/DbgLogViewer.cs @@ -1,17 +1,22 @@ -// Copyright (c) 2013-2020 Jean-Philippe Bruyère <jp_bruyere@hotmail.com> +using System.Diagnostics; +using System.Runtime.InteropServices.ComTypes; +// Copyright (c) 2013-2020 Jean-Philippe Bruyère <jp_bruyere@hotmail.com> // // This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) using System; using System.Collections.Generic; +using System.ComponentModel; using System.IO; using System.Linq; using System.Threading.Tasks; using Crow.Cairo; +using Crow.DebugLogger; +using DebugLogAnalyzer; namespace Crow { - public class DbgLogViewer : ScrollingObject + public class DbgLogViewer : Widget { public static Dictionary<DbgEvtType, Color> colors; @@ -28,7 +33,8 @@ namespace Crow } #region CTOR static DbgLogViewer() { - reloadColors (); + //reloadColors (); + } protected DbgLogViewer () : base(){} public DbgLogViewer (Interface iface, string style = null) : base(iface, style){} @@ -36,13 +42,25 @@ namespace Crow FontExtents fe; + double xScale = 1.0/1024.0, yScale = 1.0, leftMargin, topMargin = 0.0; DbgWidgetRecord curWidget, hoverWidget; DbgEvent curEvent, hoverEvent; List<DbgEvent> events = new List<DbgEvent> (); List<DbgWidgetRecord> widgets = new List<DbgWidgetRecord> (); + + public DbgEvtType Filter { + get => Configuration.Global.Get<DbgEvtType> ("DbgLogViewFilter"); + set { + if (Filter == value) + return; + Configuration.Global.Set ("DbgLogViewFilter", value); + NotifyValueChangedAuto(Filter); + RegisterForGraphicUpdate(); + } + } public List<DbgEvent> Events { get => events; set { @@ -50,22 +68,25 @@ namespace Crow 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; + 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; } - visibleTicks = maxTicks - minTicks; - XScale = (ClientRectangle.Width - leftMargin)/visibleTicks; - ScrollX = 0; - ScrollY = 0; RegisterForGraphicUpdate (); } @@ -89,6 +110,13 @@ namespace Crow 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 { @@ -96,20 +124,19 @@ namespace Crow set { if (curEvent == value) return; - if (curEvent != null) - curEvent.IsSelected = false; + /*if (curEvent != null) + curEvent.IsSelected = false;*/ curEvent = value; if (curEvent != null) { - curEvent.IsSelected = true; + //curEvent.IsSelected = true; if (curEvent is DbgWidgetEvent we) { //CurrentWidget = Widgets [we.InstanceIndex]; - currentLine = we.InstanceIndex; + hoverLine = we.InstanceIndex; } currentTick = curEvent.begin; if (curEvent.begin > minTicks + ScrollX + visibleTicks || - curEvent.end < minTicks + ScrollX) { - ScrollX = (int)(currentTick - visibleTicks / 2); - } + curEvent.end < minTicks + ScrollX) + ScrollX = curEvent.begin - minTicks - visibleTicks / 2; } NotifyValueChanged (nameof (CurrentEvent), curEvent); RegisterForRedraw (); @@ -135,13 +162,13 @@ namespace Crow } } - long currentTick = 0, selStart = -1, selEnd = -1, minTicks = 0, maxTicks = 0, visibleTicks = 0; - int currentLine = -1; + 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 { return xScale; } + get => xScale; set { if (xScale == value) return; @@ -174,28 +201,6 @@ namespace Crow updateMargins (); } } - public override int ScrollY { - get => base.ScrollY; - set { - base.ScrollY = value; - - if (widgets == null) - return; - - Rectangle cb = ClientRectangle; - cb.Left += (int)leftMargin; - cb.Width -= (int)leftMargin; - cb.Y += (int)topMargin; - cb.Height -= (int)topMargin; - - if (mousePos.Y < cb.Top || mousePos.Y > cb.Bottom) - currentLine = -1; - else - currentLine = (int)((double)(mousePos.Y - cb.Top) / fe.Height) + ScrollY; - - NotifyValueChanged ("CurrentLine", currentLine); - } - } void drawEvents (Context ctx, List<DbgEvent> evts) { @@ -204,61 +209,64 @@ namespace Crow 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) + if ((evt.Category & currentFilter) == currentFilter) { + if (evt.end - minTicks <= ScrollX) 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; - + if (evt.begin - minTicks > ScrollX + visibleTicks) + break; + double penY = topMargin + ClientRectangle.Top; - 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);*/ + 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); @@ -294,6 +302,8 @@ namespace Crow if (g.yIndex == 0) gr.SetSource (Crow.Colors.LightSalmon); + else if (currentLine == g.listIndex) + gr.SetSource(Colors.RoyalBlue); else Foreground.SetAsSource (IFace, gr); @@ -301,21 +311,8 @@ namespace Crow gr.ShowText (g.name + gIdx); } + currentFilter = Filter; 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); @@ -327,8 +324,8 @@ namespace Crow 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 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; @@ -340,7 +337,7 @@ namespace Crow gr.MoveTo (x, penY - 0.5); if (curGrad % largeGrad == 0) { gr.LineTo (x, penY - 8.5); - string str = curGrad.ToString (); + string str = ticksToMS(curGrad); TextExtents te = gr.TextExtents (str); gr.RelMoveTo (-0.5 * te.Width, -2.0); gr.ShowText (str); @@ -353,17 +350,10 @@ namespace Crow 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; - - }*/ } + string ticksToMS(long ticks) => Math.Round ((double)ticks / Stopwatch.Frequency * 1000.0, 2).ToString(); public override void Paint (Cairo.Context ctx) { base.Paint (ctx); @@ -372,40 +362,59 @@ namespace Crow 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(); + 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; - ctx.MoveTo (ctxR.X - ctx.TextExtents (currentTick.ToString ()).Width / 2, ctxR.Y + fe.Height); - ctx.ShowText (currentTick.ToString ()); + 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 (currentLine >= 0) { - double y = fe.Height * (currentLine - ScrollY) + topMargin + cb.Top; + 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.Operator = Cairo.Operator.Add; ctx.SetSource (0.1, 0.1, 0.1, 0.4); ctx.Rectangle (ContextCoordinates (r)); ctx.Fill (); } - if (CurrentWidget != null) { + 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) { @@ -451,46 +460,81 @@ namespace Crow public override void onMouseLeave (object sender, MouseMoveEventArgs e) { base.onMouseLeave (sender, e); - currentLine = -1; - currentTick = 0; + hoverLine = -1; + hoverTick = 0; } public override void onMouseMove (object sender, MouseMoveEventArgs e) - { - base.onMouseMove (sender, e); - - long lastTick = currentTick; + { + long lastTick = hoverTick; + int lastLine = hoverLine; updateMouseLocalPos (e.Position); if (IFace.IsDown (Glfw.MouseButton.Left) && selStart >= 0) - selEnd = currentTick; + selEnd = hoverTick; else if (IFace.IsDown(Glfw.MouseButton.Right)) { - ScrollX += (int)(lastTick - currentTick); + if (lastTick >= 0 && hoverTick >= 0) + ScrollX += lastTick - hoverTick; + if (lastLine >= 0 && hoverLine >= 0) + ScrollY += lastLine - hoverLine; updateMouseLocalPos (e.Position); } else { - HoverWidget = (currentLine < 0 || currentLine >= widgets.Count) ? null : widgets [currentLine]; - HoverEvent = hoverWidget?.Events.FirstOrDefault (ev => ev.begin <= currentTick && ev.end >= currentTick); + 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)); } - if (RegisteredLayoutings == LayoutingType.None && !IsDirty) - IFace.EnqueueForRepaint (this); + RegisterForRepaint(); + e.Handled = true; + base.onMouseMove (sender, e); } - public override void onMouseDown (object sender, MouseButtonEventArgs 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<DbgWidgetEvent>()?.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) { - base.onMouseDown (sender, e); - - if (e.Button == Glfw.MouseButton.Left) { + if (e.Button == Glfw.MouseButton.Left && selEnd < 0) { + currentTick = hoverTick; + currentLine = hoverLine; CurrentWidget = hoverWidget; CurrentEvent = hoverEvent; - selStart = currentTick; + } + 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) { - base.onMouseUp (sender, e); if (e.Button == Glfw.MouseButton.Left && selEnd > 0 && selEnd != selStart) { long scrX = 0; @@ -502,18 +546,18 @@ namespace Crow scrX = selEnd - minTicks; } XScale = (ClientRectangle.Width - leftMargin) / visibleTicks; - ScrollX = (int)scrX; + ScrollX = scrX; } - selStart = -1; - selEnd = -1; RegisterForRedraw (); + e.Handled = true; + base.onMouseUp (sender, e); } /// <summary> Process scrolling vertically, or if shift is down, vertically </summary> public override void onMouseWheel (object sender, MouseWheelEventArgs e) { - base.onMouseWheel (sender, e); + //base.onMouseWheel (sender, e); if (IFace.Shift) ScrollX -= (int)((double)(e.Delta * MouseWheelSpeed) / xScale); @@ -524,7 +568,7 @@ namespace Crow if (MaxScrollX > 0) XScale *= 0.5; } - ScrollX = (int)(currentTick - (int)((double)Math.Max(0, mousePos.X - (int)leftMargin) / xScale) - minTicks); + ScrollX = (long)(hoverTick - (long)((double)Math.Max(0, mousePos.X - (long)leftMargin) / xScale) - minTicks); }else ScrollY -= e.Delta * MouseWheelSpeed; } @@ -572,7 +616,7 @@ namespace Crow void updateVisibleLines () { - visibleLines = fe.Height < 1 ? 1 : (int)Math.Floor (((double)ClientRectangle.Height - topMargin) / fe.Height); + visibleLines = fe.Height < 1 ? 1 : (int)Math.Ceiling (((double)ClientRectangle.Height - topMargin) / fe.Height); NotifyValueChanged ("VisibleLines", visibleLines); updateMaxScrollY (); } @@ -585,17 +629,22 @@ namespace Crow void updateMaxScrollX () { - if (widgets == null) - MaxScrollX = 0; - else - MaxScrollX = (int)Math.Max (0L, maxTicks - minTicks - visibleTicks); + 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 - visibleLines); + else { + MaxScrollY = Math.Max (0, widgets.Count + 1 - visibleLines); + NotifyValueChanged ("ChildHeightRatio", (double)visibleLines / (widgets.Count + 1)); + } } void updateMouseLocalPos (Point mPos) @@ -613,16 +662,16 @@ namespace Crow mousePos.X = Math.Min (cb.Right, mousePos.X); if (mousePos.Y < cb.Top || mousePos.Y > cb.Bottom) - currentLine = -1; + hoverLine = -1; else - currentLine = (int)((double)(mousePos.Y - cb.Top) / fe.Height) + ScrollY; + hoverLine = (int)((double)(mousePos.Y - cb.Top) / fe.Height) + ScrollY; - NotifyValueChanged ("CurrentLine", currentLine); + NotifyValueChanged ("CurrentLine", hoverLine); 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; + hoverTick = (long)((double)(mousePos.X - cb.X) / xScale) + minTicks + ScrollX; RegisterForRedraw (); } void zoom (long start, long end) { @@ -631,6 +680,125 @@ namespace Crow XScale = ((double)ClientRectangle.Width - leftMargin)/(end - start); ScrollX = (int)(start - minTicks); } + + + long scrollX, maxScrollX; + int scrollY, maxScrollY, mouseWheelSpeed; + + /// <summary> + /// if true, key stroke are handled in derrived class + /// </summary> + protected bool KeyEventsOverrides = false; + + /// <summary> Horizontal Scrolling Position </summary> + [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 (); + } + } + /// <summary> Vertical Scrolling Position </summary> + [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); + } + } + /// <summary> Horizontal Scrolling maximum value </summary> + [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 (); + } + } + /// <summary> Vertical Scrolling maximum value </summary> + [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 (); + } + } + /// <summary> Mouse Wheel Scrolling multiplier </summary> + [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 <jp_bruyere@hotmail.com> +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using System; +using Crow.Cairo; +using System.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<Exception> 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<Exception>)Delegate.CreateDelegate(typeof(Action<Exception>), 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 <jp_bruyere@hotmail.com> +// +// 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<DbgEvent> events; + List<DbgWidgetRecord> widgets; + Exception currentException; + object dbgIFace; + AssemblyLoadContext crowLoadCtx; + Assembly crowAssembly, thisAssembly; + Type dbgIfaceType; + Action<int, int> delResize; + Func<int, int, bool> delMouseMove; + Func<MouseButton, bool> delMouseDown, delMouseUp; + Action delResetDirtyState; + Action delResetDebugger; + Action<object, string> delSaveDebugLog; + IntPtrGetterDelegate delGetSurfacePointer; + Action<string> 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<int, int>)Delegate.CreateDelegate(typeof(Action<int, int>), + dbgIFace, dbgIfaceType.GetMethod("Resize")); + + delMouseMove = (Func<int, int, bool>)Delegate.CreateDelegate(typeof(Func<int, int, bool>), + dbgIFace, dbgIfaceType.GetMethod("OnMouseMove")); + + delMouseDown = (Func<MouseButton, bool>)Delegate.CreateDelegate(typeof(Func<MouseButton, bool>), + dbgIFace, dbgIfaceType.GetMethod("OnMouseButtonDown")); + + delMouseUp = (Func<MouseButton, bool>)Delegate.CreateDelegate(typeof(Func<MouseButton, bool>), + dbgIFace, dbgIfaceType.GetMethod("OnMouseButtonUp")); + + delGetSurfacePointer = (IntPtrGetterDelegate)Delegate.CreateDelegate(typeof(IntPtrGetterDelegate), + dbgIFace, dbgIfaceType.GetProperty("SurfacePointer").GetGetMethod()); + delSetSource = (Action<string>)Delegate.CreateDelegate(typeof(Action<string>), + 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<object, string>)Delegate.CreateDelegate(typeof(Action<object, string>), + 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<DbgEvent>(); + widgets = new List<DbgWidgetRecord>(); + 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 <jp_bruyere@hotmail.com> +// +// 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 <jp_bruyere@hotmail.com> +// +// 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<DbgEvent> events = new List<DbgEvent>(); + List<DbgWidgetRecord> widgets = new List<DbgWidgetRecord>(); + 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<string>("CrowDbgAssemblyLocation"); + set { + if (CrowDbgAssemblyLocation == value) + return; + Configuration.Global.Set ("CrowDbgAssemblyLocation", value); + NotifyValueChanged(CrowDbgAssemblyLocation); + } + } + public List<DbgEvent> Events { + get => events; + set { + if (events == value) + return; + events = value; + NotifyValueChanged (nameof (Events), events); + } + } + public List<DbgWidgetRecord> Widgets { + get => widgets; + set { + if (widgets == value) + return; + widgets = value; + NotifyValueChanged (nameof (Widgets), widgets); + } + } + public DbgEvent CurrentEvent { + get => curEvent; + set { + if (curEvent == value) + return; + + 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<DbgWidgetEvent> CurWidgetRootEvents => curWidget == null? new List<DbgWidgetEvent>() : curWidget.RootEvents; + public DbgEvtType RecordedEvents { + get => Configuration.Global.Get<DbgEvtType> (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<DbgEvtType> (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<bool> (nameof(DebugLogToFile)); + set { + if (DbgLogger.ConsoleOutput != value) + return; + Configuration.Global.Set (nameof(DebugLogToFile), value); + NotifyValueChanged(DebugLogToFile); + } + } + public string DebugLogFilePath { + get => Configuration.Global.Get<string> (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<TextChange> undoStack = new Stack<TextChange> (); + Stack<TextChange> redoStack = new Stack<TextChange> (); + TextSpan selection; + Exception currentException; + public string CurrentDir { + get => Configuration.Global.Get<string> (nameof (CurrentDir)); + set { + if (CurrentDir == value) + return; + Configuration.Global.Set (nameof (CurrentDir), value); + NotifyValueChanged (CurrentDir); + } + } + public string CurrentFile { + get => Configuration.Global.Get<string> (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<char> tmp = stackalloc char[source.Length + (change.ChangedText.Length - change.Length)]; + ReadOnlySpan<char> 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 (@"<FileDialog Width='60%' Height='50%' Caption='Save as ...' CurrentDirectory='" + + dir + "' SelectedFile='" + + Path.GetFileName(CurrentFile) + "' OkClicked='saveFileDialog_OkClicked'/>").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 = @"<Label Text='Hello World' Background='MediumSeaGreen' Margin='10'/>"; + disableTextChangedEvent = false; + resetUndoRedo (); + if (!string.IsNullOrEmpty (CurrentFile)) + CurrentFile = Path.Combine (Path.GetDirectoryName (CurrentFile), "newfile.crow"); + else + CurrentFile = Path.Combine (CurrentDir, "newfile.crow"); + } + + void save () { + using (Stream s = new FileStream(CurrentFile, FileMode.Create)) { + s.WriteByte (0xEF); + s.WriteByte (0xBB); + s.WriteByte (0xBF); + byte [] buff = Encoding.UTF8.GetBytes (source); + s.Write (buff, 0, buff.Length); + } + origSource = source; + NotifyValueChanged ("IsDirty", IsDirty); + CMDSave.CanExecute = false; + } + + void reloadFromFile () { + disableTextChangedEvent = true; + if (File.Exists (CurrentFile)) { + using (Stream s = new FileStream (CurrentFile, FileMode.Open)) { + using (StreamReader sr = new StreamReader (s)) + Source = origSource = sr.ReadToEnd (); + } + } + disableTextChangedEvent = false; + resetUndoRedo (); + } + + public void goUpDirClick (object sender, MouseButtonEventArgs e) + { + string root = Directory.GetDirectoryRoot (CurrentDir); + if (CurrentDir == root) + return; + CurrentDir = Directory.GetParent (CurrentDir).FullName; + } + + void Dv_SelectedItemChanged (object sender, SelectionChangeEventArgs e) + { + FileSystemInfo fi = e.NewValue as FileSystemInfo; + if (fi == null) + return; + if (fi is DirectoryInfo) + return; + + if (IsDirty) { + MessageBox mb = MessageBox.ShowModal (this, MessageBox.Type.YesNo, "Current file has unsaved changes, are you sure?"); + mb.Yes += (mbsender, mbe) => { CurrentFile = fi.FullName; reloadFromFile (); }; + return; + } + + CurrentFile = fi.FullName; + reloadFromFile (); + } + void onTextChanged (object sender, TextChangeEventArgs e) { + if (disableTextChangedEvent) + return; + applyChange (e.Change); + } + + void onSelectedTextChanged (object sender, EventArgs e) { + selection = (sender as Label).Selection; + Console.WriteLine($"selection:{selection.Start} length:{selection.Length}"); + CMDCut.CanExecute = CMDCopy.CanExecute = !selection.IsEmpty; + } + void textView_KeyDown (object sender, Crow.KeyEventArgs e) { + if (Ctrl) { + if (e.Key == Glfw.Key.W) { + if (Shift) + CMDRedo.Execute (); + else + CMDUndo.Execute (); + } else if (e.Key == Glfw.Key.S) { + onSave (); + } + } + } + + + + public override bool OnKeyDown (Key key) { + + switch (key) { + case Key.F5: + Load ("#Dbg.DebugLog.crow").DataSource = this; + return true; + /*case Key.F6: + if (DebugLogRecording) { + DbgLogger.IncludeEvents = DbgEvtType.None; + DbgLogger.DiscardEvents = DbgEvtType.All; + if (DebugLogToFile && !string.IsNullOrEmpty(DebugLogFilePath)) + DbgLogger.Save (this, DebugLogFilePath); + DebugLogRecording = false; + } else { + DbgLogger.Reset (); + DbgLogger.IncludeEvents = RecordedEvents; + DbgLogger.DiscardEvents = DiscardedEvents; + DebugLogRecording = true; + } + return true;*/ + } + return base.OnKeyDown (key); + } + } +} + diff --git a/Samples/DebugLogAnalyzer/ui/Button.template b/Samples/DebugLogAnalyzer/ui/Button.template new file mode 100644 index 00000000..c7e2fa06 --- /dev/null +++ b/Samples/DebugLogAnalyzer/ui/Button.template @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<Border Background="{./Background}" MinimumSize="50,20" Name="Content" + Foreground="Transparent" CornerRadius="{../CornerRadius}" BorderWidth="1" + MouseEnter="{Foreground=vgradient|0:White|0.2:Grey|0.9:Grey|1:Black};{caption.Foreground=White}" + MouseLeave="{Foreground=Transparent};{caption.Foreground=LightGrey}" + MouseDown="{Foreground=vgradient|0:Black|0.05:Grey|0.85:Grey|1:White}" + MouseUp="{Foreground=vgradient|0:White|0.2:Grey|0.9:Grey|1:Black}"> + <HorizontalStack Margin="2"> + <Image Style="Icon" Path="{./Icon}"/> + <Label Font="{./Font}" Name="caption" Margin="3" Foreground="LightGrey" Text="{./Caption}"/> + </HorizontalStack> +</Border>
\ No newline at end of file diff --git a/Samples/DebugLogAnalyzer/ui/DbgEventTreeItems.itemp b/Samples/DebugLogAnalyzer/ui/DbgEventTreeItems.itemp index 2f6fbfbf..96de2dc6 100644 --- a/Samples/DebugLogAnalyzer/ui/DbgEventTreeItems.itemp +++ b/Samples/DebugLogAnalyzer/ui/DbgEventTreeItems.itemp @@ -1,9 +1,9 @@ <?xml version="1.0" encoding="UTF-8" ?> <ItemTemplate Data="Events"> - <ListItem IsSelected="{²IsSelected}" Height="Fit" + <ListItem Height="Fit" Selected="{/exp.Background=${ControlHighlight}}" Unselected="{/exp.Background=Transparent}"> - <Expandable Name="exp" Caption="{type}" MouseDoubleClick="/onClickForExpand" CacheEnabled="true" IsExpanded="{²IsExpanded}"> + <Expandable Name="exp" Caption="{type}" MouseDoubleClick="/onClickForExpand" CacheEnabled="true" > <Template> <VerticalStack> <Border CornerRadius="2" Margin="0" Height="Fit" MouseDoubleClick="./onClickForExpand" @@ -34,10 +34,10 @@ </ListItem> </ItemTemplate> <ItemTemplate Data="Events" DataType="DbgWidgetEvent"> - <ListItem IsSelected="{²IsSelected}" Height="Fit" + <ListItem Height="Fit" Selected="{/exp.Background=${ControlHighlight}}" Unselected="{/exp.Background=Transparent}"> - <Expandable Name="exp" Caption="{type}" MouseDoubleClick="/onClickForExpand" CacheEnabled="true" IsExpanded="{²IsExpanded}"> + <Expandable Name="exp" Caption="{type}" MouseDoubleClick="/onClickForExpand" CacheEnabled="true" > <Template> <VerticalStack> <Border CornerRadius="2" Margin="0" Height="Fit" MouseDoubleClick="./onClickForExpand" @@ -53,7 +53,7 @@ MouseEnter="{Background=LightGrey}" MouseLeave="{Background=Transparent}"/> <Label Text="{./Caption}" Width="80" Font="mono, 8" /> - <Label Text="{Duration}" Width="40" Font="mono, 8" TextAlignment="Center" Background="DimGrey"/> + <Label Text="{DurationMS}" Width="40" Font="mono, 8" TextAlignment="Center" Background="DimGrey"/> <Label Text="{InstanceIndex}" Width="40" Font="mono, 8" TextAlignment="Center" Background="DimGrey"/> <DbgEventWidget Event="{}" Tooltip="#Dbg.DbgEvtTooltip.crow" Width="Stretched" Height="5"/> </HorizontalStack> diff --git a/Samples/DebugLogAnalyzer/ui/DbgEvtTooltip.crow b/Samples/DebugLogAnalyzer/ui/DbgEvtTooltip.crow index 9d40f45d..2d5815e9 100644 --- a/Samples/DebugLogAnalyzer/ui/DbgEvtTooltip.crow +++ b/Samples/DebugLogAnalyzer/ui/DbgEvtTooltip.crow @@ -8,17 +8,21 @@ </HorizontalStack> <HorizontalStack Height="Fit" Width="Stretched" Spacing="2" Background="DimGrey" Margin="2"> <Label Text="Duration:" Foreground="White" Width="50%" /> - <Label Text="{Duration}" Foreground="White" Background="Onyx" Width="Stretched" TextAlignment="Right"/> + <Label Text="{DurationMS}" Foreground="White" Background="Onyx" Width="Stretched" TextAlignment="Right"/> </HorizontalStack> <HorizontalStack Height="Fit" Width="Stretched" Spacing="2"> <Label Text="Begin:" Foreground="${TooltipForeground}" Width="50%"/> - <Label Text="{begin}" Foreground="${TooltipForeground}" Width="Stretched" TextAlignment="Right"/> + <Label Text="{BeginMS}" Foreground="${TooltipForeground}" Width="Stretched" TextAlignment="Right"/> </HorizontalStack> <HorizontalStack Height="Fit" Width="Stretched" Spacing="2"> <Label Text="End:" Foreground="${TooltipForeground}" Width="50%"/> - <Label Text="{end}" Foreground="${TooltipForeground}" Width="Stretched" TextAlignment="Right"/> + <Label Text="{EndMS}" Foreground="${TooltipForeground}" Width="Stretched" TextAlignment="Right"/> </HorizontalStack> - <VerticalStack Height="Fit" Width="Stretched" > + <VerticalStack Height="Fit" Width="Stretched" IsVisible="{IsLayoutEvent}" > + <HorizontalStack Height="Fit" Width="Stretched" Spacing="2" Background="DimGrey" Margin="2"> + <Label Text="Layout:" Foreground="White" Width="50%" /> + <Label Text="{layouting}" Foreground="White" Background="Onyx" Width="Stretched" TextAlignment="Center"/> + </HorizontalStack> <HorizontalStack Height="Fit" Width="Stretched" Spacing="2" Background="DimGrey" Margin="2"> <Label Text="Result:" Foreground="White" Width="50%" /> <Label Text="{result}" Foreground="White" Background="Onyx" Width="Stretched" TextAlignment="Center"/> diff --git a/Samples/DebugLogAnalyzer/ui/DebugLog.crow b/Samples/DebugLogAnalyzer/ui/DebugLog.crow new file mode 100644 index 00000000..26839642 --- /dev/null +++ b/Samples/DebugLogAnalyzer/ui/DebugLog.crow @@ -0,0 +1,127 @@ +<?xml version="1.0"?> +<Window Caption="Debug Log" Background="0.05,0.05,0.1,0.9" Width="80%" Height="80%"> + <VerticalStack> + <TabView> + <VerticalStack Name="LogGraph" Background="DarkGrey"> + <EnumSelector RadioButtonStyle="CheckBox2" Template="#Dbg.EnumSelector.template" + Caption="Discarded Events" EnumValue="{²../dbv.Filter}" BitFieldExcludeMask="255" /> + <HorizontalStack> + <DbgLogViewer Visible="true" Name="dbv" Events="{Events}" Widgets="{Widgets}" MouseWheelSpeed="3" Font="mono, 8" + CurrentEvent="{²CurrentEvent}" CurrentWidget="{²CurrentWidget}" Background="Onyx"/> + <ScrollBar Maximum="{../dbv.MaxScrollY}" Value="{²../dbv.ScrollY}" SmallIncrement="1" LargeIncrement="10" + CursorRatio="{../dbv.ChildHeightRatio}"/> + </HorizontalStack> + <ScrollBar Style="HScrollBar" Maximum="{../dbv.MaxScrollX}" Value="{²../dbv.ScrollX}" SmallIncrement="1" LargeIncrement="10" + CursorRatio="{../dbv.ChildWidthRatio}"/> + <HorizontalStack Height="Fit" DataSource="{CurrentWidget}" Spacing="3"> + <Label Style="smallLabValue" Text="{name}"/> + <Label Style="smallLabValue" Text="{listIndex}"/> + <Label Style="smallLabValue" Text="{treeIndex}"/> + <Label Style="smallLabValue" Text="{yIndex}"/> + <Label Style="smallLabValue" Text="{xLevel}"/> + <Label Style="smallLabCaption" Text="Width:"/> + <Label Style="smallLabValue" Text="{Width}"/> + <Label Style="smallLabCaption" Text="Height:" /> + <Label Style="smallLabValue" Text="{Height}"/> + <Label Style="smallLabCaption" Text="ScrollX:" /> + <Label Style="smallLabValue" Text="{../../dbv.ScrollX}"/> + <Label Style="smallLabCaption" Text="MaxScrollX:" /> + <Label Style="smallLabValue" Text="{../../dbv.MaxScrollX}"/> + <Label Style="smallLabValue" Tooltip="XScale" Text="{../../dbv.XScale}"/> + <Label Style="smallLabValue" Tooltip="VisibleTicks" Text="{../../dbv.VisibleTicks}"/> + </HorizontalStack> + </VerticalStack> + <VerticalStack Name="AllEvents" Width="Stretched" IsVisible="false"> + <TreeView Height="Stretched" Name="dbgTV" Data="{Events}" SelectedItem="{²CurrentEvent}" Background="DarkGrey" + ItemTemplate="#Dbg.DbgEventTreeItems.itemp"/> + <ListBox Data="{CurWidgetEvents}" Height="100" SelectedItem="{²CurrentEvent}"> + <ItemTemplate> + <ListItem Margin="0" Height="16" Width="Fit" + Selected="{Background=${ControlHighlight}}" + Unselected="{Background=Transparent}"> + <DbgEventWidget Height="10" Width="Fit" Event="{}" Tooltip="#Dbg.DbgEvtTooltip.crow" TicksPerPixel="20" VerticalAlignment="Center"/> + </ListItem> + </ItemTemplate> + <Template> + <Wrapper Name="ItemsContainer" Spacing="1" Background="DarkGrey"/> + </Template> + </ListBox> + </VerticalStack> + <VerticalStack Name="CurWidgetEvents" Width="Stretched" IsVisible="false"> + <TreeView Height="Stretched" Data="{CurWidgetEvents}" SelectedItem="{²CurrentEvent}" Background="DarkGrey" + ItemTemplate="#Dbg.DbgEventTreeItems.itemp"/> + </VerticalStack> + </TabView> + <Splitter/> + <HorizontalStack Height="30%"> + <ListBox Data="{Widgets}" Width="50%" SelectedItem="{²CurrentWidget}"> + <Template> + </Template> + </ListBox> + <Splitter/> + <VerticalStack> + <HorizontalStack Height="Fit" Background="Onyx" Spacing="5"> + <Label Text="{CurrentWidget}"/> + <Label Text="events"/> + </HorizontalStack> + <ListBox Data="{CurWidgetEvents}" Background="Black" SelectedItem="{²CurrentEvent}"> + <ItemTemplate> + <ListItem Margin="0" Height="16" IsSelected="{²IsSelected}" Width="Fit" + Selected="{Background=${ControlHighlight}}" + Unselected="{Background=Transparent}"> + <DbgEventWidget Height="10" Width="Fit" Event="{}" Tooltip="#Dbg.DbgEvtTooltip.crow" TicksPerPixel="300" VerticalAlignment="Center"/> + </ListItem> + </ItemTemplate> + <Template> + <Wrapper Name="ItemsContainer" Spacing="1" Background="{./Background}"/> + </Template> + </ListBox> + <!--<TreeView Data="{Events}" SelectedItem="{²CurrentEvent}" Background="DarkGrey" + ItemTemplate="#Dbg.DbgEventTreeItems.itemp"/>--> + </VerticalStack> + <Splitter/> + <VerticalStack Height="Stretched" Width="20%" Margin="${TooltipMargin}" DataSource="{CurrentEvent}"> + <Label Text="{type}" Background="{Color}" Foreground="Black" Width="200" TextAlignment="Center" Margin="6"/> + <HorizontalStack Height="Fit" Width="Stretched" Spacing="2" Visible="{IsWidgetEvent}" Background="DimGrey" Margin="2"> + <Label Text="Instance:" Foreground="White" Width="50%" /> + <Label Text="{InstanceIndex}" Foreground="White" Background="Onyx" Width="Stretched" TextAlignment="Center"/> + </HorizontalStack> + <HorizontalStack Height="Fit" Width="Stretched" Spacing="2" Background="DimGrey" Margin="2"> + <Label Text="Duration:" Foreground="White" Width="50%" /> + <Label Text="{DurationMS}" Foreground="White" Background="Onyx" Width="Stretched" TextAlignment="Right"/> + </HorizontalStack> + <HorizontalStack Height="Fit" Width="Stretched" Spacing="2"> + <Label Text="Begin:" Foreground="${TooltipForeground}" Width="50%"/> + <Label Text="{BeginMS}" Foreground="${TooltipForeground}" Width="Stretched" TextAlignment="Right"/> + </HorizontalStack> + <HorizontalStack Height="Fit" Width="Stretched" Spacing="2"> + <Label Text="End:" Foreground="${TooltipForeground}" Width="50%"/> + <Label Text="{EndMS}" Foreground="${TooltipForeground}" Width="Stretched" TextAlignment="Right"/> + </HorizontalStack> + <VerticalStack Height="Fit" Width="Stretched" IsVisible="{IsLayoutEvent}" > + <HorizontalStack Height="Fit" Width="Stretched" Spacing="2" Background="DimGrey" Margin="2"> + <Label Text="Layout:" Foreground="White" Width="50%" /> + <Label Text="{layouting}" Foreground="White" Background="Onyx" Width="Stretched" TextAlignment="Center"/> + </HorizontalStack> + <HorizontalStack Height="Fit" Width="Stretched" Spacing="2" Background="DimGrey" Margin="2"> + <Label Text="Result:" Foreground="White" Width="50%" /> + <Label Text="{result}" Foreground="White" Background="Onyx" Width="Stretched" TextAlignment="Center"/> + </HorizontalStack> + <HorizontalStack Height="Fit" Width="Stretched" Spacing="2" Background="DimGrey" Margin="2"> + <Label Text="Old Slot:" Foreground="White" Width="50%" /> + <Label Text="{OldSlot}" Foreground="White" Background="Onyx" Width="Stretched" TextAlignment="Center"/> + </HorizontalStack> + <HorizontalStack Height="Fit" Width="Stretched" Spacing="2" Background="DimGrey" Margin="2"> + <Label Text="New Slot:" Foreground="White" Width="50%" /> + <Label Text="{NewSlot}" Foreground="White" Background="Onyx" Width="Stretched" TextAlignment="Center"/> + </HorizontalStack> + </VerticalStack> + <DbgEventWidget Height="10" Width="Stretched" Event="{}" Tooltip="#Dbg.DbgEvtTooltip.crow" /> + <GroupBox Caption="Parent Event" Height="Fit" > + <DbgEventWidget Height="10" Width="Stretched" Event="{parentEvent}" Tooltip="#Dbg.DbgEvtTooltip.crow" /> + </GroupBox> + <!--<Label Text="{}" Foreground="${TooltipForeground}"/>--> + </VerticalStack> + </HorizontalStack> + </VerticalStack> +</Window>
\ No newline at end of file diff --git a/Samples/DebugLogAnalyzer/ui/EnumSelector.template b/Samples/DebugLogAnalyzer/ui/EnumSelector.template new file mode 100644 index 00000000..2fffa3f8 --- /dev/null +++ b/Samples/DebugLogAnalyzer/ui/EnumSelector.template @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<Popper > + <Template> + <CheckBox IsChecked="{²./IsPopped}"> + <Template> + <HorizontalStack Background="{../../../Background}" Margin="5" Spacing="10"> + <Label Text="{../../../../Caption}" Width="Stretched"/> + <Label Background="SeaGreen" Text="{../../../../EnumValue}" Margin="3"/> + </HorizontalStack> + </Template> + </CheckBox> + </Template> + <Wrapper Name="Content" Height="Fit" Width="{../PopWidth}" Background="Jet" /> +</Popper>
\ No newline at end of file diff --git a/Samples/DebugLogAnalyzer/ui/dbg.style b/Samples/DebugLogAnalyzer/ui/dbg.style new file mode 100644 index 00000000..77772f10 --- /dev/null +++ b/Samples/DebugLogAnalyzer/ui/dbg.style @@ -0,0 +1,15 @@ +IcoButton { + Template = "#Dbg.Button.template"; + Background = "Onyx"; +} +CheckBox2 { + Width = "200"; +} +smallLabCaption { + Font="mono, 8"; + Foreground="Grey"; +} +smallLabValue { + Font="mono, 10"; + Foreground="White"; +}
\ No newline at end of file diff --git a/Samples/DebugLogAnalyzer/ui/dbglog.crow b/Samples/DebugLogAnalyzer/ui/dbglog.crow index 72d96bf0..fd42833c 100644 --- a/Samples/DebugLogAnalyzer/ui/dbglog.crow +++ b/Samples/DebugLogAnalyzer/ui/dbglog.crow @@ -10,12 +10,14 @@ <Label Text="{treeIndex}"/> <Label Text="{yIndex}"/> <Label Text="{xLevel}"/> - <Label Text="Width:"/> + <Label Text="Width:" Foreground="Grey"/> <Label Text="{Width}"/> - <Label Text="Height:"/> + <Label Text="Height:" Foreground="Grey"/> <Label Text="{Height}"/> - <Label Text="ScrollX"/> + <Label Text="ScrollX:" Foreground="Grey"/> <Label Text="{../../dbv.ScrollX}"/> + <Label Text="MaxScrollX:" Foreground="Grey"/> + <Label Text="{../../dbv.MaxScrollX}"/> </HorizontalStack> </VerticalStack> <Splitter/> diff --git a/Samples/DebugLogAnalyzer/ui/focused.crow b/Samples/DebugLogAnalyzer/ui/focused.crow new file mode 100644 index 00000000..755345ae --- /dev/null +++ b/Samples/DebugLogAnalyzer/ui/focused.crow @@ -0,0 +1,17 @@ +<Window Width="80%" Movable="false"> + <VerticalStack> + <HorizontalStack Height="Fit"> + <Label Text="Hover:" Width="50" Foreground="Grey"/> + <Label Text="{HoverWidget}" Font="mono, 8"/> + </HorizontalStack> + <HorizontalStack Height="Fit"> + <Label Text="Focus:" Width="50" Foreground="Grey"/> + <Label Text="{FocusedWidget}" Font="mono, 8"/> + </HorizontalStack> + <HorizontalStack Height="Fit"> + <Label Text="Active:" Width="50" Foreground="Grey"/> + <Label Text="{ActiveWidget}" Font="mono, 8"/> + </HorizontalStack> + </VerticalStack> +</Window> + diff --git a/Samples/DebugLogAnalyzer/ui/main.crow b/Samples/DebugLogAnalyzer/ui/main.crow new file mode 100644 index 00000000..24a50a2f --- /dev/null +++ b/Samples/DebugLogAnalyzer/ui/main.crow @@ -0,0 +1,161 @@ +<?xml version="1.0"?> +<VerticalStack Background="DarkGrey"> + <HorizontalStack > + <TabView Width="25%" > + <VerticalStack Name="Explorer" Background="Onyx"> + <HorizontalStack Height="Fit" Margin="2" > + <Image Margin="2" Width="16" Height="16" Path="#Crow.Icons.level-up.svg" MouseClick="goUpDirClick" + Background="Jet" MouseEnter="{Background=Grey}" MouseLeave="{Background=Jet}" /> + <TextBox Text="{²CurrentDir}" Margin="2"/> + </HorizontalStack> + <DirectoryView Margin="1" Name="dv" CurrentDirectory="{CurrentDir}" SelectedItemChanged="Dv_SelectedItemChanged"> + <Template> + <TreeView IsRoot="true" Name="treeView" Data="{./FileSystemEntries}" Background="{./Background}" + SelectedItemChanged="./onSelectedItemChanged"> + <ItemTemplate DataType="System.IO.FileInfo"> + <ListItem CornerRadius="2" Margin="0" Height="Fit" Width="Stretched" + ContextCommands="{GetCommands}" + Selected="{Background=${ControlHighlight}}" + Unselected="{Background=Transparent}"> + <HorizontalStack> + <Image Margin="1" Width="14" Height="14" Path="#Crow.Icons.file.svg"/> + <Label Text="{Name}" Width="Stretched"/> + </HorizontalStack> + </ListItem> + </ItemTemplate> + <ItemTemplate DataType="System.IO.DirectoryInfo" Data="GetFileSystemInfosOrdered"> + <ListItem ContextCommands="{GetCommands}" + Selected="{/exp.Background=${ControlHighlight}}" + Unselected="{/exp.Background=Transparent}"> + <Expandable Name="exp" Caption="{Name}" MouseDoubleClick="/onClickForExpand"> + <Template> + <VerticalStack> + <Border CornerRadius="2" Margin="0" Height="Fit" MouseDoubleClick="./onClickForExpand" + Foreground="Transparent" + MouseEnter="{Foreground=DimGrey}" + MouseLeave="{Foreground=Transparent}"> + <HorizontalStack Background="{./Background}" Spacing="1"> + <Image Margin="1" Width="9" Height="9" Focusable="true" MouseDown="./onClickForExpand" + Path="{./Image}" + Visible="{./IsExpandable}" + SvgSub="{./IsExpanded}" + MouseEnter="{Background=LightGrey}" + MouseLeave="{Background=Transparent}"/> + <Image Margin="1" Width="16" Height="16" + Path="#Crow.Icons.folder.svg" SvgSub="{./IsExpanded}"/> + <Label Text="{./Caption}"/> + </HorizontalStack> + </Border> + <Container Name="Content" Visible="false"/> + </VerticalStack> + </Template> + <HorizontalStack Height="Fit"> + <Widget Width="12" Height="10"/> + <VerticalStack Height="Fit" Name="ItemsContainer"/> + </HorizontalStack> + </Expandable> + </ListItem> + </ItemTemplate> + </TreeView> + </Template> + </DirectoryView> + </VerticalStack> + <VerticalStack Name="DebugLogger" Margin="5" IsVisible="false" Background="Onyx"> + <HorizontalStack Height="Fit"> + <Image Width="30" Height="20" Path="#Crow.Icons.IconAlerte.svg" Visible="{../../../../dbgIfaceWidget.CrowDebuggerNOK}"/> + <Label Fit="true" Text="Crow debug assembly path:"/> + <Widget Width="5" Height="5" Background="SeaGreen" IsVisible="{../../../../dbgIfaceWidget.HasFocus}"/> + <Widget Width="5" Height="5" Background="Yellow" IsVisible="{../../../../dbgIfaceWidget.IsHover}"/> + <TextBox Text="{²CrowDbgAssemblyLocation}" Margin="2"/> + </HorizontalStack> + <CheckBox Name="cbFile" Caption="Record to file" IsChecked="{²DebugLogToFile}" Background="Onyx"/> + <HorizontalStack Height="Fit" Width="Stretched" IsEnabled="{../cbFile.IsChecked}" Background="Onyx" Margin="5"> + <Label Text="Debug log output file:" Fit="true"/> + <TextBox Text="{²DebugLogFilePath}" /> + </HorizontalStack> + <EnumSelector RadioButtonStyle="CheckBox2" Template="#Dbg.EnumSelector.template" + Background="Grey" + Caption="Recorded Events" EnumValue="{²RecordedEvents}" BitFieldExcludeMask="255" /> + <EnumSelector RadioButtonStyle="CheckBox2" Template="#Dbg.EnumSelector.template" + Background="Onyx" + Caption="Discarded Events" EnumValue="{²DiscardedEvents}" BitFieldExcludeMask="255" /> + <HorizontalStack Height="Fit"> + <CheckBox Caption="Recording" IsChecked="{²DebugLogRecording}" Height="40" Width="60" + Checked="{sh.Path='A 8,8,7.5,0,6.3 O 0.8,0,0,1 f O 0,0,0,0.5 G'}" + Unchecked="{sh.Path='R 0.5,0.5,15,15 f O 0,0,0,1 G'}"> + <Template> + <Border Background="Onyx" Margin="10" CornerRadius="5"> + <Shape Name="sh" Foreground="Grey" Size="16,16" Path="R 0.5,0.5,15,15 f O 0,0,0,1 G"/> + </Border> + </Template> + </CheckBox> + <Menu Data="{../../../../dbgIfaceWidget.LoggerCommands}" Height="Stretched" Width="Fit"> + <ItemTemplate> + <Button Command="{}" Height="Stretched" Width="Fit"/> + </ItemTemplate> + </Menu> + </HorizontalStack> + </VerticalStack> + </TabView> + <Splitter Width="6" /> + <VerticalStack> + <DebugInterfaceWidget Name="dbgIfaceWidget" Height="60%" Background="Black" Focusable="true" + CrowDbgAssemblyLocation="{²CrowDbgAssemblyLocation}" + Recording="{²DebugLogRecording}" + IMLSource="{Source}" + CurrentException="{²CurrentException}" + DiscardedEvents="{DiscardedEvents}" RecordedEvents="{RecordedEvents}" DebugLogToFile="{DebugLogToFile}"/> + <Splitter/> + <!--<TabView>--> + <VerticalStack Name="Editor" Spacing="0"> + <HorizontalStack Height="Fit"> + <Button Style="IcoButton" Command="{CMDNew}" /> + <Button Style="IcoButton" Command="{CMDSave}" /> + <Button Style="IcoButton" Command="{CMDSaveAs}" /> + <Button Style="IcoButton" Command="{CMDUndo}" /> + <Button Style="IcoButton" Command="{CMDRedo}" /> + <!--<Button Style="IcoButton" Command="{CMDCut}" /> + <Button Style="IcoButton" Command="{CMDCopy}" /> + <Button Style="IcoButton" Command="{CMDPaste}" />--> + <Widget Width="Stretched" /> + <Widget Background="Red" IsVisible="{DebugLogRecording}" CornerRadius="10" Width="12" Height="12"/> + </HorizontalStack> + + <HorizontalStack> + <Editor Name="tb" Text="{Source}" Multiline="true" Font="consolas, 12" Focusable="true" Height="Stretched" Width="Stretched" + TextChanged="onTextChanged" KeyDown="textView_KeyDown" ContextCommands="{EditorCommands}" + Foreground="DarkGrey" Background="White"/> + <!--SelectionChanged="onSelectedTextChanged"--> + <ScrollBar Value="{²../tb.ScrollY}" + LargeIncrement="{../tb.PageHeight}" SmallIncrement="1" + CursorRatio="{../tb.ChildHeightRatio}" Maximum="{../tb.MaxScrollY}" /> + </HorizontalStack> + <ScrollBar Style="HScrollBar" Value="{²../tb.ScrollX}" + LargeIncrement="{../tb.PageWidth}" SmallIncrement="1" + CursorRatio="{../tb.ChildWidthRatio}" Maximum="{../tb.MaxScrollX}" /> + <HorizontalStack Height="Fit" Spacing="10" Background="DarkGrey"> + <Widget Width="10" Height="10" Background="RoyalBlue" Visible="{IsDirty}"/> + <Label Text="{CurrentFile}" Width="Stretched"/> + <Widget Width="Stretched"/> + <Label Text="Line:" Foreground="Grey"/> + <Label Text="{../../tb.CurrentLine}" Margin="2"/> + <Label Text="Col:" Foreground="Grey"/> + <Label Text="{../../tb.CurrentColumn}" Margin="2"/> + </HorizontalStack> + <Popper IsVisible="{ShowError}" Background="Red"> + <Template> + <CheckBox IsChecked="{²./IsPopped}" MouseEnter="{IsChecked='true'}" MouseLeave="{IsChecked='false'}"> + <Template> + <Label Text="{CurrentExceptionMSG}" Background="Red" Foreground="White" Width="Stretched" Margin="2" + Multiline="true"/> + </Template> + </CheckBox> + </Template> + <Label Text="{CurrentException}" Background="DarkRed" Foreground="White" Width="Stretched" Margin="2" Multiline="true"/> + </Popper> + </VerticalStack> + + <!--</TabView>--> + </VerticalStack> + </HorizontalStack> +</VerticalStack>
\ No newline at end of file |