From 76566022453d306ffe5f5b9f544ffa8cbe016d4e Mon Sep 17 00:00:00 2001 From: Tig Date: Mon, 4 Nov 2024 10:23:30 -0700 Subject: [PATCH] #nullable enable --- Terminal.Gui/Drawing/Thickness.cs | 39 +++--- Terminal.Gui/View/Adornment/Border.cs | 12 +- Terminal.Gui/View/Adornment/Margin.cs | 4 +- Terminal.Gui/View/DrawEventArgs.cs | 3 - Terminal.Gui/View/View.Adornments.cs | 88 +++++++------ Terminal.Gui/View/View.Arrangement.cs | 3 +- Terminal.Gui/View/View.Drawing.Clipping.cs | 43 ++++--- Terminal.Gui/View/View.Drawing.Primitives.cs | 6 +- Terminal.Gui/View/View.Drawing.cs | 23 ++-- Terminal.Gui/View/View.Layout.cs | 126 ++++++++++--------- Terminal.Gui/View/View.Mouse.cs | 6 +- Terminal.Gui/Views/Bar.cs | 7 +- Terminal.Gui/Views/Menuv2.cs | 1 + Terminal.Gui/Views/Shortcut.cs | 81 +++++++----- Terminal.Gui/Views/TabView.cs | 4 +- Terminal.Gui/Views/TableView/TableView.cs | 8 +- 16 files changed, 247 insertions(+), 207 deletions(-) diff --git a/Terminal.Gui/Drawing/Thickness.cs b/Terminal.Gui/Drawing/Thickness.cs index cb6fe7a8f..0950f872b 100644 --- a/Terminal.Gui/Drawing/Thickness.cs +++ b/Terminal.Gui/Drawing/Thickness.cs @@ -1,4 +1,5 @@ -using System.Numerics; +#nullable enable +using System.Numerics; using System.Text.Json.Serialization; namespace Terminal.Gui; @@ -15,10 +16,7 @@ namespace Terminal.Gui; /// with the thickness widths subtracted. /// /// -/// Use the helper API ( to draw the frame with the specified thickness. -/// -/// -/// Thickness uses intenrally. As a result, there is a potential precision loss for very +/// Thickness uses internally. As a result, there is a potential precision loss for very /// large numbers. This is typically not an issue for UI dimensions but could be relevant in other contexts. /// /// @@ -91,7 +89,7 @@ public record struct Thickness /// /// The diagnostics label to draw on the bottom of the . /// The inner rectangle remaining to be drawn. - public Rectangle Draw (Rectangle rect, ViewDiagnosticFlags diagnosticFlags = ViewDiagnosticFlags.Off, string label = null) + public Rectangle Draw (Rectangle rect, ViewDiagnosticFlags diagnosticFlags = ViewDiagnosticFlags.Off, string? label = null) { if (rect.Size.Width < 1 || rect.Size.Height < 1) { @@ -134,26 +132,26 @@ public record struct Thickness if (Right > 0) { Application.Driver?.FillRect ( - rect with - { - X = Math.Max (0, rect.X + rect.Width - Right), - Width = Math.Min (rect.Width, Right) - }, - rightChar - ); + rect with + { + X = Math.Max (0, rect.X + rect.Width - Right), + Width = Math.Min (rect.Width, Right) + }, + rightChar + ); } // Draw the Bottom side if (Bottom > 0) { Application.Driver?.FillRect ( - rect with - { - Y = rect.Y + Math.Max (0, rect.Height - Bottom), - Height = Bottom - }, - bottomChar - ); + rect with + { + Y = rect.Y + Math.Max (0, rect.Height - Bottom), + Height = Bottom + }, + bottomChar + ); } if (diagnosticFlags.HasFlag (ViewDiagnosticFlags.Ruler)) @@ -192,6 +190,7 @@ public record struct Thickness { // Draw the diagnostics label on the bottom string text = label is null ? string.Empty : $"{label} {this}"; + var tf = new TextFormatter { Text = text, diff --git a/Terminal.Gui/View/Adornment/Border.cs b/Terminal.Gui/View/Adornment/Border.cs index 723e1c906..f88d932a0 100644 --- a/Terminal.Gui/View/Adornment/Border.cs +++ b/Terminal.Gui/View/Adornment/Border.cs @@ -483,8 +483,8 @@ public class Border : Adornment Point parentLoc = Parent.SuperView?.ScreenToViewport (new (mouseEvent.ScreenPosition.X, mouseEvent.ScreenPosition.Y)) ?? mouseEvent.ScreenPosition; - int minHeight = Thickness.Vertical + Parent!.Margin.Thickness.Bottom; - int minWidth = Thickness.Horizontal + Parent!.Margin.Thickness.Right; + int minHeight = Thickness.Vertical + Parent!.Margin!.Thickness.Bottom; + int minWidth = Thickness.Horizontal + Parent!.Margin!.Thickness.Right; // TODO: This code can be refactored to be more readable and maintainable. switch (_arranging) @@ -1072,7 +1072,7 @@ public class Border : Adornment NoPadding = true, ShadowStyle = ShadowStyle.None, Text = $"{Glyphs.SizeVertical}", - X = Pos.Center () + Parent!.Margin.Thickness.Horizontal, + X = Pos.Center () + Parent!.Margin!.Thickness.Horizontal, Y = 0, Visible = false, Data = ViewArrangement.TopResizable @@ -1095,7 +1095,7 @@ public class Border : Adornment ShadowStyle = ShadowStyle.None, Text = $"{Glyphs.SizeHorizontal}", X = Pos.AnchorEnd (), - Y = Pos.Center () + Parent!.Margin.Thickness.Vertical / 2, + Y = Pos.Center () + Parent!.Margin!.Thickness.Vertical / 2, Visible = false, Data = ViewArrangement.RightResizable }; @@ -1117,7 +1117,7 @@ public class Border : Adornment ShadowStyle = ShadowStyle.None, Text = $"{Glyphs.SizeHorizontal}", X = 0, - Y = Pos.Center () + Parent!.Margin.Thickness.Vertical / 2, + Y = Pos.Center () + Parent!.Margin!.Thickness.Vertical / 2, Visible = false, Data = ViewArrangement.LeftResizable }; @@ -1138,7 +1138,7 @@ public class Border : Adornment NoPadding = true, ShadowStyle = ShadowStyle.None, Text = $"{Glyphs.SizeVertical}", - X = Pos.Center () + Parent!.Margin.Thickness.Horizontal / 2, + X = Pos.Center () + Parent!.Margin!.Thickness.Horizontal / 2, Y = Pos.AnchorEnd (), Visible = false, Data = ViewArrangement.BottomResizable diff --git a/Terminal.Gui/View/Adornment/Margin.cs b/Terminal.Gui/View/Adornment/Margin.cs index fab6f2ffa..4387f12a3 100644 --- a/Terminal.Gui/View/Adornment/Margin.cs +++ b/Terminal.Gui/View/Adornment/Margin.cs @@ -275,13 +275,13 @@ public class Margin : Adornment { case ShadowStyle.Transparent: // BUGBUG: This doesn't work right for all Border.Top sizes - Need an API on Border that gives top-right location of line corner. - _rightShadow.Y = Parent!.Border.Thickness.Top > 0 ? ScreenToViewport (Parent.Border.GetBorderRectangle ().Location).Y + 1 : 0; + _rightShadow.Y = Parent!.Border!.Thickness.Top > 0 ? ScreenToViewport (Parent.Border.GetBorderRectangle ().Location).Y + 1 : 0; break; case ShadowStyle.Opaque: // BUGBUG: This doesn't work right for all Border.Top sizes - Need an API on Border that gives top-right location of line corner. - _rightShadow.Y = Parent!.Border.Thickness.Top > 0 ? ScreenToViewport (Parent.Border.GetBorderRectangle ().Location).Y + 1 : 0; + _rightShadow.Y = Parent!.Border!.Thickness.Top > 0 ? ScreenToViewport (Parent.Border.GetBorderRectangle ().Location).Y + 1 : 0; _bottomShadow.X = Parent.Border.Thickness.Left > 0 ? ScreenToViewport (Parent.Border.GetBorderRectangle ().Location).X + 1 : 0; break; diff --git a/Terminal.Gui/View/DrawEventArgs.cs b/Terminal.Gui/View/DrawEventArgs.cs index ff54c166e..f3cfc7747 100644 --- a/Terminal.Gui/View/DrawEventArgs.cs +++ b/Terminal.Gui/View/DrawEventArgs.cs @@ -20,9 +20,6 @@ public class DrawEventArgs : CancelEventArgs OldViewport = oldViewport; } - /// If set to true, the draw operation will be canceled, if applicable. - public bool Cancel { get; set; } - /// Gets the Content-relative rectangle describing the old visible viewport into the . public Rectangle OldViewport { get; } diff --git a/Terminal.Gui/View/View.Adornments.cs b/Terminal.Gui/View/View.Adornments.cs index 0775cdcd3..f5f9c0e89 100644 --- a/Terminal.Gui/View/View.Adornments.cs +++ b/Terminal.Gui/View/View.Adornments.cs @@ -1,4 +1,5 @@ -namespace Terminal.Gui; +#nullable enable +namespace Terminal.Gui; public partial class View // Adornments { @@ -7,9 +8,10 @@ public partial class View // Adornments /// private void SetupAdornments () { - //// TODO: Move this to Adornment as a static factory method + // TODO: Move this to Adornment as a static factory method if (this is not Adornment) { + // TODO: Make the Adornments Lazy and only create them when needed Margin = new (this); Border = new (this); Padding = new (this); @@ -61,7 +63,7 @@ public partial class View // Adornments /// and its . /// /// - public Margin Margin { get; private set; } + public Margin? Margin { get; private set; } private ShadowStyle _shadowStyle; @@ -117,7 +119,7 @@ public partial class View // Adornments /// and its . /// /// - public Border Border { get; private set; } + public Border? Border { get; private set; } /// Gets or sets whether the view has a one row/col thick border. /// @@ -130,6 +132,10 @@ public partial class View // Adornments /// Setting this property to is equivalent to setting 's /// to `0` and to . /// + /// + /// Calls and raises , which allows change + /// to be cancelled. + /// /// For more advanced customization of the view's border, manipulate see directly. /// public LineStyle BorderStyle @@ -150,32 +156,32 @@ public partial class View // Adornments return; } + BorderStyleChanging?.Invoke (this, e); + + if (e.Cancel) + { + return; + } + SetBorderStyle (e.NewValue); SetAdornmentFrames (); SetNeedsLayout (); - } } /// - /// Called when the is changing. Invokes , which allows the - /// event to be cancelled. + /// Called when the is changing. /// /// - /// Override to prevent the from changing. + /// Set e.Cancel to true to prevent the from changing. /// /// - protected virtual bool OnBorderStyleChanging (CancelEventArgs e) - { - if (Border is null) - { - return false; - } + protected virtual bool OnBorderStyleChanging (CancelEventArgs e) { return false; } - BorderStyleChanging?.Invoke (this, e); - - return e.Cancel; - } + /// + /// Fired when the is changing. Allows the event to be cancelled. + /// + public event EventHandler>? BorderStyleChanging; /// /// Sets the of the view to the specified value. @@ -198,25 +204,19 @@ public partial class View // Adornments { if (value != LineStyle.None) { - if (Border.Thickness == Thickness.Empty) + if (Border!.Thickness == Thickness.Empty) { Border.Thickness = new (1); } } else { - Border.Thickness = new (0); + Border!.Thickness = new (0); } Border.LineStyle = value; } - /// - /// Fired when the is changing. Allows the event to be cancelled. - /// - [CanBeNull] - public event EventHandler> BorderStyleChanging; - /// /// The inside of the view that offsets the /// from the . @@ -232,7 +232,7 @@ public partial class View // Adornments /// and its . /// /// - public Padding Padding { get; private set; } + public Padding? Padding { get; private set; } /// /// Gets the thickness describing the sum of the Adornments' thicknesses. @@ -245,12 +245,24 @@ public partial class View // Adornments /// A thickness that describes the sum of the Adornments' thicknesses. public Thickness GetAdornmentsThickness () { - if (Margin is null) + Thickness result = Thickness.Empty; + + if (Margin is { }) { - return Thickness.Empty; + result += Margin.Thickness; } - return Margin.Thickness + Border.Thickness + Padding.Thickness; + if (Border is { }) + { + result += Border.Thickness; + } + + if (Padding is { }) + { + result += Padding.Thickness; + } + + return result; } /// Sets the Frame's of the Margin, Border, and Padding. @@ -262,13 +274,19 @@ public partial class View // Adornments return; } - if (Margin is null) + if (Margin is { }) { - return; // CreateAdornments () has not been called yet + Margin!.Frame = Rectangle.Empty with { Size = Frame.Size }; } - Margin.Frame = Rectangle.Empty with { Size = Frame.Size }; - Border.Frame = Margin.Thickness.GetInside (Margin.Frame); - Padding.Frame = Border.Thickness.GetInside (Border.Frame); + if (Border is { } && Margin is { }) + { + Border!.Frame = Margin!.Thickness.GetInside (Margin!.Frame); + } + + if (Padding is { } && Border is { }) + { + Padding!.Frame = Border!.Thickness.GetInside (Border!.Frame); + } } } diff --git a/Terminal.Gui/View/View.Arrangement.cs b/Terminal.Gui/View/View.Arrangement.cs index aec9e6553..e11b1d389 100644 --- a/Terminal.Gui/View/View.Arrangement.cs +++ b/Terminal.Gui/View/View.Arrangement.cs @@ -1,4 +1,5 @@ -namespace Terminal.Gui; +#nullable enable +namespace Terminal.Gui; public partial class View { diff --git a/Terminal.Gui/View/View.Drawing.Clipping.cs b/Terminal.Gui/View/View.Drawing.Clipping.cs index 9d4058cd3..2996751f9 100644 --- a/Terminal.Gui/View/View.Drawing.Clipping.cs +++ b/Terminal.Gui/View/View.Drawing.Clipping.cs @@ -1,6 +1,4 @@ #nullable enable -using static Unix.Terminal.Curses; - namespace Terminal.Gui; public partial class View @@ -13,21 +11,20 @@ public partial class View /// There is a single clip region for the entire application. /// /// - /// This method returns the current clip region, not a clone. If there is a need to modify the clip region, it is recommended to clone it first. + /// This method returns the current clip region, not a clone. If there is a need to modify the clip region, it is + /// recommended to clone it first. /// /// /// The current Clip. - public static Region? GetClip () - { - return Application.Driver?.Clip; - } + public static Region? GetClip () { return Application.Driver?.Clip; } /// /// Sets the Clip to the specified region. /// /// /// - /// There is a single clip region for the entire application. This method sets the clip region to the specified region. + /// There is a single clip region for the entire application. This method sets the clip region to the specified + /// region. /// /// /// @@ -47,7 +44,8 @@ public partial class View /// There is a single clip region for the entire application. This method sets the clip region to the screen. /// /// - /// This method returns the current clip region, not a clone. If there is a need to modify the clip region, it is recommended to clone it first. + /// This method returns the current clip region, not a clone. If there is a need to modify the clip region, it is + /// recommended to clone it first. /// /// /// @@ -56,6 +54,7 @@ public partial class View public static Region? SetClipToScreen () { Region? previous = GetClip (); + if (Driver is { }) { Driver.Clip = new (Application.Screen); @@ -65,7 +64,7 @@ public partial class View } /// - /// Removes the specified rectangle from the Clip. + /// Removes the specified rectangle from the Clip. /// /// /// @@ -73,17 +72,15 @@ public partial class View /// /// /// - public static void ExcludeFromClip (Rectangle rectangle) - { - Driver?.Clip?.Exclude (rectangle); - } + public static void ExcludeFromClip (Rectangle rectangle) { Driver?.Clip?.Exclude (rectangle); } /// /// Changes the Clip to the intersection of the current Clip and the of this View. /// /// /// - /// This method returns the current clip region, not a clone. If there is a need to modify the clip region, it is recommended to clone it first. + /// This method returns the current clip region, not a clone. If there is a need to modify the clip region, it is + /// recommended to clone it first. /// /// /// @@ -99,6 +96,7 @@ public partial class View Region previous = GetClip () ?? new (Application.Screen); Region frameRegion = previous.Clone (); + // Translate viewportRegion to screen-relative coords Rectangle screenRect = FrameToScreen (); frameRegion.Intersect (screenRect); @@ -109,7 +107,7 @@ public partial class View frameRegion.Exclude (adornment.Thickness.GetInside (Frame)); } - View.SetClip (frameRegion); + SetClip (frameRegion); return previous; } @@ -125,11 +123,12 @@ public partial class View /// If has set, clipping will be /// applied to just the visible content area. /// - /// - /// - /// This method returns the current clip region, not a clone. If there is a need to modify the clip region, it is recommended to clone it first. - /// - /// + /// + /// + /// This method returns the current clip region, not a clone. If there is a need to modify the clip region, it + /// is recommended to clone it first. + /// + /// /// /// /// The current Clip, which can be then re-applied @@ -161,7 +160,7 @@ public partial class View viewportRegion?.Exclude (adornment.Thickness.GetInside (viewport)); } - View.SetClip (viewportRegion); + SetClip (viewportRegion); return previous; } diff --git a/Terminal.Gui/View/View.Drawing.Primitives.cs b/Terminal.Gui/View/View.Drawing.Primitives.cs index f76162c94..3ddc9de6f 100644 --- a/Terminal.Gui/View/View.Drawing.Primitives.cs +++ b/Terminal.Gui/View/View.Drawing.Primitives.cs @@ -2,8 +2,6 @@ public partial class View { - #region Drawing Primitives - /// Moves the drawing cursor to the specified -relative location in the view. /// /// @@ -121,8 +119,6 @@ public partial class View Attribute prev = SetAttribute (new (color ?? GetNormalColor ().Background)); Driver.FillRect (toClear); SetAttribute (prev); - View.SetClip (prevClip); + SetClip (prevClip); } - - #endregion Drawing Primitives } diff --git a/Terminal.Gui/View/View.Drawing.cs b/Terminal.Gui/View/View.Drawing.cs index f3c6cceaf..158d37beb 100644 --- a/Terminal.Gui/View/View.Drawing.cs +++ b/Terminal.Gui/View/View.Drawing.cs @@ -1,13 +1,10 @@ #nullable enable -//#define HACK_DRAW_OVERLAPPED using System.ComponentModel; -using System.Diagnostics; namespace Terminal.Gui; public partial class View // Drawing APIs { - internal static void Draw (IEnumerable views, bool force) { IEnumerable viewsArray = views as View [] ?? views.ToArray (); @@ -25,7 +22,6 @@ public partial class View // Drawing APIs Margin.DrawMargins (viewsArray); } - /// /// Draws the view if it needs to be drawn. /// @@ -47,11 +43,12 @@ public partial class View // Drawing APIs } Region? saved = GetClip (); + if (NeedsDraw || SubViewNeedsDraw) { saved = ClipFrame (); DoDrawBorderAndPadding (); - View.SetClip (saved); + SetClip (saved); // By default, we clip to the viewport preventing drawing outside the viewport // We also clip to the content, but if a developer wants to draw outside the viewport, they can do @@ -108,7 +105,7 @@ public partial class View // Drawing APIs DoDrawComplete (); // QUESTION: Should this go before DoDrawComplete? What is more correct? - View.SetClip (saved); + SetClip (saved); // Exclude this view (not including Margin) from the Clip if (this is not Adornment && GetClip () is { }) @@ -221,7 +218,6 @@ public partial class View // Drawing APIs SetNormalAttribute (); } - /// /// Called when the normal attribute for the View is to be set. This is called before the View is drawn. /// @@ -245,13 +241,12 @@ public partial class View // Drawing APIs } } - #endregion + #region ClearViewport private void DoClearViewport () { - if (OnClearingViewport ()) { return; @@ -329,7 +324,6 @@ public partial class View // Drawing APIs private void DoDrawText () { - if (OnDrawingText ()) { return; @@ -513,14 +507,15 @@ public partial class View // Drawing APIs /// /// Gets or sets whether this View will use it's SuperView's for rendering any /// lines. If the rendering of any borders drawn by this Frame will be done by its parent's - /// SuperView. If (the default) this View's method will be + /// SuperView. If (the default) this View's method will + /// be /// called to render the borders. /// public virtual bool SuperViewRendersLineCanvas { get; set; } = false; /// - /// Causes the contents of to be drawn. - /// If is true, only the + /// Causes the contents of to be drawn. + /// If is true, only the /// of this view's subviews will be rendered. If is /// false (the default), this method will cause the to be rendered. /// @@ -577,6 +572,7 @@ public partial class View // Drawing APIs LineCanvas.Clear (); } } + #endregion DrawLineCanvas #region DrawComplete @@ -757,6 +753,7 @@ public partial class View // Drawing APIs { Padding?.ClearNeedsDraw (); } + foreach (View subview in Subviews) { subview.ClearNeedsDraw (); diff --git a/Terminal.Gui/View/View.Layout.cs b/Terminal.Gui/View/View.Layout.cs index 0c97e8d66..bc4ee3dbe 100644 --- a/Terminal.Gui/View/View.Layout.cs +++ b/Terminal.Gui/View/View.Layout.cs @@ -1,7 +1,5 @@ #nullable enable using System.Diagnostics; -using Microsoft.CodeAnalysis; -using static Unix.Terminal.Curses; namespace Terminal.Gui; @@ -33,10 +31,12 @@ public partial class View // Layout APIs /// . /// /// - /// Setting Frame will set , , , and to absoulte values. + /// Setting Frame will set , , , and to + /// absoulte values. /// /// - /// Changing this property will result in and to be set, resulting in the + /// Changing this property will result in and to be set, + /// resulting in the /// view being laid out and redrawn as appropriate in the next iteration of the . /// /// @@ -44,10 +44,11 @@ public partial class View // Layout APIs { get { - if (_needsLayout) + if (NeedsLayout) { //Debug.WriteLine("Frame_get with _layoutNeeded"); } + return _frame ?? Rectangle.Empty; } set @@ -193,7 +194,8 @@ public partial class View // Layout APIs /// laid out (e.g. has been called). /// /// - /// Changing this property will result in and to be set, resulting in the + /// Changing this property will result in and to be set, + /// resulting in the /// view being laid out and redrawn as appropriate in the next iteration of the . /// /// @@ -219,7 +221,6 @@ public partial class View // Layout APIs private Pos _y = Pos.Absolute (0); - /// Gets or sets the Y position for the view (the row). /// The object representing the Y position. /// @@ -236,7 +237,8 @@ public partial class View // Layout APIs /// laid out (e.g. has been called). /// /// - /// Changing this property will result in and to be set, resulting in the + /// Changing this property will result in and to be set, + /// resulting in the /// view being laid out and redrawn as appropriate in the next iteration of the . /// /// @@ -277,7 +279,8 @@ public partial class View // Layout APIs /// laid out (e.g. has been called). /// /// - /// Changing this property will result in and to be set, resulting in the + /// Changing this property will result in and to be set, + /// resulting in the /// view being laid out and redrawn as appropriate in the next iteration of the . /// /// @@ -323,7 +326,8 @@ public partial class View // Layout APIs /// laid out (e.g. has been called). /// /// - /// Changing this property will result in and to be set, resulting in the + /// Changing this property will result in and to be set, + /// resulting in the /// view being laid out and redrawn as appropriate in the next iteration of the . /// /// @@ -354,14 +358,15 @@ public partial class View // Layout APIs #region Core Layout API /// - /// INTERNAL API - Performs layout of the specified views within the specified content size. Called by the Application main loop. + /// INTERNAL API - Performs layout of the specified views within the specified content size. Called by the Application + /// main loop. /// /// The views to layout. /// The size to bound the views by. /// If any of the views needed to be laid out. internal static bool Layout (IEnumerable views, Size contentSize) { - bool neededLayout = false; + var neededLayout = false; foreach (View v in views) { @@ -404,7 +409,8 @@ public partial class View // Layout APIs } /// - /// Performs layout of the view and its subviews using the content size of either the or . + /// Performs layout of the view and its subviews using the content size of either the or + /// . /// /// /// @@ -417,14 +423,12 @@ public partial class View // Layout APIs /// /// /// If the view could not be laid out (typically because dependency was not ready). - public bool Layout () - { - return Layout (GetContainerSize ()); - } + public bool Layout () { return Layout (GetContainerSize ()); } /// /// Sets the position and size of this view, relative to the SuperView's ContentSize (nominally the same as - /// this.SuperView.GetContentSize ()) based on the values of , , , + /// this.SuperView.GetContentSize ()) based on the values of , , + /// , /// and . /// /// @@ -467,6 +471,7 @@ public partial class View // Layout APIs { newW = _width.Calculate (0, superviewContentSize.Width, this, Dimension.Width); newX = _x.Calculate (superviewContentSize.Width, newW, this, Dimension.Width); + if (newW != Frame.Width) { // Pos.Calculate gave us a new position. We need to redo dimension @@ -483,6 +488,7 @@ public partial class View // Layout APIs { newH = _height.Calculate (0, superviewContentSize.Height, this, Dimension.Height); newY = _y.Calculate (superviewContentSize.Height, newH, this, Dimension.Height); + if (newH != Frame.Height) { // Pos.Calculate gave us a new position. We need to redo dimension @@ -494,7 +500,6 @@ public partial class View // Layout APIs newY = _y.Calculate (superviewContentSize.Height, _height, this, Dimension.Height); newH = _height.Calculate (newY, superviewContentSize.Height, this, Dimension.Height); } - } catch (LayoutException le) { @@ -552,7 +557,8 @@ public partial class View // Layout APIs } /// - /// INTERNAL API - Causes the view's subviews and adornments to be laid out within the view's content areas. Assumes the view's relative layout has been set via . + /// INTERNAL API - Causes the view's subviews and adornments to be laid out within the view's content areas. Assumes + /// the view's relative layout has been set via . /// /// /// @@ -584,10 +590,12 @@ public partial class View // Layout APIs { Margin.LayoutSubviews (); } + if (Border is { Subviews.Count: > 0 }) { Border.LayoutSubviews (); } + if (Padding is { Subviews.Count: > 0 }) { Padding.LayoutSubviews (); @@ -600,6 +608,7 @@ public partial class View // Layout APIs List ordered = TopologicalSort (SuperView!, nodes, edges); List redo = new (); + foreach (View v in ordered) { if (!v.Layout (contentSize)) @@ -608,7 +617,7 @@ public partial class View // Layout APIs } } - bool layoutStillNeeded = false; + var layoutStillNeeded = false; if (redo.Count > 0) { @@ -633,7 +642,7 @@ public partial class View // Layout APIs } } - _needsLayout = layoutStillNeeded; + NeedsLayout = layoutStillNeeded; OnSubviewsLaidOut (new (contentSize)); SubviewsLaidOut?.Invoke (this, new (contentSize)); @@ -648,8 +657,10 @@ public partial class View // Layout APIs /// protected virtual void OnSubviewLayout (LayoutEventArgs args) { } - /// Raised by before any subviews - /// have been laid out. + /// + /// Raised by before any subviews + /// have been laid out. + /// /// /// Subscribe to this event to perform tasks when the layout is changing. /// @@ -677,65 +688,72 @@ public partial class View // Layout APIs #region NeedsLayout // We expose no setter for this to ensure that the ONLY place it's changed is in SetNeedsLayout - private bool _needsLayout = true; /// /// Indicates the View's Frame or the layout of the View's subviews (including Adornments) have - /// changed since the last time the View was laid out. + /// changed since the last time the View was laid out. /// /// - /// Used to prevent from needlessly computing - /// layout. - /// + /// + /// Used to prevent from needlessly computing + /// layout. + /// /// /// /// if layout is needed. /// - public bool NeedsLayout => _needsLayout; + public bool NeedsLayout { get; private set; } = true; /// - /// Sets to return , indicating this View and all of it's subviews (including adornments) need to be laid out in the next Application iteration. + /// Sets to return , indicating this View and all of it's subviews + /// (including adornments) need to be laid out in the next Application iteration. /// /// /// - /// The will cause to be called on the next so there is normally no reason to call see . + /// The will cause to be called on the next + /// so there is normally no reason to call see . /// /// - public void SetNeedsLayout () { - _needsLayout = true; + NeedsLayout = true; if (Margin is { Subviews.Count: > 0 }) { Margin.SetNeedsLayout (); } + if (Border is { Subviews.Count: > 0 }) { Border.SetNeedsLayout (); } + if (Padding is { Subviews.Count: > 0 }) { Padding.SetNeedsLayout (); } // Use a stack to avoid recursion - Stack stack = new Stack (Subviews); + Stack stack = new (Subviews); while (stack.Count > 0) { View current = stack.Pop (); + if (!current.NeedsLayout) { - current._needsLayout = true; + current.NeedsLayout = true; + if (current.Margin is { Subviews.Count: > 0 }) { current.Margin.SetNeedsLayout (); } + if (current.Border is { Subviews.Count: > 0 }) { current.Border.SetNeedsLayout (); } + if (current.Padding is { Subviews.Count: > 0 }) { current.Padding.SetNeedsLayout (); @@ -757,7 +775,7 @@ public partial class View // Layout APIs if (SuperView is null) { - foreach (var tl in Application.TopLevels) + foreach (Toplevel tl in Application.TopLevels) { tl.SetNeedsDraw (); } @@ -778,7 +796,6 @@ public partial class View // Layout APIs #region Topological Sort - /// /// INTERNAL API - Collects all views and their dependencies from a given starting view for layout purposes. Used by /// to create an ordered list of views to layout. @@ -802,7 +819,7 @@ public partial class View // Layout APIs } /// - /// INTERNAL API - Collects dimension (where Width or Height is `DimView`) dependencies for a given view. + /// INTERNAL API - Collects dimension (where Width or Height is `DimView`) dependencies for a given view. /// /// The dimension (width or height) to collect dependencies for. /// The view for which to collect dimension dependencies. @@ -813,7 +830,7 @@ public partial class View // Layout APIs /// internal void CollectDim (Dim? dim, View from, ref HashSet nNodes, ref HashSet<(View, View)> nEdges) { - if (dim!.Has (out DimView dv)) + if (dim!.Has (out DimView dv)) { if (dv.Target != this) { @@ -821,7 +838,7 @@ public partial class View // Layout APIs } } - if (dim!.Has (out DimCombine dc)) + if (dim!.Has (out DimCombine dc)) { CollectDim (dc.Left, from, ref nNodes, ref nEdges); CollectDim (dc.Right, from, ref nNodes, ref nEdges); @@ -845,6 +862,7 @@ public partial class View // Layout APIs { case PosView pv: Debug.Assert (pv.Target is { }); + if (pv.Target != this) { nEdges.Add ((pv.Target!, from)); @@ -960,22 +978,21 @@ public partial class View // Layout APIs #region Utilities /// - /// INTERNAL API - Gets the size of the SuperView's content (nominally the same as - /// the SuperView's ) or the screen size if there's no SuperView. + /// INTERNAL API - Gets the size of the SuperView's content (nominally the same as + /// the SuperView's ) or the screen size if there's no SuperView. /// /// private Size GetContainerSize () { // TODO: Get rid of refs to Top - Size superViewContentSize = SuperView?.GetContentSize () ?? - (Application.Top is { } && Application.Top != this && Application.Top.IsInitialized - ? Application.Top.GetContentSize () - : Application.Screen.Size); + Size superViewContentSize = SuperView?.GetContentSize () + ?? (Application.Top is { } && Application.Top != this && Application.Top.IsInitialized + ? Application.Top.GetContentSize () + : Application.Screen.Size); return superViewContentSize; } - // BUGBUG: This method interferes with Dialog/MessageBox default min/max size. // TODO: Get rid of MenuBar coupling as part of https://github.com/gui-cs/Terminal.Gui/issues/2975 /// @@ -1107,11 +1124,8 @@ public partial class View // Layout APIs #endregion Utilities - #region Diagnostics and Verification - - // Diagnostics to highlight when X or Y is read before the view has been initialized private Pos VerifyIsInitialized (Pos pos, string member) { @@ -1228,12 +1242,12 @@ public partial class View // Layout APIs if (bad != null) { throw new LayoutException ( - $"{view.GetType ().Name}.{name} = {bad.GetType ().Name} " - + $"which depends on the SuperView's dimensions and the SuperView uses Dim.Auto." - ); + $"{view.GetType ().Name}.{name} = {bad.GetType ().Name} " + + $"which depends on the SuperView's dimensions and the SuperView uses Dim.Auto." + ); } } } - #endregion Diagnostics and Verification -} \ No newline at end of file + #endregion Diagnostics and Verification +} diff --git a/Terminal.Gui/View/View.Mouse.cs b/Terminal.Gui/View/View.Mouse.cs index 0afe506e5..4dbb65e38 100644 --- a/Terminal.Gui/View/View.Mouse.cs +++ b/Terminal.Gui/View/View.Mouse.cs @@ -666,15 +666,15 @@ public partial class View // Mouse APIs if (start is not Adornment) { - if (start.Margin.Contains (currentLocation)) + if (start.Margin is {} && start.Margin.Contains (currentLocation)) { found = start.Margin; } - else if (start.Border.Contains (currentLocation)) + else if (start.Border is {} && start.Border.Contains (currentLocation)) { found = start.Border; } - else if (start.Padding.Contains (currentLocation)) + else if (start.Padding is { } && start.Padding.Contains(currentLocation)) { found = start.Padding; } diff --git a/Terminal.Gui/Views/Bar.cs b/Terminal.Gui/Views/Bar.cs index 29c867a57..d986b5f64 100644 --- a/Terminal.Gui/Views/Bar.cs +++ b/Terminal.Gui/Views/Bar.cs @@ -87,8 +87,11 @@ public class Bar : View, IOrientation, IDesignable /// public override void SetBorderStyle (LineStyle value) { - // The default changes the thickness. We don't want that. We just set the style. - Border.LineStyle = value; + if (Border is { }) + { + // The default changes the thickness. We don't want that. We just set the style. + Border.LineStyle = value; + } } #region IOrientation members diff --git a/Terminal.Gui/Views/Menuv2.cs b/Terminal.Gui/Views/Menuv2.cs index 928c36cd6..609fb962c 100644 --- a/Terminal.Gui/Views/Menuv2.cs +++ b/Terminal.Gui/Views/Menuv2.cs @@ -46,6 +46,7 @@ public class Menuv2 : Bar // Menuv2 arranges the items horizontally. // The first item has no left border, the last item has no right border. // The Shortcuts are configured with the command, help, and key views aligned in reverse order (EndToStart). + /// protected override void OnSubviewLayout (LayoutEventArgs args) { for (int index = 0; index < Subviews.Count; index++) diff --git a/Terminal.Gui/Views/Shortcut.cs b/Terminal.Gui/Views/Shortcut.cs index 0eaefccf1..b139fcdd1 100644 --- a/Terminal.Gui/Views/Shortcut.cs +++ b/Terminal.Gui/Views/Shortcut.cs @@ -98,7 +98,11 @@ public class Shortcut : View, IOrientation, IDesignable CanFocus = true; SuperViewRendersLineCanvas = true; - Border.Settings &= ~BorderSettings.Title; + + if (Border is { }) + { + Border.Settings &= ~BorderSettings.Title; + } Width = GetWidthDimAuto (); Height = Dim.Auto (DimAutoStyle.Content, 1); @@ -237,39 +241,39 @@ public class Shortcut : View, IOrientation, IDesignable ShowHide (); ForceCalculateNaturalWidth (); - if (Width is DimAuto widthAuto) + if (Width is DimAuto widthAuto || HelpView!.Margin is null) { + return; + } - } + // Frame.Width is smaller than the natural width. Reduce width of HelpView. + _maxHelpWidth = int.Max (0, GetContentSize ().Width - CommandView.Frame.Width - KeyView.Frame.Width); + + if (_maxHelpWidth < 3) + { + Thickness t = GetMarginThickness (); + + switch (_maxHelpWidth) + { + case 0: + case 1: + // Scrunch it by removing both margins + HelpView.Margin.Thickness = new (t.Right - 1, t.Top, t.Left - 1, t.Bottom); + + break; + + case 2: + + // Scrunch just the right margin + HelpView.Margin.Thickness = new (t.Right, t.Top, t.Left - 1, t.Bottom); + + break; + } + } else { - // Frame.Width is smaller than the natural width. Reduce width of HelpView. - _maxHelpWidth = int.Max (0, GetContentSize ().Width - CommandView.Frame.Width - KeyView.Frame.Width); - if (_maxHelpWidth < 3) - { - Thickness t = GetMarginThickness (); - switch (_maxHelpWidth) - { - case 0: - case 1: - // Scrunch it by removing both margins - HelpView.Margin.Thickness = new (t.Right - 1, t.Top, t.Left - 1, t.Bottom); - - break; - - case 2: - - // Scrunch just the right margin - HelpView.Margin.Thickness = new (t.Right, t.Top, t.Left - 1, t.Bottom); - - break; - } - } - else - { - // Reset to default - HelpView.Margin.Thickness = GetMarginThickness (); - } + // Reset to default + HelpView.Margin.Thickness = GetMarginThickness (); } } @@ -522,7 +526,11 @@ public class Shortcut : View, IOrientation, IDesignable private void SetHelpViewDefaultLayout () { - HelpView.Margin.Thickness = GetMarginThickness (); + if (HelpView.Margin is { }) + { + HelpView.Margin.Thickness = GetMarginThickness (); + } + HelpView.X = Pos.Align (Alignment.End, AlignmentModes); _maxHelpWidth = HelpView.Text.GetColumns (); HelpView.Width = Dim.Auto (DimAutoStyle.Text, maximumContentDim: Dim.Func ((() => _maxHelpWidth))); @@ -654,7 +662,11 @@ public class Shortcut : View, IOrientation, IDesignable private void SetKeyViewDefaultLayout () { - KeyView.Margin.Thickness = GetMarginThickness (); + if (KeyView.Margin is { }) + { + KeyView.Margin.Thickness = GetMarginThickness (); + } + KeyView.X = Pos.Align (Alignment.End, AlignmentModes); KeyView.Width = Dim.Auto (DimAutoStyle.Text, minimumContentDim: Dim.Func (() => MinimumKeyTextSize)); KeyView.Height = Dim.Fill (); @@ -763,7 +775,10 @@ public class Shortcut : View, IOrientation, IDesignable KeyView.ColorScheme = cs; } - CommandView.Margin.ColorScheme = base.ColorScheme; + if (CommandView.Margin is { }) + { + CommandView.Margin.ColorScheme = base.ColorScheme; + } } /// diff --git a/Terminal.Gui/Views/TabView.cs b/Terminal.Gui/Views/TabView.cs index 3d77b9b9b..2b6007d61 100644 --- a/Terminal.Gui/Views/TabView.cs +++ b/Terminal.Gui/Views/TabView.cs @@ -216,7 +216,7 @@ public class TabView : View /// /// Updates the control to use the latest state settings in . This can change the size of the /// client area of the tab (for rendering the selected tab's content). This method includes a call to - /// . + /// . /// public void ApplyStyleChanges () { @@ -288,7 +288,7 @@ public class TabView : View /// Updates to be a valid index of . /// The value to validate. - /// Changes will not be immediately visible in the display until you call . + /// Changes will not be immediately visible in the display until you call . /// The valid for the given value. public int EnsureValidScrollOffsets (int value) { return Math.Max (Math.Min (value, Tabs.Count - 1), 0); } diff --git a/Terminal.Gui/Views/TableView/TableView.cs b/Terminal.Gui/Views/TableView/TableView.cs index 1a183bf6c..7b19eb0a5 100644 --- a/Terminal.Gui/Views/TableView/TableView.cs +++ b/Terminal.Gui/Views/TableView/TableView.cs @@ -583,7 +583,7 @@ public class TableView : View, IDesignable /// not been set. /// /// - /// Changes will not be immediately visible in the display until you call + /// Changes will not be immediately visible in the display until you call /// public void EnsureSelectedCellIsVisible () { @@ -644,7 +644,7 @@ public class TableView : View, IDesignable /// (by adjusting them to the nearest existing cell). Has no effect if has not been set. /// /// - /// Changes will not be immediately visible in the display until you call + /// Changes will not be immediately visible in the display until you call /// public void EnsureValidScrollOffsets () { @@ -663,7 +663,7 @@ public class TableView : View, IDesignable /// has not been set. /// /// - /// Changes will not be immediately visible in the display until you call + /// Changes will not be immediately visible in the display until you call /// public void EnsureValidSelection () { @@ -1227,7 +1227,7 @@ public class TableView : View, IDesignable /// Updates the view to reflect changes to and to ( / /// ) etc /// - /// This always calls + /// This always calls public void Update () { if (!IsInitialized || TableIsNullOrInvisible ())