Browse Source

Refactored View command handling

Tig 10 months ago
parent
commit
1d633c2fa7

+ 1 - 1
Terminal.Gui/Input/Command.cs

@@ -6,7 +6,7 @@ namespace Terminal.Gui;
 /// <summary>Actions which can be performed by the application or bound to keys in a <see cref="View"/> control.</summary>
 public enum Command
 {
-    #region Default View Commands
+    #region Base View Commands
 
     /// <summary>Invoked when the HotKey for the View has been pressed.</summary>
     HotKey,

+ 0 - 13
Terminal.Gui/View/View.Keyboard.cs

@@ -13,19 +13,6 @@ public partial class View // Keyboard APIs
         KeyBindings = new (this);
         HotKeySpecifier = (Rune)'_';
         TitleTextFormatter.HotKeyChanged += TitleTextFormatter_HotKeyChanged;
-
-        // By default, the HotKey command sets the focus
-        AddCommand (Command.Select, () =>
-                                    {
-                                        SetFocus ();
-                                        return OnSelect ();
-                                    });
-
-        // By default, the HotKey command sets the focus
-        AddCommand (Command.HotKey, () => SetFocus ());
-
-        // By default, the Accept command raises the Accept event
-        AddCommand (Command.Accept, OnAccept);
     }
 
     /// <summary>

+ 213 - 84
Terminal.Gui/View/View.cs

@@ -108,19 +108,7 @@ namespace Terminal.Gui;
 
 public partial class View : Responder, ISupportInitializeNotification
 {
-    /// <summary>
-    ///     Cancelable event fired when the <see cref="Command.Select"/> command is invoked. Set
-    ///     <see cref="HandledEventArgs.Handled"/>
-    ///     to cancel the event.
-    /// </summary>
-    public event EventHandler<HandledEventArgs>? Select;
-
-    /// <summary>
-    ///     Cancelable event fired when the <see cref="Command.Accept"/> command is invoked. Set
-    ///     <see cref="HandledEventArgs.Handled"/>
-    ///     to cancel the event.
-    /// </summary>
-    public event EventHandler<HandledEventArgs>? Accept;
+    #region Constructors and Initialization
 
     /// <summary>Gets or sets arbitrary data for the view.</summary>
     /// <remarks>This property is not used internally.</remarks>
@@ -131,64 +119,6 @@ public partial class View : Responder, ISupportInitializeNotification
     /// <remarks>The id should be unique across all Views that share a SuperView.</remarks>
     public string Id { get; set; } = "";
 
-    /// <summary>Pretty prints the View</summary>
-    /// <returns></returns>
-    public override string ToString () { return $"{GetType ().Name}({Id}){Frame}"; }
-
-    /// <inheritdoc/>
-    protected override void Dispose (bool disposing)
-    {
-        LineCanvas.Dispose ();
-
-        DisposeKeyboard ();
-        DisposeAdornments ();
-
-        for (int i = InternalSubviews.Count - 1; i >= 0; i--)
-        {
-            View subview = InternalSubviews [i];
-            Remove (subview);
-            subview.Dispose ();
-        }
-
-        base.Dispose (disposing);
-        Debug.Assert (InternalSubviews.Count == 0);
-    }
-
-    /// <summary>
-    ///     Called when the <see cref="Command.Accept"/> command is invoked. Raises <see cref="Accept"/>
-    ///     event.
-    /// </summary>
-    /// <returns>
-    ///     If <see langword="true"/> the event was canceled. If <see langword="false"/> the event was raised but not canceled.
-    ///     If <see langword="null"/> no event was raised.
-    /// </returns>
-    protected bool? OnAccept ()
-    {
-        var args = new HandledEventArgs ();
-        Accept?.Invoke (this, args);
-
-        return Accept is null ? null : args.Handled;
-    }
-
-
-    /// <summary>
-    ///     Called when the <see cref="Command.Accept"/> command is invoked. Raises <see cref="Accept"/>
-    ///     event.
-    /// </summary>
-    /// <returns>
-    ///     If <see langword="true"/> the event was canceled. If <see langword="false"/> the event was raised but not canceled.
-    ///     If <see langword="null"/> no event was raised.
-    /// </returns>
-    protected bool? OnSelect ()
-    {
-        var args = new HandledEventArgs ();
-        Select?.Invoke (this, args);
-
-        return Select is null ? null : args.Handled;
-    }
-
-    #region Constructors and Initialization
-
     /// <summary>
     ///     Points to the current driver in use by the view, it is a convenience property for simplifying the development
     ///     of new views.
@@ -205,10 +135,21 @@ public partial class View : Responder, ISupportInitializeNotification
     public View ()
     {
         SetupAdornments ();
+
         SetupKeyboard ();
 
         //SetupMouse ();
+
         SetupText ();
+
+        // By default, the Select command does nothing
+        AddCommand (Command.Select, RaiseSelectEvent);
+
+        // By default, the HotKey command sets the focus
+        AddCommand (Command.HotKey, RaiseHotKeyEvent);
+
+        // By default, the Accept command sets the focus
+        AddCommand (Command.Accept, RaiseAcceptEvent);
     }
 
     /// <summary>
@@ -313,6 +254,161 @@ public partial class View : Responder, ISupportInitializeNotification
 
     #endregion Constructors and Initialization
 
+    #region Base Command Events
+
+    /// <summary>
+    ///     Called when the <see cref="Command.Accept"/> command is invoked. Raises <see cref="Accept"/>
+    ///     event.
+    /// </summary>
+    /// <returns>
+    ///     If <see langword="true"/> the event was canceled. If <see langword="false"/> the event was raised but not canceled.
+    ///     If <see langword="null"/> no event was raised.
+    /// </returns>
+    protected bool? RaiseAcceptEvent ()
+    {
+        HandledEventArgs args = new ();
+
+        // Best practice is to invoke the virtual method first.
+        // This allows derived classes to handle the event and potentially cancel it.
+        if (OnAccept (args) || args.Handled)
+        {
+            return true;
+        }
+
+        // If the event is not canceled by the virtual method, raise the event to notify any external subscribers.
+        Accept?.Invoke (this, args);
+
+        return Accept is null ? null : args.Handled;
+    }
+
+    /// <summary>
+    ///     Called when the <see cref="Command.Accept"/> command is received. Set <see cref="HandledEventArgs.Handled"/> to
+    ///     <see langword="true"/> to stop processing.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         The base implementation calls <see cref="SetFocus"/>.
+    ///     </para>
+    /// </remarks>
+    /// <param name="args"></param>
+    /// <returns><see langword="true"/> to stop processing.</returns>
+    protected virtual bool OnAccept (HandledEventArgs args)
+    {
+        SetFocus ();
+
+        return false;
+    }
+
+    /// <summary>
+    ///     Cancelable event raised when the <see cref="Command.Accept"/> command is invoked. Set
+    ///     <see cref="HandledEventArgs.Handled"/>
+    ///     to cancel the event.
+    /// </summary>
+    public event EventHandler<HandledEventArgs>? Accept;
+
+    /// <summary>
+    ///     Called when the <see cref="Command.Select"/> command is invoked. Raises <see cref="Select"/>
+    ///     event.
+    /// </summary>
+    /// <returns>
+    ///     If <see langword="true"/> the event was canceled. If <see langword="false"/> the event was raised but not canceled.
+    ///     If <see langword="null"/> no event was raised.
+    /// </returns>
+    protected bool? RaiseSelectEvent ()
+    {
+        HandledEventArgs args = new ();
+
+        // Best practice is to invoke the virtual method first.
+        // This allows derived classes to handle the event and potentially cancel it.
+        if (OnSelect (args) || args.Handled)
+        {
+            return true;
+        }
+
+        // If the event is not canceled by the virtual method, raise the event to notify any external subscribers.
+        Select?.Invoke (this, args);
+
+        return Select is null ? null : args.Handled;
+    }
+
+    /// <summary>
+    ///     Called when the <see cref="Command.Select"/> command is received. Set <see cref="HandledEventArgs.Handled"/> to
+    ///     <see langword="true"/> to stop processing.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         The base implementation calls <see cref="SetFocus"/>.
+    ///     </para>
+    /// </remarks>
+    /// <param name="args"></param>
+    /// <returns><see langword="true"/> to stop processing.</returns>
+    protected virtual bool OnSelect (HandledEventArgs args)
+    {
+        SetFocus ();
+
+        return false;
+    }
+
+    /// <summary>
+    ///     Cancelable event raised when the <see cref="Command.Select"/> command is invoked. Set
+    ///     <see cref="HandledEventArgs.Handled"/>
+    ///     to cancel the event.
+    /// </summary>
+    public event EventHandler<HandledEventArgs>? Select;
+
+
+    /// <summary>
+    ///     Called when the <see cref="Command.HotKey"/> command is invoked. Raises <see cref="HotKey"/>
+    ///     event.
+    /// </summary>
+    /// <returns>
+    ///     If <see langword="true"/> the event was canceled. If <see langword="false"/> the event was raised but not canceled.
+    ///     If <see langword="null"/> no event was raised.
+    /// </returns>
+    protected bool? RaiseHotKeyEvent ()
+    {
+        HandledEventArgs args = new ();
+
+        // Best practice is to invoke the virtual method first.
+        // This allows derived classes to handle the event and potentially cancel it.
+        if (OnHotKey (args) || args.Handled)
+        {
+            return true;
+        }
+
+        // If the event is not canceled by the virtual method, raise the event to notify any external subscribers.
+        HotKeyCommand?.Invoke (this, args);
+
+        return HotKeyCommand is null ? null : args.Handled;
+    }
+
+    /// <summary>
+    ///     Called when the <see cref="Command.HotKey"/> command is received. Set <see cref="HandledEventArgs.Handled"/> to
+    ///     <see langword="true"/> to stop processing.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         The base implementation calls <see cref="SetFocus"/>.
+    ///     </para>
+    /// </remarks>
+    /// <param name="args"></param>
+    /// <returns><see langword="true"/> to stop processing.</returns>
+    protected virtual bool OnHotKey (HandledEventArgs args)
+    {
+        SetFocus ();
+
+        return false;
+    }
+
+    /// <summary>
+    ///     Cancelable event raised when the <see cref="Command.HotKey"/> command is invoked. Set
+    ///     <see cref="HandledEventArgs.Handled"/>
+    ///     to cancel the event.
+    /// </summary>
+    public event EventHandler<HandledEventArgs>? HotKeyCommand;
+
+    #endregion Base Command Events
+
     #region Visibility
 
     private bool _enabled = true;
@@ -338,7 +434,10 @@ public partial class View : Responder, ISupportInitializeNotification
                 HasFocus = false;
             }
 
-            if (_enabled && CanFocus && Visible && !HasFocus
+            if (_enabled
+                && CanFocus
+                && Visible
+                && !HasFocus
                 && SuperView is null or { HasFocus: true, Visible: true, Enabled: true, Focused: null })
             {
                 SetFocus ();
@@ -397,6 +496,7 @@ public partial class View : Responder, ISupportInitializeNotification
 
             CancelEventArgs<bool> args = new (in _visible, ref value);
             VisibleChanging?.Invoke (this, args);
+
             if (args.Cancel)
             {
                 return;
@@ -412,7 +512,10 @@ public partial class View : Responder, ISupportInitializeNotification
                 }
             }
 
-            if (_visible && CanFocus && Enabled && !HasFocus
+            if (_visible
+                && CanFocus
+                && Enabled
+                && !HasFocus
                 && SuperView is null or { HasFocus: true, Visible: true, Enabled: true, Focused: null })
             {
                 SetFocus ();
@@ -428,7 +531,10 @@ public partial class View : Responder, ISupportInitializeNotification
     /// <summary>Called when <see cref="Visible"/> is changing. Can be cancelled by returning <see langword="true"/>.</summary>
     protected virtual bool OnVisibleChanging () { return false; }
 
-    /// <summary>Raised when the <see cref="Visible"/> value is being changed. Can be cancelled by setting Cancel to <see langword="true"/>.</summary>
+    /// <summary>
+    ///     Raised when the <see cref="Visible"/> value is being changed. Can be cancelled by setting Cancel to
+    ///     <see langword="true"/>.
+    /// </summary>
     public event EventHandler<CancelEventArgs<bool>>? VisibleChanging;
 
     /// <summary>Called when <see cref="Visible"/> has changed.</summary>
@@ -442,7 +548,10 @@ public partial class View : Responder, ISupportInitializeNotification
     ///     INTERNAL Indicates whether all views up the Superview hierarchy are visible.
     /// </summary>
     /// <param name="view">The view to test.</param>
-    /// <returns> <see langword="false"/> if `view.Visible` is  <see langword="false"/> or any Superview is not visible, <see langword="true"/> otherwise.</returns>
+    /// <returns>
+    ///     <see langword="false"/> if `view.Visible` is  <see langword="false"/> or any Superview is not visible,
+    ///     <see langword="true"/> otherwise.
+    /// </returns>
     internal static bool CanBeVisible (View view)
     {
         if (!view.Visible)
@@ -537,18 +646,15 @@ public partial class View : Responder, ISupportInitializeNotification
     private void SetTitleTextFormatterSize ()
     {
         TitleTextFormatter.ConstrainToSize = new (
-                                       TextFormatter.GetWidestLineLength (TitleTextFormatter.Text)
-                                       - (TitleTextFormatter.Text?.Contains ((char)HotKeySpecifier.Value) == true
-                                              ? Math.Max (HotKeySpecifier.GetColumns (), 0)
-                                              : 0),
-                                       1);
+                                                  TextFormatter.GetWidestLineLength (TitleTextFormatter.Text)
+                                                  - (TitleTextFormatter.Text?.Contains ((char)HotKeySpecifier.Value) == true
+                                                         ? Math.Max (HotKeySpecifier.GetColumns (), 0)
+                                                         : 0),
+                                                  1);
     }
 
     /// <summary>Called when the <see cref="View.Title"/> has been changed. Invokes the <see cref="TitleChanged"/> event.</summary>
-    protected void OnTitleChanged ()
-    {
-        TitleChanged?.Invoke (this, new (in _title));
-    }
+    protected void OnTitleChanged () { TitleChanged?.Invoke (this, new (in _title)); }
 
     /// <summary>
     ///     Called before the <see cref="View.Title"/> changes. Invokes the <see cref="TitleChanging"/> event, which can
@@ -574,4 +680,27 @@ public partial class View : Responder, ISupportInitializeNotification
     public event EventHandler<CancelEventArgs<string>>? TitleChanging;
 
     #endregion
+
+    /// <summary>Pretty prints the View</summary>
+    /// <returns></returns>
+    public override string ToString () { return $"{GetType ().Name}({Id}){Frame}"; }
+
+    /// <inheritdoc/>
+    protected override void Dispose (bool disposing)
+    {
+        LineCanvas.Dispose ();
+
+        DisposeKeyboard ();
+        DisposeAdornments ();
+
+        for (int i = InternalSubviews.Count - 1; i >= 0; i--)
+        {
+            View subview = InternalSubviews [i];
+            Remove (subview);
+            subview.Dispose ();
+        }
+
+        base.Dispose (disposing);
+        Debug.Assert (InternalSubviews.Count == 0);
+    }
 }

+ 1 - 1
Terminal.Gui/Views/Button.cs

@@ -69,7 +69,7 @@ public class Button : View, IDesignable
                     {
                         bool cachedIsDefault = IsDefault; // Supports "Swap Default" in Buttons scenario
 
-                        bool? handled = OnAccept ();
+                        bool? handled = RaiseAcceptEvent ();
 
                         if (handled == true)
                         {

+ 1 - 1
Terminal.Gui/Views/CheckBox.cs

@@ -165,7 +165,7 @@ public class CheckBox : View
         }
 
         // By default, Command.Accept calls OnAccept, so we need to call it here to ensure that the Accept event is fired.
-        if (OnAccept () == true)
+        if (RaiseAcceptEvent () == true)
         {
             return true;
         }

+ 42 - 26
Terminal.Gui/Views/ListView.cs

@@ -123,22 +123,46 @@ public class ListView : View, IDesignable
 
         // Things this view knows how to do
         // 
-        // BUGBUG: SHould return false if selectokn doesn't change (to support nav to next view)
+        // BUGBUG: Should return false if selection doesn't change (to support nav to next view)
         AddCommand (Command.Up, () => MoveUp ());
-        // BUGBUG: SHould return false if selectokn doesn't change (to support nav to next view)
+        // BUGBUG: Should return false if selection doesn't change (to support nav to next view)
         AddCommand (Command.Down, () => MoveDown ());
+
         AddCommand (Command.ScrollUp, () => ScrollVertical (-1));
         AddCommand (Command.ScrollDown, () => ScrollVertical (1));
         AddCommand (Command.PageUp, () => MovePageUp ());
         AddCommand (Command.PageDown, () => MovePageDown ());
         AddCommand (Command.Start, () => MoveHome ());
         AddCommand (Command.End, () => MoveEnd ());
+        AddCommand (Command.ScrollLeft, () => ScrollHorizontal (-1));
+        AddCommand (Command.ScrollRight, () => ScrollHorizontal (1));
+
         AddCommand (Command.Accept, () => OnOpenSelectedItem ());
         AddCommand (Command.Open, () => OnOpenSelectedItem ());
-        AddCommand (Command.Select, () => MarkUnmarkRow ());
+        AddCommand (Command.Select, () =>
+                                    {
+                                        if (RaiseSelectEvent () == true)
+                                        {
+                                            return true;
+
+                                        }
+                                        if (_allowsMarking)
+                                        {
+                                            return MarkUnmarkSelectedItem ();
+                                        }
+
+                                        return false;
+                                    });
+        AddCommand(Command.HotKey, () =>
+                                   {
+                                       if (SelectedItem == -1)
+                                       {
+                                           SelectedItem = 0;
+                                       }
+
+                                       return !SetFocus ();
+                                   });
 
-        AddCommand (Command.ScrollLeft, () => ScrollHorizontal (-1));
-        AddCommand (Command.ScrollRight, () => ScrollHorizontal (1));
 
         // Default keybindings for all ListViews
         KeyBindings.Add (Key.CursorUp, Command.Up);
@@ -156,6 +180,8 @@ public class ListView : View, IDesignable
 
         KeyBindings.Add (Key.End, Command.End);
 
+        KeyBindings.Add (Key.Space, Command.Select);
+
         KeyBindings.Add (Key.Enter, Command.Open);
     }
 
@@ -163,7 +189,7 @@ public class ListView : View, IDesignable
     /// <value>Set to <see langword="true"/> to allow marking elements of the list.</value>
     /// <remarks>
     ///     If set to <see langword="true"/>, <see cref="ListView"/> will render items marked items with "[x]", and
-    ///     unmarked items with "[ ]" spaces. SPACE key will toggle marking. The default is <see langword="false"/>.
+    ///     unmarked items with "[ ]". SPACE key will toggle marking. The default is <see langword="false"/>.
     /// </remarks>
     public bool AllowsMarking
     {
@@ -171,16 +197,6 @@ public class ListView : View, IDesignable
         set
         {
             _allowsMarking = value;
-
-            if (_allowsMarking)
-            {
-                KeyBindings.Add (Key.Space, Command.Select);
-            }
-            else
-            {
-                KeyBindings.Remove (Key.Space);
-            }
-
             SetNeedsDisplay ();
         }
     }
@@ -334,10 +350,10 @@ public class ListView : View, IDesignable
 
     /// <summary>
     ///     If <see cref="AllowsMarking"/> and <see cref="AllowsMultipleSelection"/> are both <see langword="true"/>,
-    ///     unmarks all marked items other than the currently selected.
+    ///     unmarks all marked items other than <see cref="SelectedItem"/>.
     /// </summary>
     /// <returns><see langword="true"/> if unmarking was successful.</returns>
-    public virtual bool AllowsAll ()
+    public bool UnmarkAllButSelected ()
     {
         if (!_allowsMarking)
         {
@@ -385,16 +401,18 @@ public class ListView : View, IDesignable
 
     /// <summary>Marks the <see cref="SelectedItem"/> if it is not already marked.</summary>
     /// <returns><see langword="true"/> if the <see cref="SelectedItem"/> was marked.</returns>
-    public virtual bool MarkUnmarkRow ()
+    public bool MarkUnmarkSelectedItem ()
     {
-        if (AllowsAll ())
+        if (UnmarkAllButSelected ())
         {
             Source.SetMark (SelectedItem, !Source.IsMarked (SelectedItem));
             SetNeedsDisplay ();
 
-            return true;
+            return Source.IsMarked (SelectedItem);
         }
 
+        // BUGBUG: Shouldn't this retrn Source.IsMarked (SelectedItem)
+
         return false;
     }
 
@@ -458,11 +476,8 @@ public class ListView : View, IDesignable
 
         _selected = Viewport.Y + me.Position.Y;
 
-        if (AllowsAll ())
+        if (!MarkUnmarkSelectedItem())
         {
-            Source.SetMark (SelectedItem, !Source.IsMarked (SelectedItem));
-            SetNeedsDisplay ();
-
             return true;
         }
 
@@ -760,7 +775,7 @@ public class ListView : View, IDesignable
         object value = _source.ToList () [_selected];
 
         // By default, Command.Accept calls OnAccept, so we need to call it here to ensure that the event is fired.
-        if (OnAccept () == true)
+        if (RaiseAcceptEvent () == true)
         {
             return true;
         }
@@ -796,6 +811,7 @@ public class ListView : View, IDesignable
     /// <param name="rowEventArgs"></param>
     public virtual void OnRowRender (ListViewRowEventArgs rowEventArgs) { RowRender?.Invoke (this, rowEventArgs); }
 
+    // TODO: Use standard event model
     /// <summary>Invokes the <see cref="SelectedItemChanged"/> event if it is defined.</summary>
     /// <returns></returns>
     public virtual bool OnSelectedChanged ()

+ 1 - 1
Terminal.Gui/Views/Menuv2.cs

@@ -76,7 +76,7 @@ public class Menuv2 : Bar
 
                 if (!e.Handled)
                 {
-                    OnAccept ();
+                    RaiseAcceptEvent ();
                 }
             }
         }

+ 2 - 2
Terminal.Gui/Views/RadioGroup.cs

@@ -97,7 +97,7 @@ public class RadioGroup : View, IDesignable, IOrientation
                     {
                         SelectedItem = _cursor;
 
-                        return OnAccept () is false;
+                        return RaiseAcceptEvent () is false;
                     }
                    );
         AddCommand (
@@ -108,7 +108,7 @@ public class RadioGroup : View, IDesignable, IOrientation
                         {
                             SelectedItem = (int)ctx.KeyBinding?.Context!;
 
-                            return OnSelect () is true or null;
+                            return RaiseSelectEvent () is true or null;
                         }
 
                         return !SetFocus ();

+ 4 - 4
Terminal.Gui/Views/Shortcut.cs

@@ -711,12 +711,12 @@ public class Shortcut : View, IOrientation, IDesignable
         switch (ctx.KeyBinding?.Scope)
         {
             case KeyBindingScope.Application:
-                cancel = base.OnAccept () == true;
+                cancel = base.RaiseAcceptEvent () == true;
 
                 break;
 
             case KeyBindingScope.Focused:
-                base.OnAccept ();
+                base.RaiseAcceptEvent ();
 
                 // cancel if we're focused
                 cancel = true;
@@ -728,7 +728,7 @@ public class Shortcut : View, IOrientation, IDesignable
                 //{
                 //    return true;
                 //}
-                cancel = base.OnAccept () == true;
+                cancel = base.RaiseAcceptEvent () == true;
 
                 if (CanFocus)
                 {
@@ -740,7 +740,7 @@ public class Shortcut : View, IOrientation, IDesignable
 
             default:
                 // Mouse
-                cancel = base.OnAccept () == true;
+                cancel = base.RaiseAcceptEvent () == true;
 
                 break;
         }

+ 1 - 1
Terminal.Gui/Views/Slider.cs

@@ -1781,7 +1781,7 @@ public class Slider<T> : View, IOrientation
     {
         SetFocusedOption ();
 
-        return OnAccept () == true;
+        return RaiseAcceptEvent () == true;
     }
 
     internal bool MovePlus ()

+ 1 - 1
Terminal.Gui/Views/TextField.cs

@@ -327,7 +327,7 @@ public class TextField : View
 
         // By Default pressing ENTER should be ignored (OnAccept will return false or null). Only cancel if the
         // event was fired and set Cancel = true.
-        AddCommand (Command.Accept, () => OnAccept () == false);
+        AddCommand (Command.Accept, () => RaiseAcceptEvent () == false);
 
         // Default keybindings for this view
         // We follow this as closely as possible: https://en.wikipedia.org/wiki/Table_of_keyboard_shortcuts

+ 1 - 1
Terminal.Gui/Views/TextView.cs

@@ -6046,7 +6046,7 @@ public class TextView : View
         {
             // By Default pressing ENTER should be ignored (OnAccept will return false or null). Only cancel if the
             // event was fired and set Cancel = true.
-            return OnAccept () == false;
+            return RaiseAcceptEvent () == false;
         }
 
         SetWrapModel ();

+ 1 - 1
Terminal.Gui/Views/TreeView/TreeView.cs

@@ -444,7 +444,7 @@ public class TreeView<T> : View, ITreeView where T : class
     public bool? ActivateSelectedObjectIfAny ()
     {
         // By default, Command.Accept calls OnAccept, so we need to call it here to ensure that the event is fired.
-        if (OnAccept () == true)
+        if (RaiseAcceptEvent () == true)
         {
             return true;
         }

+ 51 - 13
UICatalog/Scenarios/ListViewWithSelection.cs

@@ -1,7 +1,9 @@
-using System.Collections;
+using System;
+using System.Collections;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Collections.Specialized;
+using System.ComponentModel;
 using System.Text;
 using JetBrains.Annotations;
 using Terminal.Gui;
@@ -20,6 +22,10 @@ public class ListViewWithSelection : Scenario
     private ObservableCollection<Scenario> _scenarios;
     private Window _appWindow;
 
+
+    private ObservableCollection<string> _eventList = new ();
+    private ListView _eventListView;
+
     /// <inheritdoc />
     public override void Main ()
     {
@@ -32,13 +38,13 @@ public class ListViewWithSelection : Scenario
 
         _scenarios = GetScenarios ();
 
-        _customRenderCB = new CheckBox { X = 0, Y = 0, Text = "Use custom rendering" };
+        _customRenderCB = new CheckBox { X = 0, Y = 0, Text = "Use custom _rendering" };
         _appWindow.Add (_customRenderCB);
         _customRenderCB.CheckedStateChanging += _customRenderCB_Toggle;
 
         _allowMarkingCB = new CheckBox
         {
-            X = Pos.Right (_customRenderCB) + 1, Y = 0, Text = "Allow Marking", AllowCheckStateNone = false
+            X = Pos.Right (_customRenderCB) + 1, Y = 0, Text = "Allow _Marking", AllowCheckStateNone = false
         };
         _appWindow.Add (_allowMarkingCB);
         _allowMarkingCB.CheckedStateChanging += AllowMarkingCB_Toggle;
@@ -48,22 +54,23 @@ public class ListViewWithSelection : Scenario
             X = Pos.Right (_allowMarkingCB) + 1,
             Y = 0,
             Visible = _allowMarkingCB.CheckedState == CheckState.Checked,
-            Text = "Allow Multi-Select"
+            Text = "Allow Multi-_Select"
         };
         _appWindow.Add (_allowMultipleCB);
         _allowMultipleCB.CheckedStateChanging += AllowMultipleCB_Toggle;
 
         _listView = new ListView
         {
-            X = 1,
-            Y = 2,
+            Title = "_ListView",
+            X = 0,
+            Y = Pos.Bottom(_allowMarkingCB),
             Height = Dim.Fill (),
-            Width = Dim.Fill (1),
+            Width = Dim.Func (() => _listView?.MaxLength ?? 10),
 
-            //ColorScheme = Colors.ColorSchemes ["TopLevel"],
             AllowsMarking = false,
             AllowsMultipleSelection = false
         };
+        _listView.Border.Thickness = new Thickness (0, 1, 0, 0);
         _listView.RowRender += ListView_RowRender;
         _appWindow.Add (_listView);
 
@@ -104,15 +111,46 @@ public class ListViewWithSelection : Scenario
 
         _listView.SetSource (_scenarios);
 
-        var k = "Keep Content Always In Viewport";
+        var k = "_Keep Content Always In Viewport";
 
         var keepCheckBox = new CheckBox
         {
-            X = Pos.AnchorEnd (k.Length + 3), Y = 0, Text = k, CheckedState = scrollBar.AutoHideScrollBars ? CheckState.Checked : CheckState.UnChecked
+            X = Pos.Right(_allowMultipleCB) + 1,
+            Y = 0, 
+            Text = k, 
+            CheckedState = scrollBar.AutoHideScrollBars ? CheckState.Checked : CheckState.UnChecked
         };
         keepCheckBox.CheckedStateChanging += (s, e) => scrollBar.KeepContentAlwaysInViewport = e.NewValue == CheckState.Checked;
         _appWindow.Add (keepCheckBox);
 
+        _eventList = new ();
+
+        _eventListView = new ListView
+        {
+            X = Pos.Right (_listView) + 1,
+            Y = Pos.Top (_listView),
+            Width = Dim.Fill (),
+            Height = Dim.Fill (),
+            Source = new ListWrapper<string> (_eventList)
+        };
+        _eventListView.ColorScheme = Colors.ColorSchemes ["TopLevel"];
+        _appWindow.Add (_eventListView);
+
+        _listView.SelectedItemChanged += (s, a) => LogEvent (s as View, a, "SelectedItemChanged");
+        _listView.OpenSelectedItem += (s, a) => LogEvent (s as View, a, "OpenSelectedItem");
+        _listView.CollectionChanged += (s, a) => LogEvent (s as View, a, "CollectionChanged");
+        _listView.Accept += (s, a) => LogEvent (s as View, a, "Accept");
+        _listView.Select += (s, a) => LogEvent (s as View, a, "Select");
+
+        bool? LogEvent (View sender, EventArgs args, string message)
+        {
+            var msg = $"{message,-7}: {args}";
+            _eventList.Add (msg);
+            _eventListView.MoveDown ();
+
+            return null;
+        }
+
         Application.Run (_appWindow);
         _appWindow.Dispose ();
         Application.Shutdown ();
@@ -154,18 +192,18 @@ public class ListViewWithSelection : Scenario
 
         if (_listView.AllowsMarking && _listView.Source.IsMarked (obj.Row))
         {
-            obj.RowAttribute = new Attribute (Color.BrightRed, Color.BrightYellow);
+            obj.RowAttribute = new Attribute (Color.Black, Color.White);
 
             return;
         }
 
         if (obj.Row % 2 == 0)
         {
-            obj.RowAttribute = new Attribute (Color.BrightGreen, Color.Magenta);
+            obj.RowAttribute = new Attribute (Color.Green, Color.Black);
         }
         else
         {
-            obj.RowAttribute = new Attribute (Color.BrightMagenta, Color.Green);
+            obj.RowAttribute = new Attribute (Color.Black, Color.Green);
         }
     }