From 3d7463e06f0cf2e98707564f80947dc973a710bc Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jean-Philippe=20Bruy=C3=A8re?= Date: Fri, 26 Mar 2021 17:07:42 +0100 Subject: [PATCH] Splitter simplification, source header in src/Text, Table wip --- .../EventArgs/SelectedTextChangeEventArgs.cs | 28 +++ .../src/EventArgs/SelectionChangeEventArgs.cs | 2 +- Crow/src/Text/CharLocation.cs | 5 +- Crow/src/Text/Encoding.cs | 5 +- Crow/src/Text/Extensions.cs | 5 +- Crow/src/Text/IEditableTextWidget.cs | 3 + Crow/src/Text/SpanCharReader.cs | 5 +- Crow/src/Text/Text.cs | 5 +- Crow/src/Text/TextChange.cs | 5 +- Crow/src/Text/TextLine.cs | 5 +- Crow/src/Text/TextLineCollection.cs | 5 +- Crow/src/Text/TextSpan.cs | 19 ++- Crow/src/Widgets/Splitter.cs | 161 ++++-------------- Crow/src/Widgets/Table.cs | 16 +- Crow/src/Widgets/TableRow.cs | 54 +++--- Crow/src/Widgets/Widget.cs | 4 +- Samples/ShowCase/ShowCase.cs | 64 ++++--- Samples/ShowCase/ui/showcase.crow | 13 +- 18 files changed, 207 insertions(+), 197 deletions(-) create mode 100644 Crow/src/EventArgs/SelectedTextChangeEventArgs.cs diff --git a/Crow/src/EventArgs/SelectedTextChangeEventArgs.cs b/Crow/src/EventArgs/SelectedTextChangeEventArgs.cs new file mode 100644 index 00000000..91ceac13 --- /dev/null +++ b/Crow/src/EventArgs/SelectedTextChangeEventArgs.cs @@ -0,0 +1,28 @@ +// Copyright (c) 2013-2021 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) + +using Crow.Text; +using System; + +namespace Crow +{ + /// + /// Occurs in the TextBox widget when the text has changed. + /// + public class SelectedTextChangeEventArgs: EventArgs + { + /// + /// The TextChange structure representing the change. + /// + public TextSpan Old; + public TextSpan New; + + public SelectedTextChangeEventArgs (TextSpan old, TextSpan _new) : base() + { + Old = old; + New = _new; + } + } +} + diff --git a/Crow/src/EventArgs/SelectionChangeEventArgs.cs b/Crow/src/EventArgs/SelectionChangeEventArgs.cs index a2097f12..c45c880a 100644 --- a/Crow/src/EventArgs/SelectionChangeEventArgs.cs +++ b/Crow/src/EventArgs/SelectionChangeEventArgs.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2013-2020 Jean-Philippe Bruyère +// Copyright (c) 2013-2021 Jean-Philippe Bruyère // // This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) diff --git a/Crow/src/Text/CharLocation.cs b/Crow/src/Text/CharLocation.cs index 8db2ef64..cfc537c6 100644 --- a/Crow/src/Text/CharLocation.cs +++ b/Crow/src/Text/CharLocation.cs @@ -1,4 +1,7 @@ -using System; +// 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.Diagnostics; namespace Crow.Text diff --git a/Crow/src/Text/Encoding.cs b/Crow/src/Text/Encoding.cs index 7196e44c..b02e4565 100644 --- a/Crow/src/Text/Encoding.cs +++ b/Crow/src/Text/Encoding.cs @@ -1,4 +1,7 @@ -using System; +// 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.Text; diff --git a/Crow/src/Text/Extensions.cs b/Crow/src/Text/Extensions.cs index 7f5a53b7..b0b87180 100644 --- a/Crow/src/Text/Extensions.cs +++ b/Crow/src/Text/Extensions.cs @@ -1,4 +1,7 @@ -using System; +// 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.Text { diff --git a/Crow/src/Text/IEditableTextWidget.cs b/Crow/src/Text/IEditableTextWidget.cs index 20092acb..19f3ebd0 100644 --- a/Crow/src/Text/IEditableTextWidget.cs +++ b/Crow/src/Text/IEditableTextWidget.cs @@ -1,3 +1,6 @@ +// 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; diff --git a/Crow/src/Text/SpanCharReader.cs b/Crow/src/Text/SpanCharReader.cs index b8f42565..bab2c4b7 100644 --- a/Crow/src/Text/SpanCharReader.cs +++ b/Crow/src/Text/SpanCharReader.cs @@ -1,4 +1,7 @@ -using System; +// 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.Text; diff --git a/Crow/src/Text/Text.cs b/Crow/src/Text/Text.cs index 4ebb6e59..70a3c9c9 100644 --- a/Crow/src/Text/Text.cs +++ b/Crow/src/Text/Text.cs @@ -1,4 +1,7 @@ -using System; +// 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.Text; diff --git a/Crow/src/Text/TextChange.cs b/Crow/src/Text/TextChange.cs index deecc5e5..696699c1 100644 --- a/Crow/src/Text/TextChange.cs +++ b/Crow/src/Text/TextChange.cs @@ -1,4 +1,7 @@ -using System; +// 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.Text; diff --git a/Crow/src/Text/TextLine.cs b/Crow/src/Text/TextLine.cs index a574d659..e3565083 100644 --- a/Crow/src/Text/TextLine.cs +++ b/Crow/src/Text/TextLine.cs @@ -1,4 +1,7 @@ -using System; +// 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.Diagnostics; using System.Text; diff --git a/Crow/src/Text/TextLineCollection.cs b/Crow/src/Text/TextLineCollection.cs index b64c75e2..19696867 100644 --- a/Crow/src/Text/TextLineCollection.cs +++ b/Crow/src/Text/TextLineCollection.cs @@ -1,4 +1,7 @@ -using System; +// 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; using System.Collections.Generic; using System.Text; diff --git a/Crow/src/Text/TextSpan.cs b/Crow/src/Text/TextSpan.cs index 486d8e23..0ea0a638 100644 --- a/Crow/src/Text/TextSpan.cs +++ b/Crow/src/Text/TextSpan.cs @@ -1,10 +1,13 @@ -using System; +// 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.Text; namespace Crow.Text { - public struct TextSpan + public struct TextSpan : IEquatable { public readonly int Start; public readonly int End; @@ -15,5 +18,17 @@ namespace Crow.Text public bool IsEmpty => Start == End; public int Length => End - Start; + + public bool Equals(TextSpan other) + => Start == other.Start && End == other.End; + public override bool Equals(object obj) + => obj is TextSpan ts ? Equals(ts) : false; + + public override int GetHashCode() + => HashCode.Combine(Start, End); + public static bool operator ==(TextSpan left, TextSpan right) + => left.Equals (right); + public static bool operator !=(TextSpan left, TextSpan right) + => !left.Equals (right); } } diff --git a/Crow/src/Widgets/Splitter.cs b/Crow/src/Widgets/Splitter.cs index 99db30c1..b8d7a2fc 100644 --- a/Crow/src/Widgets/Splitter.cs +++ b/Crow/src/Widgets/Splitter.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2013-2020 Jean-Philippe Bruyère +// Copyright (c) 2013-2021 Jean-Philippe Bruyère // // This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) @@ -33,27 +33,9 @@ namespace Crow } } - Unit u1, u2; - int init1 = -1, init2 = -1, delta = 0, min1, min2, max1 , max2; - Widget go1 = null, go2 = null; - - void initSplit(Measure m1, int size1, Measure m2, int size2){ - if (m1 != Measure.Stretched) { - init1 = size1; - u1 = m1.Units; - } - if (m2 != Measure.Stretched) { - init2 = size2; - u2 = m2.Units; - } - } - void convertSizeInPix(Widget g1){ - - } - #region GraphicObject override public override ILayoutable Parent { - get { return base.Parent; } + get => base.Parent; set { if (value != null) { GenericStack gs = value as GenericStack; @@ -72,109 +54,42 @@ namespace Crow else IFace.MouseCursor = MouseCursor.sb_v_double_arrow; } - public override void onMouseDown (object sender, MouseButtonEventArgs e) - { - go1 = go2 = null; - init1 = init2 = -1; - delta = 0; - + + public override void onMouseMove (object sender, MouseMoveEventArgs e) + { GenericStack gs = Parent as GenericStack; + Point m = gs.ScreenPointToLocal (e.Position); int ptrSplit = gs.Children.IndexOf (this); - if (ptrSplit == 0 || ptrSplit == gs.Children.Count - 1) - return; - - go1 = gs.Children [ptrSplit - 1]; - go2 = gs.Children [ptrSplit + 1]; - - if (gs.Orientation == Orientation.Horizontal) { - initSplit (go1.Width, go1.Slot.Width, go2.Width, go2.Slot.Width); - min1 = go1.MinimumSize.Width; - min2 = go2.MinimumSize.Width; - max1 = go1.MaximumSize.Width; - max2 = go2.MaximumSize.Width; - if (init1 >= 0) - go1.Width = init1; - if (init2 >= 0) - go2.Width = init2; - } else { - initSplit (go1.Height, go1.Slot.Height, go2.Height, go2.Slot.Height); - min1 = go1.MinimumSize.Height; - min2 = go2.MinimumSize.Height; - max1 = go1.MaximumSize.Height; - max2 = go2.MaximumSize.Height; - if (init1 >= 0) - go1.Height = init1; - if (init2 >= 0) - go2.Height = init2; - } - e.Handled = true; - base.onMouseDown (sender, e); - } - public override void onMouseMove (object sender, MouseMoveEventArgs e) - { - e.Handled = true; - base.onMouseMove (sender, e); - - if (IsActive && go1 != null && go2 != null) { - GenericStack gs = Parent as GenericStack; - int newDelta = delta, size1 = init1, size2 = init2; - if (gs.Orientation == Orientation.Horizontal) { - newDelta -= e.XDelta; - if (size1 < 0) - size1 = go1.Slot.Width + delta; - if (size2 < 0) - size2 = go2.Slot.Width - delta; - } else { - newDelta -= e.YDelta; - if (size1 < 0) - size1 = go1.Slot.Height + delta; - if (size2 < 0) - size2 = go2.Slot.Height - delta; - } - if (size1 - newDelta < min1 || (max1 > 0 && size1 - newDelta > max1) || - size2 + newDelta < min2 || (max2 > 0 && size2 + newDelta > max2)) - return; - - delta = newDelta; - - if (gs.Orientation == Orientation.Horizontal) { - if (init1 >= 0) - go1.Width = init1 - delta; - if (init2 >= 0) - go2.Width = init2 + delta; + if (IFace.IsDown (Glfw.MouseButton.Left) && ptrSplit > 0 && ptrSplit < gs.Children.Count - 1) { + Widget w0 = gs.Children[ptrSplit - 1]; + Widget w1 = gs.Children[ptrSplit + 1]; + if (gs.Orientation == Orientation.Horizontal) { + int x = m.X - Slot.Width / 2 - gs.Spacing; + + if (x > w0.Slot.Left + w0.MinimumSize.Width && + x + Slot.Width + 2 * gs.Spacing < w1.Slot.Right - w1.MinimumSize.Width) { + w0.Width = x - w0.Slot.X; + x += Slot.Width + 2 * gs.Spacing; + w1.Width = w1.Slot.Right - x; + } } else { - if (init1 >= 0) - go1.Height = init1 - delta; - if (init2 >= 0) - go2.Height = init2 + delta; + int y = m.Y - Slot.Height / 2 - gs.Spacing; + + if (y > w0.Slot.Top + w0.MinimumSize.Height && + y + Slot.Height + 2 * gs.Spacing < w1.Slot.Bottom - w1.MinimumSize.Height) { + w0.Height = y - w0.Slot.Top; + y += Slot.Height + 2 * gs.Spacing; + w1.Height = w1.Slot.Bottom - y; + } } - + e.Handled = true; } - } - public override void onMouseUp (object sender, MouseButtonEventArgs e) - { - base.onMouseUp (sender, e); - - GenericStack gs = Parent as GenericStack; - if (init1 >= 0 && u1 == Unit.Percent) { - if (gs.Orientation == Orientation.Horizontal) - go1.Width = new Measure ((int)Math.Ceiling ( - go1.Width.Value * 100.0 / (double)(gs.Slot.Width - 2 * gs.Margin)), Unit.Percent); - else - go1.Height = new Measure ((int)Math.Ceiling ( - go1.Height.Value * 100.0 / (double)gs.Slot.Height), Unit.Percent); - } - if (init2 >= 0 && u2 == Unit.Percent) { - if (gs.Orientation == Orientation.Horizontal) - go2.Width = new Measure ((int)Math.Floor ( - go2.Width.Value * 100.0 / (double)(gs.Slot.Width - 2 * gs.Margin)), Unit.Percent); - else - go2.Height = new Measure ((int)Math.Floor ( - go2.Height.Value * 100.0 / (double)gs.Slot.Height), Unit.Percent); - } + + base.onMouseMove (sender, e); } + public override bool UpdateLayout (LayoutingType layoutType) { GenericStack gs = Parent as GenericStack; @@ -190,21 +105,7 @@ namespace Crow Height = Measure.Stretched; } return base.UpdateLayout (layoutType); - } - public override bool PointIsIn (ref Point m) - { - if (!(Visible & IsEnabled)||IsDragged) - return false; - if (!Parent.PointIsIn(ref m)) - return false; - m -= (Parent.getSlot().Position + Parent.ClientRectangle.Position) ; - Rectangle r = Slot; - if (Width == Measure.Stretched) - r.Inflate (0, 5); - else - r.Inflate (5, 0); - return r.ContainsOrIsEqual (m); - } + } #endregion } } diff --git a/Crow/src/Widgets/Table.cs b/Crow/src/Widgets/Table.cs index 3719e3e2..0beafb56 100644 --- a/Crow/src/Widgets/Table.cs +++ b/Crow/src/Widgets/Table.cs @@ -21,7 +21,7 @@ namespace Crow string caption; Measure width = Measure.Fit; - //public int ComputedWidth; + public int ComputedWidth; public Widget LargestWidget; public string Caption { @@ -70,18 +70,6 @@ namespace Crow //int lineWidth; ObservableList columns = new ObservableList(); - /*[DefaultValue (1)] - public int LineWidth { - get => lineWidth; - set { - if (lineWidth == value) - return; - lineWidth = value; - NotifyValueChangedAuto (lineWidth); - RegisterForLayouting (LayoutingType.Sizing | LayoutingType.ArrangeChildren); - } - }*/ - public ObservableList Columns { get => columns; set { @@ -106,6 +94,7 @@ namespace Crow layoutType &= (~(LayoutingType.X|LayoutingType.Width)); }*/ + //overriden to prevent search for largest child, all the rows as the same total width. public override void ComputeChildrenPositions () { int d = 0; childrenRWLock.EnterReadLock(); @@ -123,6 +112,7 @@ namespace Crow RegisteredLayoutings &= (~layoutType); if (layoutType == LayoutingType.Width) { + //propagate column.width to each row's children foreach (TableRow row in Children) { for (int i = 0; i < Columns.Count && i < row.Children.Count; i++) row.Children[i].Width = Columns[i].Width; diff --git a/Crow/src/Widgets/TableRow.cs b/Crow/src/Widgets/TableRow.cs index a251b6f9..dbb07661 100644 --- a/Crow/src/Widgets/TableRow.cs +++ b/Crow/src/Widgets/TableRow.cs @@ -34,8 +34,6 @@ namespace Crow } #endregion - int spacing; - public Table Table => Parent as Table; /*public override void ChildrenLayoutingConstraints(ILayoutable layoutable, ref LayoutingType layoutType) @@ -56,21 +54,25 @@ namespace Crow return; int spacing = Table.Spacing; ObservableList cols = Table.Columns; - - //int d = 0; + Widget first = Children[0]; TableRow firstRow = Table.Children[0] as TableRow; + if (firstRow == this) { base.ComputeChildrenPositions(); return; } - childrenRWLock.EnterReadLock(); + childrenRWLock.EnterReadLock(); + for (int i = 0; i < Children.Count && i < firstRow.Children.Count; i++) { - Widget w = Children[i]; - w.Slot.X = firstRow.Children[i].Slot.X; - setChildWidth (w, firstRow.Children[i].Slot.Width); - //d += spacing + firstRow.Children[i].Slot.Width; + Widget w = Children[i]; + /*if (i < cols.Count && cols[i].Width.IsFit && cols[i].LargestWidget != null) { + w.Slot.X + }else{*/ + w.Slot.X = firstRow.Children[i].Slot.X; + setChildWidth (w, firstRow.Children[i].Slot.Width); + //} } childrenRWLock.ExitReadLock(); @@ -87,8 +89,7 @@ namespace Crow if (layoutType == LayoutingType.Width) { if (firstRow.RegisteredLayoutings.HasFlag (LayoutingType.Width)) return false; - if (this != firstRow) { - //contentSize = firstRow.contentSize; + if (this != firstRow) { Slot.Width = firstRow.Slot.Width; if (Slot.Width != LastSlots.Width) { IsDirty = true; @@ -119,10 +120,27 @@ namespace Crow } return base.measureRawSize (lt); }*/ - public override void OnLayoutChanges(LayoutingType layoutType) - { - base.OnLayoutChanges(layoutType); - } + /*public override void OnChildLayoutChanges (object sender, LayoutingEventArgs arg) { + if (arg.LayoutType == LayoutingType.Width) { + Widget w = sender as Widget; + TableRow firstRow = Table.Children[0] as TableRow; + int c = Children.IndexOf(w); + if (c < Table.Columns.Count) { + Column col = Table.Columns[c]; + if (col.Width.IsFit) { + if (col.LargestWidget == null || w.Slot.Width > col.ComputedWidth) { + col.ComputedWidth = w.Slot.Width; + col.LargestWidget = w; + } else if (w == col.LargestWidget) + Console.WriteLine ("must search for largest widget"); + }else if (w == firstRow) + col.ComputedWidth = w.Slot.Width; + + } + Console.WriteLine ($"ROW:{Table.Children.IndexOf(this)} COL:{c} {w.LastSlots.Width} -> {w.Slot.Width} "); + } + base.OnChildLayoutChanges (sender, arg); + }*/ /*public override void OnChildLayoutChanges (object sender, LayoutingEventArgs arg) { Widget go = sender as Widget; TableRow row = go.Parent as TableRow; @@ -159,6 +177,7 @@ namespace Crow splitPos -= Spacing; Table.Columns[splitIndex].Width = splitPos - firstRow.Children[splitIndex].Slot.Left; Table.RegisterForLayouting (LayoutingType.Width); + e.Handled = true; } //Console.WriteLine ($"left:{firstRow.Children[splitIndex].Slot.Left} right:{firstRow.Children[splitIndex+1].Slot.Right} cb.X:{cb.X} splitPos:{splitPos} m:{m}"); } else { @@ -169,7 +188,6 @@ namespace Crow if (m.X >= r.Right) { r = Children[i+1].Slot; if (m.X <= r.Left && Table.Columns.Count - 1 > i ) { - Console.WriteLine ($"Set cursor Table row on mouse move. {m}"); IFace.MouseCursor = MouseCursor.sb_h_double_arrow; splitIndex = i; e.Handled = true; @@ -177,10 +195,8 @@ namespace Crow } } } - if (splitIndex < 0) { + if (splitIndex < 0 && IFace.MouseCursor == MouseCursor.sb_h_double_arrow) IFace.MouseCursor = MouseCursor.top_left_arrow; - Console.WriteLine ($"RESet cursor Table row on mouse move. {m}"); - } } } base.onMouseMove(sender, e); diff --git a/Crow/src/Widgets/Widget.cs b/Crow/src/Widgets/Widget.cs index 90875580..29424df3 100644 --- a/Crow/src/Widgets/Widget.cs +++ b/Crow/src/Widgets/Widget.cs @@ -634,7 +634,7 @@ namespace Crow if (width == value) return; if (value.IsFixed) { - if (value < minimumSize.Width || (value > maximumSize.Width && maximumSize.Width > 0)) + if (value < minimumSize.Width || (maximumSize.Width > 0 && value > maximumSize.Width)) return; } width = value; @@ -657,7 +657,7 @@ namespace Crow if (height == value) return; if (value.IsFixed) { - if (value < minimumSize.Height || (value > maximumSize.Height && maximumSize.Height > 0)) + if (value < minimumSize.Height || (maximumSize.Height > 0 && value > maximumSize.Height)) return; } height = value; diff --git a/Samples/ShowCase/ShowCase.cs b/Samples/ShowCase/ShowCase.cs index 60e9ea36..0628695f 100644 --- a/Samples/ShowCase/ShowCase.cs +++ b/Samples/ShowCase/ShowCase.cs @@ -56,27 +56,31 @@ namespace ShowCase CMDUndo, CMDRedo, CMDCut, CMDCopy, CMDPaste, CMDHelp, CMDAbout, CMDOptions; const string _defaultFileName = "unnamed.txt"; - string source = ""; - int dirtyUndoLevel; + string source = "", origSource; TextBox editor; Stopwatch reloadChrono = new Stopwatch (); - public new bool IsDirty { get { return undoStack.Count != dirtyUndoLevel; } } + public new bool IsDirty => source != origSource; public string Source { get => source; set { if (source == value) return; source = value; + CMDSave.CanExecute = IsDirty; if (!reloadChrono.IsRunning) reloadChrono.Restart (); NotifyValueChanged (source); + NotifyValueChanged ("IsDirty", IsDirty); } } public CommandGroup EditorCommands => new CommandGroup (CMDUndo, CMDRedo, CMDCut, CMDCopy, CMDPaste, CMDSave, CMDSaveAs); Stack undoStack = new Stack (); Stack redoStack = new Stack (); + TextSpan selection; + string SelectedText => + selection.IsEmpty ? "" : Source.AsSpan (selection.Start, selection.Length).ToString (); void undo () { if (undoStack.TryPop (out TextChange tch)) { @@ -98,6 +102,16 @@ namespace ShowCase 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)]; @@ -108,8 +122,7 @@ namespace ShowCase src.Slice (change.End).CopyTo (tmp.Slice (change.Start + change.ChangedText.Length)); disableTextChangedEvent = true; Source = tmp.ToString (); - disableTextChangedEvent = false; - NotifyValueChanged ("IsDirty", IsDirty); + disableTextChangedEvent = false; } void initCommands () @@ -120,9 +133,9 @@ namespace ShowCase CMDQuit = new Command (new Action (() => base.Quit ())) { Caption = "Quit", Icon = "#Icons.exit.svg", CanExecute = true }; CMDUndo = new Command (new Action (undo)) { Caption = "Undo", Icon = "#Icons.undo.svg", CanExecute = false }; CMDRedo = new Command (new Action (redo)) { Caption = "Redo", Icon = "#Icons.redo.svg", CanExecute = false }; - CMDCut = new Command (new Action (() => Quit ())) { Caption = "Cut", Icon = "#Icons.scissors.svg", CanExecute = false }; - CMDCopy = new Command (new Action (() => Quit ())) { Caption = "Copy", Icon = "#Icons.copy-file.svg", CanExecute = false }; - CMDPaste = new Command (new Action (() => Quit ())) { Caption = "Paste", Icon = "#Icons.paste-on-document.svg", CanExecute = false }; + CMDCut = new Command (new Action (() => cut ())) { Caption = "Cut", Icon = "#Icons.scissors.svg", CanExecute = false }; + CMDCopy = new Command (new Action (() => copy ())) { Caption = "Copy", Icon = "#Icons.copy-file.svg", CanExecute = false }; + CMDPaste = new Command (new Action (() => paste ())) { Caption = "Paste", Icon = "#Icons.paste-on-document.svg", CanExecute = false }; } void onNewFile () { @@ -190,8 +203,9 @@ namespace ShowCase byte [] buff = Encoding.UTF8.GetBytes (source); s.Write (buff, 0, buff.Length); } - dirtyUndoLevel = undoStack.Count; + origSource = source; NotifyValueChanged ("IsDirty", IsDirty); + CMDSave.CanExecute = false; } void reloadFromFile () { @@ -200,7 +214,7 @@ namespace ShowCase if (File.Exists (CurrentFile)) { using (Stream s = new FileStream (CurrentFile, FileMode.Open)) { using (StreamReader sr = new StreamReader (s)) - Source = sr.ReadToEnd (); + Source = origSource = sr.ReadToEnd (); } } disableTextChangedEvent = false; @@ -235,8 +249,7 @@ namespace ShowCase undoStack.Clear (); redoStack.Clear (); CMDUndo.CanExecute = false; - CMDRedo.CanExecute = false; - dirtyUndoLevel = 0; + CMDRedo.CanExecute = false; } void showError (Exception ex) { NotifyValueChanged ("ErrorMessage", (object)ex.Message); @@ -274,18 +287,31 @@ namespace ShowCase void onTextChanged (object sender, TextChangeEventArgs e) { if (disableTextChangedEvent) return; - undoStack.Push (e.Change.Inverse (source)); + applyChange (e.Change); + } + void applyChange (TextChange change) { + undoStack.Push (change.Inverse (source)); redoStack.Clear (); CMDUndo.CanExecute = true; CMDRedo.CanExecute = false; - apply (e.Change); + apply (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 && e.Key == Glfw.Key.W) { - if (Shift) - CMDRedo.Execute (); - else - CMDUndo.Execute (); + if (Ctrl) { + if (e.Key == Glfw.Key.W) { + if (Shift) + CMDRedo.Execute (); + else + CMDUndo.Execute (); + } else if (e.Key == Glfw.Key.S) { + onSave (); + } } } diff --git a/Samples/ShowCase/ui/showcase.crow b/Samples/ShowCase/ui/showcase.crow index 6217373c..5b0e467c 100644 --- a/Samples/ShowCase/ui/showcase.crow +++ b/Samples/ShowCase/ui/showcase.crow @@ -19,6 +19,9 @@