|
@@ -5,6 +5,242 @@ namespace Terminal.Gui;
|
|
|
|
|
|
public partial class View
|
|
public partial class View
|
|
{
|
|
{
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Indicates whether the specified SuperView-relative coordinates are within the View's <see cref="Frame"/>.
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <param name="location">SuperView-relative coordinate</param>
|
|
|
|
+ /// <returns><see langword="true"/> if the specified SuperView-relative coordinates are within the View.</returns>
|
|
|
|
+ public virtual bool Contains (in Point location) { return Frame.Contains (location); }
|
|
|
|
+
|
|
|
|
+ /// <summary>Finds the first Subview of <paramref name="start"/> that is visible at the provided location.</summary>
|
|
|
|
+ /// <remarks>
|
|
|
|
+ /// <para>
|
|
|
|
+ /// Used to determine what view the mouse is over.
|
|
|
|
+ /// </para>
|
|
|
|
+ /// </remarks>
|
|
|
|
+ /// <param name="start">The view to scope the search by.</param>
|
|
|
|
+ /// <param name="location"><paramref name="start"/>.SuperView-relative coordinate.</param>
|
|
|
|
+ /// <returns>
|
|
|
|
+ /// The view that was found at the <paramref name="location"/> coordinate.
|
|
|
|
+ /// <see langword="null"/> if no view was found.
|
|
|
|
+ /// </returns>
|
|
|
|
+
|
|
|
|
+ // CONCURRENCY: This method is not thread-safe. Undefined behavior and likely program crashes are exposed by unsynchronized access to InternalSubviews.
|
|
|
|
+ internal static View? FindDeepestView (View? start, in Point location)
|
|
|
|
+ {
|
|
|
|
+ Point currentLocation = location;
|
|
|
|
+
|
|
|
|
+ while (start is { Visible: true } && start.Contains (currentLocation))
|
|
|
|
+ {
|
|
|
|
+ Adornment? found = null;
|
|
|
|
+
|
|
|
|
+ if (start.Margin.Contains (currentLocation))
|
|
|
|
+ {
|
|
|
|
+ found = start.Margin;
|
|
|
|
+ }
|
|
|
|
+ else if (start.Border.Contains (currentLocation))
|
|
|
|
+ {
|
|
|
|
+ found = start.Border;
|
|
|
|
+ }
|
|
|
|
+ else if (start.Padding.Contains (currentLocation))
|
|
|
|
+ {
|
|
|
|
+ found = start.Padding;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ Point viewportOffset = start.GetViewportOffsetFromFrame ();
|
|
|
|
+
|
|
|
|
+ if (found is { })
|
|
|
|
+ {
|
|
|
|
+ start = found;
|
|
|
|
+ viewportOffset = found.Parent.Frame.Location;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ int startOffsetX = currentLocation.X - (start.Frame.X + viewportOffset.X);
|
|
|
|
+ int startOffsetY = currentLocation.Y - (start.Frame.Y + viewportOffset.Y);
|
|
|
|
+
|
|
|
|
+ View? subview = null;
|
|
|
|
+
|
|
|
|
+ for (int i = start.InternalSubviews.Count - 1; i >= 0; i--)
|
|
|
|
+ {
|
|
|
|
+ if (start.InternalSubviews [i].Visible
|
|
|
|
+ && start.InternalSubviews [i].Contains (new (startOffsetX + start.Viewport.X, startOffsetY + start.Viewport.Y)))
|
|
|
|
+ {
|
|
|
|
+ subview = start.InternalSubviews [i];
|
|
|
|
+ currentLocation.X = startOffsetX + start.Viewport.X;
|
|
|
|
+ currentLocation.Y = startOffsetY + start.Viewport.Y;
|
|
|
|
+
|
|
|
|
+ // start is the deepest subview under the mouse; stop searching the subviews
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (subview is null)
|
|
|
|
+ {
|
|
|
|
+ // No subview was found that's under the mouse, so we're done
|
|
|
|
+ return start;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // We found a subview of start that's under the mouse, continue...
|
|
|
|
+ start = subview;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return null;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // BUGBUG: This method interferes with Dialog/MessageBox default min/max size.
|
|
|
|
+
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Gets a new location of the <see cref="View"/> that is within the Viewport of the <paramref name="viewToMove"/>'s
|
|
|
|
+ /// <see cref="View.SuperView"/> (e.g. for dragging a Window). The `out` parameters are the new X and Y coordinates.
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <remarks>
|
|
|
|
+ /// If <paramref name="viewToMove"/> does not have a <see cref="View.SuperView"/> or it's SuperView is not
|
|
|
|
+ /// <see cref="Application.Top"/> the position will be bound by the <see cref="ConsoleDriver.Cols"/> and
|
|
|
|
+ /// <see cref="ConsoleDriver.Rows"/>.
|
|
|
|
+ /// </remarks>
|
|
|
|
+ /// <param name="viewToMove">The View that is to be moved.</param>
|
|
|
|
+ /// <param name="targetX">The target x location.</param>
|
|
|
|
+ /// <param name="targetY">The target y location.</param>
|
|
|
|
+ /// <param name="nx">The new x location that will ensure <paramref name="viewToMove"/> will be fully visible.</param>
|
|
|
|
+ /// <param name="ny">The new y location that will ensure <paramref name="viewToMove"/> will be fully visible.</param>
|
|
|
|
+ /// <param name="statusBar">The new top most statusBar</param>
|
|
|
|
+ /// <returns>
|
|
|
|
+ /// Either <see cref="Application.Top"/> (if <paramref name="viewToMove"/> does not have a Super View) or
|
|
|
|
+ /// <paramref name="viewToMove"/>'s SuperView. This can be used to ensure LayoutSubviews is called on the correct View.
|
|
|
|
+ /// </returns>
|
|
|
|
+ internal static View GetLocationEnsuringFullVisibility (
|
|
|
|
+ View viewToMove,
|
|
|
|
+ int targetX,
|
|
|
|
+ int targetY,
|
|
|
|
+ out int nx,
|
|
|
|
+ out int ny,
|
|
|
|
+ out StatusBar statusBar
|
|
|
|
+ )
|
|
|
|
+ {
|
|
|
|
+ int maxDimension;
|
|
|
|
+ View superView;
|
|
|
|
+ statusBar = null!;
|
|
|
|
+
|
|
|
|
+ if (viewToMove?.SuperView is null || viewToMove == Application.Top || viewToMove?.SuperView == Application.Top)
|
|
|
|
+ {
|
|
|
|
+ maxDimension = Driver.Cols;
|
|
|
|
+ superView = Application.Top;
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ // Use the SuperView's Viewport, not Frame
|
|
|
|
+ maxDimension = viewToMove!.SuperView.Viewport.Width;
|
|
|
|
+ superView = viewToMove.SuperView;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (superView?.Margin is { } && superView == viewToMove!.SuperView)
|
|
|
|
+ {
|
|
|
|
+ maxDimension -= superView.GetAdornmentsThickness ().Left + superView.GetAdornmentsThickness ().Right;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (viewToMove!.Frame.Width <= maxDimension)
|
|
|
|
+ {
|
|
|
|
+ nx = Math.Max (targetX, 0);
|
|
|
|
+ nx = nx + viewToMove.Frame.Width > maxDimension ? Math.Max (maxDimension - viewToMove.Frame.Width, 0) : nx;
|
|
|
|
+
|
|
|
|
+ if (nx > viewToMove.Frame.X + viewToMove.Frame.Width)
|
|
|
|
+ {
|
|
|
|
+ nx = Math.Max (viewToMove.Frame.Right, 0);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ nx = targetX;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ //System.Diagnostics.Debug.WriteLine ($"nx:{nx}, rWidth:{rWidth}");
|
|
|
|
+ var menuVisible = false;
|
|
|
|
+ var statusVisible = false;
|
|
|
|
+
|
|
|
|
+ if (viewToMove?.SuperView is null || viewToMove == Application.Top || viewToMove?.SuperView == Application.Top)
|
|
|
|
+ {
|
|
|
|
+ menuVisible = Application.Top?.MenuBar?.Visible == true;
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ View t = viewToMove!.SuperView;
|
|
|
|
+
|
|
|
|
+ while (t is { } and not Toplevel)
|
|
|
|
+ {
|
|
|
|
+ t = t.SuperView;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (t is Toplevel topLevel)
|
|
|
|
+ {
|
|
|
|
+ menuVisible = topLevel.MenuBar?.Visible == true;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (viewToMove?.SuperView is null || viewToMove == Application.Top || viewToMove?.SuperView == Application.Top)
|
|
|
|
+ {
|
|
|
|
+ maxDimension = menuVisible ? 1 : 0;
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ maxDimension = 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ny = Math.Max (targetY, maxDimension);
|
|
|
|
+
|
|
|
|
+ if (viewToMove?.SuperView is null || viewToMove == Application.Top || viewToMove?.SuperView == Application.Top)
|
|
|
|
+ {
|
|
|
|
+ statusVisible = Application.Top?.StatusBar?.Visible == true;
|
|
|
|
+ statusBar = Application.Top?.StatusBar!;
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ View t = viewToMove!.SuperView;
|
|
|
|
+
|
|
|
|
+ while (t is { } and not Toplevel)
|
|
|
|
+ {
|
|
|
|
+ t = t.SuperView;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (t is Toplevel topLevel)
|
|
|
|
+ {
|
|
|
|
+ statusVisible = topLevel.StatusBar?.Visible == true;
|
|
|
|
+ statusBar = topLevel.StatusBar!;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (viewToMove?.SuperView is null || viewToMove == Application.Top || viewToMove?.SuperView == Application.Top)
|
|
|
|
+ {
|
|
|
|
+ maxDimension = statusVisible ? Driver.Rows - 1 : Driver.Rows;
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ maxDimension = statusVisible ? viewToMove!.SuperView.Viewport.Height - 1 : viewToMove!.SuperView.Viewport.Height;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (superView?.Margin is { } && superView == viewToMove?.SuperView)
|
|
|
|
+ {
|
|
|
|
+ maxDimension -= superView.GetAdornmentsThickness ().Top + superView.GetAdornmentsThickness ().Bottom;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ny = Math.Min (ny, maxDimension);
|
|
|
|
+
|
|
|
|
+ if (viewToMove?.Frame.Height <= maxDimension)
|
|
|
|
+ {
|
|
|
|
+ ny = ny + viewToMove.Frame.Height > maxDimension
|
|
|
|
+ ? Math.Max (maxDimension - viewToMove.Frame.Height, menuVisible ? 1 : 0)
|
|
|
|
+ : ny;
|
|
|
|
+
|
|
|
|
+ if (ny > viewToMove.Frame.Y + viewToMove.Frame.Height)
|
|
|
|
+ {
|
|
|
|
+ ny = Math.Max (viewToMove.Frame.Bottom, 0);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ //System.Diagnostics.Debug.WriteLine ($"ny:{ny}, rHeight:{rHeight}");
|
|
|
|
+
|
|
|
|
+ return superView!;
|
|
|
|
+ }
|
|
|
|
+
|
|
#region Frame
|
|
#region Frame
|
|
|
|
|
|
private Rectangle _frame;
|
|
private Rectangle _frame;
|
|
@@ -15,7 +251,10 @@ public partial class View
|
|
/// <see cref="SuperView"/>'s Content, which is bound by <see cref="GetContentSize ()"/>.
|
|
/// <see cref="SuperView"/>'s Content, which is bound by <see cref="GetContentSize ()"/>.
|
|
/// </value>
|
|
/// </value>
|
|
/// <remarks>
|
|
/// <remarks>
|
|
- /// <para>Frame is relative to the <see cref="SuperView"/>'s Content, which is bound by <see cref="GetContentSize ()"/>.</para>
|
|
|
|
|
|
+ /// <para>
|
|
|
|
+ /// Frame is relative to the <see cref="SuperView"/>'s Content, which is bound by <see cref="GetContentSize ()"/>
|
|
|
|
+ /// .
|
|
|
|
+ /// </para>
|
|
/// <para>
|
|
/// <para>
|
|
/// Setting Frame will set <see cref="X"/>, <see cref="Y"/>, <see cref="Width"/>, and <see cref="Height"/> to the
|
|
/// Setting Frame will set <see cref="X"/>, <see cref="Y"/>, <see cref="Width"/>, and <see cref="Height"/> to the
|
|
/// values of the corresponding properties of the <paramref name="value"/> parameter.
|
|
/// values of the corresponding properties of the <paramref name="value"/> parameter.
|
|
@@ -45,7 +284,6 @@ public partial class View
|
|
_width = _frame.Width;
|
|
_width = _frame.Width;
|
|
_height = _frame.Height;
|
|
_height = _frame.Height;
|
|
|
|
|
|
- // TODO: Figure out if the below can be optimized.
|
|
|
|
if (IsInitialized)
|
|
if (IsInitialized)
|
|
{
|
|
{
|
|
OnResizeNeeded ();
|
|
OnResizeNeeded ();
|
|
@@ -65,8 +303,6 @@ public partial class View
|
|
// This is the only place where _frame should be set directly. Use Frame = or SetFrame instead.
|
|
// This is the only place where _frame should be set directly. Use Frame = or SetFrame instead.
|
|
_frame = frame;
|
|
_frame = frame;
|
|
|
|
|
|
- SetTextFormatterSize ();
|
|
|
|
-
|
|
|
|
OnViewportChanged (new (IsInitialized ? Viewport : Rectangle.Empty, oldViewport));
|
|
OnViewportChanged (new (IsInitialized ? Viewport : Rectangle.Empty, oldViewport));
|
|
}
|
|
}
|
|
|
|
|
|
@@ -133,7 +369,8 @@ public partial class View
|
|
/// <value>The <see cref="Pos"/> object representing the X position.</value>
|
|
/// <value>The <see cref="Pos"/> object representing the X position.</value>
|
|
/// <remarks>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// <para>
|
|
- /// The position is relative to the <see cref="SuperView"/>'s Content, which is bound by <see cref="GetContentSize ()"/>.
|
|
|
|
|
|
+ /// The position is relative to the <see cref="SuperView"/>'s Content, which is bound by
|
|
|
|
+ /// <see cref="GetContentSize ()"/>.
|
|
/// </para>
|
|
/// </para>
|
|
/// <para>
|
|
/// <para>
|
|
/// If set to a relative value (e.g. <see cref="Pos.Center"/>) the value is indeterminate until the view has been
|
|
/// If set to a relative value (e.g. <see cref="Pos.Center"/>) the value is indeterminate until the view has been
|
|
@@ -171,7 +408,8 @@ public partial class View
|
|
/// <value>The <see cref="Pos"/> object representing the Y position.</value>
|
|
/// <value>The <see cref="Pos"/> object representing the Y position.</value>
|
|
/// <remarks>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// <para>
|
|
- /// The position is relative to the <see cref="SuperView"/>'s Content, which is bound by <see cref="GetContentSize ()"/>.
|
|
|
|
|
|
+ /// The position is relative to the <see cref="SuperView"/>'s Content, which is bound by
|
|
|
|
+ /// <see cref="GetContentSize ()"/>.
|
|
/// </para>
|
|
/// </para>
|
|
/// <para>
|
|
/// <para>
|
|
/// If set to a relative value (e.g. <see cref="Pos.Center"/>) the value is indeterminate until the view has been
|
|
/// If set to a relative value (e.g. <see cref="Pos.Center"/>) the value is indeterminate until the view has been
|
|
@@ -208,7 +446,8 @@ public partial class View
|
|
/// <value>The <see cref="Dim"/> object representing the height of the view (the number of rows).</value>
|
|
/// <value>The <see cref="Dim"/> object representing the height of the view (the number of rows).</value>
|
|
/// <remarks>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// <para>
|
|
- /// The dimension is relative to the <see cref="SuperView"/>'s Content, which is bound by <see cref="GetContentSize ()"/>
|
|
|
|
|
|
+ /// The dimension is relative to the <see cref="SuperView"/>'s Content, which is bound by
|
|
|
|
+ /// <see cref="GetContentSize ()"/>
|
|
/// .
|
|
/// .
|
|
/// </para>
|
|
/// </para>
|
|
/// <para>
|
|
/// <para>
|
|
@@ -235,7 +474,7 @@ public partial class View
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
- if (_height is DimAuto)
|
|
|
|
|
|
+ if (_height is { } && _height.Has (typeof (DimAuto), out _))
|
|
{
|
|
{
|
|
// Reset ContentSize to Viewport
|
|
// Reset ContentSize to Viewport
|
|
_contentSize = null;
|
|
_contentSize = null;
|
|
@@ -243,6 +482,9 @@ public partial class View
|
|
|
|
|
|
_height = value ?? throw new ArgumentNullException (nameof (value), @$"{nameof (Height)} cannot be null");
|
|
_height = value ?? throw new ArgumentNullException (nameof (value), @$"{nameof (Height)} cannot be null");
|
|
|
|
|
|
|
|
+ // Reset TextFormatter - Will be recalculated in SetTextFormatterSize
|
|
|
|
+ TextFormatter.ConstrainToHeight = null;
|
|
|
|
+
|
|
OnResizeNeeded ();
|
|
OnResizeNeeded ();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -253,7 +495,8 @@ public partial class View
|
|
/// <value>The <see cref="Dim"/> object representing the width of the view (the number of columns).</value>
|
|
/// <value>The <see cref="Dim"/> object representing the width of the view (the number of columns).</value>
|
|
/// <remarks>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// <para>
|
|
- /// The dimension is relative to the <see cref="SuperView"/>'s Content, which is bound by <see cref="GetContentSize ()"/>
|
|
|
|
|
|
+ /// The dimension is relative to the <see cref="SuperView"/>'s Content, which is bound by
|
|
|
|
+ /// <see cref="GetContentSize ()"/>
|
|
/// .
|
|
/// .
|
|
/// </para>
|
|
/// </para>
|
|
/// <para>
|
|
/// <para>
|
|
@@ -280,7 +523,7 @@ public partial class View
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
- if (_width is DimAuto)
|
|
|
|
|
|
+ if (_width is { } && _width.Has (typeof (DimAuto), out _))
|
|
{
|
|
{
|
|
// Reset ContentSize to Viewport
|
|
// Reset ContentSize to Viewport
|
|
_contentSize = null;
|
|
_contentSize = null;
|
|
@@ -288,6 +531,9 @@ public partial class View
|
|
|
|
|
|
_width = value ?? throw new ArgumentNullException (nameof (value), @$"{nameof (Width)} cannot be null");
|
|
_width = value ?? throw new ArgumentNullException (nameof (value), @$"{nameof (Width)} cannot be null");
|
|
|
|
|
|
|
|
+ // Reset TextFormatter - Will be recalculated in SetTextFormatterSize
|
|
|
|
+ TextFormatter.ConstrainToWidth = null;
|
|
|
|
+
|
|
OnResizeNeeded ();
|
|
OnResizeNeeded ();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -296,258 +542,127 @@ public partial class View
|
|
|
|
|
|
#region Layout Engine
|
|
#region Layout Engine
|
|
|
|
|
|
- #endregion Layout Engine
|
|
|
|
-
|
|
|
|
- /// <summary>
|
|
|
|
- /// Indicates whether the specified SuperView-relative coordinates are within the View's <see cref="Frame"/>.
|
|
|
|
- /// </summary>
|
|
|
|
- /// <param name="location">SuperView-relative coordinate</param>
|
|
|
|
- /// <returns><see langword="true"/> if the specified SuperView-relative coordinates are within the View.</returns>
|
|
|
|
- public virtual bool Contains (in Point location) { return Frame.Contains (location); }
|
|
|
|
-
|
|
|
|
- /// <summary>Finds the first Subview of <paramref name="start"/> that is visible at the provided location.</summary>
|
|
|
|
|
|
+ /// <summary>Fired after the View's <see cref="LayoutSubviews"/> method has completed.</summary>
|
|
/// <remarks>
|
|
/// <remarks>
|
|
- /// <para>
|
|
|
|
- /// Used to determine what view the mouse is over.
|
|
|
|
- /// </para>
|
|
|
|
|
|
+ /// Subscribe to this event to perform tasks when the <see cref="View"/> has been resized or the layout has
|
|
|
|
+ /// otherwise changed.
|
|
/// </remarks>
|
|
/// </remarks>
|
|
- /// <param name="start">The view to scope the search by.</param>
|
|
|
|
- /// <param name="location"><paramref name="start"/>.SuperView-relative coordinate.</param>
|
|
|
|
- /// <returns>
|
|
|
|
- /// The view that was found at the <paramref name="location"/> coordinate.
|
|
|
|
- /// <see langword="null"/> if no view was found.
|
|
|
|
- /// </returns>
|
|
|
|
-
|
|
|
|
- // CONCURRENCY: This method is not thread-safe. Undefined behavior and likely program crashes are exposed by unsynchronized access to InternalSubviews.
|
|
|
|
- internal static View? FindDeepestView (View? start, in Point location)
|
|
|
|
- {
|
|
|
|
- Point currentLocation = location;
|
|
|
|
- while (start is { Visible: true } && start.Contains (currentLocation))
|
|
|
|
- {
|
|
|
|
- Adornment? found = null;
|
|
|
|
-
|
|
|
|
- if (start.Margin.Contains (currentLocation))
|
|
|
|
- {
|
|
|
|
- found = start.Margin;
|
|
|
|
- }
|
|
|
|
- else if (start.Border.Contains (currentLocation))
|
|
|
|
- {
|
|
|
|
- found = start.Border;
|
|
|
|
- }
|
|
|
|
- else if (start.Padding.Contains (currentLocation))
|
|
|
|
- {
|
|
|
|
- found = start.Padding;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- Point viewportOffset = start.GetViewportOffsetFromFrame ();
|
|
|
|
-
|
|
|
|
- if (found is { })
|
|
|
|
- {
|
|
|
|
- start = found;
|
|
|
|
- viewportOffset = found.Parent.Frame.Location;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- int startOffsetX = currentLocation.X - (start.Frame.X + viewportOffset.X);
|
|
|
|
- int startOffsetY = currentLocation.Y - (start.Frame.Y + viewportOffset.Y);
|
|
|
|
-
|
|
|
|
- View? subview = null;
|
|
|
|
-
|
|
|
|
- for (int i = start.InternalSubviews.Count - 1; i >= 0; i--)
|
|
|
|
- {
|
|
|
|
- if (start.InternalSubviews [i].Visible
|
|
|
|
- && start.InternalSubviews [i].Contains (new (startOffsetX + start.Viewport.X, startOffsetY + start.Viewport.Y)))
|
|
|
|
- {
|
|
|
|
- subview = start.InternalSubviews [i];
|
|
|
|
- currentLocation.X = startOffsetX + start.Viewport.X;
|
|
|
|
- currentLocation.Y = startOffsetY + start.Viewport.Y;
|
|
|
|
-
|
|
|
|
- // start is the deepest subview under the mouse; stop searching the subviews
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (subview is null)
|
|
|
|
- {
|
|
|
|
- // No subview was found that's under the mouse, so we're done
|
|
|
|
- return start;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // We found a subview of start that's under the mouse, continue...
|
|
|
|
- start = subview;
|
|
|
|
- }
|
|
|
|
|
|
+ public event EventHandler<LayoutEventArgs> LayoutComplete;
|
|
|
|
|
|
- return null;
|
|
|
|
- }
|
|
|
|
|
|
+ /// <summary>Fired after the View's <see cref="LayoutSubviews"/> method has completed.</summary>
|
|
|
|
+ /// <remarks>
|
|
|
|
+ /// Subscribe to this event to perform tasks when the <see cref="View"/> has been resized or the layout has
|
|
|
|
+ /// otherwise changed.
|
|
|
|
+ /// </remarks>
|
|
|
|
+ public event EventHandler<LayoutEventArgs> LayoutStarted;
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
- /// Gets a new location of the <see cref="View"/> that is within the Viewport of the <paramref name="viewToMove"/>'s
|
|
|
|
- /// <see cref="View.SuperView"/> (e.g. for dragging a Window). The `out` parameters are the new X and Y coordinates.
|
|
|
|
|
|
+ /// Adjusts <see cref="Frame"/> given the SuperView's ContentSize (nominally the same as
|
|
|
|
+ /// <c>this.SuperView.GetContentSize ()</c>)
|
|
|
|
+ /// and the position (<see cref="X"/>, <see cref="Y"/>) and dimension (<see cref="Width"/>, and
|
|
|
|
+ /// <see cref="Height"/>).
|
|
/// </summary>
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <remarks>
|
|
- /// If <paramref name="viewToMove"/> does not have a <see cref="View.SuperView"/> or it's SuperView is not
|
|
|
|
- /// <see cref="Application.Top"/> the position will be bound by the <see cref="ConsoleDriver.Cols"/> and
|
|
|
|
- /// <see cref="ConsoleDriver.Rows"/>.
|
|
|
|
|
|
+ /// <para>
|
|
|
|
+ /// If <see cref="X"/>, <see cref="Y"/>, <see cref="Width"/>, or <see cref="Height"/> are
|
|
|
|
+ /// absolute, they will be updated to reflect the new size and position of the view. Otherwise, they
|
|
|
|
+ /// are left unchanged.
|
|
|
|
+ /// </para>
|
|
|
|
+ /// <para>
|
|
|
|
+ /// If any of the view's subviews have a position or dimension dependent on either <see cref="GetContentSize"/> or
|
|
|
|
+ /// other subviews, <see cref="LayoutSubview"/> on
|
|
|
|
+ /// will be called for that subview.
|
|
|
|
+ /// </para>
|
|
/// </remarks>
|
|
/// </remarks>
|
|
- /// <param name="viewToMove">The View that is to be moved.</param>
|
|
|
|
- /// <param name="targetX">The target x location.</param>
|
|
|
|
- /// <param name="targetY">The target y location.</param>
|
|
|
|
- /// <param name="nx">The new x location that will ensure <paramref name="viewToMove"/> will be fully visible.</param>
|
|
|
|
- /// <param name="ny">The new y location that will ensure <paramref name="viewToMove"/> will be fully visible.</param>
|
|
|
|
- /// <param name="statusBar">The new top most statusBar</param>
|
|
|
|
- /// <returns>
|
|
|
|
- /// Either <see cref="Application.Top"/> (if <paramref name="viewToMove"/> does not have a Super View) or
|
|
|
|
- /// <paramref name="viewToMove"/>'s SuperView. This can be used to ensure LayoutSubviews is called on the correct View.
|
|
|
|
- /// </returns>
|
|
|
|
- internal static View GetLocationEnsuringFullVisibility (
|
|
|
|
- View viewToMove,
|
|
|
|
- int targetX,
|
|
|
|
- int targetY,
|
|
|
|
- out int nx,
|
|
|
|
- out int ny,
|
|
|
|
- out StatusBar statusBar
|
|
|
|
- )
|
|
|
|
|
|
+ /// <param name="superviewContentSize">
|
|
|
|
+ /// The size of the SuperView's content (nominally the same as <c>this.SuperView.GetContentSize ()</c>).
|
|
|
|
+ /// </param>
|
|
|
|
+ internal void SetRelativeLayout (Size superviewContentSize)
|
|
{
|
|
{
|
|
- int maxDimension;
|
|
|
|
- View superView;
|
|
|
|
- statusBar = null!;
|
|
|
|
|
|
+ Debug.Assert (_x is { });
|
|
|
|
+ Debug.Assert (_y is { });
|
|
|
|
+ Debug.Assert (_width is { });
|
|
|
|
+ Debug.Assert (_height is { });
|
|
|
|
|
|
- if (viewToMove?.SuperView is null || viewToMove == Application.Top || viewToMove?.SuperView == Application.Top)
|
|
|
|
|
|
+ CheckDimAuto ();
|
|
|
|
+ SetTextFormatterSize ();
|
|
|
|
+
|
|
|
|
+ int newX, newW, newY, newH;
|
|
|
|
+
|
|
|
|
+ // Calculate the new X, Y, Width, and Height
|
|
|
|
+ // If the Width or Height is Dim.Auto, calculate the Width or Height first. Otherwise, calculate the X or Y first.
|
|
|
|
+ if (_width is DimAuto)
|
|
{
|
|
{
|
|
- maxDimension = Driver.Cols;
|
|
|
|
- superView = Application.Top;
|
|
|
|
|
|
+ newW = _width.Calculate (0, superviewContentSize.Width, this, Dimension.Width);
|
|
|
|
+ newX = _x.Calculate (superviewContentSize.Width, newW, this, Dimension.Width);
|
|
}
|
|
}
|
|
else
|
|
else
|
|
{
|
|
{
|
|
- // Use the SuperView's Viewport, not Frame
|
|
|
|
- maxDimension = viewToMove!.SuperView.Viewport.Width;
|
|
|
|
- superView = viewToMove.SuperView;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (superView?.Margin is { } && superView == viewToMove!.SuperView)
|
|
|
|
- {
|
|
|
|
- maxDimension -= superView.GetAdornmentsThickness ().Left + superView.GetAdornmentsThickness ().Right;
|
|
|
|
|
|
+ newX = _x.Calculate (superviewContentSize.Width, _width, this, Dimension.Width);
|
|
|
|
+ newW = _width.Calculate (newX, superviewContentSize.Width, this, Dimension.Width);
|
|
}
|
|
}
|
|
|
|
|
|
- if (viewToMove!.Frame.Width <= maxDimension)
|
|
|
|
|
|
+ if (_height is DimAuto)
|
|
{
|
|
{
|
|
- nx = Math.Max (targetX, 0);
|
|
|
|
- nx = nx + viewToMove.Frame.Width > maxDimension ? Math.Max (maxDimension - viewToMove.Frame.Width, 0) : nx;
|
|
|
|
-
|
|
|
|
- if (nx > viewToMove.Frame.X + viewToMove.Frame.Width)
|
|
|
|
- {
|
|
|
|
- nx = Math.Max (viewToMove.Frame.Right, 0);
|
|
|
|
- }
|
|
|
|
|
|
+ newH = _height.Calculate (0, superviewContentSize.Height, this, Dimension.Height);
|
|
|
|
+ newY = _y.Calculate (superviewContentSize.Height, newH, this, Dimension.Height);
|
|
}
|
|
}
|
|
else
|
|
else
|
|
{
|
|
{
|
|
- nx = targetX;
|
|
|
|
|
|
+ newY = _y.Calculate (superviewContentSize.Height, _height, this, Dimension.Height);
|
|
|
|
+ newH = _height.Calculate (newY, superviewContentSize.Height, this, Dimension.Height);
|
|
}
|
|
}
|
|
|
|
|
|
- //System.Diagnostics.Debug.WriteLine ($"nx:{nx}, rWidth:{rWidth}");
|
|
|
|
- var menuVisible = false;
|
|
|
|
- var statusVisible = false;
|
|
|
|
|
|
+ Rectangle newFrame = new (newX, newY, newW, newH);
|
|
|
|
|
|
- if (viewToMove?.SuperView is null || viewToMove == Application.Top || viewToMove?.SuperView == Application.Top)
|
|
|
|
- {
|
|
|
|
- menuVisible = Application.Top?.MenuBar?.Visible == true;
|
|
|
|
- }
|
|
|
|
- else
|
|
|
|
|
|
+ if (Frame != newFrame)
|
|
{
|
|
{
|
|
- View t = viewToMove!.SuperView;
|
|
|
|
|
|
+ // Set the frame. Do NOT use `Frame` as it overwrites X, Y, Width, and Height
|
|
|
|
+ SetFrame (newFrame);
|
|
|
|
|
|
- while (t is { } and not Toplevel)
|
|
|
|
|
|
+ if (_x is PosAbsolute)
|
|
{
|
|
{
|
|
- t = t.SuperView;
|
|
|
|
|
|
+ _x = Frame.X;
|
|
}
|
|
}
|
|
|
|
|
|
- if (t is Toplevel topLevel)
|
|
|
|
|
|
+ if (_y is PosAbsolute)
|
|
{
|
|
{
|
|
- menuVisible = topLevel.MenuBar?.Visible == true;
|
|
|
|
|
|
+ _y = Frame.Y;
|
|
}
|
|
}
|
|
- }
|
|
|
|
-
|
|
|
|
- if (viewToMove?.SuperView is null || viewToMove == Application.Top || viewToMove?.SuperView == Application.Top)
|
|
|
|
- {
|
|
|
|
- maxDimension = menuVisible ? 1 : 0;
|
|
|
|
- }
|
|
|
|
- else
|
|
|
|
- {
|
|
|
|
- maxDimension = 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- ny = Math.Max (targetY, maxDimension);
|
|
|
|
|
|
|
|
- if (viewToMove?.SuperView is null || viewToMove == Application.Top || viewToMove?.SuperView == Application.Top)
|
|
|
|
- {
|
|
|
|
- statusVisible = Application.Top?.StatusBar?.Visible == true;
|
|
|
|
- statusBar = Application.Top?.StatusBar!;
|
|
|
|
- }
|
|
|
|
- else
|
|
|
|
- {
|
|
|
|
- View t = viewToMove!.SuperView;
|
|
|
|
|
|
+ if (_width is DimAbsolute)
|
|
|
|
+ {
|
|
|
|
+ _width = Frame.Width;
|
|
|
|
+ }
|
|
|
|
|
|
- while (t is { } and not Toplevel)
|
|
|
|
|
|
+ if (_height is DimAbsolute)
|
|
{
|
|
{
|
|
- t = t.SuperView;
|
|
|
|
|
|
+ _height = Frame.Height;
|
|
}
|
|
}
|
|
|
|
|
|
- if (t is Toplevel topLevel)
|
|
|
|
|
|
+ if (!string.IsNullOrEmpty (Title))
|
|
{
|
|
{
|
|
- statusVisible = topLevel.StatusBar?.Visible == true;
|
|
|
|
- statusBar = topLevel.StatusBar!;
|
|
|
|
|
|
+ SetTitleTextFormatterSize ();
|
|
}
|
|
}
|
|
- }
|
|
|
|
|
|
|
|
- if (viewToMove?.SuperView is null || viewToMove == Application.Top || viewToMove?.SuperView == Application.Top)
|
|
|
|
- {
|
|
|
|
- maxDimension = statusVisible ? Driver.Rows - 1 : Driver.Rows;
|
|
|
|
- }
|
|
|
|
- else
|
|
|
|
- {
|
|
|
|
- maxDimension = statusVisible ? viewToMove!.SuperView.Viewport.Height - 1 : viewToMove!.SuperView.Viewport.Height;
|
|
|
|
|
|
+ SetNeedsLayout ();
|
|
|
|
+ SetNeedsDisplay ();
|
|
}
|
|
}
|
|
|
|
|
|
- if (superView?.Margin is { } && superView == viewToMove?.SuperView)
|
|
|
|
|
|
+ if (TextFormatter.ConstrainToWidth is null)
|
|
{
|
|
{
|
|
- maxDimension -= superView.GetAdornmentsThickness ().Top + superView.GetAdornmentsThickness ().Bottom;
|
|
|
|
|
|
+ TextFormatter.ConstrainToWidth = GetContentSize ().Width;
|
|
}
|
|
}
|
|
|
|
|
|
- ny = Math.Min (ny, maxDimension);
|
|
|
|
-
|
|
|
|
- if (viewToMove?.Frame.Height <= maxDimension)
|
|
|
|
|
|
+ if (TextFormatter.ConstrainToHeight is null)
|
|
{
|
|
{
|
|
- ny = ny + viewToMove.Frame.Height > maxDimension
|
|
|
|
- ? Math.Max (maxDimension - viewToMove.Frame.Height, menuVisible ? 1 : 0)
|
|
|
|
- : ny;
|
|
|
|
-
|
|
|
|
- if (ny > viewToMove.Frame.Y + viewToMove.Frame.Height)
|
|
|
|
- {
|
|
|
|
- ny = Math.Max (viewToMove.Frame.Bottom, 0);
|
|
|
|
- }
|
|
|
|
|
|
+ TextFormatter.ConstrainToHeight = GetContentSize ().Height;
|
|
}
|
|
}
|
|
-
|
|
|
|
- //System.Diagnostics.Debug.WriteLine ($"ny:{ny}, rHeight:{rHeight}");
|
|
|
|
-
|
|
|
|
- return superView!;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- /// <summary>Fired after the View's <see cref="LayoutSubviews"/> method has completed.</summary>
|
|
|
|
- /// <remarks>
|
|
|
|
- /// Subscribe to this event to perform tasks when the <see cref="View"/> has been resized or the layout has
|
|
|
|
- /// otherwise changed.
|
|
|
|
- /// </remarks>
|
|
|
|
- public event EventHandler<LayoutEventArgs> LayoutComplete;
|
|
|
|
-
|
|
|
|
- /// <summary>Fired after the View's <see cref="LayoutSubviews"/> method has completed.</summary>
|
|
|
|
- /// <remarks>
|
|
|
|
- /// Subscribe to this event to perform tasks when the <see cref="View"/> has been resized or the layout has
|
|
|
|
- /// otherwise changed.
|
|
|
|
- /// </remarks>
|
|
|
|
- public event EventHandler<LayoutEventArgs> LayoutStarted;
|
|
|
|
-
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
- /// Invoked when a view starts executing or when the dimensions of the view have changed, for example in response to
|
|
|
|
- /// the container view or terminal resizing.
|
|
|
|
|
|
+ /// Invoked when the dimensions of the view have changed, for example in response to the container view or terminal
|
|
|
|
+ /// resizing.
|
|
/// </summary>
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// <para>
|
|
@@ -570,13 +685,11 @@ public partial class View
|
|
|
|
|
|
CheckDimAuto ();
|
|
CheckDimAuto ();
|
|
|
|
|
|
- var contentSize = GetContentSize ();
|
|
|
|
|
|
+ Size contentSize = GetContentSize ();
|
|
OnLayoutStarted (new (contentSize));
|
|
OnLayoutStarted (new (contentSize));
|
|
|
|
|
|
LayoutAdornments ();
|
|
LayoutAdornments ();
|
|
|
|
|
|
- SetTextFormatterSize ();
|
|
|
|
-
|
|
|
|
// Sort out the dependencies of the X, Y, Width, Height properties
|
|
// Sort out the dependencies of the X, Y, Width, Height properties
|
|
HashSet<View> nodes = new ();
|
|
HashSet<View> nodes = new ();
|
|
HashSet<(View, View)> edges = new ();
|
|
HashSet<(View, View)> edges = new ();
|
|
@@ -605,7 +718,7 @@ public partial class View
|
|
|
|
|
|
private void LayoutSubview (View v, Size contentSize)
|
|
private void LayoutSubview (View v, Size contentSize)
|
|
{
|
|
{
|
|
- // BUGBUG: Calling SetRelativeLayout before LayoutSubviews is problematic. Need to resolve.
|
|
|
|
|
|
+ // Note, SetRelativeLayout calls SetTextFormatterSize
|
|
v.SetRelativeLayout (contentSize);
|
|
v.SetRelativeLayout (contentSize);
|
|
v.LayoutSubviews ();
|
|
v.LayoutSubviews ();
|
|
v.LayoutNeeded = false;
|
|
v.LayoutNeeded = false;
|
|
@@ -620,9 +733,6 @@ public partial class View
|
|
/// </summary>
|
|
/// </summary>
|
|
internal virtual void OnLayoutComplete (LayoutEventArgs args) { LayoutComplete?.Invoke (this, args); }
|
|
internal virtual void OnLayoutComplete (LayoutEventArgs args) { LayoutComplete?.Invoke (this, args); }
|
|
|
|
|
|
- // BUGBUG: We need an API/event that is called from SetRelativeLayout instead of/in addition to
|
|
|
|
- // BUGBUG: OnLayoutStarted which is called from LayoutSubviews.
|
|
|
|
-
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
/// Raises the <see cref="LayoutStarted"/> event. Called from <see cref="LayoutSubviews"/> before any subviews
|
|
/// Raises the <see cref="LayoutStarted"/> event. Called from <see cref="LayoutSubviews"/> before any subviews
|
|
/// have been laid out.
|
|
/// have been laid out.
|
|
@@ -646,12 +756,10 @@ public partial class View
|
|
|
|
|
|
// Determine our container's ContentSize -
|
|
// Determine our container's ContentSize -
|
|
// First try SuperView.Viewport, then Application.Top, then Driver.Viewport.
|
|
// First try SuperView.Viewport, then Application.Top, then Driver.Viewport.
|
|
- // Finally, if none of those are valid, use int.MaxValue (for Unit tests).
|
|
|
|
|
|
+ // Finally, if none of those are valid, use 2048 (for Unit tests).
|
|
Size superViewContentSize = SuperView is { IsInitialized: true } ? SuperView.GetContentSize () :
|
|
Size superViewContentSize = SuperView is { IsInitialized: true } ? SuperView.GetContentSize () :
|
|
- Application.Top is { } && Application.Top != this && Application.Top.IsInitialized ? Application.Top.GetContentSize () :
|
|
|
|
- Application.Driver?.Screen.Size ?? new (int.MaxValue, int.MaxValue);
|
|
|
|
-
|
|
|
|
- SetTextFormatterSize ();
|
|
|
|
|
|
+ Application.Top is { } && Application.Top != this && Application.Top.IsInitialized ? Application.Top.GetContentSize () :
|
|
|
|
+ Application.Screen.Size;
|
|
|
|
|
|
SetRelativeLayout (superViewContentSize);
|
|
SetRelativeLayout (superViewContentSize);
|
|
|
|
|
|
@@ -689,90 +797,15 @@ public partial class View
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
- /// Adjusts <see cref="Frame"/> given the SuperView's ContentSize (nominally the same as
|
|
|
|
- /// <c>this.SuperView.GetContentSize ()</c>)
|
|
|
|
- /// and the position (<see cref="X"/>, <see cref="Y"/>) and dimension (<see cref="Width"/>, and
|
|
|
|
- /// <see cref="Height"/>).
|
|
|
|
|
|
+ /// Collects all views and their dependencies from a given starting view for layout purposes. Used by
|
|
|
|
+ /// <see cref="TopologicalSort"/> to create an ordered list of views to layout.
|
|
/// </summary>
|
|
/// </summary>
|
|
- /// <remarks>
|
|
|
|
- /// <para>
|
|
|
|
- /// If <see cref="X"/>, <see cref="Y"/>, <see cref="Width"/>, or <see cref="Height"/> are
|
|
|
|
- /// absolute, they will be updated to reflect the new size and position of the view. Otherwise, they
|
|
|
|
- /// are left unchanged.
|
|
|
|
- /// </para>
|
|
|
|
- /// </remarks>
|
|
|
|
- /// <param name="superviewContentSize">
|
|
|
|
- /// The size of the SuperView's content (nominally the same as <c>this.SuperView.GetContentSize ()</c>).
|
|
|
|
|
|
+ /// <param name="from">The starting view from which to collect dependencies.</param>
|
|
|
|
+ /// <param name="nNodes">A reference to a set of views representing nodes in the layout graph.</param>
|
|
|
|
+ /// <param name="nEdges">
|
|
|
|
+ /// A reference to a set of tuples representing edges in the layout graph, where each tuple consists of a pair of views
|
|
|
|
+ /// indicating a dependency.
|
|
/// </param>
|
|
/// </param>
|
|
- internal void SetRelativeLayout (Size superviewContentSize)
|
|
|
|
- {
|
|
|
|
- Debug.Assert (_x is { });
|
|
|
|
- Debug.Assert (_y is { });
|
|
|
|
- Debug.Assert (_width is { });
|
|
|
|
- Debug.Assert (_height is { });
|
|
|
|
-
|
|
|
|
- CheckDimAuto ();
|
|
|
|
- int newX, newW, newY, newH;
|
|
|
|
-
|
|
|
|
- if (_width is DimAuto)
|
|
|
|
- {
|
|
|
|
- newW = _width.Calculate (0, superviewContentSize.Width, this, Dimension.Width);
|
|
|
|
- newX = _x.Calculate (superviewContentSize.Width, newW, this, Dimension.Width);
|
|
|
|
- }
|
|
|
|
- else
|
|
|
|
- {
|
|
|
|
- newX = _x.Calculate (superviewContentSize.Width, _width, this, Dimension.Width);
|
|
|
|
- newW = _width.Calculate (newX, superviewContentSize.Width, this, Dimension.Width);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (_height is DimAuto)
|
|
|
|
- {
|
|
|
|
- newH = _height.Calculate (0, superviewContentSize.Height, this, Dimension.Height);
|
|
|
|
- newY = _y.Calculate (superviewContentSize.Height, newH, this, Dimension.Height);
|
|
|
|
- }
|
|
|
|
- else
|
|
|
|
- {
|
|
|
|
- newY = _y.Calculate (superviewContentSize.Height, _height, this, Dimension.Height);
|
|
|
|
- newH = _height.Calculate (newY, superviewContentSize.Height, this, Dimension.Height);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- Rectangle newFrame = new (newX, newY, newW, newH);
|
|
|
|
-
|
|
|
|
- if (Frame != newFrame)
|
|
|
|
- {
|
|
|
|
- // Set the frame. Do NOT use `Frame` as it overwrites X, Y, Width, and Height
|
|
|
|
- SetFrame (newFrame);
|
|
|
|
-
|
|
|
|
- if (_x is PosAbsolute)
|
|
|
|
- {
|
|
|
|
- _x = Frame.X;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (_y is PosAbsolute)
|
|
|
|
- {
|
|
|
|
- _y = Frame.Y;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (_width is DimAbsolute)
|
|
|
|
- {
|
|
|
|
- _width = Frame.Width;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (_height is DimAbsolute)
|
|
|
|
- {
|
|
|
|
- _height = Frame.Height;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (!string.IsNullOrEmpty (Title))
|
|
|
|
- {
|
|
|
|
- SetTitleTextFormatterSize ();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- SetNeedsLayout ();
|
|
|
|
- SetNeedsDisplay ();
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
internal void CollectAll (View from, ref HashSet<View> nNodes, ref HashSet<(View, View)> nEdges)
|
|
internal void CollectAll (View from, ref HashSet<View> nNodes, ref HashSet<(View, View)> nEdges)
|
|
{
|
|
{
|
|
foreach (View? v in from.InternalSubviews)
|
|
foreach (View? v in from.InternalSubviews)
|
|
@@ -785,6 +818,16 @@ public partial class View
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Collects dimension (where Width or Height is `DimView`) dependencies for a given view.
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <param name="dim">The dimension (width or height) to collect dependencies for.</param>
|
|
|
|
+ /// <param name="from">The view for which to collect dimension dependencies.</param>
|
|
|
|
+ /// <param name="nNodes">A reference to a set of views representing nodes in the layout graph.</param>
|
|
|
|
+ /// <param name="nEdges">
|
|
|
|
+ /// A reference to a set of tuples representing edges in the layout graph, where each tuple consists of a pair of views
|
|
|
|
+ /// indicating a dependency.
|
|
|
|
+ /// </param>
|
|
internal void CollectDim (Dim? dim, View from, ref HashSet<View> nNodes, ref HashSet<(View, View)> nEdges)
|
|
internal void CollectDim (Dim? dim, View from, ref HashSet<View> nNodes, ref HashSet<(View, View)> nEdges)
|
|
{
|
|
{
|
|
switch (dim)
|
|
switch (dim)
|
|
@@ -808,6 +851,16 @@ public partial class View
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Collects position (where X or Y is `PosView`) dependencies for a given view.
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <param name="pos">The position (X or Y) to collect dependencies for.</param>
|
|
|
|
+ /// <param name="from">The view for which to collect position dependencies.</param>
|
|
|
|
+ /// <param name="nNodes">A reference to a set of views representing nodes in the layout graph.</param>
|
|
|
|
+ /// <param name="nEdges">
|
|
|
|
+ /// A reference to a set of tuples representing edges in the layout graph, where each tuple consists of a pair of views
|
|
|
|
+ /// indicating a dependency.
|
|
|
|
+ /// </param>
|
|
internal void CollectPos (Pos pos, View from, ref HashSet<View> nNodes, ref HashSet<(View, View)> nEdges)
|
|
internal void CollectPos (Pos pos, View from, ref HashSet<View> nNodes, ref HashSet<(View, View)> nEdges)
|
|
{
|
|
{
|
|
switch (pos)
|
|
switch (pos)
|
|
@@ -966,7 +1019,6 @@ public partial class View
|
|
/// </remarks>
|
|
/// </remarks>
|
|
public bool ValidatePosDim { get; set; }
|
|
public bool ValidatePosDim { get; set; }
|
|
|
|
|
|
-
|
|
|
|
// TODO: Move this logic into the Pos/Dim classes
|
|
// TODO: Move this logic into the Pos/Dim classes
|
|
/// <summary>
|
|
/// <summary>
|
|
/// Throws an <see cref="InvalidOperationException"/> if any SubViews are using Dim objects that depend on this
|
|
/// Throws an <see cref="InvalidOperationException"/> if any SubViews are using Dim objects that depend on this
|
|
@@ -980,8 +1032,8 @@ public partial class View
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
- DimAuto? widthAuto = Width as DimAuto;
|
|
|
|
- DimAuto? heightAuto = Height as DimAuto;
|
|
|
|
|
|
+ var widthAuto = Width as DimAuto;
|
|
|
|
+ var heightAuto = Height as DimAuto;
|
|
|
|
|
|
// Verify none of the subviews are using Dim objects that depend on the SuperView's dimensions.
|
|
// Verify none of the subviews are using Dim objects that depend on the SuperView's dimensions.
|
|
foreach (View view in Subviews)
|
|
foreach (View view in Subviews)
|
|
@@ -1046,8 +1098,10 @@ public partial class View
|
|
throw new InvalidOperationException (
|
|
throw new InvalidOperationException (
|
|
$"{view.GetType ().Name}.{name} = {bad.GetType ().Name} "
|
|
$"{view.GetType ().Name}.{name} = {bad.GetType ().Name} "
|
|
+ $"which depends on the SuperView's dimensions and the SuperView uses Dim.Auto."
|
|
+ $"which depends on the SuperView's dimensions and the SuperView uses Dim.Auto."
|
|
- );
|
|
|
|
|
|
+ );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ #endregion Layout Engine
|
|
}
|
|
}
|