diff --git a/Terminal.Gui/Application.cs b/Terminal.Gui/Application.cs
index 897d95b33..f6f597ee0 100644
--- a/Terminal.Gui/Application.cs
+++ b/Terminal.Gui/Application.cs
@@ -88,7 +88,7 @@ public static partial class Application
#if DEBUG_IDISPOSABLE
// Don't dispose the toplevels. It's up to caller dispose them
- Debug.Assert (t.WasDisposed);
+ //Debug.Assert (t.WasDisposed);
#endif
}
@@ -1495,7 +1495,7 @@ public static partial class Application
View = view
};
- if (MouseGrabView.Viewport.Contains (viewRelativeMouseEvent.X, viewRelativeMouseEvent.Y) is false)
+ if ((MouseGrabView.Viewport with { Location = Point.Empty }).Contains (viewRelativeMouseEvent.X, viewRelativeMouseEvent.Y) is false)
{
// The mouse has moved outside the Viewport of the view that
// grabbed the mouse, so we tell the view that last got
@@ -1551,7 +1551,7 @@ public static partial class Application
View = view
};
}
- else if (view.ViewportToScreen (view.Viewport).Contains (a.MouseEvent.X, a.MouseEvent.Y))
+ else if (view.ViewportToScreen (Rectangle.Empty with {Size = view.Viewport.Size}).Contains (a.MouseEvent.X, a.MouseEvent.Y))
{
Point viewportLocation = view.ScreenToViewport (a.MouseEvent.X, a.MouseEvent.Y);
@@ -1591,10 +1591,16 @@ public static partial class Application
//Debug.WriteLine ($"OnMouseEvent: ({a.MouseEvent.X},{a.MouseEvent.Y}) - {a.MouseEvent.Flags}");
- if (view.OnMouseEvent (me))
+ while (view is {} && !view.OnMouseEvent (me))
{
- // Should we bubble up the event, if it is not handled?
- //return;
+ if (view is Adornment ad)
+ {
+ view = ad.Parent.SuperView;
+ }
+ else
+ {
+ view = view.SuperView;
+ }
}
BringOverlappedTopToFront ();
diff --git a/Terminal.Gui/Text/TextFormatter.cs b/Terminal.Gui/Text/TextFormatter.cs
index 5e970d86f..db13b5930 100644
--- a/Terminal.Gui/Text/TextFormatter.cs
+++ b/Terminal.Gui/Text/TextFormatter.cs
@@ -202,14 +202,14 @@ public class TextFormatter
/// Causes the text to be formatted (references ). Sets to
/// false.
///
- /// Specifies the screen-relative location and maximum size for drawing the text.
+ /// Specifies the screen-relative location and maximum size for drawing the text.
/// The color to use for all text except the hotkey
/// The color to use to draw the hotkey
/// Specifies the screen-relative location and maximum container size.
/// The console driver currently used by the application.
///
public void Draw (
- Rectangle viewport,
+ Rectangle screen,
Attribute normalColor,
Attribute hotColor,
Rectangle maximum = default,
@@ -240,46 +240,46 @@ public class TextFormatter
}
bool isVertical = IsVerticalDirection (Direction);
- Rectangle maxViewport = viewport;
+ Rectangle maxScreen = screen;
if (driver is { })
{
// INTENT: What, exactly, is the intent of this?
- maxViewport = maximum == default (Rectangle)
- ? viewport
+ maxScreen = maximum == default (Rectangle)
+ ? screen
: new (
- Math.Max (maximum.X, viewport.X),
- Math.Max (maximum.Y, viewport.Y),
+ Math.Max (maximum.X, screen.X),
+ Math.Max (maximum.Y, screen.Y),
Math.Max (
- Math.Min (maximum.Width, maximum.Right - viewport.Left),
+ Math.Min (maximum.Width, maximum.Right - screen.Left),
0
),
Math.Max (
Math.Min (
maximum.Height,
- maximum.Bottom - viewport.Top
+ maximum.Bottom - screen.Top
),
0
)
);
}
- if (maxViewport.Width == 0 || maxViewport.Height == 0)
+ if (maxScreen.Width == 0 || maxScreen.Height == 0)
{
return;
}
- int lineOffset = !isVertical && viewport.Y < 0 ? Math.Abs (viewport.Y) : 0;
+ int lineOffset = !isVertical && screen.Y < 0 ? Math.Abs (screen.Y) : 0;
for (int line = lineOffset; line < linesFormatted.Count; line++)
{
- if ((isVertical && line > viewport.Width) || (!isVertical && line > viewport.Height))
+ if ((isVertical && line > screen.Width) || (!isVertical && line > screen.Height))
{
continue;
}
- if ((isVertical && line >= maxViewport.Left + maxViewport.Width)
- || (!isVertical && line >= maxViewport.Top + maxViewport.Height + lineOffset))
+ if ((isVertical && line >= maxScreen.Left + maxScreen.Width)
+ || (!isVertical && line >= maxScreen.Top + maxScreen.Height + lineOffset))
{
break;
}
@@ -305,14 +305,14 @@ public class TextFormatter
if (isVertical)
{
int runesWidth = GetWidestLineLength (linesFormatted, line, TabWidth);
- x = viewport.Right - runesWidth;
- CursorPosition = viewport.Width - runesWidth + (_hotKeyPos > -1 ? _hotKeyPos : 0);
+ x = screen.Right - runesWidth;
+ CursorPosition = screen.Width - runesWidth + (_hotKeyPos > -1 ? _hotKeyPos : 0);
}
else
{
int runesWidth = StringExtensions.ToString (runes).GetColumns ();
- x = viewport.Right - runesWidth;
- CursorPosition = viewport.Width - runesWidth + (_hotKeyPos > -1 ? _hotKeyPos : 0);
+ x = screen.Right - runesWidth;
+ CursorPosition = screen.Width - runesWidth + (_hotKeyPos > -1 ? _hotKeyPos : 0);
}
}
else if (Alignment is TextAlignment.Left or TextAlignment.Justified)
@@ -322,11 +322,11 @@ public class TextFormatter
int runesWidth = line > 0
? GetWidestLineLength (linesFormatted, 0, line, TabWidth)
: 0;
- x = viewport.Left + runesWidth;
+ x = screen.Left + runesWidth;
}
else
{
- x = viewport.Left;
+ x = screen.Left;
}
CursorPosition = _hotKeyPos > -1 ? _hotKeyPos : 0;
@@ -336,16 +336,16 @@ public class TextFormatter
if (isVertical)
{
int runesWidth = GetWidestLineLength (linesFormatted, line, TabWidth);
- x = viewport.Left + line + (viewport.Width - runesWidth) / 2;
+ x = screen.Left + line + (screen.Width - runesWidth) / 2;
- CursorPosition = (viewport.Width - runesWidth) / 2 + (_hotKeyPos > -1 ? _hotKeyPos : 0);
+ CursorPosition = (screen.Width - runesWidth) / 2 + (_hotKeyPos > -1 ? _hotKeyPos : 0);
}
else
{
int runesWidth = StringExtensions.ToString (runes).GetColumns ();
- x = viewport.Left + (viewport.Width - runesWidth) / 2;
+ x = screen.Left + (screen.Width - runesWidth) / 2;
- CursorPosition = (viewport.Width - runesWidth) / 2 + (_hotKeyPos > -1 ? _hotKeyPos : 0);
+ CursorPosition = (screen.Width - runesWidth) / 2 + (_hotKeyPos > -1 ? _hotKeyPos : 0);
}
}
else
@@ -358,35 +358,35 @@ public class TextFormatter
{
if (isVertical)
{
- y = viewport.Bottom - runes.Length;
+ y = screen.Bottom - runes.Length;
}
else
{
- y = viewport.Bottom - linesFormatted.Count + line;
+ y = screen.Bottom - linesFormatted.Count + line;
}
}
else if (VerticalAlignment is VerticalTextAlignment.Top or VerticalTextAlignment.Justified)
{
if (isVertical)
{
- y = viewport.Top;
+ y = screen.Top;
}
else
{
- y = viewport.Top + line;
+ y = screen.Top + line;
}
}
else if (VerticalAlignment == VerticalTextAlignment.Middle)
{
if (isVertical)
{
- int s = (viewport.Height - runes.Length) / 2;
- y = viewport.Top + s;
+ int s = (screen.Height - runes.Length) / 2;
+ y = screen.Top + s;
}
else
{
- int s = (viewport.Height - linesFormatted.Count) / 2;
- y = viewport.Top + line + s;
+ int s = (screen.Height - linesFormatted.Count) / 2;
+ y = screen.Top + line + s;
}
}
else
@@ -394,9 +394,9 @@ public class TextFormatter
throw new ArgumentOutOfRangeException ($"{nameof (VerticalAlignment)}");
}
- int colOffset = viewport.X < 0 ? Math.Abs (viewport.X) : 0;
- int start = isVertical ? viewport.Top : viewport.Left;
- int size = isVertical ? viewport.Height : viewport.Width;
+ int colOffset = screen.X < 0 ? Math.Abs (screen.X) : 0;
+ int start = isVertical ? screen.Top : screen.Left;
+ int size = isVertical ? screen.Height : screen.Width;
int current = start + colOffset;
List lastZeroWidthPos = null;
Rune rune = default;
@@ -422,8 +422,8 @@ public class TextFormatter
break;
}
- if ((!isVertical && current - start > maxViewport.Left + maxViewport.Width - viewport.X + colOffset)
- || (isVertical && idx > maxViewport.Top + maxViewport.Height - viewport.Y))
+ if ((!isVertical && current - start > maxScreen.Left + maxScreen.Width - screen.X + colOffset)
+ || (isVertical && idx > maxScreen.Top + maxScreen.Height - screen.Y))
{
break;
}
diff --git a/Terminal.Gui/View/Adornment/Adornment.cs b/Terminal.Gui/View/Adornment/Adornment.cs
index 46401dbca..aff0bb308 100644
--- a/Terminal.Gui/View/Adornment/Adornment.cs
+++ b/Terminal.Gui/View/Adornment/Adornment.cs
@@ -267,7 +267,7 @@ public class Adornment : View
if (!Parent.CanFocus || !Parent.Arrangement.HasFlag (ViewArrangement.Movable))
{
- return true;
+ return false;
}
// BUGBUG: See https://github.com/gui-cs/Terminal.Gui/issues/3312
diff --git a/Terminal.Gui/View/Adornment/Border.cs b/Terminal.Gui/View/Adornment/Border.cs
index fc63e9031..be0051360 100644
--- a/Terminal.Gui/View/Adornment/Border.cs
+++ b/Terminal.Gui/View/Adornment/Border.cs
@@ -186,6 +186,11 @@ public class Border : Adornment
///
public override void OnDrawContent (Rectangle viewport)
{
+ if (Parent?.Title == "Title")
+ {
+
+ }
+
base.OnDrawContent (viewport);
if (Thickness == Thickness.Empty)
diff --git a/Terminal.Gui/View/Layout/ViewLayout.cs b/Terminal.Gui/View/Layout/ViewLayout.cs
index cf23a7a14..f5a2c21a8 100644
--- a/Terminal.Gui/View/Layout/ViewLayout.cs
+++ b/Terminal.Gui/View/Layout/ViewLayout.cs
@@ -1,4 +1,5 @@
using System.Diagnostics;
+using System.IO.Compression;
namespace Terminal.Gui;
@@ -73,12 +74,9 @@ public partial class View
_height = _frame.Height;
// TODO: Figure out if the below can be optimized.
- if (IsInitialized /*|| LayoutStyle == LayoutStyle.Absolute*/)
+ if (IsInitialized)
{
- LayoutAdornments ();
- SetTextFormatterSize ();
- SetNeedsLayout ();
- SetNeedsDisplay ();
+ OnResizeNeeded ();
}
}
}
@@ -102,12 +100,11 @@ public partial class View
}
Point viewportOffset = super.GetViewportOffset ();
- viewportOffset.Offset (super.Frame.X, super.Frame.Y);
+ viewportOffset.Offset (super.Frame.X - super.Viewport.X, super.Frame.Y - super.Viewport.Y);
ret.X += viewportOffset.X;
ret.Y += viewportOffset.Y;
super = super.SuperView;
}
-
return ret;
}
@@ -120,15 +117,18 @@ public partial class View
/// Screen-relative row.
public virtual Point ScreenToFrame (int x, int y)
{
- Point superViewBoundsOffset = SuperView?.GetViewportOffset () ?? Point.Empty;
+ Point superViewViewportOffset = SuperView?.GetViewportOffset () ?? Point.Empty;
+
if (SuperView is null)
{
- superViewBoundsOffset.Offset (x - Frame.X, y - Frame.Y);
- return superViewBoundsOffset;
- }
+ superViewViewportOffset.Offset (x - Frame.X, y - Frame.Y);
- var frame = SuperView.ScreenToFrame (x - superViewBoundsOffset.X, y - superViewBoundsOffset.Y);
+ return superViewViewportOffset;
+ }
+ superViewViewportOffset.Offset (-SuperView.Viewport.X, -SuperView.Viewport.Y);
+ Point frame = SuperView.ScreenToFrame (x - superViewViewportOffset.X, y - superViewViewportOffset.Y);
frame.Offset (-Frame.X, -Frame.Y);
+
return frame;
}
@@ -284,126 +284,6 @@ public partial class View
#endregion Frame
- #region Viewport
-
- private Point _viewportOffset;
-
- ///
- /// Gets or sets the rectangle describing the portion of the View's content that is visible to the user.
- /// The viewport Location is relative to the top-left corner of the inner rectangle of the s.
- /// If the viewport Size is the sames as the the Location will be 0, 0.
- /// Non-zero values for the location indicate the visible area is offset into the View's virtual .
- ///
- /// The rectangle describing the location and size of the viewport into the View's virtual content, described by .
- ///
- ///
- /// If is the value of Viewport is indeterminate until
- /// the view has been initialized ( is true) and has been
- /// called.
- ///
- ///
- /// Updates to the Viewport Size updates , and has the same impact as updating the
- /// .
- ///
- ///
- /// Altering the Viewport Size will eventually (when the view is next laid out) cause the
- /// and methods to be called.
- ///
- ///
- public virtual Rectangle Viewport
- {
- get
- {
-#if DEBUG
- if (LayoutStyle == LayoutStyle.Computed && !IsInitialized)
- {
- Debug.WriteLine (
- $"WARNING: Viewport is being accessed before the View has been initialized. This is likely a bug in {this}"
- );
- }
-#endif // DEBUG
-
- if (Margin is null || Border is null || Padding is null)
- {
- // CreateAdornments has not been called yet.
- return new (_viewportOffset, Frame.Size);
- }
-
- Thickness totalThickness = GetAdornmentsThickness ();
-
- return new (_viewportOffset,
- new (Math.Max (0, Frame.Size.Width - totalThickness.Horizontal),
- Math.Max (0, Frame.Size.Height - totalThickness.Vertical)));
- }
- set
- {
- _viewportOffset = value.Location;
-
- Thickness totalThickness = GetAdornmentsThickness ();
-
- Frame = Frame with
- {
- Size = new (
- value.Size.Width + totalThickness.Horizontal,
- value.Size.Height + totalThickness.Vertical)
- };
- }
- }
-
- /// Converts a -relative rectangle to a screen-relative rectangle.
- public Rectangle ViewportToScreen (in Rectangle viewport)
- {
- // Translate bounds to Frame (our SuperView's Viewport-relative coordinates)
- Rectangle screen = FrameToScreen ();
- Point viewportOffset = GetViewportOffset ();
- screen.Offset (viewportOffset.X + viewport.X, viewportOffset.Y + viewport.Y);
-
- if (SuperView is { })
- {
- screen.Offset(-SuperView.Viewport.X, -SuperView.Viewport.Y);
- }
-
-
- return new (screen.Location, viewport.Size);
- }
-
- /// Converts a screen-relative coordinate to a Viewport-relative coordinate.
- /// The coordinate relative to this view's .
- /// Screen-relative column.
- /// Screen-relative row.
- public Point ScreenToViewport (int x, int y)
- {
- Point viewportOffset = GetViewportOffset ();
- Point screen = ScreenToFrame (x, y);
- screen.Offset (-viewportOffset.X + Viewport.X, -viewportOffset.Y + Viewport.Y);
-
- return screen;
- }
-
- ///
- /// Helper to get the X and Y offset of the Viewport from the Frame. This is the sum of the Left and Top properties
- /// of , and .
- ///
- public Point GetViewportOffset ()
- {
- return Padding is null ? Point.Empty : Padding.Thickness.GetInside (Padding.Frame).Location;
- }
-
- private Size _contentSize;
- ///
- /// Gets or sets the size of the View's content. If the value is Size.Empty the size of the content is
- /// the same as the size of the , and Viewport.Location will always be 0, 0.
- /// If a positive size is provided, describes the portion of the content currently visible
- /// to the view. This enables virtual scrolling.
- ///
- public Size ContentSize
- {
- get => _contentSize == Size.Empty ? Viewport.Size : _contentSize;
- set => _contentSize = value;
- }
-
- #endregion Viewport
-
#region AutoSize
private bool _autoSize;
@@ -657,25 +537,20 @@ public partial class View
#endregion Layout Engine
- internal bool LayoutNeeded { get; private set; } = true;
-
///
- /// Indicates whether the specified SuperView-relative coordinates are within the View's .
+ /// Indicates whether the specified SuperView-relative coordinates are within the View's .
///
/// SuperView-relative X coordinate.
/// SuperView-relative Y coordinate.
/// if the specified SuperView-relative coordinates are within the View.
- public virtual bool Contains (int x, int y)
- {
- return Frame.Contains (x, y);
- }
+ public virtual bool Contains (int x, int y) { return Frame.Contains (x, y); }
#nullable enable
/// Finds the first Subview of that is visible at the provided location.
///
- ///
- /// Used to determine what view the mouse is over.
- ///
+ ///
+ /// Used to determine what view the mouse is over.
+ ///
///
/// The view to scope the search by.
/// .SuperView-relative X coordinate.
@@ -725,10 +600,10 @@ public partial class View
{
View nextStart = start.InternalSubviews [i];
- if (nextStart.Visible && nextStart.Contains (startOffsetX, startOffsetY))
+ if (nextStart.Visible && nextStart.Contains (startOffsetX + start.Viewport.X, startOffsetY + start.Viewport.Y))
{
// TODO: Remove recursion
- return FindDeepestView (nextStart, startOffsetX, startOffsetY) ?? nextStart;
+ return FindDeepestView (nextStart, startOffsetX + start.Viewport.X, startOffsetY + start.Viewport.Y) ?? nextStart;
}
}
}
@@ -767,6 +642,7 @@ public partial class View
{
int maxDimension;
View superView;
+ statusBar = null;
if (viewToMove?.SuperView is null || viewToMove == Application.Top || viewToMove?.SuperView == Application.Top)
{
@@ -801,7 +677,8 @@ public partial class View
}
//System.Diagnostics.Debug.WriteLine ($"nx:{nx}, rWidth:{rWidth}");
- bool menuVisible, statusVisible;
+ bool menuVisible = false;
+ bool statusVisible = false;
if (viewToMove?.SuperView is null || viewToMove == Application.Top || viewToMove?.SuperView == Application.Top)
{
@@ -811,12 +688,15 @@ public partial class View
{
View t = viewToMove.SuperView;
- while (t is not Toplevel)
+ while (t is { } and not Toplevel)
{
t = t.SuperView;
}
- menuVisible = ((Toplevel)t).MenuBar?.Visible == true;
+ if (t is Toplevel toplevel)
+ {
+ menuVisible = toplevel.MenuBar?.Visible == true;
+ }
}
if (viewToMove?.SuperView is null || viewToMove == Application.Top || viewToMove?.SuperView == Application.Top)
@@ -839,13 +719,16 @@ public partial class View
{
View t = viewToMove.SuperView;
- while (t is not Toplevel)
+ while (t is { } and not Toplevel)
{
t = t.SuperView;
}
- statusVisible = ((Toplevel)t).StatusBar?.Visible == true;
- statusBar = ((Toplevel)t).StatusBar;
+ 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)
@@ -854,7 +737,7 @@ public partial class View
}
else
{
- maxDimension = statusVisible ? viewToMove.SuperView.Frame.Height - 1 : viewToMove.SuperView.Frame.Height;
+ maxDimension = statusVisible ? viewToMove.SuperView.Viewport.Height - 1 : viewToMove.SuperView.Viewport.Height;
}
if (superView.Margin is { } && superView == viewToMove.SuperView)
@@ -935,7 +818,7 @@ public partial class View
foreach (View v in ordered)
{
- LayoutSubview (v, new (GetViewportOffset (), ContentSize));
+ LayoutSubview (v, ContentSize);
}
// If the 'to' is rooted to 'from' and the layoutstyle is Computed it's a special-case.
@@ -944,7 +827,7 @@ public partial class View
{
foreach ((View from, View to) in edges)
{
- LayoutSubview (to, from.Frame);
+ LayoutSubview (to, from.ContentSize);
}
}
@@ -952,6 +835,12 @@ public partial class View
OnLayoutComplete (new () { OldViewport = oldViewport });
}
+ private void LayoutSubview (View v, Size contentSize)
+ {
+ v.SetRelativeLayout (contentSize);
+ v.LayoutSubviews ();
+ v.LayoutNeeded = false;
+ }
/// Indicates that the view does not need to be laid out.
protected void ClearLayoutNeeded () { LayoutNeeded = false; }
@@ -985,8 +874,8 @@ public partial class View
// First try SuperView.Viewport, then Application.Top, then Driver.Viewport.
// Finally, if none of those are valid, use int.MaxValue (for Unit tests).
Size contentSize = SuperView is { IsInitialized: true } ? SuperView.ContentSize :
- Application.Top is { } && Application.Top.IsInitialized ? Application.Top.ContentSize :
- Application.Driver?.Viewport.Size ?? new (int.MaxValue, int.MaxValue);
+ Application.Top is { } && Application.Top.IsInitialized ? Application.Top.ContentSize :
+ Application.Driver?.Viewport.Size ?? new (int.MaxValue, int.MaxValue);
SetRelativeLayout (contentSize);
// TODO: Determine what, if any of the below is actually needed here.
@@ -1003,6 +892,7 @@ public partial class View
SetNeedsLayout ();
}
}
+ internal bool LayoutNeeded { get; private set; } = true;
///
/// Sets the internal flag for this View and all of it's subviews and it's SuperView.
@@ -1027,13 +917,20 @@ public partial class View
}
///
- /// Applies the view's position (, ) and dimension (, and
- /// ) to , given the SuperView's ContentSize (nominally the
- /// same as this.SuperView.ContentSize).
+ /// Adjusts given the SuperView's ContentSize (nominally the same as
+ /// this.SuperView.ContentSize)
+ /// and the position (, ) and dimension (, and
+ /// ).
///
+ ///
+ ///
+ /// If , , , or are
+ /// absolute, they will be updated to reflect the new size and position of the view. Otherwise, they
+ /// are left unchanged.
+ ///
+ ///
///
- /// The size of the SuperView's content (nominally the same as
- /// this.SuperView.ContentSize).
+ /// The size of the SuperView's content (nominally the same as this.SuperView.ContentSize).
///
internal void SetRelativeLayout (Size superviewContentSize)
{
@@ -1423,17 +1320,6 @@ public partial class View
return result;
} // TopologicalSort
- private void LayoutSubview (View v, Rectangle viewport)
- {
- //if (v.LayoutStyle == LayoutStyle.Computed) {
- v.SetRelativeLayout (viewport.Size);
-
- //}
-
- v.LayoutSubviews ();
- v.LayoutNeeded = false;
- }
-
#region Diagnostics
// Diagnostics to highlight when Width or Height is read before the view has been initialized
diff --git a/Terminal.Gui/View/ViewDrawing.cs b/Terminal.Gui/View/ViewDrawing.cs
index 6e1f61466..f6718ccbf 100644
--- a/Terminal.Gui/View/ViewDrawing.cs
+++ b/Terminal.Gui/View/ViewDrawing.cs
@@ -85,7 +85,30 @@ public partial class View
/// Clears with the normal background.
///
- public void Clear () { Clear (Viewport); }
+ public void Clear () { Clear (new (Point.Empty, Viewport.Size)); }
+
+ /// Clears the portion of the content that is visible with the normal background. If the content does not fill the Viewport,
+ /// the area not filled will be cleared with DarkGray.
+ ///
+ public void ClearVisibleContent ()
+ {
+ if (Driver is null)
+ {
+ return;
+ }
+
+ Rectangle toClear = new (-Viewport.Location.X, -Viewport.Location.Y, ContentSize.Width, ContentSize.Height);
+
+ // If toClear does not fill the Viewport, we need to clear the area outside toClear with DarkGray.
+ // TODO: Need a configurable color for this
+ Attribute prev = Driver.SetAttribute (new Attribute (ColorName.DarkGray, ColorName.DarkGray));
+
+ Rectangle viewport = new (Point.Empty, Viewport.Size);
+ Driver.FillRect (ViewportToScreen (viewport));
+ Driver.SetAttribute (prev);
+
+ Clear (toClear);
+ }
/// Clears the specified -relative rectangle with the normal background.
///
@@ -100,7 +123,7 @@ public partial class View
Attribute prev = Driver.SetAttribute (GetNormalColor ());
// Clamp the region to the bounds of the view
- viewport = Rectangle.Intersect (viewport, Viewport);
+ viewport = Rectangle.Intersect (viewport, new (Point.Empty, Viewport.Size));
Driver.FillRect (ViewportToScreen (viewport));
Driver.SetAttribute (prev);
}
@@ -124,7 +147,7 @@ public partial class View
}
Rectangle previous = Driver.Clip;
- Driver.Clip = Rectangle.Intersect (previous, ViewportToScreen (Viewport));
+ Driver.Clip = Rectangle.Intersect (previous, ViewportToScreen (Viewport with { Location = Point.Empty }));
return previous;
}
@@ -321,12 +344,12 @@ public partial class View
/// Moves the drawing cursor to the specified view-relative column and row in the view.
///
- ///
- /// If the provided coordinates are outside the visible content area, this method does nothing.
- ///
- ///
- /// The top-left corner of the visible content area is ViewPort.Location.
- ///
+ ///
+ /// If the provided coordinates are outside the visible content area, this method does nothing.
+ ///
+ ///
+ /// The top-left corner of the visible content area is ViewPort.Location.
+ ///
///
/// Column (viewport-relative).
/// Row (viewport-relative).
@@ -406,7 +429,7 @@ public partial class View
{
if (SuperView is { })
{
- Clear (viewport);
+ ClearVisibleContent ();
}
if (!string.IsNullOrEmpty (TextFormatter.Text))
@@ -419,8 +442,13 @@ public partial class View
// This should NOT clear
// TODO: If the output is not in the Viewport, do nothing
+ if (Viewport.Y != 0)
+ { }
+
+ var drawRect = new Rectangle (ContentToScreen (Point.Empty), ContentSize);
+
TextFormatter?.Draw (
- ViewportToScreen (new Rectangle(Point.Empty, ContentSize)),
+ drawRect,
HasFocus ? GetFocusColor () : GetNormalColor (),
HasFocus ? ColorScheme.HotFocus : GetHotNormalColor (),
Rectangle.Empty
@@ -609,7 +637,7 @@ public partial class View
foreach (View subview in Subviews)
{
- subview.ClearNeedsDisplay();
+ subview.ClearNeedsDisplay ();
}
}
}
diff --git a/Terminal.Gui/View/ViewMouse.cs b/Terminal.Gui/View/ViewMouse.cs
index 6ef2c783c..5b16579b7 100644
--- a/Terminal.Gui/View/ViewMouse.cs
+++ b/Terminal.Gui/View/ViewMouse.cs
@@ -104,7 +104,7 @@ public partial class View
{
if (!Enabled)
{
- return true;
+ return false;
}
if (!CanBeVisible (this))
diff --git a/Terminal.Gui/View/ViewScrolling.cs b/Terminal.Gui/View/ViewScrolling.cs
new file mode 100644
index 000000000..1821ae9f0
--- /dev/null
+++ b/Terminal.Gui/View/ViewScrolling.cs
@@ -0,0 +1,261 @@
+using System.Diagnostics;
+
+namespace Terminal.Gui;
+
+///
+/// Controls the scrolling behavior of a view.
+///
+[Flags]
+public enum ScrollSettings
+{
+ ///
+ /// Default settings.
+ ///
+ Default = 0,
+
+ ///
+ /// If set, does not restrict vertical scrolling to the content size.
+ ///
+ NoRestrictVertical = 1,
+
+ ///
+ /// If set, does not restrict horizontal scrolling to the content size.
+ ///
+ NoRestrictHorizontal = 2,
+
+ ///
+ /// If set, does not restrict either vertical or horizontal scrolling to the content size.
+ ///
+ NoRestrict = NoRestrictVertical | NoRestrictHorizontal
+}
+
+public partial class View
+{
+ #region Content Area
+
+ private Size _contentSize;
+
+ ///
+ /// Gets or sets the size of the View's content. If the value is Size.Empty the size of the content is
+ /// the same as the size of the , and Viewport.Location will always be 0, 0.
+ /// If a positive size is provided, describes the portion of the content currently visible
+ /// to the view. This enables virtual scrolling.
+ ///
+ public Size ContentSize
+ {
+ get => _contentSize == Size.Empty ? Viewport.Size : _contentSize;
+ set => _contentSize = value;
+ }
+
+ ///
+ /// Converts a content-relative location to a screen-relative location.
+ ///
+ ///
+ /// The screen-relative location.
+ public Point ContentToScreen (in Point location)
+ {
+ // Translate to Viewport
+ Point viewportOffset = GetViewportOffset ();
+ Point contentRelativeToViewport = location;
+ contentRelativeToViewport.Offset (-Viewport.X, -Viewport.Y);
+
+ // Translate to Frame (our SuperView's Viewport-relative coordinates)
+ Rectangle screen = ViewportToScreen (new (contentRelativeToViewport, Size.Empty));
+
+ return screen.Location;
+ }
+
+ /// Converts a screen-relative coordinate to a Content-relative coordinate.
+ ///
+ /// Content-relative means relative to the top-left corner of the view's Content.
+ ///
+ /// Column relative to the left side of the Content.
+ /// Row relative to the top of the Content
+ /// The coordinate relative to this view's Content.
+ public Point ScreenToContent (int x, int y)
+ {
+ Point viewportOffset = GetViewportOffset ();
+ Point screen = ScreenToFrame (x, y);
+ screen.Offset (Viewport.X - viewportOffset.X, Viewport.Y - viewportOffset.Y);
+
+ return screen;
+ }
+
+ #endregion Content Area
+
+ #region Viewport
+
+ ///
+ /// Gets or sets the scrolling behavior of the view.
+ ///
+ public ScrollSettings ScrollSettings { get; set; }
+
+ private Point _viewportOffset;
+
+ ///
+ /// Gets or sets the rectangle describing the portion of the View's content that is visible to the user.
+ /// The viewport Location is relative to the top-left corner of the inner rectangle of the s.
+ /// If the viewport Size is the sames as the the Location will be 0, 0.
+ /// Non-zero values for the location indicate the visible area is offset into the View's virtual
+ /// .
+ ///
+ ///
+ /// The rectangle describing the location and size of the viewport into the View's virtual content, described by
+ /// .
+ ///
+ ///
+ ///
+ /// If is the value of Viewport is indeterminate until
+ /// the view has been initialized ( is true) and has been
+ /// called.
+ ///
+ ///
+ /// Updates to the Viewport Size updates , and has the same impact as updating the
+ /// .
+ ///
+ ///
+ /// Altering the Viewport Size will eventually (when the view is next laid out) cause the
+ /// and methods to be called.
+ ///
+ ///
+ public virtual Rectangle Viewport
+ {
+ get
+ {
+#if DEBUG
+ if (LayoutStyle == LayoutStyle.Computed && !IsInitialized)
+ {
+ Debug.WriteLine (
+ $"WARNING: Viewport is being accessed before the View has been initialized. This is likely a bug in {this}"
+ );
+ }
+#endif // DEBUG
+
+ if (Margin is null || Border is null || Padding is null)
+ {
+ // CreateAdornments has not been called yet.
+ return new (_viewportOffset, Frame.Size);
+ }
+
+ Thickness totalThickness = GetAdornmentsThickness ();
+
+ return new (
+ _viewportOffset,
+ new (
+ Math.Max (0, Frame.Size.Width - totalThickness.Horizontal),
+ Math.Max (0, Frame.Size.Height - totalThickness.Vertical)));
+ }
+ set
+ {
+ _viewportOffset = value.Location;
+
+ Thickness totalThickness = GetAdornmentsThickness ();
+ Size newSize = new (value.Size.Width + totalThickness.Horizontal,
+ value.Size.Height + totalThickness.Vertical);
+ if (newSize == Frame.Size)
+ {
+ SetNeedsLayout ();
+ return;
+ }
+
+ Frame = Frame with
+ {
+ Size = newSize
+ };
+ }
+ }
+
+ ///
+ /// Converts a -relative location to a screen-relative location.
+ ///
+ ///
+ /// Viewport-relative means relative to the top-left corner of the inner rectangle of the .
+ ///
+ public Rectangle ViewportToScreen (in Rectangle location)
+ {
+ // Translate bounds to Frame (our SuperView's Viewport-relative coordinates)
+ Rectangle screen = FrameToScreen ();
+ Point viewportOffset = GetViewportOffset ();
+ screen.Offset (viewportOffset.X + location.X, viewportOffset.Y + location.Y);
+
+ return new (screen.Location, location.Size);
+ }
+
+ /// Converts a screen-relative coordinate to a Viewport-relative coordinate.
+ /// The coordinate relative to this view's .
+ ///
+ /// Viewport-relative means relative to the top-left corner of the inner rectangle of the .
+ ///
+ /// Column relative to the left side of the Viewport.
+ /// Row relative to the top of the Viewport
+ public Point ScreenToViewport (int x, int y)
+ {
+ Point viewportOffset = GetViewportOffset ();
+ Point screen = ScreenToFrame (x, y);
+ screen.Offset (-viewportOffset.X, -viewportOffset.Y);
+
+ return screen;
+ }
+
+ ///
+ /// Helper to get the X and Y offset of the Viewport from the Frame. This is the sum of the Left and Top properties
+ /// of , and .
+ ///
+ public Point GetViewportOffset () { return Padding is null ? Point.Empty : Padding.Thickness.GetInside (Padding.Frame).Location; }
+
+ ///
+ /// Scrolls the view vertically by the specified number of rows.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// if the was changed.
+ public bool? ScrollVertical (int rows)
+ {
+ if (ContentSize == Size.Empty || ContentSize == Viewport.Size)
+ {
+ return false;
+ }
+
+ if (!ScrollSettings.HasFlag (ScrollSettings.NoRestrictVertical)
+ && (Viewport.Y + rows > ContentSize.Height - Viewport.Height || Viewport.Y + rows < 0))
+ {
+ return false;
+ }
+
+ Viewport = Viewport with { Y = Viewport.Y + rows };
+
+ return true;
+ }
+
+ ///
+ /// Scrolls the view horizontally by the specified number of columns.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// if the was changed.
+ public bool? ScrollHorizontal (int cols)
+ {
+ if (ContentSize == Size.Empty || ContentSize == Viewport.Size)
+ {
+ return false;
+ }
+
+ if (!ScrollSettings.HasFlag (ScrollSettings.NoRestrictHorizontal)
+ && (Viewport.X + cols > ContentSize.Width - Viewport.Width || Viewport.X + cols < 0))
+ {
+ return false;
+ }
+
+ Viewport = Viewport with { X = Viewport.X + cols };
+
+ return true;
+ }
+
+ #endregion Viewport
+}
diff --git a/Terminal.Gui/View/ViewSubViews.cs b/Terminal.Gui/View/ViewSubViews.cs
index b9cacc711..a6c7b0c5e 100644
--- a/Terminal.Gui/View/ViewSubViews.cs
+++ b/Terminal.Gui/View/ViewSubViews.cs
@@ -468,7 +468,7 @@ public partial class View
{
return true;
}
-
+
return false;
}
diff --git a/Terminal.Gui/View/ViewText.cs b/Terminal.Gui/View/ViewText.cs
index 7292afef5..1eb626dce 100644
--- a/Terminal.Gui/View/ViewText.cs
+++ b/Terminal.Gui/View/ViewText.cs
@@ -324,7 +324,7 @@ public partial class View
return false;
}
- sizeRequired = Viewport.Size;
+ sizeRequired = ContentSize;
if (AutoSize || string.IsNullOrEmpty (TextFormatter.Text))
{
@@ -338,9 +338,9 @@ public partial class View
// TODO: v2 - This uses frame.Width; it should only use Viewport
if (_frame.Width < colWidth
- && (Width is null || (Viewport.Width >= 0 && Width is Dim.DimAbsolute && Width.Anchor (0) >= 0 && Width.Anchor (0) < colWidth)))
+ && (Width is null || (ContentSize.Width >= 0 && Width is Dim.DimAbsolute && Width.Anchor (0) >= 0 && Width.Anchor (0) < colWidth)))
{
- sizeRequired = new (colWidth, Viewport.Height);
+ sizeRequired = new (colWidth, ContentSize.Height);
return true;
}
@@ -349,7 +349,7 @@ public partial class View
default:
if (_frame.Height < 1 && (Height is null || (Height is Dim.DimAbsolute && Height.Anchor (0) == 0)))
{
- sizeRequired = new (Viewport.Width, 1);
+ sizeRequired = new (ContentSize.Width, 1);
return true;
}
diff --git a/Terminal.Gui/Views/ColorPicker.cs b/Terminal.Gui/Views/ColorPicker.cs
index 09ffcc3d3..f9fe50c23 100644
--- a/Terminal.Gui/Views/ColorPicker.cs
+++ b/Terminal.Gui/Views/ColorPicker.cs
@@ -100,7 +100,7 @@ public class ColorPicker : View
if (me.X > Viewport.Width || me.Y > Viewport.Height)
{
- return true;
+ return false;
}
Cursor = new Point (me.X / _boxWidth, me.Y / _boxHeight);
diff --git a/Terminal.Gui/Views/ScrollView.cs b/Terminal.Gui/Views/ScrollView.cs
index 65c47eb47..22161404a 100644
--- a/Terminal.Gui/Views/ScrollView.cs
+++ b/Terminal.Gui/Views/ScrollView.cs
@@ -194,7 +194,6 @@ public class ScrollView : View
{
// We're not initialized so we can't do anything fancy. Just cache value.
_contentOffset = new Point (-Math.Abs (value.X), -Math.Abs (value.Y));
- ;
return;
}
@@ -205,7 +204,7 @@ public class ScrollView : View
/// Represents the contents of the data shown inside the scrollview
/// The size of the content.
- public Size ContentSize
+ public new Size ContentSize
{
get => _contentSize;
set
diff --git a/Terminal.Gui/Views/Toplevel.cs b/Terminal.Gui/Views/Toplevel.cs
index dcca0a57e..b747dc048 100644
--- a/Terminal.Gui/Views/Toplevel.cs
+++ b/Terminal.Gui/Views/Toplevel.cs
@@ -260,7 +260,7 @@ public partial class Toplevel : View
{
//Driver.SetAttribute (GetNormalColor ());
// TODO: It's bad practice for views to always clear. Defeats the purpose of clipping etc...
- Clear ();
+ ClearVisibleContent ();
LayoutSubviews ();
PositionToplevels ();
diff --git a/UICatalog/Scenarios/Adornments.cs b/UICatalog/Scenarios/Adornments.cs
index 02778702e..b60d09cee 100644
--- a/UICatalog/Scenarios/Adornments.cs
+++ b/UICatalog/Scenarios/Adornments.cs
@@ -82,8 +82,6 @@ public class Adornments : Scenario
//BorderStyle = LineStyle.None,
};
- view.X = 36;
- view.Y = 0;
view.Width = Dim.Percent (60);
view.Height = Dim.Percent (80);
@@ -452,15 +450,13 @@ public class Adornments : Scenario
Add (_diagCheckBox);
_viewToEdit.X = Pos.Right (rbBorderStyle);
_viewToEdit.Y = 0;
- _viewToEdit.Width = Dim.Fill ();
- _viewToEdit.Height = Dim.Fill ();
Add (_viewToEdit);
_viewToEdit.LayoutComplete += (s, e) =>
{
if (ckbTitle.Checked == true)
{
- //_viewToEdit.Title = _origTitle;
+ _viewToEdit.Title = _origTitle;
}
else
{
diff --git a/UICatalog/Scenarios/VirtualContentScrolling.cs b/UICatalog/Scenarios/VirtualContentScrolling.cs
index 98bd53eb1..096dcc6bb 100644
--- a/UICatalog/Scenarios/VirtualContentScrolling.cs
+++ b/UICatalog/Scenarios/VirtualContentScrolling.cs
@@ -12,18 +12,27 @@ public class VirtualScrolling : Scenario
{
private ViewDiagnosticFlags _diagnosticFlags;
- public class VirtualDemoView : Window
+ public class VirtualDemoView : View
{
public VirtualDemoView ()
{
Text = "Virtual Demo View Text. This is long text.\nThe second line.\n3\n4\n5th line.";
- Arrangement = ViewArrangement.Fixed;
- ContentSize = new Size (100, 50);
+ CanFocus = true;
+ Arrangement = ViewArrangement.Movable;
+ ColorScheme = Colors.ColorSchemes ["Toplevel"];
+ BorderStyle = LineStyle.Rounded;
+
+ // TODO: Add a way to set the scroll settings in the Scenario
+ ContentSize = new Size (100, 60);
+ //ScrollSettings = ScrollSettings.NoRestrict;
// Things this view knows how to do
AddCommand (Command.ScrollDown, () => ScrollVertical (1));
AddCommand (Command.ScrollUp, () => ScrollVertical (-1));
+ AddCommand (Command.ScrollRight, () => ScrollHorizontal (1));
+ AddCommand (Command.ScrollLeft, () => ScrollHorizontal (-1));
+
//AddCommand (Command.PageUp, () => PageUp ());
//AddCommand (Command.PageDown, () => PageDown ());
//AddCommand (Command.TopHome, () => Home ());
@@ -32,51 +41,66 @@ public class VirtualScrolling : Scenario
// Default keybindings for all ListViews
KeyBindings.Add (Key.CursorUp, Command.ScrollUp);
KeyBindings.Add (Key.CursorDown, Command.ScrollDown);
+ KeyBindings.Add (Key.CursorLeft, Command.ScrollLeft);
+ KeyBindings.Add (Key.CursorRight, Command.ScrollRight);
//KeyBindings.Add (Key.PageUp, Command.PageUp);
//KeyBindings.Add (Key.PageDown, Command.PageDown);
//KeyBindings.Add (Key.Home, Command.TopHome);
//KeyBindings.Add (Key.End, Command.BottomEnd);
+ Border.Add (new Label () { X = 23 });
LayoutComplete += VirtualDemoView_LayoutComplete;
+
+ MouseEvent += VirtualDemoView_MouseEvent;
+ }
+
+ private void VirtualDemoView_MouseEvent (object sender, MouseEventEventArgs e)
+ {
+ if (e.MouseEvent.Flags == MouseFlags.WheeledDown)
+ {
+ ScrollVertical (1);
+ return;
+ }
+ if (e.MouseEvent.Flags == MouseFlags.WheeledUp)
+ {
+ ScrollVertical (-1);
+
+ return;
+ }
+
+ if (e.MouseEvent.Flags == MouseFlags.WheeledRight)
+ {
+ ScrollHorizontal (1);
+ return;
+ }
+ if (e.MouseEvent.Flags == MouseFlags.WheeledLeft)
+ {
+ ScrollHorizontal (-1);
+
+ return;
+ }
+
}
private void VirtualDemoView_LayoutComplete (object sender, LayoutEventArgs e)
{
- Title = Viewport.ToString ();
+ var status = Border.Subviews.OfType