From bfbe87e941684bbbdcaf1459fb1dfbad0bc37651 Mon Sep 17 00:00:00 2001 From: Kiko kiko Date: Wed, 1 Apr 2026 20:23:51 +0200 Subject: [PATCH 1/4] Add restore glyph and support for restored window state in title bar --- src/Modern.Forms/ControlPaint.cs | 23 ++++++++++++++ src/Modern.Forms/FormTitleBar.cs | 53 ++++++++++++++++++++++++++++---- 2 files changed, 70 insertions(+), 6 deletions(-) diff --git a/src/Modern.Forms/ControlPaint.cs b/src/Modern.Forms/ControlPaint.cs index fdc08c9..8e20470 100644 --- a/src/Modern.Forms/ControlPaint.cs +++ b/src/Modern.Forms/ControlPaint.cs @@ -106,6 +106,29 @@ public static void DrawMaximizeGlyph (PaintEventArgs e, Rectangle rectangle) e.Canvas.DrawRectangle (rectangle, Theme.ForegroundColorOnAccent); } + /// + /// Draws a restore glyph, as seen on FormTitleBar when the window is maximized. + /// + public static void DrawRestoreGlyph (PaintEventArgs e, Rectangle rectangle) + { + var offset = e.LogicalToDeviceUnits (2); + + var back = new Rectangle ( + rectangle.X + offset, + rectangle.Y, + rectangle.Width - offset, + rectangle.Height - offset); + + var front = new Rectangle ( + rectangle.X, + rectangle.Y + offset, + rectangle.Width - offset, + rectangle.Height - offset); + + e.Canvas.DrawRectangle (back, Theme.ForegroundColorOnAccent); + e.Canvas.DrawRectangle (front, Theme.ForegroundColorOnAccent); + } + /// /// Draws a minimize glyph, as seen on FormTitleBar. /// diff --git a/src/Modern.Forms/FormTitleBar.cs b/src/Modern.Forms/FormTitleBar.cs index 118a315..d2674f2 100644 --- a/src/Modern.Forms/FormTitleBar.cs +++ b/src/Modern.Forms/FormTitleBar.cs @@ -30,7 +30,7 @@ public FormTitleBar () minimize_button.Click += (o, e) => { var form_min = FindForm (); - if (form_min != null) + if (form_min is not null) form_min.WindowState = FormWindowState.Minimized; }; @@ -38,8 +38,13 @@ public FormTitleBar () maximize_button.Click += (o, e) => { var form = FindForm (); - if (form != null) - form.WindowState = form.WindowState == FormWindowState.Maximized ? FormWindowState.Normal : FormWindowState.Maximized; + if (form is not null) { + form.WindowState = form.WindowState == FormWindowState.Maximized + ? FormWindowState.Normal + : FormWindowState.Maximized; + + UpdateMaximizeButtonGlyph (); + } }; close_button = Controls.AddImplicitControl (new TitleBarButton (TitleBarButton.TitleBarButtonGlyph.Close)); @@ -126,6 +131,8 @@ protected override void OnSizeChanged (EventArgs e) // Keep our form image a square form_image.Width = Height; + + UpdateMaximizeButtonGlyph(); } /// @@ -136,7 +143,7 @@ public bool ShowImage { set { if (show_image != value) { show_image = value; - form_image.Visible = value && form_image is not null; + form_image.Visible = value && form_image.Image is not null; Invalidate (); // TODO: Shouldn't be required } } @@ -145,11 +152,38 @@ public bool ShowImage { /// public override ControlStyle Style { get; } = new ControlStyle (DefaultStyle); + private void UpdateMaximizeButtonGlyph () + { + var form = FindForm (); + + if (form == null || !maximize_button.Visible) { + maximize_button.Glyph = TitleBarButton.TitleBarButtonGlyph.Maximize; + return; + } + + maximize_button.Glyph = form.WindowState == FormWindowState.Maximized + ? TitleBarButton.TitleBarButtonGlyph.Restore + : TitleBarButton.TitleBarButtonGlyph.Maximize; + } + internal sealed class TitleBarButton : Button { private const int BUTTON_PADDING = 10; - private readonly TitleBarButtonGlyph glyph; + private TitleBarButtonGlyph glyph; + + /// + /// Gets or sets the glyph displayed by the button. + /// + public TitleBarButtonGlyph Glyph { + get => glyph; + set { + if (glyph != value) { + glyph = value; + Invalidate (); + } + } + } public TitleBarButton (TitleBarButtonGlyph glyph) { @@ -183,14 +217,21 @@ protected override void OnPaint (PaintEventArgs e) case TitleBarButtonGlyph.Maximize: ControlPaint.DrawMaximizeGlyph (e, glyph_bounds); break; + case TitleBarButtonGlyph.Restore: + ControlPaint.DrawRestoreGlyph (e, glyph_bounds); + break; } } + /// + /// Specifies which glyph is displayed by the title bar button. + /// public enum TitleBarButtonGlyph { Close, Minimize, - Maximize + Maximize, + Restore } } } From d133809f345175249cb69d6c0360ad3bc9937f98 Mon Sep 17 00:00:00 2001 From: Kiko kiko Date: Fri, 3 Apr 2026 13:48:29 +0200 Subject: [PATCH 2/4] Fix restore glyph --- src/Modern.Forms/ControlPaint.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Modern.Forms/ControlPaint.cs b/src/Modern.Forms/ControlPaint.cs index 8e20470..caf9885 100644 --- a/src/Modern.Forms/ControlPaint.cs +++ b/src/Modern.Forms/ControlPaint.cs @@ -111,22 +111,30 @@ public static void DrawMaximizeGlyph (PaintEventArgs e, Rectangle rectangle) /// public static void DrawRestoreGlyph (PaintEventArgs e, Rectangle rectangle) { + var color = Theme.ForegroundColorOnAccent; var offset = e.LogicalToDeviceUnits (2); var back = new Rectangle ( - rectangle.X + offset, + rectangle.X, rectangle.Y, rectangle.Width - offset, rectangle.Height - offset); var front = new Rectangle ( - rectangle.X, + rectangle.X + offset, rectangle.Y + offset, rectangle.Width - offset, rectangle.Height - offset); - e.Canvas.DrawRectangle (back, Theme.ForegroundColorOnAccent); - e.Canvas.DrawRectangle (front, Theme.ForegroundColorOnAccent); + e.Canvas.DrawLine (back.Left, back.Top, back.Right, back.Top, color); + + e.Canvas.DrawLine (back.Left, back.Top, back.Left, back.Bottom, color); + + e.Canvas.DrawLine (back.Right, back.Top, back.Right, front.Top, color); + + e.Canvas.DrawLine (back.Left, back.Bottom, front.Left, back.Bottom, color); + + e.Canvas.DrawRectangle (front, color); } /// From 0503efbb44557ee48de790b9acc0901adba76d08 Mon Sep 17 00:00:00 2001 From: Jonathan Pobst Date: Sat, 4 Apr 2026 09:58:06 -1000 Subject: [PATCH 3/4] Tweak restore glyph. --- src/Modern.Forms/ControlPaint.cs | 25 +++++++++++-------- src/Modern.Forms/Extensions/SkiaExtensions.cs | 10 ++++++++ 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/Modern.Forms/ControlPaint.cs b/src/Modern.Forms/ControlPaint.cs index caf9885..3d077be 100644 --- a/src/Modern.Forms/ControlPaint.cs +++ b/src/Modern.Forms/ControlPaint.cs @@ -115,26 +115,30 @@ public static void DrawRestoreGlyph (PaintEventArgs e, Rectangle rectangle) var offset = e.LogicalToDeviceUnits (2); var back = new Rectangle ( - rectangle.X, + rectangle.X + offset, rectangle.Y, - rectangle.Width - offset, - rectangle.Height - offset); + rectangle.Width - offset - 1, + rectangle.Height - offset - 1); var front = new Rectangle ( - rectangle.X + offset, + rectangle.X, rectangle.Y + offset, rectangle.Width - offset, rectangle.Height - offset); - e.Canvas.DrawLine (back.Left, back.Top, back.Right, back.Top, color); - - e.Canvas.DrawLine (back.Left, back.Top, back.Left, back.Bottom, color); + // Draw "front" "window" + e.Canvas.DrawRectangle (front, color); - e.Canvas.DrawLine (back.Right, back.Top, back.Right, front.Top, color); + // Draw "back" "window" + using var path = new SKPath (); - e.Canvas.DrawLine (back.Left, back.Bottom, front.Left, back.Bottom, color); + path.MoveTo (back.Left, front.Top); + path.LineTo (back.Left, back.Top); + path.LineTo (back.Right, back.Top); + path.LineTo (back.Right, back.Bottom); + path.LineTo (front.Right, back.Bottom); - e.Canvas.DrawRectangle (front, color); + e.Canvas.DrawPath (path, color); } /// @@ -145,7 +149,6 @@ public static void DrawMinimizeGlyph (PaintEventArgs e, Rectangle rectangle) e.Canvas.DrawLine (rectangle.X, rectangle.Y, rectangle.Right, rectangle.Y, Theme.ForegroundColorOnAccent); } - /// /// Draws a RadioButton glyph. /// diff --git a/src/Modern.Forms/Extensions/SkiaExtensions.cs b/src/Modern.Forms/Extensions/SkiaExtensions.cs index f01ca02..aa1429d 100644 --- a/src/Modern.Forms/Extensions/SkiaExtensions.cs +++ b/src/Modern.Forms/Extensions/SkiaExtensions.cs @@ -144,6 +144,16 @@ public static void DrawLine (this SKCanvas canvas, float x1, float y1, float x2, canvas.DrawLine (x1, y1, x2, y2, paint); } + /// + /// Draws a line. + /// + public static void DrawPath (this SKCanvas canvas, SKPath path, SKColor color, int thickness = 1) + { + using var paint = new SKPaint { Color = color, StrokeWidth = thickness, IsStroke = true }; + + canvas.DrawPath (path, paint); + } + /// /// Draws an unfilled rectangle. /// From ce4e4234631461906bd77f5bc4643b5e33f857ed Mon Sep 17 00:00:00 2001 From: Jonathan Pobst Date: Sat, 4 Apr 2026 10:12:11 -1000 Subject: [PATCH 4/4] Address review feedback --- src/Modern.Forms/Extensions/SkiaExtensions.cs | 2 +- src/Modern.Forms/FormTitleBar.cs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Modern.Forms/Extensions/SkiaExtensions.cs b/src/Modern.Forms/Extensions/SkiaExtensions.cs index aa1429d..7bdb2fd 100644 --- a/src/Modern.Forms/Extensions/SkiaExtensions.cs +++ b/src/Modern.Forms/Extensions/SkiaExtensions.cs @@ -145,7 +145,7 @@ public static void DrawLine (this SKCanvas canvas, float x1, float y1, float x2, } /// - /// Draws a line. + /// Draws a path. /// public static void DrawPath (this SKCanvas canvas, SKPath path, SKColor color, int thickness = 1) { diff --git a/src/Modern.Forms/FormTitleBar.cs b/src/Modern.Forms/FormTitleBar.cs index d2674f2..0c06c47 100644 --- a/src/Modern.Forms/FormTitleBar.cs +++ b/src/Modern.Forms/FormTitleBar.cs @@ -69,6 +69,7 @@ public bool AllowMaximize { get => maximize_button.Visible; set { maximize_button.Visible = value; + UpdateMaximizeButtonGlyph (); Invalidate (); // TODO: Shouldn't be necessary, should automatically be triggered } } @@ -132,7 +133,7 @@ protected override void OnSizeChanged (EventArgs e) // Keep our form image a square form_image.Width = Height; - UpdateMaximizeButtonGlyph(); + UpdateMaximizeButtonGlyph (); } ///