Added transparent shadow.

updated Config Mgr
This commit is contained in:
Tig
2024-06-24 22:33:41 -07:00
parent 8c82881ca2
commit 6df264ee8c
15 changed files with 267 additions and 140 deletions

View File

@@ -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": {

View File

@@ -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
}
/// <inheritdoc />
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;
/// <inheritdoc />
public override ShadowStyle ShadowStyle
{
get => base.ShadowStyle;
set
{
base.ShadowStyle = SetShadow (value);
}
}
/// <summary>
/// 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.
/// </summary>
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;
/// <inheritdoc/>
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
}
);
}
}
/// <summary>
/// Draws a shadow on the right or bottom of the view.
/// </summary>
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';
/// <summary>
/// Gets or sets the orientation of the shadow.
/// </summary>
public Orientation Orientation { get; set; }
/// <inheritdoc />
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 ();
}
/// <inheritdoc/>
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);
}
}
}
}

View File

@@ -0,0 +1,23 @@
#nullable enable
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
}

View File

@@ -0,0 +1,140 @@
#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
{
// 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';
/// <inheritdoc/>
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 ();
}
/// <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; }
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);
}
}
}

View File

@@ -1,4 +1,5 @@
using System.ComponentModel;
using System.Text.Json.Serialization;
namespace Terminal.Gui;
@@ -59,11 +60,7 @@ public partial class View
/// </remarks>
public Margin Margin { get; private set; }
[SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
public static bool DefaultShadow { get; set; } = false;
private bool _shadow;
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.
@@ -72,19 +69,19 @@ public partial class View
/// 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 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;
}
}
}

View File

@@ -5,6 +5,8 @@
// Miguel de Icaza (miguel@gnome.org)
//
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>
@@ -37,7 +39,9 @@ public class Button : View
/// Gets or sets whether <see cref="Button"/>s are shown with a shadow effect by default.
/// </summary>
[SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
public new static bool DefaultShadow { get; set; } = false;
[JsonConverter (typeof (JsonStringEnumConverter))]
public static ShadowStyle DefaultShadow { get; set; } = ShadowStyle.Opaque;
/// <summary>Initializes a new instance of <see cref="Button"/>.</summary>
public Button ()
@@ -72,7 +76,7 @@ public class Button : View
TitleChanged += Button_TitleChanged;
MouseClick += Button_MouseClick;
Shadow = DefaultShadow;
ShadowStyle = DefaultShadow;
}
private bool _wantContinuousButtonPressed;

View File

@@ -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.Transparent;
/// <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.Rounded;
private readonly List<Button> _buttons = new ();
/// <summary>
@@ -66,6 +73,9 @@ public class Dialog : Window
public Dialog ()
{
Arrangement = ViewArrangement.Movable;
ShadowStyle = DefaultShadow;
BorderStyle = DefaultBorderStyle;
X = Pos.Center ();
Y = Pos.Center ();

View File

@@ -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)),
};

View File

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

View File

@@ -32,8 +32,6 @@
{
"UI Catalog Theme": {
"Dialog.DefaultButtonAlignment": "Fill",
"Button.DefaultShadow": true,
"Window.DefaultShadow": true,
"ColorSchemes": [
{
"UI Catalog Scheme": {

View File

@@ -433,7 +433,7 @@ public class Buttons : Scenario
Title = $"{CM.Glyphs.DownArrow}",
WantContinuousButtonPressed = true,
CanFocus = false,
Shadow = false,
ShadowStyle = ShadowStyle.None,
};
_number = new ()
@@ -458,7 +458,7 @@ public class Buttons : Scenario
Title = $"{CM.Glyphs.UpArrow}",
WantContinuousButtonPressed = true,
CanFocus = false,
Shadow = false,
ShadowStyle = ShadowStyle.None,
};
CanFocus = true;

View File

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

View File

@@ -1,4 +1,5 @@
using System;
using System.Linq;
using Terminal.Gui;
namespace UICatalog.Scenarios;
@@ -9,11 +10,22 @@ 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)
{
var ckbShadow = new CheckBox
_rgShadow = new RadioGroup
{
X = 0,
//Y = Pos.AnchorEnd(),
@@ -22,13 +34,21 @@ public class MarginEditor : AdornmentEditor
SuperViewRendersLineCanvas = true,
Title = "_Shadow",
BorderStyle = LineStyle.Dashed,
Enabled = AdornmentToEdit is { },
RadioLabels = Enum.GetNames (typeof (ShadowStyle)),
};
ckbShadow.Toggled += (sender, args) =>
{
((Margin)AdornmentToEdit).EnableShadow (args.NewValue!.Value);
};
Add (ckbShadow);
if (AdornmentToEdit is { })
{
_rgShadow.SelectedItem = (int)((Margin)AdornmentToEdit).ShadowStyle;
}
_rgShadow.SelectedItemChanged += (sender, args) =>
{
((Margin)AdornmentToEdit).ShadowStyle = (ShadowStyle)args.SelectedItem;
};
Add (_rgShadow);
}
}

View File

@@ -35,14 +35,14 @@ public class ThreeD : Scenario
Height = Dim.Percent (30),
Title = "Shadow Window",
Arrangement = ViewArrangement.Movable,
Shadow = true
ShadowStyle = ShadowStyle.Transparent
};
var buttonInWin = new Button
{
X = Pos.Center (),
Y = Pos.Center (), Text = "Button in Window",
Shadow = true
ShadowStyle = ShadowStyle.Opaque
};
win.Add (buttonInWin);
app.Add (win);
@@ -51,7 +51,7 @@ public class ThreeD : Scenario
{
X = Pos.Right (editor) + 10,
Y = Pos.Center (), Text = "Button",
Shadow = true
ShadowStyle = ShadowStyle.Opaque
};
app.Add (button);

View File

@@ -527,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)
};
@@ -547,7 +547,7 @@ internal class UICatalogApp
//AllowsMarking = false,
CanFocus = true,
Title = "_Scenarios",
BorderStyle = LineStyle.Single,
BorderStyle = CategoryList.BorderStyle,
SuperViewRendersLineCanvas = true
};