mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-26 15:57:56 +01:00
Merge pull request #3584 from tig/v2_3583-ShadowStyleOpaque
Fixes #3583. `ShadowStyle.Opaque` broke
This commit is contained in:
@@ -1,7 +1,5 @@
|
||||
#nullable enable
|
||||
|
||||
using System.Drawing;
|
||||
|
||||
namespace Terminal.Gui;
|
||||
|
||||
/// <summary>The Margin for a <see cref="View"/>.</summary>
|
||||
@@ -28,93 +26,43 @@ public class Margin : Adornment
|
||||
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, CancelEventArgs<HighlightStyle> e)
|
||||
|
||||
private ShadowView? _bottomShadow;
|
||||
private ShadowView? _rightShadow;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void BeginInit ()
|
||||
{
|
||||
if (ShadowStyle != Gui.ShadowStyle.None)
|
||||
base.BeginInit ();
|
||||
|
||||
if (Parent is null)
|
||||
{
|
||||
if (_pressed && e.CurrentValue == 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.CurrentValue.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;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
ShadowStyle = base.ShadowStyle;
|
||||
|
||||
/// <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 ();
|
||||
}
|
||||
}
|
||||
}
|
||||
Add (
|
||||
_rightShadow = new()
|
||||
{
|
||||
X = Pos.AnchorEnd (1),
|
||||
Y = 0,
|
||||
Width = 1,
|
||||
Height = Dim.Fill (),
|
||||
ShadowStyle = ShadowStyle,
|
||||
Orientation = Orientation.Vertical
|
||||
},
|
||||
_bottomShadow = new()
|
||||
{
|
||||
X = 0,
|
||||
Y = Pos.AnchorEnd (1),
|
||||
Width = Dim.Fill (),
|
||||
Height = 1,
|
||||
ShadowStyle = ShadowStyle,
|
||||
Orientation = Orientation.Horizontal
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -139,18 +87,48 @@ public class Margin : Adornment
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ShadowStyle ShadowStyle
|
||||
/// <inheritdoc/>
|
||||
public override void OnDrawContent (Rectangle viewport)
|
||||
{
|
||||
get => base.ShadowStyle;
|
||||
set
|
||||
Rectangle screen = ViewportToScreen (viewport);
|
||||
Attribute normalAttr = GetNormalColor ();
|
||||
|
||||
Driver?.SetAttribute (normalAttr);
|
||||
|
||||
// This just draws/clears the thickness, not the insides.
|
||||
if (ShadowStyle != ShadowStyle.None)
|
||||
{
|
||||
base.ShadowStyle = SetShadow (value);
|
||||
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>
|
||||
/// 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 ShadowStyle SetShadow (ShadowStyle style)
|
||||
@@ -181,42 +159,73 @@ public class Margin : Adornment
|
||||
{
|
||||
_bottomShadow.ShadowStyle = style;
|
||||
}
|
||||
|
||||
return style;
|
||||
}
|
||||
|
||||
private ShadowView? _bottomShadow;
|
||||
private ShadowView? _rightShadow;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void BeginInit ()
|
||||
public override ShadowStyle ShadowStyle
|
||||
{
|
||||
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
|
||||
}
|
||||
);
|
||||
get => base.ShadowStyle;
|
||||
set => base.ShadowStyle = SetShadow (value);
|
||||
}
|
||||
}
|
||||
|
||||
private void Margin_Highlight (object? sender, CancelEventArgs<HighlightStyle> e)
|
||||
{
|
||||
if (ShadowStyle != ShadowStyle.None)
|
||||
{
|
||||
if (_pressed && e.NewValue == HighlightStyle.None)
|
||||
{
|
||||
// If the view is pressed and the highlight is being removed, move the shadow back.
|
||||
// Note, for visual effects reasons, we only move horizontally.
|
||||
// TODO: Add a setting or flag that lets the view move vertically as well.
|
||||
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.NewValue.HasFlag (HighlightStyle.Pressed))
|
||||
{
|
||||
// If the view is not pressed and we want highlight move the shadow
|
||||
// Note, for visual effects reasons, we only move horizontally.
|
||||
// TODO: Add a setting or flag that lets the view move vertically as well.
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Margin_LayoutStarted (object? sender, LayoutEventArgs e)
|
||||
{
|
||||
// Adjust the shadow such that it is drawn aligned with the Border
|
||||
if (ShadowStyle != 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,13 +10,13 @@ public class MarginTests (ITestOutputHelper output)
|
||||
{
|
||||
((FakeDriver)Application.Driver).SetBufferSize (5, 5);
|
||||
var view = new View { Height = 3, Width = 3 };
|
||||
view.Margin.Thickness = new Thickness (1);
|
||||
view.Margin.Thickness = new (1);
|
||||
|
||||
var superView = new View ();
|
||||
|
||||
superView.ColorScheme = new ColorScheme
|
||||
superView.ColorScheme = new()
|
||||
{
|
||||
Normal = new Attribute (Color.Red, Color.Green), Focus = new Attribute (Color.Green, Color.Red)
|
||||
Normal = new (Color.Red, Color.Green), Focus = new (Color.Green, Color.Red)
|
||||
};
|
||||
|
||||
superView.Add (view);
|
||||
|
||||
@@ -27,6 +27,132 @@ public class ShadowStyleTests (ITestOutputHelper _output)
|
||||
view.Dispose ();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (ShadowStyle.None, 0, 0, 0, 0)]
|
||||
[InlineData (ShadowStyle.Opaque, 1, 0, 0, 1)]
|
||||
[InlineData (ShadowStyle.Transparent, 1, 0, 0, 1)]
|
||||
public void ShadowStyle_Button1Pressed_Causes_Movement (ShadowStyle style, int expectedLeft, int expectedTop, int expectedRight, int expectedBottom)
|
||||
{
|
||||
var superView = new View
|
||||
{
|
||||
Height = 10, Width = 10
|
||||
};
|
||||
|
||||
View view = new ()
|
||||
{
|
||||
Width = Dim.Auto (),
|
||||
Height = Dim.Auto (),
|
||||
Text = "0123",
|
||||
HighlightStyle = HighlightStyle.Pressed,
|
||||
ShadowStyle = style,
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
superView.Add (view);
|
||||
superView.BeginInit ();
|
||||
superView.EndInit ();
|
||||
|
||||
Thickness origThickness = view.Margin.Thickness;
|
||||
view.NewMouseEvent (new () { Flags = MouseFlags.Button1Pressed, Position = new (0, 0) });
|
||||
Assert.Equal (new (expectedLeft, expectedTop, expectedRight, expectedBottom), view.Margin.Thickness);
|
||||
|
||||
view.NewMouseEvent (new () { Flags = MouseFlags.Button1Released, Position = new (0, 0) });
|
||||
Assert.Equal (origThickness, view.Margin.Thickness);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (ShadowStyle.None, 0, 0, 0, 0)]
|
||||
[InlineData (ShadowStyle.Opaque, 0, 0, 1, 1)]
|
||||
[InlineData (ShadowStyle.Transparent, 0, 0, 1, 1)]
|
||||
public void ShadowStyle_Margin_Thickness (ShadowStyle style, int expectedLeft, int expectedTop, int expectedRight, int expectedBottom)
|
||||
{
|
||||
var superView = new View
|
||||
{
|
||||
Height = 10, Width = 10
|
||||
};
|
||||
|
||||
View view = new ()
|
||||
{
|
||||
Width = Dim.Auto (),
|
||||
Height = Dim.Auto (),
|
||||
Text = "0123",
|
||||
HighlightStyle = HighlightStyle.Pressed,
|
||||
ShadowStyle = style,
|
||||
CanFocus = true
|
||||
};
|
||||
|
||||
superView.Add (view);
|
||||
superView.BeginInit ();
|
||||
superView.EndInit ();
|
||||
|
||||
Assert.Equal (new (expectedLeft, expectedTop, expectedRight, expectedBottom), view.Margin.Thickness);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (
|
||||
ShadowStyle.None,
|
||||
"""
|
||||
011
|
||||
111
|
||||
111
|
||||
""")]
|
||||
[InlineData (
|
||||
ShadowStyle.Transparent,
|
||||
"""
|
||||
011
|
||||
131
|
||||
111
|
||||
""")]
|
||||
[InlineData (
|
||||
ShadowStyle.Opaque,
|
||||
"""
|
||||
011
|
||||
121
|
||||
111
|
||||
""")]
|
||||
[SetupFakeDriver]
|
||||
public void ShadowView_Colors (ShadowStyle style, string expectedAttrs)
|
||||
{
|
||||
Color fg = Color.Red;
|
||||
Color bg = Color.Green;
|
||||
|
||||
// 0 - View
|
||||
// 1 - SuperView
|
||||
// 2 - Opaque - fg is Black, bg is SuperView.Bg
|
||||
// 3 - Transparent - fg is darker fg, bg is darker bg
|
||||
Attribute [] attributes =
|
||||
{
|
||||
Attribute.Default,
|
||||
new (fg, bg),
|
||||
new (Color.Black, bg),
|
||||
new (fg.GetDarkerColor (), bg.GetDarkerColor ())
|
||||
};
|
||||
|
||||
var superView = new View
|
||||
{
|
||||
Height = 3,
|
||||
Width = 3,
|
||||
Text = "012ABC!@#",
|
||||
ColorScheme = new (new Attribute (fg, bg))
|
||||
};
|
||||
superView.TextFormatter.WordWrap = true;
|
||||
|
||||
View view = new ()
|
||||
{
|
||||
Width = Dim.Auto (),
|
||||
Height = Dim.Auto (),
|
||||
Text = " ",
|
||||
ShadowStyle = style,
|
||||
ColorScheme = new (Attribute.Default)
|
||||
};
|
||||
superView.Add (view);
|
||||
superView.BeginInit ();
|
||||
superView.EndInit ();
|
||||
|
||||
superView.Draw ();
|
||||
TestHelpers.AssertDriverAttributesAre (expectedAttrs, Application.Driver, attributes);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (ShadowStyle.None, 3)]
|
||||
[InlineData (ShadowStyle.Opaque, 4)]
|
||||
|
||||
@@ -647,5 +647,4 @@ public class MouseTests (ITestOutputHelper output) : TestsAllViews
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user