From 6df264ee8cf4a771b08595a1a3a0086a450ae034 Mon Sep 17 00:00:00 2001 From: Tig Date: Mon, 24 Jun 2024 22:33:41 -0700 Subject: [PATCH] Added transparent shadow. updated Config Mgr --- Terminal.Gui/Resources/config.json | 4 +- Terminal.Gui/View/Adornment/Margin.cs | 134 +++++--------------- Terminal.Gui/View/Adornment/ShadowStyle.cs | 23 ++++ Terminal.Gui/View/Adornment/ShadowView.cs | 140 +++++++++++++++++++++ Terminal.Gui/View/ViewAdornments.cs | 17 ++- Terminal.Gui/Views/Button.cs | 8 +- Terminal.Gui/Views/Dialog.cs | 20 ++- Terminal.Gui/Views/MessageBox.cs | 7 +- Terminal.Gui/Views/Window.cs | 4 +- UICatalog/Resources/config.json | 2 - UICatalog/Scenarios/Buttons.cs | 4 +- UICatalog/Scenarios/ExpanderButton.cs | 2 +- UICatalog/Scenarios/MarginEditor.cs | 32 ++++- UICatalog/Scenarios/ThreeD.cs | 6 +- UICatalog/UICatalog.cs | 4 +- 15 files changed, 267 insertions(+), 140 deletions(-) create mode 100644 Terminal.Gui/View/Adornment/ShadowStyle.cs create mode 100644 Terminal.Gui/View/Adornment/ShadowView.cs diff --git a/Terminal.Gui/Resources/config.json b/Terminal.Gui/Resources/config.json index bdb642d36..7ec77e4f4 100644 --- a/Terminal.Gui/Resources/config.json +++ b/Terminal.Gui/Resources/config.json @@ -27,8 +27,10 @@ "Default": { "Dialog.DefaultButtonAlignment": "End", "Dialog.DefaultButtonAlignmentModes": "AddSpaceBetweenItems", - "FrameView.DefaultBorderStyle": "Single", + "FrameView.DefaultBorderStyle": "Rounded", "Window.DefaultBorderStyle": "Single", + "Dialog.DefaultBorderStyle": "Double", + "MessageBox.DefaultBorderStyle": "Double", "ColorSchemes": [ { "TopLevel": { diff --git a/Terminal.Gui/View/Adornment/Margin.cs b/Terminal.Gui/View/Adornment/Margin.cs index 1734de200..6c5660150 100644 --- a/Terminal.Gui/View/Adornment/Margin.cs +++ b/Terminal.Gui/View/Adornment/Margin.cs @@ -1,5 +1,6 @@ #nullable enable -using static Unix.Terminal.Curses; + +using System.Drawing; namespace Terminal.Gui; @@ -27,7 +28,7 @@ public class Margin : Adornment private void Margin_LayoutStarted (object? sender, LayoutEventArgs e) { // Adjust the shadow such that it is drawn aligned with the Border - if (_shadow && _rightShadow is { } && _bottomShadow is { }) + 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; @@ -37,7 +38,7 @@ public class Margin : Adornment private bool _pressed; private void Margin_Highlight (object? sender, HighlightEventArgs e) { - if (_shadow) + if (ShadowStyle != Gui.ShadowStyle.None) { if (_pressed && e.HighlightStyle == HighlightStyle.None) { @@ -75,6 +76,7 @@ public class Margin : Adornment } + /// public override void OnDrawContent (Rectangle viewport) { Rectangle screen = ViewportToScreen (viewport); @@ -82,7 +84,7 @@ public class Margin : Adornment Driver.SetAttribute (normalAttr); // This just draws/clears the thickness, not the insides. - if (Parent?.Shadow == true) + if (ShadowStyle != ShadowStyle.None) { screen = Rectangle.Inflate (screen, -1, -1); } @@ -132,45 +134,53 @@ public class Margin : Adornment } } - private bool _shadow; + /// + public override ShadowStyle ShadowStyle + { + get => base.ShadowStyle; + set + { + base.ShadowStyle = SetShadow (value); + } + } /// - /// Gets or sets whether the Margin includes a shadow effect. The shadow is drawn on the right and bottom sides of the + /// Sets whether the Margin includes a shadow effect. The shadow is drawn on the right and bottom sides of the /// Margin. /// - public bool EnableShadow (bool enable) + public ShadowStyle SetShadow (ShadowStyle style) { - if (_shadow == enable) + if (ShadowStyle == style) { - return _shadow; + // return style; } - if (_shadow) + if (ShadowStyle != ShadowStyle.None) { + // Turn off shadow Thickness = new (Thickness.Left, Thickness.Top, Thickness.Right - 1, Thickness.Bottom - 1); } - _shadow = enable; - - if (_shadow) + if (style != ShadowStyle.None) { + // Turn on shadow Thickness = new (Thickness.Left, Thickness.Top, Thickness.Right + 1, Thickness.Bottom + 1); } if (_rightShadow is { }) { - _rightShadow.Visible = _shadow; + _rightShadow.ShadowStyle = style; } if (_bottomShadow is { }) { - _bottomShadow.Visible = _shadow; + _bottomShadow.ShadowStyle = style; } - return _shadow; + return style; } - private View? _bottomShadow; - private View? _rightShadow; + private ShadowView? _bottomShadow; + private ShadowView? _rightShadow; /// public override void BeginInit () @@ -182,8 +192,7 @@ public class Margin : Adornment return; } - Attribute attr = Parent.GetNormalColor (); - + ShadowStyle = base.ShadowStyle; Add ( _rightShadow = new ShadowView { @@ -191,7 +200,7 @@ public class Margin : Adornment Y = 0, Width = 1, Height = Dim.Fill (), - Visible = _shadow, + ShadowStyle = ShadowStyle, Orientation = Orientation.Vertical }, _bottomShadow = new ShadowView @@ -200,88 +209,9 @@ public class Margin : Adornment Y = Pos.AnchorEnd (1), Width = Dim.Fill (), Height = 1, - Visible = _shadow, + ShadowStyle = ShadowStyle, Orientation = Orientation.Horizontal } ); } -} - -/// -/// Draws a shadow on the right or bottom of the view. -/// -internal class ShadowView : View -{ - // TODO: Add these to CM.Glyphs - private readonly char VERTICAL_START_GLYPH = '\u258C'; // Half: '\u2596'; - private readonly char VERTICAL_GLYPH = '\u258C'; - private readonly char HORIZONTAL_START_GLYPH = '\u2580'; // Half: '\u259d'; - private readonly char HORIZONTAL_GLYPH = '\u2580'; - private readonly char HORIZONTAL_END_GLYPH = '\u2598'; - - /// - /// Gets or sets the orientation of the shadow. - /// - public Orientation Orientation { get; set; } - - /// - public override Attribute GetNormalColor () - { - if (SuperView is Adornment adornment) - { - if (adornment.Parent.SuperView is { }) - { - Attribute attr = adornment.Parent.SuperView.GetNormalColor (); - return new (new Attribute (attr.Foreground.GetDarkerColor (), attr.Background)); - } - else - { - Attribute attr = Application.Top.GetNormalColor (); - return new (new Attribute (attr.Foreground.GetDarkerColor (), attr.Background)); - } - } - return base.GetNormalColor (); - } - - /// - public override void OnDrawContent (Rectangle viewport) - { - //base.OnDrawContent (viewport); - - if (Orientation == Orientation.Vertical) - { - DrawVerticalShadow (viewport); - } - else - { - DrawHorizontalShadow (viewport); - } - } - - private void DrawHorizontalShadow (Rectangle rectangle) - { - // Draw the start glyph - AddRune (0, 0, (Rune)HORIZONTAL_START_GLYPH); - - // Fill the rest of the rectangle with the glyph - for (var i = 1; i < rectangle.Width - 1; i++) - { - AddRune (i, 0, (Rune)HORIZONTAL_GLYPH); - } - - // Last is special - AddRune (rectangle.Width - 1, 0, (Rune)HORIZONTAL_END_GLYPH); - } - - private void DrawVerticalShadow (Rectangle viewport) - { - // Draw the start glyph - AddRune (0, 0, (Rune)VERTICAL_START_GLYPH); - - // Fill the rest of the rectangle with the glyph - for (var i = 1; i < viewport.Height; i++) - { - AddRune (0, i, (Rune)VERTICAL_GLYPH); - } - } -} +} \ No newline at end of file diff --git a/Terminal.Gui/View/Adornment/ShadowStyle.cs b/Terminal.Gui/View/Adornment/ShadowStyle.cs new file mode 100644 index 000000000..e95d76d5b --- /dev/null +++ b/Terminal.Gui/View/Adornment/ShadowStyle.cs @@ -0,0 +1,23 @@ +#nullable enable +namespace Terminal.Gui; + +/// +/// Defines the style of shadow to be drawn on the right and bottom sides of the . +/// +public enum ShadowStyle +{ + /// + /// No shadow. + /// + None, + + /// + /// A shadow that is drawn using block elements. Ideal for smaller views such as buttons. + /// + Opaque, + + /// + /// A shadow that is drawn using the underlying text with a darker background. Ideal for larger views such as windows. + /// + Transparent +} diff --git a/Terminal.Gui/View/Adornment/ShadowView.cs b/Terminal.Gui/View/Adornment/ShadowView.cs new file mode 100644 index 000000000..b0480c23f --- /dev/null +++ b/Terminal.Gui/View/Adornment/ShadowView.cs @@ -0,0 +1,140 @@ +#nullable enable +namespace Terminal.Gui; + +/// +/// Draws a shadow on the right or bottom of the view. Used internally by . +/// +internal class ShadowView : View +{ + // TODO: Add these to CM.Glyphs + private readonly char VERTICAL_START_GLYPH = '\u258C'; // Half: '\u2596'; + private readonly char VERTICAL_GLYPH = '\u258C'; + private readonly char HORIZONTAL_START_GLYPH = '\u2580'; // Half: '\u259d'; + private readonly char HORIZONTAL_GLYPH = '\u2580'; + private readonly char HORIZONTAL_END_GLYPH = '\u2598'; + + /// + public override Attribute GetNormalColor () + { + if (SuperView is Adornment adornment) + { + Attribute attr = Attribute.Default; + if (adornment.Parent.SuperView is { }) + { + attr = adornment.Parent.SuperView.GetNormalColor (); + } + else + { + 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 (); + } + + /// + 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; + } + } + + /// + /// Gets or sets the orientation of the shadow. + /// + public Orientation Orientation { get; set; } + + private ShadowStyle _shadowStyle; + 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, (Rune)HORIZONTAL_START_GLYPH); + + // Fill the rest of the rectangle with the glyph + for (var i = 1; i < rectangle.Width - 1; i++) + { + AddRune (i, 0, (Rune)HORIZONTAL_GLYPH); + } + + // Last is special + AddRune (rectangle.Width - 1, 0, (Rune)HORIZONTAL_END_GLYPH); + } + + private void DrawHorizontalShadowTransparent (Rectangle viewport) + { + Rectangle screen = ViewportToScreen (viewport); + + 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, (Rune)VERTICAL_START_GLYPH); + + // Fill the rest of the rectangle with the glyph + for (var i = 1; i < viewport.Height; i++) + { + AddRune (0, i, (Rune)VERTICAL_GLYPH); + } + } + + private void DrawVerticalShadowTransparent (Rectangle viewport) + { + Rectangle screen = ViewportToScreen (viewport); + + // Fill the rest of the rectangle with the glyph + for (int i = screen.Y; i < screen.Y + viewport.Height; i++) + { + Driver.Move (screen.X, i); + Driver.AddRune (Driver.Contents [i, screen.X].Rune); + } + } +} diff --git a/Terminal.Gui/View/ViewAdornments.cs b/Terminal.Gui/View/ViewAdornments.cs index a34cc65a7..27223ef11 100644 --- a/Terminal.Gui/View/ViewAdornments.cs +++ b/Terminal.Gui/View/ViewAdornments.cs @@ -1,4 +1,5 @@ using System.ComponentModel; +using System.Text.Json.Serialization; namespace Terminal.Gui; @@ -59,11 +60,7 @@ public partial class View /// public Margin Margin { get; private set; } - [SerializableConfigurationProperty (Scope = typeof (ThemeScope))] - public static bool DefaultShadow { get; set; } = false; - - - private bool _shadow; + private ShadowStyle _shadowStyle; /// /// 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. @@ -72,19 +69,19 @@ public partial class View /// Setting this property to will add a shadow to the right and bottom sides of the Margin. /// The View 's will be expanded to include the shadow. /// - public bool Shadow + public virtual ShadowStyle ShadowStyle { - get => _shadow; + get => _shadowStyle; set { - if (_shadow == value) + if (_shadowStyle == value) { return; } - _shadow = value; + _shadowStyle = value; if (Margin is { }) { - _shadow = Margin.EnableShadow (value); + Margin.ShadowStyle = value; } } } diff --git a/Terminal.Gui/Views/Button.cs b/Terminal.Gui/Views/Button.cs index 5a93405e2..98e299c4c 100644 --- a/Terminal.Gui/Views/Button.cs +++ b/Terminal.Gui/Views/Button.cs @@ -5,6 +5,8 @@ // Miguel de Icaza (miguel@gnome.org) // +using System.Text.Json.Serialization; + namespace Terminal.Gui; /// Button is a that provides an item that invokes raises the event. @@ -37,7 +39,9 @@ public class Button : View /// Gets or sets whether s are shown with a shadow effect by default. /// [SerializableConfigurationProperty (Scope = typeof (ThemeScope))] - public new static bool DefaultShadow { get; set; } = false; + [JsonConverter (typeof (JsonStringEnumConverter))] + + public static ShadowStyle DefaultShadow { get; set; } = ShadowStyle.Opaque; /// Initializes a new instance of . public Button () @@ -72,7 +76,7 @@ public class Button : View TitleChanged += Button_TitleChanged; MouseClick += Button_MouseClick; - Shadow = DefaultShadow; + ShadowStyle = DefaultShadow; } private bool _wantContinuousButtonPressed; diff --git a/Terminal.Gui/Views/Dialog.cs b/Terminal.Gui/Views/Dialog.cs index b0ee0169a..f6f670fcc 100644 --- a/Terminal.Gui/Views/Dialog.cs +++ b/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 + + /// + /// Gets or sets whether all s are shown with a shadow effect by default. + /// + [SerializableConfigurationProperty (Scope = typeof (ThemeScope))] + [JsonConverter (typeof (JsonStringEnumConverter))] + public new static ShadowStyle DefaultShadow { get; set; } = ShadowStyle.Transparent; + /// /// Defines the default border styling for . Can be configured via /// . /// - //[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.Rounded; + private readonly List