From cd43ee363da86b9aebe407f3d5e89df5cf9431ff Mon Sep 17 00:00:00 2001 From: Tig Date: Sun, 30 Jun 2024 11:53:54 -0700 Subject: [PATCH] Revamped to further simplify and make more correct --- Terminal.Gui/Application/Application.cs | 6 +- Terminal.Gui/View/Adornment/Adornment.cs | 6 +- Terminal.Gui/View/Adornment/Border.cs | 5 +- Terminal.Gui/View/CancelEventArgs.cs | 57 +++++++- Terminal.Gui/View/HighlightEventArgs.cs | 2 +- Terminal.Gui/View/StateEventArgs.cs | 27 ++++ Terminal.Gui/View/View.cs | 17 +-- Terminal.Gui/View/ViewAdornments.cs | 3 +- Terminal.Gui/View/ViewMouse.cs | 19 +-- Terminal.Gui/View/ViewText.cs | 9 +- Terminal.Gui/Views/Button.cs | 4 +- Terminal.Gui/Views/CheckBox.cs | 27 +++- Terminal.Gui/Views/ComboBox.cs | 45 ++++-- Terminal.Gui/Views/Label.cs | 4 +- Terminal.Gui/Views/Shortcut.cs | 2 +- Terminal.Gui/Views/TextField.cs | 6 +- Terminal.Gui/Views/TextValidateField.cs | 22 +-- Terminal.Gui/Views/TextView.cs | 3 +- Terminal.Gui/Views/Tile.cs | 6 +- Terminal.Gui/Views/Wizard/Wizard.cs | 4 +- UICatalog/Scenarios/Buttons.cs | 8 +- UICatalog/Scenarios/CharacterMap.cs | 162 ++++++++++----------- UICatalog/Scenarios/CsvEditor.cs | 2 +- UICatalog/Scenarios/ExpanderButton.cs | 5 +- UnitTests/Application/ApplicationTests.cs | 8 +- UnitTests/Application/KeyboardTests.cs | 6 +- UnitTests/Dialogs/WizardTests.cs | 2 +- UnitTests/UICatalog/ScenarioTests.cs | 4 +- UnitTests/View/Adornment/AdornmentTests.cs | 3 +- UnitTests/View/TitleTests.cs | 3 +- UnitTests/Views/ComboBoxTests.cs | 1 + UnitTests/Views/TextFieldTests.cs | 12 +- 32 files changed, 299 insertions(+), 191 deletions(-) create mode 100644 Terminal.Gui/View/StateEventArgs.cs diff --git a/Terminal.Gui/Application/Application.cs b/Terminal.Gui/Application/Application.cs index 476796b77..31ea01efa 100644 --- a/Terminal.Gui/Application/Application.cs +++ b/Terminal.Gui/Application/Application.cs @@ -308,7 +308,7 @@ public static partial class Application SupportedCultures = GetSupportedCultures (); _mainThreadId = Thread.CurrentThread.ManagedThreadId; _initialized = true; - InitializedChanged?.Invoke (null, new (false, _initialized)); + InitializedChanged?.Invoke (null, new (in _initialized)); } private static void Driver_SizeChanged (object sender, SizeChangedEventArgs e) { OnSizeChanging (e); } @@ -349,7 +349,7 @@ public static partial class Application // TODO: Throw an exception if Init hasn't been called. ResetState (); PrintJsonErrors (); - InitializedChanged?.Invoke (null, new (true, _initialized)); + InitializedChanged?.Invoke (null, new (in _initialized)); } /// @@ -358,7 +358,7 @@ public static partial class Application /// /// Intended to support unit tests that need to know when the application has been initialized. /// - public static event EventHandler> InitializedChanged; + public static event EventHandler> InitializedChanged; #endregion Initialization (Init/Shutdown) diff --git a/Terminal.Gui/View/Adornment/Adornment.cs b/Terminal.Gui/View/Adornment/Adornment.cs index 57cd7866e..82d370661 100644 --- a/Terminal.Gui/View/Adornment/Adornment.cs +++ b/Terminal.Gui/View/Adornment/Adornment.cs @@ -61,16 +61,16 @@ public class Adornment : View Parent?.LayoutSubviews (); } - OnThicknessChanged (new (current, Thickness)); + OnThicknessChanged (new (Thickness)); } } } /// Fired whenever the property changes. - public event EventHandler> ThicknessChanged; + public event EventHandler> ThicknessChanged; /// Called whenever the property changes. - public void OnThicknessChanged (CancelEventArgs args) + public void OnThicknessChanged (EventArgs args) { ThicknessChanged?.Invoke (this, args); } diff --git a/Terminal.Gui/View/Adornment/Border.cs b/Terminal.Gui/View/Adornment/Border.cs index b6f011281..554eb5ea8 100644 --- a/Terminal.Gui/View/Adornment/Border.cs +++ b/Terminal.Gui/View/Adornment/Border.cs @@ -299,7 +299,8 @@ public class Border : Adornment _startGrabPoint = new (mouseEvent.Position.X + Frame.X, mouseEvent.Position.Y + Frame.Y); _dragPosition = mouseEvent.Position; Application.GrabMouse (this); - SetHighlight (new (HighlightStyle, HighlightStyle)); + + SetHighlight (HighlightStyle); } return true; @@ -343,7 +344,7 @@ public class Border : Adornment { _dragPosition = null; Application.UngrabMouse (); - SetHighlight (new (HighlightStyle, HighlightStyle.None)); + SetHighlight (HighlightStyle.None); return true; } diff --git a/Terminal.Gui/View/CancelEventArgs.cs b/Terminal.Gui/View/CancelEventArgs.cs index f8643b772..4cc4b666f 100644 --- a/Terminal.Gui/View/CancelEventArgs.cs +++ b/Terminal.Gui/View/CancelEventArgs.cs @@ -12,14 +12,67 @@ namespace Terminal.Gui; /// should be set to /// to prevent the state change from occurring. /// -public class CancelEventArgs : CancelEventArgs +public class CancelEventArgs : CancelEventArgs where T : notnull { /// Initializes a new instance of the class. /// The current (old) value of the property. /// The value the property will be set to if the event is not cancelled. /// Whether the event should be canceled or not. /// The type of the value for the change being canceled. - public CancelEventArgs (T currentValue, T newValue, bool cancel = false) : base (cancel) + public CancelEventArgs (ref readonly T currentValue, ref T newValue, bool cancel = false) : base (cancel) + { + CurrentValue = currentValue; + NewValue = newValue; + } + + /// The value the property will be set to if the event is not cancelled. + public T NewValue { get; set; } + + /// The current value of the property. + public T CurrentValue { get; } +} + + +/// +/// for events that convey changes to a property of type . +/// +/// The type of the value that was part of the change being canceled. +/// +/// Events that use this class can be cancellable. Where applicable, the property +/// should be set to +/// to prevent the state change from occurring. +/// +public class EventArgs : EventArgs where T : notnull +{ + /// Initializes a new instance of the class. + /// The current value of the property. + /// The type of the value. + public EventArgs (ref readonly T currentValue) : base () + { + CurrentValue = currentValue; + } + + /// The current value of the property. + public T CurrentValue { get; } +} + +/// +/// for events that convey changes to a property of type . +/// +/// The type of the value that was part of the change being canceled. +/// +/// Events that use this class can be cancellable. Where applicable, the property +/// should be set to +/// to prevent the state change from occurring. +/// +public class CancelEventArgsStruct : CancelEventArgs where T : notnull +{ + /// Initializes a new instance of the class. + /// The current (old) value of the property. + /// The value the property will be set to if the event is not cancelled. + /// Whether the event should be canceled or not. + /// The type of the value for the change being canceled. + public CancelEventArgsStruct (T currentValue, T newValue, bool cancel = false) : base (cancel) { CurrentValue = currentValue; NewValue = newValue; diff --git a/Terminal.Gui/View/HighlightEventArgs.cs b/Terminal.Gui/View/HighlightEventArgs.cs index 4336b8846..d4f300485 100644 --- a/Terminal.Gui/View/HighlightEventArgs.cs +++ b/Terminal.Gui/View/HighlightEventArgs.cs @@ -6,5 +6,5 @@ public class HighlightEventArgs : CancelEventArgs { /// - public HighlightEventArgs (HighlightStyle currentValue, HighlightStyle newValue) : base (currentValue, newValue) { } + public HighlightEventArgs (ref HighlightStyle currentValue, ref HighlightStyle newValue) : base (ref currentValue, ref newValue) { } } diff --git a/Terminal.Gui/View/StateEventArgs.cs b/Terminal.Gui/View/StateEventArgs.cs new file mode 100644 index 000000000..10cb47886 --- /dev/null +++ b/Terminal.Gui/View/StateEventArgs.cs @@ -0,0 +1,27 @@ +#nullable enable +using System.ComponentModel; + +namespace Terminal.Gui; + +/// for events that convey state changes to a class. +/// +/// Events that use this class can be cancellable. The property should be set to +/// to prevent the state change from occurring. +/// +public class StateEventArgs : CancelEventArgs +{ + /// Creates a new instance of the class. + /// + /// + public StateEventArgs (T oldValue, T newValue) + { + OldValue = oldValue; + NewValue = newValue; + } + + /// The new state + public T NewValue { get; set; } + + /// The previous state + public T OldValue { get; } +} diff --git a/Terminal.Gui/View/View.cs b/Terminal.Gui/View/View.cs index 833978130..bfe0acd65 100644 --- a/Terminal.Gui/View/View.cs +++ b/Terminal.Gui/View/View.cs @@ -457,7 +457,7 @@ public partial class View : Responder, ISupportInitializeNotification return; } - if (!OnTitleChanging (_title, value)) + if (!OnTitleChanging (ref value)) { string old = _title; _title = value; @@ -472,7 +472,7 @@ public partial class View : Responder, ISupportInitializeNotification Id = _title; } #endif // DEBUG - OnTitleChanged (old, _title); + OnTitleChanged (); } } } @@ -488,12 +488,9 @@ public partial class View : Responder, ISupportInitializeNotification } /// Called when the has been changed. Invokes the event. - /// The that is/has been replaced. - /// The new to be replaced. - public virtual void OnTitleChanged (string oldTitle, string newTitle) + protected void OnTitleChanged () { - CancelEventArgs args = new (oldTitle, newTitle); - TitleChanged?.Invoke (this, args); + TitleChanged?.Invoke (this, new (ref _title)); } /// @@ -503,16 +500,16 @@ public partial class View : Responder, ISupportInitializeNotification /// The that is/has been replaced. /// The new to be replaced. /// `true` if an event handler canceled the Title change. - public virtual bool OnTitleChanging (string oldTitle, string newTitle) + protected bool OnTitleChanging (ref string newTitle) { - CancelEventArgs args = new (oldTitle, newTitle); + CancelEventArgs args = new (ref _title, ref newTitle); TitleChanging?.Invoke (this, args); return args.Cancel; } /// Event fired after the has been changed. - public event EventHandler> TitleChanged; + public event EventHandler> TitleChanged; /// /// Event fired when the is changing. Set to `true` diff --git a/Terminal.Gui/View/ViewAdornments.cs b/Terminal.Gui/View/ViewAdornments.cs index 68c866e38..accb15aba 100644 --- a/Terminal.Gui/View/ViewAdornments.cs +++ b/Terminal.Gui/View/ViewAdornments.cs @@ -124,7 +124,8 @@ public partial class View get => Border?.LineStyle ?? LineStyle.Single; set { - CancelEventArgs e = new (Border?.LineStyle ?? LineStyle.None, value); + var old = Border?.LineStyle ?? LineStyle.None; + CancelEventArgs e = new (ref old, ref value); OnBorderStyleChanging (e); } diff --git a/Terminal.Gui/View/ViewMouse.cs b/Terminal.Gui/View/ViewMouse.cs index e3cdc0ca0..fc05744c1 100644 --- a/Terminal.Gui/View/ViewMouse.cs +++ b/Terminal.Gui/View/ViewMouse.cs @@ -283,7 +283,7 @@ public partial class View // We're grabbed. Clicked event comes after the last Release. This is our signal to ungrab Application.UngrabMouse (); - if (SetHighlight (new (HighlightStyle, HighlightStyle.None))) + if (SetHighlight (HighlightStyle.None)) { return true; } @@ -318,7 +318,7 @@ public partial class View { if (Application.MouseGrabView == this) { - SetHighlight (new (HighlightStyle, HighlightStyle.None)); + SetHighlight (HighlightStyle.None); } return mouseEvent.Handled = true; @@ -432,7 +432,7 @@ public partial class View /// /// /// , if the Highlight event was handled, otherwise. - internal bool SetHighlight (HighlightEventArgs args) + internal bool SetHighlight (HighlightStyle newHighlightStyle) { // TODO: Make the highlight colors configurable if (!CanFocus) @@ -441,6 +441,9 @@ public partial class View } // Enable override via virtual method and/or event + HighlightStyle copy = HighlightStyle; + var args = new HighlightEventArgs (ref copy, ref newHighlightStyle); + if (OnHighlight (args) == true) { return true; @@ -541,10 +544,7 @@ public partial class View if (Viewport.Contains (mouseEvent.Position)) { if (this is not Adornment - && SetHighlight ( - new ( - HighlightStyle, - HighlightStyle.HasFlag (HighlightStyle.Pressed) ? HighlightStyle.Pressed : HighlightStyle.None))) + && SetHighlight (HighlightStyle.HasFlag (HighlightStyle.Pressed) ? HighlightStyle.Pressed : HighlightStyle.None)) { return true; } @@ -552,10 +552,7 @@ public partial class View else { if (this is not Adornment - && SetHighlight ( - new ( - HighlightStyle, - HighlightStyle.HasFlag (HighlightStyle.PressedOutside) ? HighlightStyle.PressedOutside : HighlightStyle.None))) + && SetHighlight (HighlightStyle.HasFlag (HighlightStyle.PressedOutside) ? HighlightStyle.PressedOutside : HighlightStyle.None)) { return true; diff --git a/Terminal.Gui/View/ViewText.cs b/Terminal.Gui/View/ViewText.cs index 23adac7fa..baf12cae3 100644 --- a/Terminal.Gui/View/ViewText.cs +++ b/Terminal.Gui/View/ViewText.cs @@ -69,23 +69,22 @@ public partial class View Id = _text; } #endif - OnTextChanged (new CancelEventArgs (old, Text)); + OnTextChanged (); } } /// /// Called when the has changed. Fires the event. /// - /// The event arguments - public void OnTextChanged (CancelEventArgs args) + public void OnTextChanged () { - TextChanged?.Invoke (this, args); + TextChanged?.Invoke (this, EventArgs.Empty); } /// /// Text changed event, raised when the text has changed. /// - public event EventHandler> TextChanged; + public event EventHandler TextChanged; /// /// Gets or sets how the View's is aligned horizontally when drawn. Changing this property will diff --git a/Terminal.Gui/Views/Button.cs b/Terminal.Gui/Views/Button.cs index fc14ec9b5..e811b90c8 100644 --- a/Terminal.Gui/Views/Button.cs +++ b/Terminal.Gui/Views/Button.cs @@ -110,9 +110,9 @@ public class Button : View e.Handled = InvokeCommand (Command.HotKey) == true; } - private void Button_TitleChanged (object sender, CancelEventArgs e) + private void Button_TitleChanged (object sender, EventArgs e) { - base.Text = e.NewValue; + base.Text = e.CurrentValue; TextFormatter.HotKeySpecifier = HotKeySpecifier; } diff --git a/Terminal.Gui/Views/CheckBox.cs b/Terminal.Gui/Views/CheckBox.cs index e28e276ad..26cda4f14 100644 --- a/Terminal.Gui/Views/CheckBox.cs +++ b/Terminal.Gui/Views/CheckBox.cs @@ -42,9 +42,9 @@ public class CheckBox : View e.Handled = OnToggled () == true; } - private void Checkbox_TitleChanged (object? sender, CancelEventArgs e) + private void Checkbox_TitleChanged (object? sender, EventArgs e) { - base.Text = e.NewValue; + base.Text = e.CurrentValue; TextFormatter.HotKeySpecifier = HotKeySpecifier; } @@ -63,7 +63,7 @@ public class CheckBox : View } /// - /// If allows to be null, true or false. If + /// If allows to be null, true, or false. If /// only allows to be true or false. /// public bool AllowNullChecked @@ -76,7 +76,23 @@ public class CheckBox : View } } - /// The state of the + /// + /// The state of the . + /// + /// + /// + /// If and is , the + /// will display the ConfigurationManager.Glyphs.NullChecked character (☒). + /// + /// + /// If , the + /// will display the ConfigurationManager.Glyphs.UnChecked character (☐). + /// + /// + /// If , the + /// will display the ConfigurationManager.Glyphs.Checked character (☑). + /// + /// public bool? Checked { get => _checked; @@ -99,7 +115,8 @@ public class CheckBox : View /// If the event was canceled. public bool? OnToggled () { - CancelEventArgs e = new (Checked, false); + bool ? oldValue = Checked; + CancelEventArgs e = new (ref _checked, ref oldValue); if (AllowNullChecked) { diff --git a/Terminal.Gui/Views/ComboBox.cs b/Terminal.Gui/Views/ComboBox.cs index 4d50eacf5..f88c5c736 100644 --- a/Terminal.Gui/Views/ComboBox.cs +++ b/Terminal.Gui/Views/ComboBox.cs @@ -68,7 +68,7 @@ public class ComboBox : View SetNeedsLayout (); SetNeedsDisplay (); - Search_Changed (this, new CancelEventArgs (string.Empty, Text)); + ShowHideList (Text); }; // Things this view knows how to do @@ -185,15 +185,19 @@ public class ComboBox : View // Only need to refresh list if its been added to a container view if (SuperView is { } && SuperView.Subviews.Contains (this)) { - SelectedItem = -1; - _search.Text = string.Empty; - Search_Changed (this, new CancelEventArgs (string.Empty, _search.Text)); + Text = string.Empty; +// SelectedItem = -1; +// _search.Text = string.Empty; +// ResetSearchSet (); + +// HideList (); +//// ShowHideList (string.Empty); SetNeedsDisplay (); } } } - /// The currently selected list item + /// The text of the currently selected list item public new string Text { get => _text; @@ -243,7 +247,7 @@ public class ComboBox : View public event EventHandler Expanded; /// - protected internal override bool OnMouseEvent (MouseEvent me) + protected internal override bool OnMouseEvent (MouseEvent me) { if (me.Position.X == Viewport.Right - 1 && me.Position.Y == Viewport.Top @@ -423,12 +427,12 @@ public class ComboBox : View if (SelectedItem > -1 && _listview.Source?.Count > 0) { - _search.Text = _text = _listview.Source.ToList () [SelectedItem].ToString (); + Text = _listview.Source.ToList () [SelectedItem].ToString (); } } else if (!ReadOnly) { - _search.Text = _text = ""; + Text = string.Empty; _selectedItem = _lastSelectedItem; OnSelectedChanged (); } @@ -660,7 +664,7 @@ public class ComboBox : View // Tell TextField to handle Accept Command (Enter) void Search_Accept (object sender, HandledEventArgs e) { e.Handled = true; } - private void Search_Changed (object sender, CancelEventArgs e) + private void Search_Changed (object sender, EventArgs e) { if (_source is null) { @@ -668,13 +672,18 @@ public class ComboBox : View return; } - if (string.IsNullOrEmpty (_search.Text) && string.IsNullOrEmpty (e.CurrentValue)) + ShowHideList (Text); + } + + private void ShowHideList (string oldText) + { + if (string.IsNullOrEmpty (_search.Text) && string.IsNullOrEmpty (oldText)) { ResetSearchSet (); } - else if (_search.Text != e.CurrentValue) + else if (_search.Text != oldText) { - if (_search.Text.Length < e.CurrentValue.Length) + if (_search.Text.Length < oldText.Length) { _selectedItem = -1; } @@ -730,7 +739,7 @@ public class ComboBox : View SetValue (_listview.SelectedItem > -1 ? _searchSet [_listview.SelectedItem] : _text); _search.CursorPosition = _search.Text.GetColumns (); - Search_Changed (this, new CancelEventArgs (_search.Text, _search.Text)); + ShowHideList (Text); OnOpenSelectedItem (); Reset (true); HideList (); @@ -756,7 +765,13 @@ public class ComboBox : View _listview.ResumeSuspendCollectionChangedEvent (); } - private void SetSearchText (string value) { _search.Text = _text = value; } + // Sets the search text field Text as well as our own Text property + private void SetSearchText (string value) + { +// _text = value; + _search.Text = value; + _text = value; + } private void SetValue (object text, bool isFromSelectedItem = false) { @@ -813,7 +828,7 @@ public class ComboBox : View set => _hideDropdownListOnClick = WantContinuousButtonPressed = value; } - protected internal override bool OnMouseEvent (MouseEvent me) + protected internal override bool OnMouseEvent (MouseEvent me) { var res = false; bool isMousePositionValid = IsMousePositionValid (me); diff --git a/Terminal.Gui/Views/Label.cs b/Terminal.Gui/Views/Label.cs index bdbaa2dff..b05593fda 100644 --- a/Terminal.Gui/Views/Label.cs +++ b/Terminal.Gui/Views/Label.cs @@ -31,9 +31,9 @@ public class Label : View e.Handled = InvokeCommand (Command.HotKey) == true; } - private void Label_TitleChanged (object sender, CancelEventArgs e) + private void Label_TitleChanged (object sender, EventArgs e) { - base.Text = e.NewValue; + base.Text = e.CurrentValue; TextFormatter.HotKeySpecifier = HotKeySpecifier; } diff --git a/Terminal.Gui/Views/Shortcut.cs b/Terminal.Gui/Views/Shortcut.cs index 261a227a1..fd528506d 100644 --- a/Terminal.Gui/Views/Shortcut.cs +++ b/Terminal.Gui/Views/Shortcut.cs @@ -446,7 +446,7 @@ public class Shortcut : View CommandView.Y = 0; //Pos.Center (); } -private void Shortcut_TitleChanged (object sender, CancelEventArgs e) +private void Shortcut_TitleChanged (object sender, EventArgs e) { // If the Title changes, update the CommandView text. // This is a helper to make it easier to set the CommandView text. diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs index c59b9b0f7..a4e220966 100644 --- a/Terminal.Gui/Views/TextField.cs +++ b/Terminal.Gui/Views/TextField.cs @@ -533,7 +533,7 @@ public class TextField : View } string newText = value.Replace ("\t", "").Split ("\n") [0]; - CancelEventArgs args = new (oldText, newText); + CancelEventArgs args = new (ref oldText, ref newText); OnTextChanging (args); if (args.Cancel) @@ -547,6 +547,8 @@ public class TextField : View } ClearAllSelection (); + + // Note we use NewValue here; TextChanging subscribers may have changed it _text = args.NewValue.EnumerateRunes ().ToList (); if (!Secret && !_historyText.IsFromHistory) @@ -563,7 +565,7 @@ public class TextField : View ); } - OnTextChanged (new (oldText, StringExtensions.ToString (_text))); + OnTextChanged (); ProcessAutocomplete (); diff --git a/Terminal.Gui/Views/TextValidateField.cs b/Terminal.Gui/Views/TextValidateField.cs index b71dd5a02..49c66ba17 100644 --- a/Terminal.Gui/Views/TextValidateField.cs +++ b/Terminal.Gui/Views/TextValidateField.cs @@ -64,8 +64,8 @@ namespace Terminal.Gui /// Method that invoke the event if it's defined. /// The previous text before replaced. - /// Returns the - void OnTextChanged (CancelEventArgs oldValue); + /// Returns the + void OnTextChanged (EventArgs oldValue); /// /// Changed event, raised when the text has changed. @@ -74,7 +74,7 @@ namespace Terminal.Gui /// containing the old value. /// /// - event EventHandler> TextChanged; + event EventHandler> TextChanged; } ////////////////////////////////////////////////////////////////////////////// @@ -125,7 +125,7 @@ namespace Terminal.Gui } /// - public event EventHandler> TextChanged; + public event EventHandler> TextChanged; /// public string Text @@ -206,7 +206,7 @@ namespace Terminal.Gui if (result) { - OnTextChanged (new CancelEventArgs (oldValue, Text)); + OnTextChanged (new EventArgs (ref oldValue)); } return result; @@ -220,14 +220,14 @@ namespace Terminal.Gui if (result) { - OnTextChanged (new CancelEventArgs (oldValue, Text)); + OnTextChanged (new EventArgs (ref oldValue)); } return result; } /// - public void OnTextChanged (CancelEventArgs args) { TextChanged?.Invoke (this, args); } + public void OnTextChanged (EventArgs args) { TextChanged?.Invoke (this, args); } } #endregion @@ -260,7 +260,7 @@ namespace Terminal.Gui public bool ValidateOnInput { get; set; } = true; /// - public event EventHandler> TextChanged; + public event EventHandler> TextChanged; /// public string Text @@ -333,7 +333,7 @@ namespace Terminal.Gui { string oldValue = Text; _text.RemoveAt (pos); - OnTextChanged (new CancelEventArgs (oldValue, Text)); + OnTextChanged (new EventArgs (oldValue)); } return true; @@ -349,7 +349,7 @@ namespace Terminal.Gui { string oldValue = Text; _text.Insert (pos, (Rune)ch); - OnTextChanged (new CancelEventArgs (oldValue, Text)); + OnTextChanged (new EventArgs (ref oldValue)); return true; } @@ -358,7 +358,7 @@ namespace Terminal.Gui } /// - public void OnTextChanged (CancelEventArgs args) { TextChanged?.Invoke (this, args); } + public void OnTextChanged (EventArgs args) { TextChanged?.Invoke (this, args); } /// Compiles the regex pattern for validation./> private void CompileMask () { _regex = new Regex (StringExtensions.ToString (_pattern), RegexOptions.Compiled); } diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index e57d83519..62cce0b87 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -2820,7 +2820,6 @@ public class TextView : View } set { - string old = Text; ResetPosition (); _model.LoadString (value); @@ -2830,7 +2829,7 @@ public class TextView : View _model = _wrapManager.WrapModel (Viewport.Width, out _, out _, out _, out _); } - OnTextChanged (new (old, Text)); + OnTextChanged (); SetNeedsDisplay (); _historyText.Clear (Text); diff --git a/Terminal.Gui/Views/Tile.cs b/Terminal.Gui/Views/Tile.cs index f495f42fa..e917cab7b 100644 --- a/Terminal.Gui/Views/Tile.cs +++ b/Terminal.Gui/Views/Tile.cs @@ -61,7 +61,7 @@ public class Tile /// The new to be replaced. public virtual void OnTitleChanged (string oldTitle, string newTitle) { - var args = new CancelEventArgs (oldTitle, newTitle); + var args = new EventArgs (newTitle); TitleChanged?.Invoke (this, args); } @@ -74,14 +74,14 @@ public class Tile /// true if an event handler cancelled the Title change. public virtual bool OnTitleChanging (string oldTitle, string newTitle) { - var args = new CancelEventArgs (oldTitle, newTitle); + var args = new CancelEventArgs (ref oldTitle, ref newTitle); TitleChanging?.Invoke (this, args); return args.Cancel; } /// Event fired after the has been changed. - public event EventHandler> TitleChanged; + public event EventHandler> TitleChanged; /// /// Event fired when the is changing. diff --git a/Terminal.Gui/Views/Wizard/Wizard.cs b/Terminal.Gui/Views/Wizard/Wizard.cs index 589cc8ec5..75377c8b4 100644 --- a/Terminal.Gui/Views/Wizard/Wizard.cs +++ b/Terminal.Gui/Views/Wizard/Wizard.cs @@ -562,11 +562,11 @@ public class Wizard : Dialog // gets the first step if CurrentStep == null } - private void Wizard_TitleChanged (object sender, CancelEventArgs e) + private void Wizard_TitleChanged (object sender, EventArgs e) { if (string.IsNullOrEmpty (_wizardTitle)) { - _wizardTitle = e.NewValue; + _wizardTitle = e.CurrentValue; } } } diff --git a/UICatalog/Scenarios/Buttons.cs b/UICatalog/Scenarios/Buttons.cs index c996ddb23..e0be70cd2 100644 --- a/UICatalog/Scenarios/Buttons.cs +++ b/UICatalog/Scenarios/Buttons.cs @@ -341,7 +341,7 @@ public class Buttons : Scenario }; numericUpDown.ValueChanged += NumericUpDown_ValueChanged; - void NumericUpDown_ValueChanged (object sender, CancelEventArgs e) { } + void NumericUpDown_ValueChanged (object sender, EventArgs e) { } main.Add (label, numericUpDown); @@ -518,7 +518,7 @@ public class Buttons : Scenario } T oldValue = value; - CancelEventArgs args = new CancelEventArgs (_value, value); + CancelEventArgs args = new (ref _value, ref value); ValueChanging?.Invoke (this, args); if (args.Cancel) @@ -528,7 +528,7 @@ public class Buttons : Scenario _value = value; _number.Text = _value.ToString (); - ValueChanged?.Invoke (this, new (oldValue, _value)); + ValueChanged?.Invoke (this, new (ref _value)); } } @@ -542,7 +542,7 @@ public class Buttons : Scenario /// Fired when the value has changed. /// [CanBeNull] - public event EventHandler> ValueChanged; + public event EventHandler> ValueChanged; /// /// The number of digits to display. The will be resized to fit this number of characters plus the buttons. The default is 3. diff --git a/UICatalog/Scenarios/CharacterMap.cs b/UICatalog/Scenarios/CharacterMap.cs index 32d2c03ce..867795a75 100644 --- a/UICatalog/Scenarios/CharacterMap.cs +++ b/UICatalog/Scenarios/CharacterMap.cs @@ -77,19 +77,8 @@ public class CharacterMap : Scenario }; top.Add (_errorLabel); -#if TEXT_CHANGED_TO_JUMP - jumpEdit.TextChanged += JumpEdit_TextChanged; -#else jumpEdit.Accept += JumpEditOnAccept; - void JumpEditOnAccept (object sender, HandledEventArgs e) - { - JumpEdit_TextChanged (sender, new (jumpEdit.Text, jumpEdit.Text)); - - // Cancel the event to prevent ENTER from being handled elsewhere - e.Handled = true; - } -#endif _categoryList = new () { X = Pos.Right (_charMap), Y = Pos.Bottom (jumpLabel), Height = Dim.Fill () }; _categoryList.FullRowSelect = true; @@ -181,6 +170,83 @@ public class CharacterMap : Scenario Application.Run (top); top.Dispose (); Application.Shutdown (); + + return; + + void JumpEditOnAccept (object sender, HandledEventArgs e) + { + if (jumpEdit.Text.Length == 0) + { + return; + } + + uint result = 0; + + if (jumpEdit.Text.StartsWith ("U+", StringComparison.OrdinalIgnoreCase) || jumpEdit.Text.StartsWith ("\\u")) + { + try + { + result = uint.Parse (jumpEdit.Text [2..], NumberStyles.HexNumber); + } + catch (FormatException) + { + _errorLabel.Text = "Invalid hex value"; + + return; + } + } + else if (jumpEdit.Text.StartsWith ("0", StringComparison.OrdinalIgnoreCase) || jumpEdit.Text.StartsWith ("\\u")) + { + try + { + result = uint.Parse (jumpEdit.Text, NumberStyles.HexNumber); + } + catch (FormatException) + { + _errorLabel.Text = "Invalid hex value"; + + return; + } + } + else + { + try + { + result = uint.Parse (jumpEdit.Text, NumberStyles.Integer); + } + catch (FormatException) + { + _errorLabel.Text = "Invalid value"; + + return; + } + } + + if (result > RuneExtensions.MaxUnicodeCodePoint) + { + _errorLabel.Text = "Beyond maximum codepoint"; + + return; + } + + _errorLabel.Text = $"U+{result:x5}"; + + EnumerableTableSource table = (EnumerableTableSource)_categoryList.Table; + + _categoryList.SelectedRow = table.Data + .Select ((item, index) => new { item, index }) + .FirstOrDefault (x => x.item.Start <= result && x.item.End >= result) + ?.index + ?? -1; + _categoryList.EnsureSelectedCellIsVisible (); + + // Ensure the typed glyph is selected + _charMap.SelectedCodePoint = (int)result; + + + // Cancel the event to prevent ENTER from being handled elsewhere + e.Handled = true; + } } private void _categoryList_Initialized (object sender, EventArgs e) { _charMap.Width = Dim.Fill () - _categoryList.Width; } @@ -240,78 +306,6 @@ public class CharacterMap : Scenario return item; } - private void JumpEdit_TextChanged (object sender, CancelEventArgs e) - { - var jumpEdit = sender as TextField; - - if (jumpEdit.Text.Length == 0) - { - return; - } - - uint result = 0; - - if (jumpEdit.Text.StartsWith ("U+", StringComparison.OrdinalIgnoreCase) || jumpEdit.Text.StartsWith ("\\u")) - { - try - { - result = uint.Parse (jumpEdit.Text [2..], NumberStyles.HexNumber); - } - catch (FormatException) - { - _errorLabel.Text = "Invalid hex value"; - - return; - } - } - else if (jumpEdit.Text.StartsWith ("0", StringComparison.OrdinalIgnoreCase) || jumpEdit.Text.StartsWith ("\\u")) - { - try - { - result = uint.Parse (jumpEdit.Text, NumberStyles.HexNumber); - } - catch (FormatException) - { - _errorLabel.Text = "Invalid hex value"; - - return; - } - } - else - { - try - { - result = uint.Parse (jumpEdit.Text, NumberStyles.Integer); - } - catch (FormatException) - { - _errorLabel.Text = "Invalid value"; - - return; - } - } - - if (result > RuneExtensions.MaxUnicodeCodePoint) - { - _errorLabel.Text = "Beyond maximum codepoint"; - - return; - } - - _errorLabel.Text = $"U+{result:x5}"; - - EnumerableTableSource table = (EnumerableTableSource)_categoryList.Table; - - _categoryList.SelectedRow = table.Data - .Select ((item, index) => new { item, index }) - .FirstOrDefault (x => x.item.Start <= result && x.item.End >= result) - ?.index - ?? -1; - _categoryList.EnsureSelectedCellIsVisible (); - - // Ensure the typed glyph is selected - _charMap.SelectedCodePoint = (int)result; - } } internal class CharMap : View @@ -1005,7 +999,7 @@ internal class CharMap : View document.RootElement, new JsonSerializerOptions - { WriteIndented = true } + { WriteIndented = true } ); } diff --git a/UICatalog/Scenarios/CsvEditor.cs b/UICatalog/Scenarios/CsvEditor.cs index 2a05c4178..eda859932 100644 --- a/UICatalog/Scenarios/CsvEditor.cs +++ b/UICatalog/Scenarios/CsvEditor.cs @@ -565,7 +565,7 @@ public class CsvEditor : Scenario } } - private void SelectedCellLabel_TextChanged (object sender, CancelEventArgs e) + private void SelectedCellLabel_TextChanged (object sender, EventArgs e) { // if user is in the text control and editing the selected cell if (!_selectedCellTextField.HasFocus) diff --git a/UICatalog/Scenarios/ExpanderButton.cs b/UICatalog/Scenarios/ExpanderButton.cs index 5484b2065..2959d338a 100644 --- a/UICatalog/Scenarios/ExpanderButton.cs +++ b/UICatalog/Scenarios/ExpanderButton.cs @@ -133,15 +133,16 @@ public class ExpanderButton : Button /// Called when the orientation is changing. Invokes the event. /// + /// /// True of the event was cancelled. protected virtual bool OnCollapsedChanging (bool newValue) { - CancelEventArgs args = new (Collapsed, newValue); + CancelEventArgs args = new (ref _collapsed, ref newValue); CollapsedChanging?.Invoke (this, args); if (!args.Cancel) { - _collapsed = newValue; + _collapsed = args.NewValue; ExpandOrCollapse (_collapsed); diff --git a/UnitTests/Application/ApplicationTests.cs b/UnitTests/Application/ApplicationTests.cs index 4140b5da5..a7c1fc64a 100644 --- a/UnitTests/Application/ApplicationTests.cs +++ b/UnitTests/Application/ApplicationTests.cs @@ -295,9 +295,9 @@ public class ApplicationTests return; - void OnApplicationOnInitializedChanged (object s, CancelEventArgs a) + void OnApplicationOnInitializedChanged (object s, EventArgs a) { - if (a.NewValue) + if (a.CurrentValue) { initialized = true; } @@ -1151,9 +1151,9 @@ public class ApplicationTests return; - void OnApplicationOnInitializedChanged (object s, CancelEventArgs a) + void OnApplicationOnInitializedChanged (object s, EventArgs a) { - if (a.NewValue) + if (a.CurrentValue) { Application.Iteration += OnApplicationOnIteration; initialized = true; diff --git a/UnitTests/Application/KeyboardTests.cs b/UnitTests/Application/KeyboardTests.cs index c805587fb..3483b8396 100644 --- a/UnitTests/Application/KeyboardTests.cs +++ b/UnitTests/Application/KeyboardTests.cs @@ -129,10 +129,10 @@ public class KeyboardTests return; - void OnApplicationOnInitializedChanged (object s, CancelEventArgs a) + void OnApplicationOnInitializedChanged (object s, EventArgs a) { - _output.WriteLine ("OnApplicationOnInitializedChanged: {0}", a.NewValue); - if (a.NewValue) + _output.WriteLine ("OnApplicationOnInitializedChanged: {0}", a.CurrentValue); + if (a.CurrentValue) { Application.Iteration += OnApplicationOnIteration; initialized = true; diff --git a/UnitTests/Dialogs/WizardTests.cs b/UnitTests/Dialogs/WizardTests.cs index 65594c276..c8ef785cb 100644 --- a/UnitTests/Dialogs/WizardTests.cs +++ b/UnitTests/Dialogs/WizardTests.cs @@ -597,7 +597,7 @@ public class WizardTests () Assert.Equal (string.Empty, r.Title); var expected = string.Empty; - r.TitleChanged += (s, args) => { Assert.Equal (r.Title, args.NewValue); }; + r.TitleChanged += (s, args) => { Assert.Equal (r.Title, args.CurrentValue); }; expected = "title"; r.Title = expected; diff --git a/UnitTests/UICatalog/ScenarioTests.cs b/UnitTests/UICatalog/ScenarioTests.cs index 10b5abc80..22a9e25c6 100644 --- a/UnitTests/UICatalog/ScenarioTests.cs +++ b/UnitTests/UICatalog/ScenarioTests.cs @@ -73,9 +73,9 @@ public class ScenarioTests : TestsAllViews return; - void OnApplicationOnInitializedChanged (object s, CancelEventArgs a) + void OnApplicationOnInitializedChanged (object s, EventArgs a) { - if (a.NewValue) + if (a.CurrentValue) { Application.Iteration += OnApplicationOnIteration; initialized = true; diff --git a/UnitTests/View/Adornment/AdornmentTests.cs b/UnitTests/View/Adornment/AdornmentTests.cs index dacf8dd0f..e278fbd45 100644 --- a/UnitTests/View/Adornment/AdornmentTests.cs +++ b/UnitTests/View/Adornment/AdornmentTests.cs @@ -326,8 +326,7 @@ public class AdornmentTests (ITestOutputHelper output) adornment.ThicknessChanged += (s, e) => { raised = true; - Assert.Equal (Thickness.Empty, e.CurrentValue); - Assert.Equal (new Thickness (1, 2, 3, 4), e.NewValue); + Assert.Equal (new Thickness (1, 2, 3, 4), e.CurrentValue); Assert.Equal (new Thickness (1, 2, 3, 4), adornment.Thickness); }; adornment.Thickness = new Thickness (1, 2, 3, 4); diff --git a/UnitTests/View/TitleTests.cs b/UnitTests/View/TitleTests.cs index 4b21e42b9..ca154583e 100644 --- a/UnitTests/View/TitleTests.cs +++ b/UnitTests/View/TitleTests.cs @@ -53,8 +53,7 @@ public class TitleTests (ITestOutputHelper output) r.TitleChanged += (s, args) => { - Assert.Equal (expectedOld, args.CurrentValue); - Assert.Equal (r.Title, args.NewValue); + Assert.Equal (r.Title, args.CurrentValue); }; expected = "title"; diff --git a/UnitTests/Views/ComboBoxTests.cs b/UnitTests/Views/ComboBoxTests.cs index 09de946af..f74b6befc 100644 --- a/UnitTests/Views/ComboBoxTests.cs +++ b/UnitTests/Views/ComboBoxTests.cs @@ -828,6 +828,7 @@ Three ", Assert.Equal ("Tw", cb.Text); Assert.False (cb.IsShow); cb.SetSource (null); + Assert.False (cb.IsShow); Assert.False (cb.NewKeyDownEvent (Key.Enter)); Assert.True (cb.NewKeyDownEvent (Key.F4)); // with no source also expand empty Assert.True (cb.IsShow); diff --git a/UnitTests/Views/TextFieldTests.cs b/UnitTests/Views/TextFieldTests.cs index 90a4a45a0..e50617933 100644 --- a/UnitTests/Views/TextFieldTests.cs +++ b/UnitTests/Views/TextFieldTests.cs @@ -429,8 +429,11 @@ public class TextFieldTests (ITestOutputHelper output) var oldText = ""; var tf = new TextField { Width = 10, Text = "-1" }; - tf.TextChanging += (s, e) => newText = e.NewValue; - tf.TextChanged += (s, e) => oldText = e.CurrentValue; + tf.TextChanging += (s, e) => + { + newText = e.NewValue; + oldText = e.CurrentValue; + }; var top = new Toplevel (); top.Add (tf); @@ -1191,10 +1194,13 @@ public class TextFieldTests (ITestOutputHelper output) [TextFieldTestsAutoInitShutdown] public void TextChanged_Event () { - _textField.TextChanged += (s, e) => { Assert.Equal ("TAB to jump between text fields.", e.CurrentValue); }; + bool eventFired = false; + _textField.TextChanged += (s, e) => eventFired = true; _textField.Text = "changed"; + Assert.True (eventFired); Assert.Equal ("changed", _textField.Text); + } [Fact]