Sfoglia il codice sorgente

Merge pull request #3376 from tig/v2_2144-3D-effect

Fixes #2114. Adds `View.ShadowStyle` - Enabled but not turned on by default
Tig 1 anno fa
parent
commit
61699d6af5
44 ha cambiato i file con 1469 aggiunte e 377 eliminazioni
  1. 1 0
      Terminal.Gui/Application/Application.cs
  2. 2 0
      Terminal.Gui/Configuration/ConfigurationManager.cs
  3. 5 4
      Terminal.Gui/Drawing/Cell.cs
  4. 21 0
      Terminal.Gui/Drawing/Color.cs
  5. 20 0
      Terminal.Gui/Drawing/Glyphs.cs
  6. 2 2
      Terminal.Gui/Drawing/LineCanvas.cs
  7. 4 0
      Terminal.Gui/Resources/config.json
  8. 184 3
      Terminal.Gui/View/Adornment/Margin.cs
  9. 22 0
      Terminal.Gui/View/Adornment/ShadowStyle.cs
  10. 139 0
      Terminal.Gui/View/Adornment/ShadowView.cs
  11. 27 0
      Terminal.Gui/View/ViewAdornments.cs
  12. 16 10
      Terminal.Gui/View/ViewDrawing.cs
  13. 11 4
      Terminal.Gui/View/ViewEventArgs.cs
  14. 17 3
      Terminal.Gui/View/ViewMouse.cs
  15. 2 2
      Terminal.Gui/View/ViewSubViews.cs
  16. 15 2
      Terminal.Gui/Views/Button.cs
  17. 4 0
      Terminal.Gui/Views/ColorPicker.cs
  18. 4 2
      Terminal.Gui/Views/DatePicker.cs
  19. 17 6
      Terminal.Gui/Views/Dialog.cs
  20. 5 2
      Terminal.Gui/Views/MessageBox.cs
  21. 271 270
      Terminal.Gui/Views/Shortcut.cs
  22. 9 0
      Terminal.Gui/Views/Window.cs
  23. 132 1
      UICatalog/Resources/config.json
  24. 3 4
      UICatalog/Scenarios/Adornments.cs
  25. 1 0
      UICatalog/Scenarios/Bars.cs
  26. 1 0
      UICatalog/Scenarios/BorderEditor.cs
  27. 6 7
      UICatalog/Scenarios/Buttons.cs
  28. 1 0
      UICatalog/Scenarios/ExpanderButton.cs
  29. 2 2
      UICatalog/Scenarios/HotKeys.cs
  30. 7 4
      UICatalog/Scenarios/LineDrawing.cs
  31. 36 0
      UICatalog/Scenarios/MarginEditor.cs
  32. 2 2
      UICatalog/Scenarios/PosAlignDemo.cs
  33. 65 0
      UICatalog/Scenarios/ShadowStyles.cs
  34. 13 2
      UICatalog/Scenarios/Shortcuts.cs
  35. 28 14
      UICatalog/Scenarios/Sliders.cs
  36. 5 3
      UICatalog/UICatalog.cs
  37. 0 9
      UnitTests/Configuration/ConfigurationMangerTests.cs
  38. 0 6
      UnitTests/Configuration/SettingsScopeTests.cs
  39. 98 0
      UnitTests/View/Adornment/ShadowStyletests.cs
  40. 1 1
      UnitTests/View/NeedsDisplayTests.cs
  41. 6 4
      UnitTests/View/ViewTests.cs
  42. 1 1
      UnitTests/Views/LabelTests.cs
  43. 258 2
      UnitTests/Views/ShortcutTests.cs
  44. 5 5
      UnitTests/Views/ToplevelTests.cs

+ 1 - 0
Terminal.Gui/Application/Application.cs

@@ -973,6 +973,7 @@ public static partial class Application
 
         if (state.Toplevel.NeedsDisplay || state.Toplevel.SubViewNeedsDisplay || state.Toplevel.LayoutNeeded || OverlappedChildNeedsDisplay ())
         {
+            state.Toplevel.SetNeedsDisplay();
             state.Toplevel.Draw ();
             Driver.UpdateScreen ();
 

+ 2 - 0
Terminal.Gui/Configuration/ConfigurationManager.cs

@@ -4,6 +4,7 @@ using System.Collections;
 using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
 using System.Reflection;
+using System.Runtime.Versioning;
 using System.Text.Encodings.Web;
 using System.Text.Json;
 using System.Text.Json.Serialization;
@@ -49,6 +50,7 @@ namespace Terminal.Gui;
 ///         Lowest Precedence.
 ///     </para>
 /// </summary>
+[ComponentGuarantees (ComponentGuaranteesOptions.None)]
 public static class ConfigurationManager
 {
     /// <summary>

+ 5 - 4
Terminal.Gui/Drawing/Cell.cs

@@ -4,18 +4,19 @@
 ///     Represents a single row/column in a Terminal.Gui rendering surface (e.g. <see cref="LineCanvas"/> and
 ///     <see cref="ConsoleDriver"/>).
 /// </summary>
-public class Cell
+public record struct Cell ()
 {
-    private Rune _rune;
 
     /// <summary>The attributes to use when drawing the Glyph.</summary>
-    public Attribute? Attribute { get; set; }
+    public Attribute? Attribute { get; set; } = null;
 
     /// <summary>
     ///     Gets or sets a value indicating whether this <see cref="T:Terminal.Gui.Cell"/> has been modified since the
     ///     last time it was drawn.
     /// </summary>
-    public bool IsDirty { get; set; }
+    public bool IsDirty { get; set; } = false;
+
+    private Rune _rune = default;
 
     /// <summary>The character to display. If <see cref="Rune"/> is <see langword="null"/>, then <see cref="Rune"/> is ignored.</summary>
     public Rune Rune

+ 21 - 0
Terminal.Gui/Drawing/Color.cs

@@ -259,6 +259,27 @@ public readonly partial record struct Color : ISpanParsable<Color>, IUtf8SpanPar
 
     }
 
+    /// <summary>
+    /// Gets a color that is the same hue as the current color, but with a different lightness.
+    /// </summary>
+    /// <returns></returns>
+    public Color GetDarkerColor ()
+    {
+        // TODO: This is a temporary implementation; just enough to show how it could work. 
+        var hsl = ColorHelper.ColorConverter.RgbToHsl (new RGB (R, G, B));
+
+        var amount = .3;
+        if (hsl.L <= 5)
+        {
+            return DarkGray;
+        }
+        hsl.L = (byte)(hsl.L * amount);
+
+        var rgb = ColorHelper.ColorConverter.HslToRgb (hsl);
+        return new (rgb.R, rgb.G, rgb.B);
+
+    }
+
     #region Legacy Color Names
 
     /// <summary>The black color.</summary>

+ 20 - 0
Terminal.Gui/Drawing/Glyphs.cs

@@ -437,4 +437,24 @@ public class GlyphDefinitions
     public Rune CrossHv { get; set; } = (Rune)'╋';
 
     #endregion
+
+    #region ----------------- ShadowStyle -----------------
+
+
+    /// <summary>Shadow - Vertical Start - Left Half Block - ▌ U+0258c</summary>
+    public Rune ShadowVerticalStart { get; set; } =  (Rune)'▌'; // Half: '\u2596'  ▖;
+
+    /// <summary>Shadow - Vertical - Left Half Block - ▌ U+0258c</summary>
+    public Rune ShadowVertical { get; set; } = (Rune)'▌';
+
+    /// <summary>Shadow - Horizontal Start - Upper Half Block - ▀ U+02580</summary>
+    public Rune ShadowHorizontalStart { get; set; } = (Rune)'▀'; // Half: ▝ U+0259d;
+
+    /// <summary>Shadow - Horizontal - Upper Half Block - ▀ U+02580</summary>
+    public Rune ShadowHorizontal { get; set; } = (Rune)'▀';
+
+    /// <summary>Shadow - Horizontal End - Quadrant Upper Left - ▘ U+02598</summary>
+    public Rune ShadowHorizontalEnd { get; set; } = (Rune)'▘';
+
+    #endregion
 }

+ 2 - 2
Terminal.Gui/Drawing/LineCanvas.cs

@@ -165,9 +165,9 @@ public class LineCanvas : IDisposable
     ///     intersection symbols.
     /// </summary>
     /// <returns>A map of all the points within the canvas.</returns>
-    public Dictionary<Point, Cell> GetCellMap ()
+    public Dictionary<Point, Cell?> GetCellMap ()
     {
-        Dictionary<Point, Cell> map = new ();
+        Dictionary<Point, Cell?> map = new ();
 
         // walk through each pixel of the bitmap
         for (int y = Viewport.Y; y < Viewport.Y + Viewport.Height; y++)

+ 4 - 0
Terminal.Gui/Resources/config.json

@@ -20,6 +20,7 @@
   "Application.AlternateBackwardKey": "Ctrl+PageUp",
   "Application.AlternateForwardKey": "Ctrl+PageDown",
   "Application.QuitKey": "Esc",
+
   "Theme": "Default",
   "Themes": [
     {
@@ -28,6 +29,9 @@
         "Dialog.DefaultButtonAlignmentModes": "AddSpaceBetweenItems",
         "FrameView.DefaultBorderStyle": "Single",
         "Window.DefaultBorderStyle": "Single",
+        "Dialog.DefaultBorderStyle": "Single",
+        "MessageBox.DefaultBorderStyle": "Double",
+        "Button.DefaultShadow": "None",
         "ColorSchemes": [
           {
             "TopLevel": {

+ 184 - 3
Terminal.Gui/View/Adornment/Margin.cs

@@ -1,4 +1,8 @@
-namespace Terminal.Gui;
+#nullable enable
+
+using System.Drawing;
+
+namespace Terminal.Gui;
 
 /// <summary>The Margin for a <see cref="View"/>.</summary>
 /// <remarks>
@@ -15,6 +19,102 @@ public class Margin : Adornment
     public Margin (View parent) : base (parent)
     {
         /* Do nothing; View.CreateAdornment requires a constructor that takes a parent */
+
+        HighlightStyle |= HighlightStyle.Pressed;
+        Highlight += Margin_Highlight;
+        LayoutStarted += Margin_LayoutStarted;
+
+        // Margin should not be focusable
+        CanFocus = false;
+    }
+
+    private void Margin_LayoutStarted (object? sender, LayoutEventArgs e)
+    {
+        // Adjust the shadow such that it is drawn aligned with the Border
+        if (ShadowStyle != Gui.ShadowStyle.None && _rightShadow is { } && _bottomShadow is { })
+        {
+            _rightShadow.Y = Parent.Border.Thickness.Top > 0 ? Parent.Border.Thickness.Top - (Parent.Border.Thickness.Top > 2 && Parent.Border.ShowTitle ? 1 : 0) : 1;
+            _bottomShadow.X = Parent.Border.Thickness.Left > 0 ? Parent.Border.Thickness.Left : 1;
+        }
+    }
+
+    private bool _pressed;
+    private void Margin_Highlight (object? sender, HighlightEventArgs e)
+    {
+        if (ShadowStyle != Gui.ShadowStyle.None)
+        {
+            if (_pressed && e.HighlightStyle == HighlightStyle.None)
+            {
+                Thickness = new (Thickness.Left - 1, Thickness.Top, Thickness.Right + 1, Thickness.Bottom);
+
+                if (_rightShadow is { })
+                {
+                    _rightShadow.Visible = true;
+                }
+
+                if (_bottomShadow is { })
+                {
+                    _bottomShadow.Visible = true;
+                }
+
+                _pressed = false;
+                return;
+            }
+
+            if (!_pressed && (e.HighlightStyle.HasFlag (HighlightStyle.Pressed) /*|| e.HighlightStyle.HasFlag (HighlightStyle.PressedOutside)*/))
+            {
+                Thickness = new (Thickness.Left + 1, Thickness.Top, Thickness.Right - 1, Thickness.Bottom);
+                _pressed = true;
+                if (_rightShadow is { })
+                {
+                    _rightShadow.Visible = false;
+                }
+
+                if (_bottomShadow is { })
+                {
+                    _bottomShadow.Visible = false;
+                }
+            }
+        }
+
+    }
+
+    /// <inheritdoc />
+    public override void OnDrawContent (Rectangle viewport)
+    {
+        Rectangle screen = ViewportToScreen (viewport);
+        Attribute normalAttr = GetNormalColor ();
+
+        Driver?.SetAttribute (normalAttr);
+
+
+        // This just draws/clears the thickness, not the insides.
+        if (ShadowStyle != ShadowStyle.None)
+        {
+            screen = Rectangle.Inflate (screen, -1, -1);
+        }
+        Thickness.Draw (screen, ToString ());
+
+        if (Subviews.Count > 0)
+        {
+            // Draw subviews
+            // TODO: Implement OnDrawSubviews (cancelable);
+            if (Subviews is { } && SubViewNeedsDisplay)
+            {
+                IEnumerable<View> subviewsNeedingDraw = Subviews.Where (
+                                                                        view => view.Visible
+                                                                                && (view.NeedsDisplay || view.SubViewNeedsDisplay || view.LayoutNeeded)
+                                                                       );
+                foreach (View view in subviewsNeedingDraw)
+                {
+                    if (view.LayoutNeeded)
+                    {
+                        view.LayoutSubviews ();
+                    }
+                    view.Draw ();
+                }
+            }
+        }
     }
 
     /// <summary>
@@ -30,7 +130,7 @@ public class Margin : Adornment
                 return base.ColorScheme;
             }
 
-            return Parent?.SuperView?.ColorScheme ?? Colors.ColorSchemes ["TopLevel"];
+            return (Parent?.SuperView?.ColorScheme ?? Colors.ColorSchemes ["TopLevel"])!;
         }
         set
         {
@@ -38,4 +138,85 @@ public class Margin : Adornment
             Parent?.SetNeedsDisplay ();
         }
     }
-}
+
+    /// <inheritdoc />
+    public override ShadowStyle ShadowStyle
+    {
+        get => base.ShadowStyle;
+        set
+        {
+            base.ShadowStyle = SetShadow (value);
+        }
+    }
+
+    /// <summary>
+    ///    Sets whether the Margin includes a shadow effect. The shadow is drawn on the right and bottom sides of the
+    ///     Margin.
+    /// </summary>
+    public ShadowStyle SetShadow (ShadowStyle style)
+    {
+        if (ShadowStyle == style)
+        {
+            // return style;
+        }
+
+        if (ShadowStyle != ShadowStyle.None)
+        {
+            // Turn off shadow
+            Thickness = new (Thickness.Left, Thickness.Top, Thickness.Right - 1, Thickness.Bottom - 1);
+        }
+
+        if (style != ShadowStyle.None)
+        {
+            // Turn on shadow
+            Thickness = new (Thickness.Left, Thickness.Top, Thickness.Right + 1, Thickness.Bottom + 1);
+        }
+
+        if (_rightShadow is { })
+        {
+            _rightShadow.ShadowStyle = style;
+        }
+
+        if (_bottomShadow is { })
+        {
+            _bottomShadow.ShadowStyle = style;
+        }
+        return style;
+    }
+
+    private ShadowView? _bottomShadow;
+    private ShadowView? _rightShadow;
+
+    /// <inheritdoc/>
+    public override void BeginInit ()
+    {
+        base.BeginInit ();
+
+        if (Parent is null)
+        {
+            return;
+        }
+
+        ShadowStyle = base.ShadowStyle;
+        Add (
+             _rightShadow = new ShadowView
+             {
+                 X = Pos.AnchorEnd (1),
+                 Y = 0,
+                 Width = 1,
+                 Height = Dim.Fill (),
+                 ShadowStyle = ShadowStyle,
+                 Orientation = Orientation.Vertical
+             },
+             _bottomShadow = new ShadowView
+             {
+                 X = 0,
+                 Y = Pos.AnchorEnd (1),
+                 Width = Dim.Fill (),
+                 Height = 1,
+                 ShadowStyle = ShadowStyle,
+                 Orientation = Orientation.Horizontal
+             }
+            );
+    }
+}

+ 22 - 0
Terminal.Gui/View/Adornment/ShadowStyle.cs

@@ -0,0 +1,22 @@
+namespace Terminal.Gui;
+
+/// <summary>
+///     Defines the style of shadow to be drawn on the right and bottom sides of the <see cref="View"/>.
+/// </summary>
+public enum ShadowStyle
+{
+    /// <summary>
+    ///     No shadow.
+    /// </summary>
+    None,
+
+    /// <summary>
+    ///     A shadow that is drawn using block elements. Ideal for smaller views such as buttons.
+    /// </summary>
+    Opaque,
+
+    /// <summary>
+    ///     A shadow that is drawn using the underlying text with a darker background. Ideal for larger views such as windows.
+    /// </summary>
+    Transparent
+}

+ 139 - 0
Terminal.Gui/View/Adornment/ShadowView.cs

@@ -0,0 +1,139 @@
+#nullable enable
+namespace Terminal.Gui;
+
+/// <summary>
+///     Draws a shadow on the right or bottom of the view. Used internally by <see cref="Margin"/>.
+/// </summary>
+internal class ShadowView : View
+{
+    private ShadowStyle _shadowStyle;
+
+    /// <inheritdoc/>
+    public override Attribute GetNormalColor ()
+    {
+        if (SuperView is Adornment adornment)
+        {
+            var attr = Attribute.Default;
+
+            if (adornment.Parent.SuperView is { })
+            {
+                attr = adornment.Parent.SuperView.GetNormalColor ();
+            }
+            else if (Application.Top is { })
+            {
+                attr = Application.Top.GetNormalColor ();
+            }
+
+            return new (
+                        new Attribute (
+                                       ShadowStyle == ShadowStyle.Opaque ? Color.Black : attr.Foreground.GetDarkerColor (),
+                                       ShadowStyle == ShadowStyle.Opaque ? attr.Background : attr.Background.GetDarkerColor ()));
+        }
+
+        return base.GetNormalColor ();
+    }
+
+    /// <inheritdoc/>
+    public override void OnDrawContent (Rectangle viewport)
+    {
+        //base.OnDrawContent (viewport);
+        switch (ShadowStyle)
+        {
+            case ShadowStyle.Opaque:
+                if (Orientation == Orientation.Vertical)
+                {
+                    DrawVerticalShadowOpaque (viewport);
+                }
+                else
+                {
+                    DrawHorizontalShadowOpaque (viewport);
+                }
+
+                break;
+
+            case ShadowStyle.Transparent:
+                //Attribute prevAttr = Driver.GetAttribute ();
+                //var attr = new Attribute (prevAttr.Foreground, prevAttr.Background);
+                //Driver.SetAttribute (attr);
+
+                if (Orientation == Orientation.Vertical)
+                {
+                    DrawVerticalShadowTransparent (viewport);
+                }
+                else
+                {
+                    DrawHorizontalShadowTransparent (viewport);
+                }
+
+                //Driver.SetAttribute (prevAttr);
+
+                break;
+        }
+    }
+
+    /// <summary>
+    ///     Gets or sets the orientation of the shadow.
+    /// </summary>
+    public Orientation Orientation { get; set; }
+
+    public override ShadowStyle ShadowStyle
+    {
+        get => _shadowStyle;
+        set
+        {
+            Visible = value != ShadowStyle.None;
+            _shadowStyle = value;
+        }
+    }
+
+    private void DrawHorizontalShadowOpaque (Rectangle rectangle)
+    {
+        // Draw the start glyph
+        AddRune (0, 0, Glyphs.ShadowHorizontalStart);
+
+        // Fill the rest of the rectangle with the glyph - note we skip the last since vertical will draw it
+        for (var i = 1; i < rectangle.Width - 1; i++)
+        {
+            AddRune (i, 0, Glyphs.ShadowHorizontal);
+        }
+
+        // Last is special
+        AddRune (rectangle.Width - 1, 0, Glyphs.ShadowHorizontalEnd);
+    }
+
+    private void DrawHorizontalShadowTransparent (Rectangle viewport)
+    {
+        Rectangle screen = ViewportToScreen (viewport);
+
+        // Fill the rest of the rectangle - note we skip the last since vertical will draw it
+        for (int i = screen.X; i < screen.X + screen.Width - 1; i++)
+        {
+            Driver.Move (i, screen.Y);
+            Driver.AddRune (Driver.Contents [screen.Y, i].Rune);
+        }
+    }
+
+    private void DrawVerticalShadowOpaque (Rectangle viewport)
+    {
+        // Draw the start glyph
+        AddRune (0, 0, Glyphs.ShadowVerticalStart);
+
+        // Fill the rest of the rectangle with the glyph
+        for (var i = 1; i < viewport.Height; i++)
+        {
+            AddRune (0, i, Glyphs.ShadowVertical);
+        }
+    }
+
+    private void DrawVerticalShadowTransparent (Rectangle viewport)
+    {
+        Rectangle screen = ViewportToScreen (viewport);
+
+        // Fill the rest of the rectangle
+        for (int i = screen.Y; i < screen.Y + viewport.Height; i++)
+        {
+            Driver.Move (screen.X, i);
+            Driver.AddRune (Driver.Contents [i, screen.X].Rune);
+        }
+    }
+}

+ 27 - 0
Terminal.Gui/View/ViewAdornments.cs

@@ -1,4 +1,5 @@
 using System.ComponentModel;
+using System.Text.Json.Serialization;
 
 namespace Terminal.Gui;
 
@@ -59,6 +60,32 @@ public partial class View
     /// </remarks>
     public Margin Margin { get; private set; }
 
+    private ShadowStyle _shadowStyle;
+    /// <summary>
+    ///     Gets or sets whether the View is shown with a shadow effect. The shadow is drawn on the right and bottom sides of the
+    ///     Margin.
+    /// </summary>
+    /// <remarks>
+    ///     Setting this property to <see langword="true"/> will add a shadow to the right and bottom sides of the Margin.
+    ///     The View 's <see cref="Frame"/> will be expanded to include the shadow.
+    /// </remarks>
+    public virtual ShadowStyle ShadowStyle
+    {
+        get => _shadowStyle;
+        set
+        {
+            if (_shadowStyle == value)
+            {
+                return;
+            }
+            _shadowStyle = value;
+            if (Margin is { })
+            {
+                Margin.ShadowStyle = value;
+            }
+        }
+    }
+
     /// <summary>
     ///     The <see cref="Adornment"/> that offsets the <see cref="Viewport"/> from the <see cref="Margin"/>.
     ///     The Border provides the space for a visual border (drawn using

+ 16 - 10
Terminal.Gui/View/ViewDrawing.cs

@@ -542,14 +542,17 @@ public partial class View
         // If we have a SuperView, it'll render our frames.
         if (!SuperViewRendersLineCanvas && LineCanvas.Viewport != Rectangle.Empty)
         {
-            foreach (KeyValuePair<Point, Cell> p in LineCanvas.GetCellMap ())
+            foreach (KeyValuePair<Point, Cell?> p in LineCanvas.GetCellMap ())
             {
                 // Get the entire map
-                Driver.SetAttribute (p.Value.Attribute ?? ColorScheme.Normal);
-                Driver.Move (p.Key.X, p.Key.Y);
+                if (p.Value is { })
+                {
+                    Driver.SetAttribute (p.Value.Value.Attribute ?? ColorScheme.Normal);
+                    Driver.Move (p.Key.X, p.Key.Y);
 
-                // TODO: #2616 - Support combining sequences that don't normalize
-                Driver.AddRune (p.Value.Rune);
+                    // TODO: #2616 - Support combining sequences that don't normalize
+                    Driver.AddRune (p.Value.Value.Rune);
+                }
             }
 
             LineCanvas.Clear ();
@@ -564,14 +567,17 @@ public partial class View
                 subview.LineCanvas.Clear ();
             }
 
-            foreach (KeyValuePair<Point, Cell> p in LineCanvas.GetCellMap ())
+            foreach (KeyValuePair<Point, Cell?> p in LineCanvas.GetCellMap ())
             {
                 // Get the entire map
-                Driver.SetAttribute (p.Value.Attribute ?? ColorScheme.Normal);
-                Driver.Move (p.Key.X, p.Key.Y);
+                if (p.Value is { })
+                {
+                    Driver.SetAttribute (p.Value.Value.Attribute ?? ColorScheme.Normal);
+                    Driver.Move (p.Key.X, p.Key.Y);
 
-                // TODO: #2616 - Support combining sequences that don't normalize
-                Driver.AddRune (p.Value.Rune);
+                    // TODO: #2616 - Support combining sequences that don't normalize
+                    Driver.AddRune (p.Value.Value.Rune);
+                }
             }
 
             LineCanvas.Clear ();

+ 11 - 4
Terminal.Gui/View/ViewEventArgs.cs

@@ -58,8 +58,11 @@ public class DrawEventArgs : EventArgs
 public class FocusEventArgs : EventArgs
 {
     /// <summary>Constructs.</summary>
-    /// <param name="view">The view that gets or loses focus.</param>
-    public FocusEventArgs (View view) { View = view; }
+    /// <param name="leaving">The view that gets or loses focus.</param>
+    public FocusEventArgs (View leaving, View entering) {
+        Leaving = leaving;
+        Entering = entering;
+    }
 
     /// <summary>
     ///     Indicates if the current focus event has already been processed and the driver should stop notifying any other
@@ -68,6 +71,10 @@ public class FocusEventArgs : EventArgs
     /// </summary>
     public bool Handled { get; set; }
 
-    /// <summary>Indicates the current view that gets or loses focus.</summary>
-    public View View { get; set; }
+    /// <summary>Indicates the view that is losing focus.</summary>
+    public View Leaving { get; set; }
+
+    /// <summary>Indicates the view that is gaining focus.</summary>
+    public View Entering { get; set; }
+
 }

+ 17 - 3
Terminal.Gui/View/ViewMouse.cs

@@ -324,7 +324,7 @@ public partial class View
             {
                 Application.GrabMouse (this);
 
-                if (CanFocus)
+                if (!HasFocus && CanFocus)
                 {
                     // Set the focus, but don't invoke Accept
                     SetFocus ();
@@ -335,14 +335,14 @@ public partial class View
 
             if (Viewport.Contains (mouseEvent.Position))
             {
-                if (SetHighlight (HighlightStyle.HasFlag (HighlightStyle.Pressed) ? HighlightStyle.Pressed : HighlightStyle.None) == true)
+                if (this is not Adornment && SetHighlight (HighlightStyle.HasFlag (HighlightStyle.Pressed) ? HighlightStyle.Pressed : HighlightStyle.None) == true)
                 {
                     return true;
                 }
             }
             else
             {
-                if (SetHighlight (HighlightStyle.HasFlag (HighlightStyle.PressedOutside) ? HighlightStyle.PressedOutside : HighlightStyle.None) == true)
+                if (this is not Adornment && SetHighlight (HighlightStyle.HasFlag (HighlightStyle.PressedOutside) ? HighlightStyle.PressedOutside : HighlightStyle.None) == true)
 
                 {
                     return true;
@@ -533,6 +533,20 @@ public partial class View
         HighlightEventArgs args = new (highlight);
         Highlight?.Invoke (this, args);
 
+        if (args.Cancel)
+        {
+            return true;
+        }
+
+        args = new (highlight);
+        Margin?.Highlight?.Invoke (this, args);
+
+        //args = new (highlight);
+        //Border?.Highlight?.Invoke (this, args);
+
+        //args = new (highlight);
+        //Padding?.Highlight?.Invoke (this, args);
+
         return args.Cancel;
     }
 

+ 2 - 2
Terminal.Gui/View/ViewSubViews.cs

@@ -505,7 +505,7 @@ public partial class View
     /// <returns><c>true</c>, if the event was handled, <c>false</c> otherwise.</returns>
     public virtual bool OnEnter (View view)
     {
-        var args = new FocusEventArgs (view);
+        var args = new FocusEventArgs (view, this);
         Enter?.Invoke (this, args);
 
         if (args.Handled)
@@ -521,7 +521,7 @@ public partial class View
     /// <returns><c>true</c>, if the event was handled, <c>false</c> otherwise.</returns>
     public virtual bool OnLeave (View view)
     {
-        var args = new FocusEventArgs (view);
+        var args = new FocusEventArgs (this, view);
         Leave?.Invoke (this, args);
 
         if (args.Handled)

+ 15 - 2
Terminal.Gui/Views/Button.cs

@@ -5,6 +5,8 @@
 //   Miguel de Icaza ([email protected])
 //
 
+using System.Text.Json.Serialization;
+
 namespace Terminal.Gui;
 
 /// <summary>Button is a <see cref="View"/> that provides an item that invokes raises the <see cref="View.Accept"/> event.</summary>
@@ -33,6 +35,14 @@ public class Button : View
     private readonly Rune _rightDefault;
     private bool _isDefault;
 
+    /// <summary>
+    /// Gets or sets whether <see cref="Button"/>s are shown with a shadow effect by default.
+    /// </summary>
+    [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
+    [JsonConverter (typeof (JsonStringEnumConverter))]
+
+    public static ShadowStyle DefaultShadow { get; set; } = ShadowStyle.None;
+
     /// <summary>Initializes a new instance of <see cref="Button"/>.</summary>
     public Button ()
     {
@@ -44,14 +54,15 @@ public class Button : View
         _leftDefault = Glyphs.LeftDefaultIndicator;
         _rightDefault = Glyphs.RightDefaultIndicator;
 
+        Height = Dim.Auto (DimAutoStyle.Text);
         Width = Dim.Auto (DimAutoStyle.Text);
-        Height = Dim.Auto (DimAutoStyle.Text, minimumContentDim: 1);
 
         CanFocus = true;
         HighlightStyle |= HighlightStyle.Pressed;
 #if HOVER
         HighlightStyle |= HighlightStyle.Hover;
 #endif
+
         // Override default behavior of View
         AddCommand (Command.HotKey, () =>
         {
@@ -64,6 +75,8 @@ public class Button : View
 
         TitleChanged += Button_TitleChanged;
         MouseClick += Button_MouseClick;
+
+        ShadowStyle = DefaultShadow;
     }
 
     private bool _wantContinuousButtonPressed;
@@ -94,7 +107,7 @@ public class Button : View
 
     private void Button_MouseClick (object sender, MouseEventEventArgs e)
     {
-       e.Handled = InvokeCommand (Command.HotKey) == true;
+        e.Handled = InvokeCommand (Command.HotKey) == true;
     }
 
     private void Button_TitleChanged (object sender, StateEventArgs<string> e)

+ 4 - 0
Terminal.Gui/Views/ColorPicker.cs

@@ -107,6 +107,10 @@ public class ColorPicker : View
         get => (ColorName)_selectColorIndex;
         set
         {
+            if (value == (ColorName)_selectColorIndex)
+            {
+                return;
+            }
             var prev = (ColorName)_selectColorIndex;
             _selectColorIndex = (int)value;
 

+ 4 - 2
Terminal.Gui/Views/DatePicker.cs

@@ -219,7 +219,8 @@ public class DatePicker : View
             Text = GetBackButtonText (),
             WantContinuousButtonPressed = true,
             NoPadding = true,
-            NoDecorations = true
+            NoDecorations = true,
+            ShadowStyle = ShadowStyle.None
         };
 
         _previousMonthButton.Accept += (sender, e) =>
@@ -237,7 +238,8 @@ public class DatePicker : View
             Text = GetForwardButtonText (),
             WantContinuousButtonPressed = true,
             NoPadding = true,
-            NoDecorations = true
+            NoDecorations = true,
+            ShadowStyle = ShadowStyle.None
         };
 
         _nextMonthButton.Accept += (sender, e) =>

+ 17 - 6
Terminal.Gui/Views/Dialog.cs

@@ -42,16 +42,23 @@ public class Dialog : Window
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
     public static int DefaultMinimumHeight { get; set; } = 25;
 
-    // TODO: Reenable once border/borderframe design is settled
+
+    /// <summary>
+    /// Gets or sets whether all <see cref="Window"/>s are shown with a shadow effect by default.
+    /// </summary>
+    [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
+    [JsonConverter (typeof (JsonStringEnumConverter))]
+    public new static ShadowStyle DefaultShadow { get; set; } = ShadowStyle.None;
+
     /// <summary>
     ///     Defines the default border styling for <see cref="Dialog"/>. Can be configured via
     ///     <see cref="ConfigurationManager"/>.
     /// </summary>
 
-    //[SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
-    //public static Border DefaultBorder { get; set; } = new Border () {
-    //	LineStyle = LineStyle.Single,
-    //};
+    [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
+    [JsonConverter (typeof (JsonStringEnumConverter))]
+    public new static LineStyle DefaultBorderStyle { get; set; } = LineStyle.Single;
+
     private readonly List<Button> _buttons = new ();
 
     /// <summary>
@@ -60,11 +67,15 @@ public class Dialog : Window
     /// <remarks>
     ///     By default, <see cref="View.X"/>, <see cref="View.Y"/>, <see cref="View.Width"/>, and <see cref="View.Height"/> are
     ///     set
-    ///     such that the <see cref="Dialog"/> will be centered in, and no larger than 90% of the screen dimensions.
+    ///     such that the <see cref="Dialog"/> will be centered in, and no larger than 90% of <see cref="Application.Top"/>, if there is one. Otherwise,
+    ///     it will be bound by the screen dimensions.
     /// </remarks>
     public Dialog ()
     {
         Arrangement = ViewArrangement.Movable;
+        ShadowStyle = DefaultShadow;
+        BorderStyle = DefaultBorderStyle;
+
         X = Pos.Center ();
         Y = Pos.Center ();
 

+ 5 - 2
Terminal.Gui/Views/MessageBox.cs

@@ -1,4 +1,6 @@
-namespace Terminal.Gui;
+using System.Text.Json.Serialization;
+
+namespace Terminal.Gui;
 
 /// <summary>
 ///     MessageBox displays a modal message to the user, with a title, a message and a series of options that the user
@@ -29,6 +31,7 @@ public static class MessageBox
     ///     <see cref="ConfigurationManager"/>.
     /// </summary>
     [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
+    [JsonConverter (typeof (JsonStringEnumConverter))]
     public static LineStyle DefaultBorderStyle { get; set; } = LineStyle.Single;
 
     /// <summary>
@@ -364,7 +367,7 @@ public static class MessageBox
             Buttons = buttonList.ToArray (),
             ButtonAlignment = Alignment.Center,
             ButtonAlignmentModes = AlignmentModes.StartToEnd | AlignmentModes.AddSpaceBetweenItems,
-            BorderStyle = DefaultBorderStyle,
+            BorderStyle = MessageBox.DefaultBorderStyle,
             Width = Dim.Auto (DimAutoStyle.Content, minimumContentDim: 1, Dim.Percent (90)),
             Height = Dim.Auto (DimAutoStyle.Content, minimumContentDim: 1, Dim.Percent (90)),
         };

+ 271 - 270
Terminal.Gui/Views/Shortcut.cs

@@ -1,4 +1,5 @@
 using System.ComponentModel;
+using System.Threading.Channels;
 
 namespace Terminal.Gui;
 
@@ -84,8 +85,8 @@ public class Shortcut : View
 
         // If the user clicks anywhere on the Shortcut, other than the CommandView, invoke the Command
         MouseClick += Shortcut_MouseClick;
-        HelpView.MouseClick += Shortcut_MouseClick;
-        KeyView.MouseClick += Shortcut_MouseClick;
+        HelpView.MouseClick += Subview_MouseClick;
+        KeyView.MouseClick += Subview_MouseClick;
         LayoutStarted += OnLayoutStarted;
         Initialized += OnInitialized;
 
@@ -340,6 +341,12 @@ public class Shortcut : View
         }
     }
 
+    private void Subview_MouseClick (object sender, MouseEventEventArgs e)
+    {
+        // TODO: Remove. This does nothing.
+        return;
+    }
+
     #region Command
 
     private View _commandView = new ();
@@ -409,9 +416,6 @@ public class Shortcut : View
             // If you want it to get focus, you need to set it.
             _commandView.CanFocus = false;
 
-            _commandView.MouseClick += Shortcut_MouseClick;
-            _commandView.Accept += CommandViewAccept;
-
             _commandView.HotKeyChanged += (s, e) =>
                                           {
                                               if (e.NewKey != Key.Empty)
@@ -432,360 +436,357 @@ public class Shortcut : View
             UpdateKeyBinding ();
 
             return;
-
-            void CommandViewAccept (object sender, CancelEventArgs e)
-            {
-                // When the CommandView fires its Accept event, we want to act as though the
-                // Shortcut was clicked.
-                //if (base.OnAccept () == true)
-                //{
-                //    e.Cancel = true;
-                //}
-            }
         }
     }
 
     private void SetCommandViewDefaultLayout ()
-    {
-        CommandView.Margin.Thickness = GetMarginThickness ();
-        CommandView.X = Pos.Align (Alignment.End, AlignmentModes);
-        CommandView.Y = 0; //Pos.Center ();
-    }
+{
+    CommandView.Margin.Thickness = GetMarginThickness ();
+    CommandView.X = Pos.Align (Alignment.End, AlignmentModes);
+    CommandView.Y = 0; //Pos.Center ();
+}
 
-    private void Shortcut_TitleChanged (object sender, StateEventArgs<string> e)
-    {
-        // If the Title changes, update the CommandView text.
-        // This is a helper to make it easier to set the CommandView text.
-        // CommandView is public and replaceable, but this is a convenience.
-        _commandView.Text = Title;
-    }
+private void Shortcut_TitleChanged (object sender, StateEventArgs<string> e)
+{
+    // If the Title changes, update the CommandView text.
+    // This is a helper to make it easier to set the CommandView text.
+    // CommandView is public and replaceable, but this is a convenience.
+    _commandView.Text = Title;
+}
 
-    #endregion Command
+#endregion Command
 
-    #region Help
+#region Help
 
-    /// <summary>
-    ///     The subview that displays the help text for the command. Internal for unit testing.
-    /// </summary>
-    internal View HelpView { get; } = new ();
+/// <summary>
+///     The subview that displays the help text for the command. Internal for unit testing.
+/// </summary>
+internal View HelpView { get; } = new ();
 
-    private void SetHelpViewDefaultLayout ()
-    {
-        HelpView.Margin.Thickness = GetMarginThickness ();
-        HelpView.X = Pos.Align (Alignment.End, AlignmentModes);
-        HelpView.Y = 0; //Pos.Center ();
-        HelpView.Width = Dim.Auto (DimAutoStyle.Text);
-        HelpView.Height = CommandView?.Visible == true ? Dim.Height (CommandView) : 1;
-
-        HelpView.Visible = true;
-        HelpView.VerticalTextAlignment = Alignment.Center;
-    }
+private void SetHelpViewDefaultLayout ()
+{
+    HelpView.Margin.Thickness = GetMarginThickness ();
+    HelpView.X = Pos.Align (Alignment.End, AlignmentModes);
+    HelpView.Y = 0; //Pos.Center ();
+    HelpView.Width = Dim.Auto (DimAutoStyle.Text);
+    HelpView.Height = CommandView?.Visible == true ? Dim.Height (CommandView) : 1;
+
+    HelpView.Visible = true;
+    HelpView.VerticalTextAlignment = Alignment.Center;
+}
 
-    /// <summary>
-    ///     Gets or sets the help text displayed in the middle of the Shortcut. Identical in function to <see cref="HelpText"/>
-    ///     .
-    /// </summary>
-    public override string Text
+/// <summary>
+///     Gets or sets the help text displayed in the middle of the Shortcut. Identical in function to <see cref="HelpText"/>
+///     .
+/// </summary>
+public override string Text
+{
+    get => HelpView?.Text;
+    set
     {
-        get => HelpView?.Text;
-        set
+        if (HelpView != null)
         {
-            if (HelpView != null)
-            {
-                HelpView.Text = value;
-                ShowHide ();
-            }
+            HelpView.Text = value;
+            ShowHide ();
         }
     }
+}
 
-    /// <summary>
-    ///     Gets or sets the help text displayed in the middle of the Shortcut.
-    /// </summary>
-    public string HelpText
+/// <summary>
+///     Gets or sets the help text displayed in the middle of the Shortcut.
+/// </summary>
+public string HelpText
+{
+    get => HelpView?.Text;
+    set
     {
-        get => HelpView?.Text;
-        set
+        if (HelpView != null)
         {
-            if (HelpView != null)
-            {
-                HelpView.Text = value;
-                ShowHide ();
-            }
+            HelpView.Text = value;
+            ShowHide ();
         }
     }
+}
 
-    #endregion Help
+#endregion Help
 
-    #region Key
+#region Key
 
-    private Key _key = Key.Empty;
+private Key _key = Key.Empty;
 
-    /// <summary>
-    ///     Gets or sets the <see cref="Key"/> that will be bound to the <see cref="Command.Accept"/> command.
-    /// </summary>
-    public Key Key
+/// <summary>
+///     Gets or sets the <see cref="Key"/> that will be bound to the <see cref="Command.Accept"/> command.
+/// </summary>
+public Key Key
+{
+    get => _key;
+    set
     {
-        get => _key;
-        set
+        if (value == null)
         {
-            if (value == null)
-            {
-                throw new ArgumentNullException ();
-            }
+            throw new ArgumentNullException ();
+        }
 
-            _key = value;
+        _key = value;
 
-            UpdateKeyBinding ();
+        UpdateKeyBinding ();
 
-            KeyView.Text = Key == Key.Empty ? string.Empty : $"{Key}";
-            ShowHide ();
-        }
+        KeyView.Text = Key == Key.Empty ? string.Empty : $"{Key}";
+        ShowHide ();
     }
+}
 
-    private KeyBindingScope _keyBindingScope = KeyBindingScope.HotKey;
+private KeyBindingScope _keyBindingScope = KeyBindingScope.HotKey;
 
-    /// <summary>
-    ///     Gets or sets the scope for the key binding for how <see cref="Key"/> is bound to <see cref="Command"/>.
-    /// </summary>
-    public KeyBindingScope KeyBindingScope
+/// <summary>
+///     Gets or sets the scope for the key binding for how <see cref="Key"/> is bound to <see cref="Command"/>.
+/// </summary>
+public KeyBindingScope KeyBindingScope
+{
+    get => _keyBindingScope;
+    set
     {
-        get => _keyBindingScope;
-        set
-        {
-            _keyBindingScope = value;
+        _keyBindingScope = value;
 
-            UpdateKeyBinding ();
-        }
+        UpdateKeyBinding ();
     }
+}
 
-    /// <summary>
-    ///     Gets the subview that displays the key. Internal for unit testing.
-    /// </summary>
+/// <summary>
+///     Gets the subview that displays the key. Internal for unit testing.
+/// </summary>
 
-    internal View KeyView { get; } = new ();
+internal View KeyView { get; } = new ();
 
-    private int _minimumKeyTextSize;
+private int _minimumKeyTextSize;
 
-    /// <summary>
-    /// Gets or sets the minimum size of the key text. Useful for aligning the key text with other <see cref="Shortcut"/>s.
-    /// </summary>
-    public int MinimumKeyTextSize
+/// <summary>
+/// Gets or sets the minimum size of the key text. Useful for aligning the key text with other <see cref="Shortcut"/>s.
+/// </summary>
+public int MinimumKeyTextSize
+{
+    get => _minimumKeyTextSize;
+    set
     {
-        get => _minimumKeyTextSize;
-        set
+        if (value == _minimumKeyTextSize)
         {
-            if (value == _minimumKeyTextSize)
-            {
-                //return;
-            }
-
-            _minimumKeyTextSize = value;
-            SetKeyViewDefaultLayout ();
-            CommandView.SetNeedsLayout ();
-            HelpView.SetNeedsLayout ();
-            KeyView.SetNeedsLayout ();
-            SetSubViewNeedsDisplay ();
+            //return;
         }
+
+        _minimumKeyTextSize = value;
+        SetKeyViewDefaultLayout ();
+        CommandView.SetNeedsLayout ();
+        HelpView.SetNeedsLayout ();
+        KeyView.SetNeedsLayout ();
+        SetSubViewNeedsDisplay ();
     }
+}
 
-    private int GetMinimumKeyViewSize () { return MinimumKeyTextSize; }
+private int GetMinimumKeyViewSize () { return MinimumKeyTextSize; }
 
-    private void SetKeyViewDefaultLayout ()
-    {
-        KeyView.Margin.Thickness = GetMarginThickness ();
-        KeyView.X = Pos.Align (Alignment.End, AlignmentModes);
-        KeyView.Y = 0; //Pos.Center ();
-        KeyView.Width = Dim.Auto (DimAutoStyle.Text, Dim.Func (GetMinimumKeyViewSize));
-        KeyView.Height = CommandView?.Visible == true ? Dim.Height (CommandView) : 1;
-
-        KeyView.Visible = true;
-
-        // Right align the text in the keyview
-        KeyView.TextAlignment = Alignment.End;
-        KeyView.VerticalTextAlignment = Alignment.Center;
-        KeyView.KeyBindings.Clear ();
-    }
+private void SetKeyViewDefaultLayout ()
+{
+    KeyView.Margin.Thickness = GetMarginThickness ();
+    KeyView.X = Pos.Align (Alignment.End, AlignmentModes);
+    KeyView.Y = 0; //Pos.Center ();
+    KeyView.Width = Dim.Auto (DimAutoStyle.Text, Dim.Func (GetMinimumKeyViewSize));
+    KeyView.Height = CommandView?.Visible == true ? Dim.Height (CommandView) : 1;
+
+    KeyView.Visible = true;
+
+    // Right align the text in the keyview
+    KeyView.TextAlignment = Alignment.End;
+    KeyView.VerticalTextAlignment = Alignment.Center;
+    KeyView.KeyBindings.Clear ();
+}
 
-    private void UpdateKeyBinding ()
+private void UpdateKeyBinding ()
+{
+    if (Key != null)
     {
-        if (Key != null)
-        {
-            // Disable the command view key bindings
-            CommandView.KeyBindings.Remove (Key);
-            CommandView.KeyBindings.Remove (CommandView.HotKey);
-            KeyBindings.Remove (Key);
-            KeyBindings.Add (Key, KeyBindingScope | KeyBindingScope.HotKey, Command.Accept);
-            //KeyBindings.Add (Key, KeyBindingScope.HotKey, Command.Accept);
-        }
+        // Disable the command view key bindings
+        CommandView.KeyBindings.Remove (Key);
+        CommandView.KeyBindings.Remove (CommandView.HotKey);
+        KeyBindings.Remove (Key);
+        KeyBindings.Add (Key, KeyBindingScope | KeyBindingScope.HotKey, Command.Accept);
+        //KeyBindings.Add (Key, KeyBindingScope.HotKey, Command.Accept);
     }
+}
 
-    #endregion Key
+#endregion Key
 
-    #region Accept Handling
+#region Accept Handling
 
-    /// <summary>
-    ///     Called when the <see cref="Command.Accept"/> command is received. This
-    ///     occurs
-    ///     - if the user clicks anywhere on the shortcut with the mouse
-    ///     - if the user presses Key
-    ///     - if the user presses the HotKey specified by CommandView
-    ///     - if HasFocus and the user presses Space or Enter (or any other key bound to Command.Accept).
-    /// </summary>
-    protected bool? OnAccept (CommandContext ctx)
+/// <summary>
+///     Called when the <see cref="Command.Accept"/> command is received. This
+///     occurs
+///     - if the user clicks anywhere on the shortcut with the mouse
+///     - if the user presses Key
+///     - if the user presses the HotKey specified by CommandView
+///     - if HasFocus and the user presses Space or Enter (or any other key bound to Command.Accept).
+/// </summary>
+protected bool? OnAccept (CommandContext ctx)
+{
+    var cancel = false;
+
+    switch (ctx.KeyBinding?.Scope)
     {
-        var cancel = false;
+        case KeyBindingScope.Application:
+            cancel = base.OnAccept () == true;
 
-        switch (ctx.KeyBinding?.Scope)
-        {
-            case KeyBindingScope.Application:
-                cancel = base.OnAccept () == true;
+            break;
 
-                break;
+        case KeyBindingScope.Focused:
+            base.OnAccept ();
 
-            case KeyBindingScope.Focused:
-                // TODO: Figure this out
-                cancel = base.OnAccept () == true;
+            // cancel if we're focused
+            cancel = true;
 
-                break;
+            break;
 
-            case KeyBindingScope.HotKey:
-                cancel = base.OnAccept () == true;
+        case KeyBindingScope.HotKey:
+            cancel = base.OnAccept () == true;
 
-                if (CanFocus)
-                {
-                    SetFocus ();
-                }
+            if (CanFocus)
+            {
+                SetFocus ();
+                cancel = true;
+            }
 
-                break;
+            break;
 
-            default:
-                cancel = base.OnAccept () == true;
-                break;
-        }
+        default:
+            // Mouse
+            cancel = base.OnAccept () == true;
 
-        CommandView.InvokeCommand (Command.Accept);
+            break;
+    }
 
-        if (!cancel)
-        {
-            Action?.Invoke ();
-        }
+    CommandView.InvokeCommand (Command.Accept, ctx.Key, ctx.KeyBinding);
 
-        return cancel;
+    if (Action is { })
+    {
+        Action.Invoke ();
+        // Assume if there's a subscriber to Action, it's handled.
+        cancel = true;
     }
 
-    /// <summary>
-    ///     Gets or sets the action to be invoked when the shortcut key is pressed or the shortcut is clicked on with the
-    ///     mouse.
-    /// </summary>
-    /// <remarks>
-    ///     Note, the <see cref="View.Accept"/> event is fired first, and if cancelled, the event will not be invoked.
-    /// </remarks>
-    [CanBeNull]
-    public Action Action { get; set; }
+    return cancel;
+}
 
-    #endregion Accept Handling
+/// <summary>
+///     Gets or sets the action to be invoked when the shortcut key is pressed or the shortcut is clicked on with the
+///     mouse.
+/// </summary>
+/// <remarks>
+///     Note, the <see cref="View.Accept"/> event is fired first, and if cancelled, the event will not be invoked.
+/// </remarks>
+[CanBeNull]
+public Action Action { get; set; }
 
-    private bool? OnSelect (CommandContext ctx)
-    {
-        if (CommandView.GetSupportedCommands ().Contains (Command.Select))
-        {
-           return CommandView.InvokeCommand (Command.Select, ctx.Key, ctx.KeyBinding);
-        }
-        return false;
+#endregion Accept Handling
 
+private bool? OnSelect (CommandContext ctx)
+{
+    if (CommandView.GetSupportedCommands ().Contains (Command.Select))
+    {
+        return CommandView.InvokeCommand (Command.Select, ctx.Key, ctx.KeyBinding);
     }
+    return false;
+
+}
 
 
-    #region Focus
+#region Focus
 
-    /// <inheritdoc/>
-    public override ColorScheme ColorScheme
+/// <inheritdoc/>
+public override ColorScheme ColorScheme
+{
+    get => base.ColorScheme;
+    set
     {
-        get => base.ColorScheme;
-        set
-        {
-            base.ColorScheme = value;
-            SetColors ();
-        }
+        base.ColorScheme = value;
+        SetColors ();
     }
+}
 
-    /// <summary>
-    /// </summary>
-    internal void SetColors ()
-    {
-        // Border should match superview.
-        Border.ColorScheme = SuperView?.ColorScheme;
+/// <summary>
+/// </summary>
+internal void SetColors ()
+{
+    // Border should match superview.
+    Border.ColorScheme = SuperView?.ColorScheme;
 
-        if (HasFocus)
-        {
-            // When we have focus, we invert the colors
-            base.ColorScheme = new (base.ColorScheme)
-            {
-                Normal = base.ColorScheme.Focus,
-                HotNormal = base.ColorScheme.HotFocus,
-                HotFocus = base.ColorScheme.HotNormal,
-                Focus = base.ColorScheme.Normal
-            };
-        }
-        else
+    if (HasFocus)
+    {
+        // When we have focus, we invert the colors
+        base.ColorScheme = new (base.ColorScheme)
         {
-            base.ColorScheme = SuperView?.ColorScheme ?? base.ColorScheme;
-        }
+            Normal = base.ColorScheme.Focus,
+            HotNormal = base.ColorScheme.HotFocus,
+            HotFocus = base.ColorScheme.HotNormal,
+            Focus = base.ColorScheme.Normal
+        };
+    }
+    else
+    {
+        base.ColorScheme = SuperView?.ColorScheme ?? base.ColorScheme;
+    }
 
-        // Set KeyView's colors to show "hot"
-        if (IsInitialized && base.ColorScheme is { })
+    // Set KeyView's colors to show "hot"
+    if (IsInitialized && base.ColorScheme is { })
+    {
+        var cs = new ColorScheme (base.ColorScheme)
         {
-            var cs = new ColorScheme (base.ColorScheme)
-            {
-                Normal = base.ColorScheme.HotNormal,
-                HotNormal = base.ColorScheme.Normal
-            };
-            KeyView.ColorScheme = cs;
-        }
+            Normal = base.ColorScheme.HotNormal,
+            HotNormal = base.ColorScheme.Normal
+        };
+        KeyView.ColorScheme = cs;
     }
+}
 
-    View _lastFocusedView;
-    /// <inheritdoc/>
-    public override bool OnEnter (View view)
-    {
-        SetColors ();
-        _lastFocusedView = view;
+View _lastFocusedView;
+/// <inheritdoc/>
+public override bool OnEnter (View view)
+{
+    SetColors ();
+    _lastFocusedView = view;
 
-        return base.OnEnter (view);
-    }
+    return base.OnEnter (view);
+}
 
-    /// <inheritdoc/>
-    public override bool OnLeave (View view)
-    {
-        SetColors ();
-        _lastFocusedView = this;
+/// <inheritdoc/>
+public override bool OnLeave (View view)
+{
+    SetColors ();
+    _lastFocusedView = this;
 
-        return base.OnLeave (view);
-    }
+    return base.OnLeave (view);
+}
 
-    #endregion Focus
+#endregion Focus
 
-    /// <inheritdoc/>
-    protected override void Dispose (bool disposing)
+/// <inheritdoc/>
+protected override void Dispose (bool disposing)
+{
+    if (disposing)
     {
-        if (disposing)
+        if (CommandView?.IsAdded == false)
         {
-            if (CommandView?.IsAdded == false)
-            {
-                CommandView.Dispose ();
-            }
-
-            if (HelpView?.IsAdded == false)
-            {
-                HelpView.Dispose ();
-            }
+            CommandView.Dispose ();
+        }
 
-            if (KeyView?.IsAdded == false)
-            {
-                KeyView.Dispose ();
-            }
+        if (HelpView?.IsAdded == false)
+        {
+            HelpView.Dispose ();
         }
 
-        base.Dispose (disposing);
+        if (KeyView?.IsAdded == false)
+        {
+            KeyView.Dispose ();
+        }
     }
+
+    base.Dispose (disposing);
+}
 }

+ 9 - 0
Terminal.Gui/Views/Window.cs

@@ -14,6 +14,14 @@ namespace Terminal.Gui;
 /// </remarks>
 public class Window : Toplevel
 {
+
+    /// <summary>
+    /// Gets or sets whether all <see cref="Window"/>s are shown with a shadow effect by default.
+    /// </summary>
+    [SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
+    public static ShadowStyle DefaultShadow { get; set; } = ShadowStyle.None;
+
+
     /// <summary>
     ///     Initializes a new instance of the <see cref="Window"/> class.
     /// </summary>
@@ -22,6 +30,7 @@ public class Window : Toplevel
         CanFocus = true;
         ColorScheme = Colors.ColorSchemes ["Base"]; // TODO: make this a theme property
         BorderStyle = DefaultBorderStyle;
+        ShadowStyle = DefaultShadow;
 
         // This enables the default button to be activated by the Enter key.
         AddCommand (

+ 132 - 1
UICatalog/Resources/config.json

@@ -29,9 +29,140 @@
     }
   },
   "Themes": [
+    {
+      "Hot Dog Stand": {
+        "Button.DefaultShadow": "Opaque",
+        "Dialog.DefaultShadow": "Transparent",
+        "Dialog.DefaultButtonAlignment": "Center",
+        "ColorSchemes": [
+          {
+            "Toplevel": {
+              "Normal": {
+                "Foreground": "Black",
+                "Background": "#FFFF00"
+              },
+              "Focus": {
+                "Foreground": "White",
+                "Background": "Black"
+              },
+              "HotNormal": {
+                "Foreground": "#FF0000",
+                "Background": "#FFFF00"
+              },
+              "HotFocus": {
+                "Foreground": "#FFFF00",
+                "Background": "White"
+              },
+              "Disabled": {
+                "Foreground": "BrightGreen",
+                "Background": "Gray"
+              }
+            }
+          },
+          {
+            "Base": {
+              "Normal": {
+                "Foreground": "Black",
+                "Background": "#FFFF00"
+              },
+              "Focus": {
+                "Foreground": "White",
+                "Background": "Black"
+              },
+              "HotNormal": {
+                "Foreground": "#FF0000",
+                "Background": "#FFFF00"
+              },
+              "HotFocus": {
+                "Foreground": "#FFFF00",
+                "Background": "White"
+              },
+              "Disabled": {
+                "Foreground": "BrightGreen",
+                "Background": "Gray"
+              }
+            }
+          },
+          {
+            "Dialog": {
+              "Normal": {
+                "Foreground": "Gray",
+                "Background": "#FF0000"
+              },
+              "Focus": {
+                "Foreground": "#FF0000",
+                "Background": "White"
+              },
+              "HotNormal": {
+                "Foreground": "#FF0000",
+                "Background": "#FFF000"
+              },
+              "HotFocus": {
+                "Foreground": "Black",
+                "Background": "White"
+              },
+              "Disabled": {
+                "Foreground": "BrightYellow",
+                "Background": "Gray"
+              }
+            }
+          },
+          {
+            "Menu": {
+              "Normal": {
+                "Foreground": "Black",
+                "Background": "WHite"
+              },
+              "Focus": {
+                "Foreground": "White",
+                "Background": "Black"
+              },
+              "HotNormal": {
+                "Foreground": "#FF00",
+                "Background": "White"
+              },
+              "HotFocus": {
+                "Foreground": "#FF00",
+                "Background": "Black"
+              },
+              "Disabled": {
+                "Foreground": "Gray",
+                "Background": "White"
+              }
+            }
+          },
+          {
+            "Error": {
+              "Normal": {
+                "Foreground": "#FF00",
+                "Background": "#FFF000"
+              },
+              "Focus": {
+                "Foreground": "Black",
+                "Background": "#FFF000"
+              },
+              "HotNormal": {
+                "Foreground": "DarkGray",
+                "Background": "BrightYellow"
+              },
+              "HotFocus": {
+                "Foreground": "Red",
+                "Background": "BrightYellow"
+              },
+              "Disabled": {
+                "Foreground": "Gray",
+                "Background": "BrightYellow"
+              }
+            }
+          }
+        ]
+      }
+    },
     {
       "UI Catalog Theme": {
-        "Dialog.DefaultButtonAlignment": "Fill",
+        "Button.DefaultShadow": "Opaque",
+        "Dialog.DefaultShadow": "Transparent",
+        "Dialog.DefaultButtonAlignment": "Center",
         "ColorSchemes": [
           {
             "UI Catalog Scheme": {

+ 3 - 4
UICatalog/Scenarios/Adornments.cs

@@ -21,10 +21,9 @@ public class Adornments : Scenario
             AutoSelectViewToEdit = true,
             // This is for giggles, to show that the editor can be moved around.
             Arrangement = ViewArrangement.Movable,
-            X = Pos.AnchorEnd()
-
+            X = Pos.AnchorEnd(),
         };
-        editor.Border.Thickness = new Thickness (1, 3, 1, 1);
+        editor.Border.Thickness = new Thickness (1, 2, 1, 1);
 
         app.Add (editor);
 
@@ -104,7 +103,7 @@ public class Adornments : Scenario
                                   window.Padding.Add (labelInPadding);
 
                                   var textFieldInPadding = new TextField
-                                  { X = Pos.Right (labelInPadding) + 1, Y = Pos.Top (labelInPadding), Width = 15, Text = "some text" };
+                                      { X = Pos.Right (labelInPadding) + 1, Y = Pos.Top (labelInPadding), Width = 15, Text = "some text" };
                                   textFieldInPadding.Accept += (s, e) => MessageBox.Query (20, 7, "TextField", textFieldInPadding.Text, "Ok");
                                   window.Padding.Add (textFieldInPadding);
 

+ 1 - 0
UICatalog/Scenarios/Bars.cs

@@ -193,6 +193,7 @@ public class Bars : Scenario
                                  {
                                      eventSource.Add ($"Accept: {sh!.SuperView.Id} {sh!.CommandView.Text}");
                                      eventLog.MoveDown ();
+                                     args.Cancel = true;
                                  };
                 }
             }

+ 1 - 0
UICatalog/Scenarios/BorderEditor.cs

@@ -32,6 +32,7 @@ public class BorderEditor : AdornmentEditor
         _rbBorderStyle = new RadioGroup
         {
             X = 0,
+            // BUGBUG: Hack until dimauto is working properly
             Y = Pos.Bottom (Subviews [^1]),
             Width = Dim.Width (Subviews [^2]) + Dim.Width (Subviews [^1]) - 1,
             SelectedItem = (int)(((Border)AdornmentToEdit)?.LineStyle ?? LineStyle.None),

+ 6 - 7
UICatalog/Scenarios/Buttons.cs

@@ -129,7 +129,7 @@ public class Buttons : Scenario
             X = 0,
             Y = Pos.Bottom (removeButton) + 1,
             Width = Dim.Percent (50),
-            Height = 5,
+            Height = 6,
             Title = "Computed Layout"
         };
         main.Add (computedFrame);
@@ -140,7 +140,6 @@ public class Buttons : Scenario
             X = 0,
             Y = Pos.Center () - 1,
             Width = 30,
-            Height = 1,
             ColorScheme = Colors.ColorSchemes ["Error"],
             Text = "Move This \u263b Button v_ia Pos"
         };
@@ -157,7 +156,6 @@ public class Buttons : Scenario
             Y = Pos.Center () + 1,
             X = 0,
             Width = 30,
-            Height = 1,
             Text = "Grow This \u263a Button _via Pos",
             ColorScheme = Colors.ColorSchemes ["Error"],
         };
@@ -173,7 +171,7 @@ public class Buttons : Scenario
             X = Pos.Right (computedFrame),
             Y = Pos.Bottom (removeButton) + 1,
             Width = Dim.Fill (),
-            Height = 5,
+            Height = 6,
             Title = "Absolute Layout"
         };
         main.Add (absoluteFrame);
@@ -211,7 +209,8 @@ public class Buttons : Scenario
 
         var label = new Label
         {
-            X = 2, Y = Pos.Bottom (computedFrame) + 1, Text = "Text Alignment (changes the four buttons above): "
+            X = 2, Y = Pos.Bottom (computedFrame) + 1, 
+            Text = "Text Alignment (changes the four buttons above): "
         };
         main.Add (label);
 
@@ -262,7 +261,6 @@ public class Buttons : Scenario
         {
             X = 2,
             Y = Pos.Bottom (radioGroup) + 1,
-            Height = 1,
             Width = Dim.Width (computedFrame) - 2,
             ColorScheme = Colors.ColorSchemes ["TopLevel"],
             Text = mhkb
@@ -276,7 +274,6 @@ public class Buttons : Scenario
         {
             X = Pos.Left (absoluteFrame) + 1,
             Y = Pos.Bottom (radioGroup) + 1,
-            Height = 1,
             Width = Dim.Width (absoluteFrame) - 2,
             ColorScheme = Colors.ColorSchemes ["TopLevel"],
             Text = muhkb
@@ -433,6 +430,7 @@ public class Buttons : Scenario
                 Title = $"{CM.Glyphs.DownArrow}",
                 WantContinuousButtonPressed = true,
                 CanFocus = false,
+                ShadowStyle = ShadowStyle.None,
             };
 
             _number = new ()
@@ -457,6 +455,7 @@ public class Buttons : Scenario
                 Title = $"{CM.Glyphs.UpArrow}",
                 WantContinuousButtonPressed = true,
                 CanFocus = false,
+                ShadowStyle = ShadowStyle.None,
             };
 
             CanFocus = true;

+ 1 - 0
UICatalog/Scenarios/ExpanderButton.cs

@@ -36,6 +36,7 @@ public class ExpanderButton : Button
         Height = 1;
         NoDecorations = true;
         NoPadding = true;
+        ShadowStyle = ShadowStyle.None;
 
         AddCommand (Command.HotKey, Toggle);
         AddCommand (Command.ToggleExpandCollapse, Toggle);

+ 2 - 2
UICatalog/Scenarios/HotKeys.cs

@@ -77,7 +77,7 @@ public class HotKeys : Scenario
         {
             Title = "B_utton with Frame (focusable)",
             CanFocus = true,
-            X = Pos.Right (buttonWithFrameLabel) + 1, Y = Pos.Top (buttonWithFrameLabel), Width = 40, Height = 3,
+            X = Pos.Right (buttonWithFrameLabel) + 1, Y = Pos.Top (buttonWithFrameLabel), Width = 40,
             BorderStyle = LineStyle.Dashed
         };
         app.Add (buttonWithFrameFocusable);
@@ -88,7 +88,7 @@ public class HotKeys : Scenario
         var buttonWithFrame = new Button
         {
             Title = "Button with Frame (not focusab_le)",
-            X = Pos.Right (buttonWithFrameLabel) + 1, Y = Pos.Top (buttonWithFrameLabel), Width = 40, Height = 3,
+            X = Pos.Right (buttonWithFrameLabel) + 1, Y = Pos.Top (buttonWithFrameLabel), Width = 40,
             CanFocus = false,
             BorderStyle = LineStyle.Dashed
         };

+ 7 - 4
UICatalog/Scenarios/LineDrawing.cs

@@ -42,12 +42,15 @@ public class LineDrawing : Scenario
 
             foreach (LineCanvas canvas in _layers)
             {
-                foreach (KeyValuePair<Point, Cell> c in canvas.GetCellMap ())
+                foreach (KeyValuePair<Point, Cell?> c in canvas.GetCellMap ())
                 {
-                    Driver.SetAttribute (c.Value.Attribute ?? ColorScheme.Normal);
+                    if (c.Value is { })
+                    {
+                        Driver.SetAttribute (c.Value.Value.Attribute ?? ColorScheme.Normal);
 
-                    // TODO: #2616 - Support combining sequences that don't normalize
-                    AddRune (c.Key.X, c.Key.Y, c.Value.Rune);
+                        // TODO: #2616 - Support combining sequences that don't normalize
+                        AddRune (c.Key.X, c.Key.Y, c.Value.Value.Rune);
+                    }
                 }
             }
         }

+ 36 - 0
UICatalog/Scenarios/MarginEditor.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Linq;
 using Terminal.Gui;
 
 namespace UICatalog.Scenarios;
@@ -9,10 +10,45 @@ public class MarginEditor : AdornmentEditor
     {
         Title = "_Margin";
         Initialized += MarginEditor_Initialized;
+        AdornmentChanged += MarginEditor_AdornmentChanged;
+    }
+
+    RadioGroup _rgShadow;
+
+    private void MarginEditor_AdornmentChanged (object sender, EventArgs e)
+    {
+        if (AdornmentToEdit is { })
+        {
+            _rgShadow.SelectedItem = (int)((Margin)AdornmentToEdit).ShadowStyle;
+        }
     }
 
     private void MarginEditor_Initialized (object sender, EventArgs e)
     {
+        _rgShadow = new RadioGroup
+        {
+            X = 0,
+            //Y = Pos.AnchorEnd(),
+            // BUGBUG: Hack until dimauto is working properly
+            Y = Pos.Bottom (Subviews [^1]),
+
+            SuperViewRendersLineCanvas = true,
+            Title = "_Shadow",
+            BorderStyle = LineStyle.Dashed,
+            Enabled = AdornmentToEdit is { },
+            RadioLabels = Enum.GetNames (typeof (ShadowStyle)),
+        };
+
+        if (AdornmentToEdit is { })
+        {
+            _rgShadow.SelectedItem = (int)((Margin)AdornmentToEdit).ShadowStyle;
+        }
+
+        _rgShadow.SelectedItemChanged += (sender, args) =>
+                                        {
+                                            ((Margin)AdornmentToEdit).ShadowStyle = (ShadowStyle)args.SelectedItem;
+                                        };
 
+        Add (_rgShadow);
     }
 }

+ 2 - 2
UICatalog/Scenarios/PosAlignDemo.cs

@@ -326,7 +326,7 @@ public sealed class PosAlignDemo : Scenario
                                         aligner.Alignment,
                                         aligner.AlignmentModes,
                                         posAlign!.GroupId);
-                    view.Margin.Thickness = new (_leftMargin, 0, 0, 0);
+                    view.Margin.Thickness = new (_leftMargin, view.Margin.Thickness.Top, view.Margin.Thickness.Right, view.Margin.Thickness.Bottom);
                 }
                 else
                 {
@@ -337,7 +337,7 @@ public sealed class PosAlignDemo : Scenario
                                         aligner.AlignmentModes,
                                         posAlign!.GroupId);
 
-                    view.Margin.Thickness = new (0, _topMargin, 0, 0);
+                    view.Margin.Thickness = new (view.Margin.Thickness.Left, _topMargin, view.Margin.Thickness.Right, view.Margin.Thickness.Bottom);
                 }
             }
         }

+ 65 - 0
UICatalog/Scenarios/ShadowStyles.cs

@@ -0,0 +1,65 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics.Metrics;
+using System.Linq;
+using Terminal.Gui;
+
+namespace UICatalog.Scenarios;
+
+[ScenarioMetadata ("ShadowStyles Demo", "Demonstrates ShadowStyles Effects.")]
+[ScenarioCategory ("Layout")]
+public class ShadowStyles : Scenario
+{
+    public override void Main ()
+    {
+        Application.Init ();
+
+        Window app = new ()
+        {
+            Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}"
+        };
+
+
+        var editor = new AdornmentsEditor ()
+        {
+            AutoSelectViewToEdit = true,
+        };
+        app.Add (editor);
+
+        Window win = new ()
+        {
+            X = Pos.Right (editor),
+            Y = 0,
+            Width = Dim.Percent (30),
+            Height = Dim.Percent (30),
+            Title = "Shadow Window",
+            Arrangement = ViewArrangement.Movable,
+            ShadowStyle = ShadowStyle.Transparent
+        };
+
+        var buttonInWin = new Button
+        {
+            X = Pos.Center (),
+            Y = Pos.Center (), Text = "Button in Window",
+            ShadowStyle = ShadowStyle.Opaque
+        };
+        win.Add (buttonInWin);
+        app.Add (win);
+
+        var button = new Button
+        {
+            X = Pos.Right (editor) + 10,
+            Y = Pos.Center (), Text = "Button",
+            ShadowStyle = ShadowStyle.Opaque
+        };
+        app.Add (button);
+
+        Application.Run (app);
+        app.Dispose ();
+
+        Application.Shutdown ();
+
+        return;
+    }
+}

+ 13 - 2
UICatalog/Scenarios/Shortcuts.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.ObjectModel;
+using System.ComponentModel;
 using System.Diagnostics;
 using System.Linq;
 using System.Text;
@@ -141,7 +142,7 @@ public class Shortcuts : Scenario
             Width = Dim.Width (vShortcut3),
             CommandView = new Button
             {
-                Title = "B_utton",
+                Title = "_Button",
             },
             HelpText = "Width is Fill",
             Key = Key.K,
@@ -362,11 +363,21 @@ public class Shortcuts : Scenario
                                        eventLog.MoveDown ();
                                        args.Cancel = true;
                                    };
+
+                shortcut.CommandView.Accept += (o, args) =>
+                                               {
+                                                   eventSource.Add ($"CommandView.Accept: {shortcut!.CommandView.Text}");
+                                                   eventLog.MoveDown ();
+                                               };
             }
         }
 
         //((CheckBox)vShortcut5.CommandView).OnToggled ();
     }
 
-    private void Button_Clicked (object sender, EventArgs e) { MessageBox.Query ("Hi", $"You clicked {sender}"); }
+    private void Button_Clicked (object sender, CancelEventArgs e)
+    {
+        //e.Cancel = true;
+        MessageBox.Query ("Hi", $"You clicked {sender}"); 
+    }
 }

+ 28 - 14
UICatalog/Scenarios/Sliders.cs

@@ -174,7 +174,7 @@ public class Sliders : Scenario
             BorderStyle = LineStyle.Single
         };
 
-        optionsSlider.Style.SetChar.Attribute = new Attribute (Color.BrightGreen, Color.Black);
+        optionsSlider.Style.SetChar = optionsSlider.Style.SetChar with { Attribute = new Attribute (Color.BrightGreen, Color.Black) };
         optionsSlider.Style.LegendAttributes.SetAttribute = new Attribute (Color.Green, Color.Black);
 
         optionsSlider.Options = new List<SliderOption<string>>
@@ -440,10 +440,10 @@ public class Sliders : Scenario
 
         foreach (Slider s in app.Subviews.OfType<Slider> ())
         {
-            s.Style.OptionChar.Attribute = app.GetNormalColor ();
-            s.Style.SetChar.Attribute = app.GetNormalColor ();
+            s.Style.OptionChar = s.Style.OptionChar with { Attribute = app.GetNormalColor () };
+            s.Style.SetChar = s.Style.SetChar with { Attribute = app.GetNormalColor () };
             s.Style.LegendAttributes.SetAttribute = app.GetNormalColor ();
-            s.Style.RangeChar.Attribute = app.GetNormalColor ();
+            s.Style.RangeChar = s.Style.RangeChar with { Attribute = app.GetNormalColor () };
         }
 
         Slider<(Color, Color)> sliderFGColor = new ()
@@ -463,7 +463,7 @@ public class Sliders : Scenario
             UseMinimumSize = true
         };
 
-        sliderFGColor.Style.SetChar.Attribute = new Attribute (Color.BrightGreen, Color.Black);
+        sliderFGColor.Style.SetChar = sliderFGColor.Style.SetChar with { Attribute = new Attribute (Color.BrightGreen, Color.Black) };
         sliderFGColor.Style.LegendAttributes.SetAttribute = new Attribute (Color.Green, Color.Blue);
 
         List<SliderOption<(Color, Color)>> colorOptions = new ();
@@ -505,16 +505,30 @@ public class Sliders : Scenario
                                                                                )
                                                     };
 
-                                                    s.Style.OptionChar.Attribute = new Attribute (data.Item1, s.ColorScheme.Normal.Background);
+                                                    s.Style.OptionChar = s.Style.OptionChar with
+                                                    {
+                                                        Attribute = new Attribute (data.Item1, s.ColorScheme.Normal.Background)
+                                                    };
 
-                                                    s.Style.SetChar.Attribute = new Attribute (
-                                                                                               data.Item1,
-                                                                                               s.Style.SetChar.Attribute?.Background
-                                                                                               ?? s.ColorScheme.Normal.Background
-                                                                                              );
+                                                    s.Style.SetChar = s.Style.SetChar with
+                                                    {
+                                                        Attribute = new Attribute (
+                                                                                   data.Item1,
+                                                                                   s.Style.SetChar.Attribute?.Background
+                                                                                   ?? s.ColorScheme.Normal.Background
+                                                                                  )
+                                                    };
                                                     s.Style.LegendAttributes.SetAttribute = new Attribute (data.Item1, s.ColorScheme.Normal.Background);
-                                                    s.Style.RangeChar.Attribute = new Attribute (data.Item1, s.ColorScheme.Normal.Background);
-                                                    s.Style.SpaceChar.Attribute = new Attribute (data.Item1, s.ColorScheme.Normal.Background);
+
+                                                    s.Style.RangeChar = s.Style.RangeChar with
+                                                    {
+                                                        Attribute = new Attribute (data.Item1, s.ColorScheme.Normal.Background)
+                                                    };
+
+                                                    s.Style.SpaceChar = s.Style.SpaceChar with
+                                                    {
+                                                        Attribute = new Attribute (data.Item1, s.ColorScheme.Normal.Background)
+                                                    };
 
                                                     s.Style.LegendAttributes.NormalAttribute =
                                                         new Attribute (data.Item1, s.ColorScheme.Normal.Background);
@@ -536,7 +550,7 @@ public class Sliders : Scenario
             UseMinimumSize = true
         };
 
-        sliderBGColor.Style.SetChar.Attribute = new Attribute (Color.BrightGreen, Color.Black);
+        sliderBGColor.Style.SetChar = sliderBGColor.Style.SetChar with { Attribute = new Attribute (Color.BrightGreen, Color.Black) };
         sliderBGColor.Style.LegendAttributes.SetAttribute = new Attribute (Color.Green, Color.Blue);
 
         sliderBGColor.Options = colorOptions;

+ 5 - 3
UICatalog/UICatalog.cs

@@ -330,7 +330,6 @@ internal class UICatalogApp
             // made by Scenario.Init() above
             // TODO: Throw if shutdown was not called already
             Application.Shutdown ();
-
             VerifyObjectsWereDisposed ();
         }
 
@@ -388,6 +387,8 @@ internal class UICatalogApp
 
         public UICatalogTopLevel ()
         {
+            _diagnosticFlags = View.Diagnostics;
+
             _themeMenuItems = CreateThemeMenuItems ();
             _themeMenuBarItem = new ("_Themes", _themeMenuItems);
 
@@ -450,6 +451,7 @@ internal class UICatalogApp
             StatusBar = new ()
             {
                 Visible = ShowStatusBar,
+                AlignmentModes = AlignmentModes.IgnoreFirstOrLast
             };
 
             if (StatusBar is { })
@@ -525,7 +527,7 @@ internal class UICatalogApp
                 AllowsMarking = false,
                 CanFocus = true,
                 Title = "_Categories",
-                BorderStyle = LineStyle.Single,
+                BorderStyle = LineStyle.Rounded,
                 SuperViewRendersLineCanvas = true,
                 Source = new ListWrapper<string> (_categories)
             };
@@ -545,7 +547,7 @@ internal class UICatalogApp
                 //AllowsMarking = false,
                 CanFocus = true,
                 Title = "_Scenarios",
-                BorderStyle = LineStyle.Single,
+                BorderStyle = CategoryList.BorderStyle,
                 SuperViewRendersLineCanvas = true
             };
 

+ 0 - 9
UnitTests/Configuration/ConfigurationMangerTests.cs

@@ -35,14 +35,12 @@ public class ConfigurationManagerTests
             Assert.Equal (KeyCode.Q, Application.QuitKey.KeyCode);
             Assert.Equal (KeyCode.F, Application.AlternateForwardKey.KeyCode);
             Assert.Equal (KeyCode.B, Application.AlternateBackwardKey.KeyCode);
-            Assert.True (Application.IsMouseDisabled);
         }
 
         // act
         Settings ["Application.QuitKey"].PropertyValue = Key.Q;
         Settings ["Application.AlternateForwardKey"].PropertyValue = Key.F;
         Settings ["Application.AlternateBackwardKey"].PropertyValue = Key.B;
-        Settings ["Application.IsMouseDisabled"].PropertyValue = true;
 
         Apply ();
 
@@ -156,7 +154,6 @@ public class ConfigurationManagerTests
         Settings ["Application.QuitKey"].PropertyValue = Key.Q;
         Settings ["Application.AlternateForwardKey"].PropertyValue = Key.F;
         Settings ["Application.AlternateBackwardKey"].PropertyValue = Key.B;
-        Settings ["Application.IsMouseDisabled"].PropertyValue = true;
 
         Updated += ConfigurationManager_Updated;
         var fired = false;
@@ -177,7 +174,6 @@ public class ConfigurationManagerTests
                           KeyCode.PageUp | KeyCode.CtrlMask,
                           ((Key)Settings ["Application.AlternateBackwardKey"].PropertyValue).KeyCode
                          );
-            Assert.False ((bool)Settings ["Application.IsMouseDisabled"].PropertyValue);
         }
 
         Load (true);
@@ -235,14 +231,12 @@ public class ConfigurationManagerTests
         Settings ["Application.QuitKey"].PropertyValue = Key.Q;
         Settings ["Application.AlternateForwardKey"].PropertyValue = Key.F;
         Settings ["Application.AlternateBackwardKey"].PropertyValue = Key.B;
-        Settings ["Application.IsMouseDisabled"].PropertyValue = true;
         Settings.Apply ();
 
         // assert apply worked
         Assert.Equal (KeyCode.Q, Application.QuitKey.KeyCode);
         Assert.Equal (KeyCode.F, Application.AlternateForwardKey.KeyCode);
         Assert.Equal (KeyCode.B, Application.AlternateBackwardKey.KeyCode);
-        Assert.True (Application.IsMouseDisabled);
 
         //act
         Reset ();
@@ -253,13 +247,11 @@ public class ConfigurationManagerTests
         Assert.Equal (Key.Esc, Application.QuitKey);
         Assert.Equal (KeyCode.PageDown | KeyCode.CtrlMask, Application.AlternateForwardKey.KeyCode);
         Assert.Equal (KeyCode.PageUp | KeyCode.CtrlMask, Application.AlternateBackwardKey.KeyCode);
-        Assert.False (Application.IsMouseDisabled);
 
         // arrange
         Settings ["Application.QuitKey"].PropertyValue = Key.Q;
         Settings ["Application.AlternateForwardKey"].PropertyValue = Key.F;
         Settings ["Application.AlternateBackwardKey"].PropertyValue = Key.B;
-        Settings ["Application.IsMouseDisabled"].PropertyValue = true;
         Settings.Apply ();
 
         Locations = ConfigLocations.DefaultOnly;
@@ -274,7 +266,6 @@ public class ConfigurationManagerTests
         Assert.Equal (KeyCode.Esc, Application.QuitKey.KeyCode);
         Assert.Equal (KeyCode.PageDown | KeyCode.CtrlMask, Application.AlternateForwardKey.KeyCode);
         Assert.Equal (KeyCode.PageUp | KeyCode.CtrlMask, Application.AlternateBackwardKey.KeyCode);
-        Assert.False (Application.IsMouseDisabled);
         Reset ();
     }
 

+ 0 - 6
UnitTests/Configuration/SettingsScopeTests.cs

@@ -20,13 +20,11 @@ public class SettingsScopeTests
                       KeyCode.PageUp | KeyCode.CtrlMask,
                       ((Key)Settings ["Application.AlternateBackwardKey"].PropertyValue).KeyCode
                      );
-        Assert.False ((bool)Settings ["Application.IsMouseDisabled"].PropertyValue);
 
         // act
         Settings ["Application.QuitKey"].PropertyValue = Key.Q;
         Settings ["Application.AlternateForwardKey"].PropertyValue = Key.F;
         Settings ["Application.AlternateBackwardKey"].PropertyValue = Key.B;
-        Settings ["Application.IsMouseDisabled"].PropertyValue = true;
 
         Settings.Apply ();
 
@@ -34,7 +32,6 @@ public class SettingsScopeTests
         Assert.Equal (KeyCode.Q, Application.QuitKey.KeyCode);
         Assert.Equal (KeyCode.F, Application.AlternateForwardKey.KeyCode);
         Assert.Equal (KeyCode.B, Application.AlternateBackwardKey.KeyCode);
-        Assert.True (Application.IsMouseDisabled);
     }
 
     [Fact]
@@ -49,13 +46,11 @@ public class SettingsScopeTests
         ///Don't set Quitkey
         updatedSettings ["Application.AlternateForwardKey"].PropertyValue = Key.F;
         updatedSettings ["Application.AlternateBackwardKey"].PropertyValue = Key.B;
-        updatedSettings ["Application.IsMouseDisabled"].PropertyValue = true;
 
         Settings.Update (updatedSettings);
         Assert.Equal (KeyCode.End, ((Key)Settings ["Application.QuitKey"].PropertyValue).KeyCode);
         Assert.Equal (KeyCode.F, ((Key)updatedSettings ["Application.AlternateForwardKey"].PropertyValue).KeyCode);
         Assert.Equal (KeyCode.B, ((Key)updatedSettings ["Application.AlternateBackwardKey"].PropertyValue).KeyCode);
-        Assert.True ((bool)updatedSettings ["Application.IsMouseDisabled"].PropertyValue);
     }
 
     [Fact]
@@ -72,7 +67,6 @@ public class SettingsScopeTests
         Assert.True (Settings ["Application.QuitKey"].PropertyValue is Key);
         Assert.True (Settings ["Application.AlternateForwardKey"].PropertyValue is Key);
         Assert.True (Settings ["Application.AlternateBackwardKey"].PropertyValue is Key);
-        Assert.True (Settings ["Application.IsMouseDisabled"].PropertyValue is bool);
 
         Assert.True (Settings ["Theme"].PropertyValue is string);
         Assert.Equal ("Default", Settings ["Theme"].PropertyValue as string);

+ 98 - 0
UnitTests/View/Adornment/ShadowStyletests.cs

@@ -0,0 +1,98 @@
+using Xunit.Abstractions;
+
+namespace Terminal.Gui.ViewTests;
+
+public class ShadowStyleTests (ITestOutputHelper _output)
+{
+    [Fact]
+    public void Default_None ()
+    {
+        var view = new View ();
+        Assert.Equal (ShadowStyle.None, view.ShadowStyle);
+        Assert.Equal (ShadowStyle.None, view.Margin.ShadowStyle);
+        view.Dispose ();
+    }
+
+    [Theory]
+    [InlineData (ShadowStyle.None)]
+    [InlineData (ShadowStyle.Opaque)]
+    [InlineData (ShadowStyle.Transparent)]
+    public void Set_View_Sets_Margin (ShadowStyle style)
+    {
+        var view = new View ();
+
+        view.ShadowStyle = style;
+        Assert.Equal (style, view.ShadowStyle);
+        Assert.Equal (style, view.Margin.ShadowStyle);
+        view.Dispose ();
+    }
+
+    [Theory]
+    [InlineData (ShadowStyle.None, 3)]
+    [InlineData (ShadowStyle.Opaque, 4)]
+    [InlineData (ShadowStyle.Transparent, 4)]
+    public void Style_Changes_Magin_Thickness (ShadowStyle style, int expected)
+    {
+        var view = new View ();
+        view.Margin.Thickness = new (3);
+        view.ShadowStyle = style;
+        Assert.Equal (new (3, 3, expected, expected), view.Margin.Thickness);
+
+        view.ShadowStyle = ShadowStyle.None;
+        Assert.Equal (new (3), view.Margin.Thickness);
+        view.Dispose ();
+    }
+
+    // Visual tests
+    [Theory]
+    [InlineData (
+                    ShadowStyle.None,
+                    """
+                    01#$
+                    AB#$
+                    !@#$
+                    !@#$
+                    """)]
+    [InlineData (
+                    ShadowStyle.Opaque,
+                    """
+                    01#$
+                    AB▌$
+                    !▀▘$
+                    !@#$
+                    """)]
+    [InlineData (
+                    ShadowStyle.Transparent,
+                    """
+                    01#$
+                    AB#$
+                    !@#$
+                    !@#$
+                    """)]
+    [SetupFakeDriver]
+    public void Visual_Test (ShadowStyle style, string expected)
+    {
+        var superView = new View
+        {
+            Width = 4,
+            Height = 4,
+            Text = "!@#$".Repeat (4)
+        };
+        superView.TextFormatter.WordWrap = true;
+
+        var view = new View
+        {
+            Text = "01\nAB",
+            Width = Dim.Auto (),
+            Height = Dim.Auto ()
+        };
+        view.ShadowStyle = style;
+        superView.Add (view);
+        superView.BeginInit ();
+        superView.EndInit ();
+        superView.Draw ();
+
+        TestHelpers.AssertDriverContentsWithFrameAre (expected, _output);
+        view.Dispose ();
+    }
+}

+ 1 - 1
UnitTests/View/NeedsDisplayTests.cs

@@ -14,7 +14,7 @@ public class NeedsDisplayTests ()
         view.BeginInit();
         view.EndInit();
         Assert.False (view.NeedsDisplay);
-        Assert.False (view.SubViewNeedsDisplay);
+        //Assert.False (view.SubViewNeedsDisplay);
     }
 
 

+ 6 - 4
UnitTests/View/ViewTests.cs

@@ -460,14 +460,16 @@ At 0,0
             ColorScheme = Colors.ColorSchemes ["Menu"], X = 0, Y = 0, Text = "This should be the first line."
         };
 
-        var button = new Button
+        var view = new View
         {
             X = 0, // don't overcomplicate unit tests
             Y = 1,
+            Height = Dim.Auto (DimAutoStyle.Text),
+            Width = Dim.Auto(DimAutoStyle.Text),
             Text = "Press me!"
         };
 
-        frame.Add (label, button);
+        frame.Add (label, view);
 
         frame.X = Pos.Center ();
         frame.Y = Pos.Center ();
@@ -486,7 +488,7 @@ At 0,0
 
         label.LayoutComplete += (s, e) => { Assert.Equal (new (0, 0, 38, 1), label._needsDisplayRect); };
 
-        button.LayoutComplete += (s, e) => { Assert.Equal (new (0, 0, 13, 1), button._needsDisplayRect); };
+        view.LayoutComplete += (s, e) => { Assert.Equal (new (0, 0, 13, 1), view._needsDisplayRect); };
 
         Assert.Equal (new (0, 0, 80, 25), top.Frame);
         Assert.Equal (new (20, 8, 40, 8), frame.Frame);
@@ -501,7 +503,7 @@ At 0,0
                                     )
                      );
         Assert.Equal (new (0, 0, 30, 1), label.Frame);
-        Assert.Equal (new (0, 1, 13, 1), button.Frame); // this proves frame was set
+        Assert.Equal (new (0, 1, 9, 1), view.Frame); // this proves frame was set
         Application.End (runState);
         top.Dispose ();
     }

+ 1 - 1
UnitTests/Views/LabelTests.cs

@@ -247,7 +247,7 @@ This TextFormatter (tf2) with fill will be cleared on rewritten.       ",
         label.Text = "This label is rewritten.";
         Assert.True (label.NeedsDisplay);
         Assert.True (label.LayoutNeeded);
-        Assert.False (label.SubViewNeedsDisplay);
+        //Assert.False (label.SubViewNeedsDisplay);
         label.Draw ();
 
         tf1.Text = "This TextFormatter (tf1) is rewritten.";

+ 258 - 2
UnitTests/Views/ShortcutTests.cs

@@ -87,7 +87,7 @@ public class ShortcutTests
 
         shortcut = new ();
 
-        shortcut.CommandView = new()
+        shortcut.CommandView = new ()
         {
             Text = "T"
         };
@@ -104,7 +104,7 @@ public class ShortcutTests
 
         Assert.Equal (shortcut.Text, shortcut.HelpText);
 
-        shortcut = new()
+        shortcut = new ()
         {
             HelpText = "H"
         };
@@ -314,4 +314,260 @@ public class ShortcutTests
         Assert.False (shortcut.CanFocus);
         Assert.True (shortcut.CommandView.CanFocus);
     }
+
+    [Theory]
+    //  0123456789
+    // " C  0  A "
+    [InlineData (-1, 0)]
+    [InlineData (0, 1)]
+    [InlineData (1, 1)]
+    [InlineData (2, 1)]
+    [InlineData (3, 1)]
+    [InlineData (4, 1)]
+    [InlineData (5, 1)]
+    [InlineData (6, 1)]
+    [InlineData (7, 1)]
+    [InlineData (8, 1)]
+    [InlineData (9, 0)]
+    [AutoInitShutdown]
+    public void MouseClick_Fires_Accept (int x, int expectedAccept)
+    {
+        Toplevel current = new Toplevel ();
+        var shortcut = new Shortcut
+        {
+            Key = Key.A,
+            Text = "0",
+            Title = "C"
+        };
+        current.Add (shortcut);
+
+        Application.Begin (current);
+
+        int accepted = 0;
+        shortcut.Accept += (s, e) => accepted++;
+
+        Application.OnMouseEvent (new MouseEvent ()
+        {
+            Position = new Point (x, 0),
+            Flags = MouseFlags.Button1Clicked,
+        });
+
+        Assert.Equal (expectedAccept, accepted);
+
+        current.Dispose ();
+    }
+
+    [Theory]
+    //  0123456789
+    // " C  0  A "
+    [InlineData (-1, 0, 0)]
+    [InlineData (0, 1, 1)]
+    [InlineData (1, 0, 1)] // BUGBUG: This should be 1,1,1. We need to fix the logic in the Shortcut class.
+    [InlineData (2, 1, 1)]
+    [InlineData (3, 1, 1)]
+    [InlineData (4, 1, 1)]
+    [InlineData (5, 1, 1)]
+    [InlineData (6, 1, 1)]
+    [InlineData (7, 1, 1)]
+    [InlineData (8, 1, 1)]
+    [InlineData (9, 0, 0)]
+    [AutoInitShutdown]
+    public void MouseClick_Button_CommandView_Fires_Accept (int x, int expectedAccept, int expectedButtonAccept)
+    {
+        Toplevel current = new Toplevel ();
+        var shortcut = new Shortcut
+        {
+            Key = Key.A,
+            Text = "0",
+        };
+        shortcut.CommandView = new Button ()
+        {
+            Title = "C",
+            NoDecorations = true,
+            NoPadding = true,
+        };
+        int buttonAccepted = 0;
+        shortcut.CommandView.Accept += (s, e) =>
+                                       {
+                                           buttonAccepted++;
+                                       };
+        current.Add (shortcut);
+
+        Application.Begin (current);
+
+        int accepted = 0;
+        shortcut.Accept += (s, e) => accepted++;
+
+        //Assert.True (shortcut.HasFocus);
+
+        Application.OnMouseEvent (new MouseEvent ()
+        {
+            Position = new Point (x, 0),
+            Flags = MouseFlags.Button1Clicked,
+        });
+
+        Assert.Equal (expectedAccept, accepted);
+        Assert.Equal (expectedButtonAccept, buttonAccepted);
+
+        current.Dispose ();
+    }
+
+    [Theory]
+    [InlineData (true, KeyCode.A, 1)]
+    [InlineData (true, KeyCode.C, 1)]
+    [InlineData (true, KeyCode.C | KeyCode.AltMask, 1)]
+    [InlineData (true, KeyCode.Enter, 1)]
+    [InlineData (true, KeyCode.Space, 0)]
+    [InlineData (true, KeyCode.F1, 0)]
+
+    [InlineData (false, KeyCode.A, 1)]
+    [InlineData (false, KeyCode.C, 1)]
+    [InlineData (false, KeyCode.C | KeyCode.AltMask, 1)]
+    [InlineData (false, KeyCode.Enter, 0)]
+    [InlineData (false, KeyCode.Space, 0)]
+    [InlineData (false, KeyCode.F1, 0)]
+    [AutoInitShutdown]
+    public void KeyDown_Invokes_Accept (bool canFocus, KeyCode key, int expectedAccept)
+    {
+        Toplevel current = new Toplevel ();
+        var shortcut = new Shortcut
+        {
+            Key = Key.A,
+            Text = "0",
+            Title = "_C",
+            CanFocus = canFocus
+        };
+        current.Add (shortcut);
+
+        Application.Begin (current);
+        Assert.Equal (canFocus, shortcut.HasFocus);
+
+        int accepted = 0;
+        shortcut.Accept += (s, e) => accepted++;
+
+        Application.OnKeyDown (key);
+
+        Assert.Equal (expectedAccept, accepted);
+
+        current.Dispose ();
+
+    }
+
+
+    [Theory]
+    [InlineData (KeyCode.A, 1)]
+    [InlineData (KeyCode.C, 1)]
+    [InlineData (KeyCode.C | KeyCode.AltMask, 1)]
+    [InlineData (KeyCode.Enter, 1)]
+    [InlineData (KeyCode.Space, 0)]
+    [InlineData (KeyCode.F1, 0)]
+    [AutoInitShutdown]
+    public void KeyDown_App_Scope_Invokes_Accept (KeyCode key, int expectedAccept)
+    {
+        Toplevel current = new Toplevel ();
+        var shortcut = new Shortcut
+        {
+            Key = Key.A,
+            KeyBindingScope = KeyBindingScope.Application,
+            Text = "0",
+            Title = "_C",
+        };
+        current.Add (shortcut);
+
+        Application.Begin (current);
+
+        int accepted = 0;
+        shortcut.Accept += (s, e) => accepted++;
+
+        Application.OnKeyDown (key);
+
+        Assert.Equal (expectedAccept, accepted);
+
+        current.Dispose ();
+    }
+
+
+    [Theory]
+    [InlineData (true, KeyCode.A, 1)]
+    [InlineData (true, KeyCode.C, 1)]
+    [InlineData (true, KeyCode.C | KeyCode.AltMask, 1)]
+    [InlineData (true, KeyCode.Enter, 1)]
+    [InlineData (true, KeyCode.Space, 0)]
+    [InlineData (true, KeyCode.F1, 0)]
+
+    [InlineData (false, KeyCode.A, 1)]
+    [InlineData (false, KeyCode.C, 1)]
+    [InlineData (false, KeyCode.C | KeyCode.AltMask, 1)]
+    [InlineData (false, KeyCode.Enter, 0)]
+    [InlineData (false, KeyCode.Space, 0)]
+    [InlineData (false, KeyCode.F1, 0)]
+    [AutoInitShutdown]
+    public void KeyDown_Invokes_Action (bool canFocus, KeyCode key, int expectedAction)
+    {
+        Toplevel current = new Toplevel ();
+        var shortcut = new Shortcut
+        {
+            Key = Key.A,
+            Text = "0",
+            Title = "_C",
+            CanFocus = canFocus
+        };
+        current.Add (shortcut);
+
+        Application.Begin (current);
+        Assert.Equal (canFocus, shortcut.HasFocus);
+
+        int action = 0;
+        shortcut.Action += () => action++;
+
+        Application.OnKeyDown (key);
+
+        Assert.Equal (expectedAction, action);
+
+        current.Dispose ();
+
+    }
+
+    [Theory]
+    [InlineData (true, KeyCode.A, 1)]
+    [InlineData (true, KeyCode.C, 1)]
+    [InlineData (true, KeyCode.C | KeyCode.AltMask, 1)]
+    [InlineData (true, KeyCode.Enter, 1)]
+    [InlineData (true, KeyCode.Space, 0)]
+    [InlineData (true, KeyCode.F1, 0)]
+
+    [InlineData (false, KeyCode.A, 1)]
+    [InlineData (false, KeyCode.C, 1)]
+    [InlineData (false, KeyCode.C | KeyCode.AltMask, 1)]
+    [InlineData (false, KeyCode.Enter, 0)]
+    [InlineData (false, KeyCode.Space, 0)]
+    [InlineData (false, KeyCode.F1, 0)]
+    [AutoInitShutdown]
+    public void KeyDown_App_Scope_Invokes_Action (bool canFocus, KeyCode key, int expectedAction)
+    {
+        Toplevel current = new Toplevel ();
+        var shortcut = new Shortcut
+        {
+            Key = Key.A,
+            KeyBindingScope = KeyBindingScope.Application,
+            Text = "0",
+            Title = "_C",
+            CanFocus = canFocus
+        };
+        current.Add (shortcut);
+
+        Application.Begin (current);
+        Assert.Equal (canFocus, shortcut.HasFocus);
+
+        int action = 0;
+        shortcut.Action += () => action++;
+
+        Application.OnKeyDown (key);
+
+        Assert.Equal (expectedAction, action);
+
+        current.Dispose ();
+
+    }
+
 }

+ 5 - 5
UnitTests/Views/ToplevelTests.cs

@@ -1167,12 +1167,12 @@ public class ToplevelTests (ITestOutputHelper output)
                         if (iterations == 1)
                         {
                             steps [0] = iterations;
-                            Assert.Null (e.View);
+                            Assert.Null (e.Leaving);
                         }
                         else
                         {
                             steps [3] = iterations;
-                            Assert.Equal (diag, e.View);
+                            Assert.Equal (diag, e.Leaving);
                         }
                     };
 
@@ -1181,7 +1181,7 @@ public class ToplevelTests (ITestOutputHelper output)
                         // This will never be raised
                         iterations++;
                         isLeaveTop = true;
-                        Assert.Equal (diag, e.View);
+                        Assert.Equal (diag, e.Leaving);
                     };
         top.Add (vt);
 
@@ -1207,7 +1207,7 @@ public class ToplevelTests (ITestOutputHelper output)
                         iterations++;
                         steps [1] = iterations;
                         isEnterDiag = true;
-                        Assert.Null (e.View);
+                        Assert.Null (e.Leaving);
                     };
 
         vd.Leave += (s, e) =>
@@ -1215,7 +1215,7 @@ public class ToplevelTests (ITestOutputHelper output)
                         iterations++;
                         steps [2] = iterations;
                         isLeaveDiag = true;
-                        Assert.Equal (top, e.View);
+                        Assert.Equal (top, e.Entering);
                     };
         diag.Add (vd);