Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #2114. 3D Effect #3376

Draft
wants to merge 2 commits into
base: v2_develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions Terminal.Gui/Application/Application.cs
Original file line number Diff line number Diff line change
Expand Up @@ -973,6 +973,7 @@ public static void RunIteration (ref RunState state, ref bool firstIteration)

if (state.Toplevel.NeedsDisplay || state.Toplevel.SubViewNeedsDisplay || state.Toplevel.LayoutNeeded || OverlappedChildNeedsDisplay ())
{
state.Toplevel.SetNeedsDisplay();
state.Toplevel.Draw ();
Driver.UpdateScreen ();

Expand Down
21 changes: 21 additions & 0 deletions Terminal.Gui/Drawing/Color.cs
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,27 @@ public Color GetHighlightColor ()

}

/// <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>
Expand Down
214 changes: 212 additions & 2 deletions Terminal.Gui/View/Adornment/Margin.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
namespace Terminal.Gui;
#nullable enable
namespace Terminal.Gui;

/// <summary>The Margin for a <see cref="View"/>.</summary>
/// <remarks>
Expand All @@ -15,8 +16,64 @@ public Margin ()
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;
}

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 {})
{
_rightShadow.Y = Parent.Border.Thickness.Top - (Parent.Border.Thickness.Top > 2 && Parent.Border.ShowTitle ? 1 : 0);
_bottomShadow.X = Parent.Border.Thickness.Left;
}
}

private bool _pressed;
private void Margin_Highlight (object? sender, HighlightEventArgs e)
{
if (_shadow)
{
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;
}
}
}

}


/// <summary>
/// The color scheme for the Margin. If set to <see langword="null"/>, gets the <see cref="Adornment.Parent"/>'s
/// <see cref="View.SuperView"/> scheme. color scheme.
Expand All @@ -30,12 +87,165 @@ public override ColorScheme ColorScheme
return base.ColorScheme;
}

return Parent?.SuperView?.ColorScheme ?? Colors.ColorSchemes ["TopLevel"];
return (Parent?.SuperView?.ColorScheme ?? Colors.ColorSchemes ["TopLevel"])!;
}
set
{
base.ColorScheme = value;
Parent?.SetNeedsDisplay ();
}
}

private bool _shadow;

/// <summary>
/// Gets or 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)
{
if (_shadow == enable)
{
return _shadow;
}

if (_shadow)
{
Thickness = new (Thickness.Left, Thickness.Top, Thickness.Right - 1, Thickness.Bottom - 1);
}

_shadow = enable;

if (_shadow)
{
Thickness = new (Thickness.Left, Thickness.Top, Thickness.Right + 1, Thickness.Bottom + 1);
}

if (_rightShadow is { })
{
_rightShadow.Visible = _shadow;
}

if (_bottomShadow is { })
{
_bottomShadow.Visible = _shadow;
}
return _shadow;
}

private View? _bottomShadow;
private View? _rightShadow;

/// <inheritdoc/>
public override void BeginInit ()
{
base.BeginInit ();

if (Parent is null)
{
return;
}

Attribute attr = Parent.GetNormalColor ();

Add (
_rightShadow = new ShadowView
{
X = Pos.AnchorEnd (1),
Y = 0,
Width = 1,
Height = Dim.Fill (),
Visible = _shadow,
Orientation = Orientation.Vertical
},
_bottomShadow = new ShadowView
{
X = 0,
Y = Pos.AnchorEnd (1),
Width = Dim.Fill (),
Height = 1,
Visible = _shadow,
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 = '\u2596';
private readonly char VERTICAL_GLYPH = '\u258C';
private readonly char HORIZONTAL_START_GLYPH = '\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);
}
}
}
30 changes: 30 additions & 0 deletions Terminal.Gui/View/ViewAdornments.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,36 @@
/// </remarks>
public Margin Margin { get; private set; }

[SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
public static bool DefaultShadow { get; set; } = false;

Check warning on line 63 in Terminal.Gui/View/ViewAdornments.cs

View workflow job for this annotation

GitHub Actions / build_and_test

Missing XML comment for publicly visible type or member 'View.DefaultShadow'


private bool _shadow;
/// <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 bool Shadow
{
get => _shadow;
set
{
if (_shadow == value)
{
return;
}
_shadow = value;
if (Margin is { })
{
_shadow = Margin.EnableShadow (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
Expand Down
15 changes: 11 additions & 4 deletions Terminal.Gui/View/ViewEventArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,11 @@ public DrawEventArgs (Rectangle newViewport, Rectangle oldViewport)
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
Expand All @@ -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; }

}
18 changes: 16 additions & 2 deletions Terminal.Gui/View/ViewMouse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -335,14 +335,14 @@ private bool HandlePressed (MouseEvent mouseEvent)

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;
Expand Down Expand Up @@ -533,6 +533,20 @@ internal bool SetHighlight (HighlightStyle style)
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;
}

Expand Down
4 changes: 2 additions & 2 deletions Terminal.Gui/View/ViewSubViews.cs
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ public bool CanFocus
/// <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)
Expand All @@ -521,7 +521,7 @@ public virtual bool OnEnter (View 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)
Expand Down