diff --git a/README.md b/README.md
index 8ace962338..6ffb641f54 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-![Terminal.Gui](https://socialify.git.ci/gui-cs/Terminal.GuiV2Docs/image?description=1&font=Rokkitt&forks=1&language=1&logo=https%3A%2F%2Fraw.githubusercontent.com%2Fgui-cs%2FTerminal.Gui%2Fdevelop%2Fdocfx%2Fimages%2Flogo.png&name=1&owner=1&pattern=Circuit%20Board&stargazers=1&theme=Auto)
-![.NET Core](https://github.com/gui-cs/Terminal.GuiV2Docs/workflows/.NET%20Core/badge.svg?branch=develop)
-![Code scanning - action](https://github.com/gui-cs/Terminal.GuiV2Docs/workflows/Code%20scanning%20-%20action/badge.svg)
+![Terminal.Gui](https://socialify.git.ci/gui-cs/Terminal.Gui/image?description=1&font=Rokkitt&forks=1&language=1&logo=https%3A%2F%2Fraw.githubusercontent.com%2Fgui-cs%2FTerminal.Gui%2Fdevelop%2Fdocfx%2Fimages%2Flogo.png&name=1&owner=1&pattern=Circuit%20Board&stargazers=1&theme=Auto)
+![.NET Core](https://github.com/gui-cs/Terminal.Gui/workflows/.NET%20Core/badge.svg?branch=develop)
+![Code scanning - action](https://github.com/gui-cs/Terminal.Gui/workflows/Code%20scanning%20-%20action/badge.svg)
[![Version](https://img.shields.io/nuget/v/Terminal.Gui.svg)](https://www.nuget.org/packages/Terminal.Gui)
![Code Coverage](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/migueldeicaza/90ef67a684cb71db1817921a970f8d27/raw/code-coverage.json)
[![Downloads](https://img.shields.io/nuget/dt/Terminal.Gui)](https://www.nuget.org/packages/Terminal.Gui)
@@ -31,8 +31,10 @@ dotnet run
## Documentation
-* [Documentation Home](https://gui-cs.github.io/Terminal.GuiV2Docs)
+* [Getting Started](https://gui-cs.github.io/Terminal.GuiV2Docs/docs/getting-started.html)
+* [What's new in v2](https://gui-cs.github.io/Terminal.GuiV2Docs/docs/newinv2.html)
* [API Documentation](https://gui-cs.github.io/Terminal.GuiV2Docs/api/Terminal.Gui.html)
+* [Documentation Home](https://gui-cs.github.io/Terminal.GuiV2Docs)
## Showcase & Examples
diff --git a/ReactiveExample/ReactiveExample.csproj b/ReactiveExample/ReactiveExample.csproj
index e3e43708ea..68974a8b41 100644
--- a/ReactiveExample/ReactiveExample.csproj
+++ b/ReactiveExample/ReactiveExample.csproj
@@ -12,7 +12,7 @@
-
+
diff --git a/Terminal.Gui/Application.cs b/Terminal.Gui/Application.cs
index 1901bba2ac..5e17af95f5 100644
--- a/Terminal.Gui/Application.cs
+++ b/Terminal.Gui/Application.cs
@@ -88,7 +88,7 @@ internal static void ResetState ()
#if DEBUG_IDISPOSABLE
// Don't dispose the toplevels. It's up to caller dispose them
- Debug.Assert (t.WasDisposed);
+ //Debug.Assert (t.WasDisposed);
#endif
}
@@ -99,6 +99,7 @@ internal static void ResetState ()
// Don't dispose the Top. It's up to caller dispose it
if (Top is { })
{
+
Debug.Assert (Top.WasDisposed);
// If End wasn't called _cachedRunStateToplevel may be null
@@ -132,7 +133,7 @@ internal static void ResetState ()
// Don't reset ForceDriver; it needs to be set before Init is called.
//ForceDriver = string.Empty;
- Force16Colors = false;
+ //Force16Colors = false;
_forceFakeConsole = false;
// Run State stuff
@@ -525,7 +526,10 @@ public static RunState Begin (Toplevel toplevel)
MoveCurrent (Current);
}
- toplevel.SetRelativeLayout (Driver.Bounds);
+ //if (Toplevel.LayoutStyle == LayoutStyle.Computed) {
+ toplevel.SetRelativeLayout (Driver.Screen.Size);
+
+ //}
// BUGBUG: This call is likely not needed.
toplevel.LayoutSubviews ();
@@ -638,7 +642,7 @@ public static T Run (Func errorHandler = null, ConsoleDriver
public static void Run (Toplevel view, Func errorHandler = null, ConsoleDriver driver = null)
{
ArgumentNullException.ThrowIfNull (view);
-
+
if (_initialized)
{
if (Driver is null)
@@ -878,7 +882,7 @@ public static void RunIteration (ref RunState state, ref bool firstIteration)
}
else
{
- Driver.UpdateCursor ();
+ //Driver.UpdateCursor ();
}
if (state.Toplevel != Top && !state.Toplevel.Modal && (Top.NeedsDisplay || Top.SubViewNeedsDisplay || Top.LayoutNeeded))
@@ -1307,7 +1311,7 @@ public static bool OnSizeChanging (SizeChangedEventArgs args)
foreach (Toplevel t in _topLevels)
{
- t.SetRelativeLayout (Rectangle.Empty with { Size = args.Size });
+ t.SetRelativeLayout (args.Size);
t.LayoutSubviews ();
t.PositionToplevels ();
t.OnSizeChanging (new (args.Size));
@@ -1437,15 +1441,15 @@ private static void OnUnGrabbedMouse (View view)
///
///
/// Use this event to receive mouse events in screen coordinates. Use to
- /// receive mouse events relative to a 's bounds.
+ /// receive mouse events relative to a .
///
/// The will contain the that contains the mouse coordinates.
///
- public static event EventHandler MouseEvent;
+ public static event EventHandler? MouseEvent;
/// Called when a mouse event occurs. Raises the event.
/// This method can be used to simulate a mouse event, e.g. in unit tests.
- /// The mouse event with coordinates relative to the screen.
+ /// The mouse event with coordinates relative to the screen.
internal static void OnMouseEvent (MouseEvent mouseEvent)
{
if (IsMouseDisabled)
@@ -1453,7 +1457,6 @@ internal static void OnMouseEvent (MouseEvent mouseEvent)
return;
}
- // TODO: In PR #3273, FindDeepestView will return adornments. Update logic below to fix adornment mouse handling
var view = View.FindDeepestView (Current, mouseEvent.X, mouseEvent.Y);
if (view is { })
@@ -1472,18 +1475,18 @@ internal static void OnMouseEvent (MouseEvent mouseEvent)
{
// If the mouse is grabbed, send the event to the view that grabbed it.
// The coordinates are relative to the Bounds of the view that grabbed the mouse.
- Point boundsLoc = MouseGrabView.ScreenToBounds (mouseEvent.X, mouseEvent.Y);
+ Point frameLoc = MouseGrabView.ScreenToViewport (mouseEvent.X, mouseEvent.Y);
var viewRelativeMouseEvent = new MouseEvent
{
- X = boundsLoc.X,
- Y = boundsLoc.Y,
+ X = frameLoc.X,
+ Y = frameLoc.Y,
Flags = mouseEvent.Flags,
ScreenPosition = new (mouseEvent.X, mouseEvent.Y),
View = MouseGrabView
};
- if (MouseGrabView.Bounds.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 bounds of the view that grabbed the mouse
_mouseEnteredView?.NewMouseLeaveEvent (mouseEvent);
@@ -1546,14 +1549,14 @@ internal static void OnMouseEvent (MouseEvent mouseEvent)
View = view
};
}
- else if (view.BoundsToScreen (view.Bounds).Contains (mouseEvent.X, mouseEvent.Y))
+ else if (view.ViewportToScreen (Rectangle.Empty with { Size = view.Viewport.Size }).Contains (mouseEvent.X, mouseEvent.Y))
{
- Point boundsPoint = view.ScreenToBounds (mouseEvent.X, mouseEvent.Y);
+ Point viewportLocation = view.ScreenToViewport (mouseEvent.X, mouseEvent.Y);
me = new ()
{
- X = boundsPoint.X,
- Y = boundsPoint.Y,
+ X = viewportLocation.X,
+ Y = viewportLocation.Y,
Flags = mouseEvent.Flags,
ScreenPosition = new (mouseEvent.X, mouseEvent.Y),
View = view
@@ -1586,10 +1589,37 @@ internal static void OnMouseEvent (MouseEvent mouseEvent)
//Debug.WriteLine ($"OnMouseEvent: ({a.MouseEvent.X},{a.MouseEvent.Y}) - {a.MouseEvent.Flags}");
- if (view.NewMouseEvent (me) == false)
+ while (view.NewMouseEvent (me) != true)
{
- // Should we bubble up the event, if it is not handled?
- //return;
+ if (MouseGrabView is { })
+ {
+ break;
+ }
+
+ if (view is Adornment adornmentView)
+ {
+ view = adornmentView.Parent.SuperView;
+ }
+ else
+ {
+ view = view.SuperView;
+ }
+
+ if (view is null)
+ {
+ break;
+ }
+
+ Point boundsPoint = view.ScreenToViewport (mouseEvent.X, mouseEvent.Y);
+
+ me = new ()
+ {
+ X = boundsPoint.X,
+ Y = boundsPoint.Y,
+ Flags = mouseEvent.Flags,
+ ScreenPosition = new (mouseEvent.X, mouseEvent.Y),
+ View = view
+ };
}
BringOverlappedTopToFront ();
diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs
index 1974b6147e..7187d71912 100644
--- a/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs
+++ b/Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs
@@ -18,15 +18,31 @@ public abstract class ConsoleDriver
// This is in addition to the dirty flag on each cell.
internal bool [] _dirtyLines;
- /// Gets the dimensions of the terminal.
- public Rectangle Bounds => new (0, 0, Cols, Rows);
+ // QUESTION: When non-full screen apps are supported, will this represent the app size, or will that be in Application?
+ /// Gets the location and size of the terminal screen.
+ public Rectangle Screen => new (0, 0, Cols, Rows);
+
+ private Rectangle _clip;
///
/// Gets or sets the clip rectangle that and are subject
/// to.
///
- /// The rectangle describing the bounds of .
- public Rectangle Clip { get; set; }
+ /// The rectangle describing the of region.
+ public Rectangle Clip
+ {
+ get => _clip;
+ set
+ {
+ if (_clip == value)
+ {
+ return;
+ }
+
+ // Don't ever let Clip be bigger than Screen
+ _clip = Rectangle.Intersect (Screen, value);
+ }
+ }
/// Get the operating system clipboard.
public IClipboard Clipboard { get; internal set; }
@@ -278,16 +294,6 @@ public void AddStr (string str)
for (var i = 0; i < runes.Count; i++)
{
- //if (runes [i].IsCombiningMark()) {
-
- // // Attempt to normalize
- // string combined = runes [i-1] + runes [i].ToString();
-
- // // Normalize to Form C (Canonical Composition)
- // string normalized = combined.Normalize (NormalizationForm.FormC);
-
- // runes [i-]
- //}
AddRune (runes [i]);
}
}
@@ -295,31 +301,27 @@ public void AddStr (string str)
/// Clears the of the driver.
public void ClearContents ()
{
- // TODO: This method is really "Clear Contents" now and should not be abstract (or virtual)
Contents = new Cell [Rows, Cols];
//CONCURRENCY: Unsynchronized access to Clip isn't safe.
- Clip = new (0, 0, Cols, Rows);
+ // TODO: ClearContents should not clear the clip; it should only clear the contents. Move clearing it elsewhere.
+ Clip = Screen;
_dirtyLines = new bool [Rows];
lock (Contents)
{
- // Can raise an exception while is still resizing.
- try
+ for (var row = 0; row < Rows; row++)
{
- for (var row = 0; row < Rows; row++)
+ for (var c = 0; c < Cols; c++)
{
- for (var c = 0; c < Cols; c++)
+ Contents [row, c] = new Cell
{
- Contents [row, c] = new Cell
- {
- Rune = (Rune)' ', Attribute = new Attribute (Color.White, Color.Black), IsDirty = true
- };
- _dirtyLines [row] = true;
- }
+ Rune = (Rune)' ',
+ Attribute = new Attribute (Color.White, Color.Black),
+ IsDirty = true
+ };
+ _dirtyLines [row] = true;
}
}
- catch (IndexOutOfRangeException)
- { }
}
}
@@ -327,18 +329,28 @@ public void ClearContents ()
/// upon success
public abstract bool EnsureCursorVisibility ();
- // TODO: Move FillRect to ./Drawing
- /// Fills the specified rectangle with the specified rune.
- ///
- ///
+ /// Fills the specified rectangle with the specified rune, using
+ ///
+ /// The value of is honored. Any parts of the rectangle not in the clip will not be drawn.
+ ///
+ /// The Screen-relative rectangle.
+ /// The Rune used to fill the rectangle
public void FillRect (Rectangle rect, Rune rune = default)
{
- for (int r = rect.Y; r < rect.Y + rect.Height; r++)
+ rect = Rectangle.Intersect (rect, Clip);
+ lock (Contents)
{
- for (int c = rect.X; c < rect.X + rect.Width; c++)
+ for (int r = rect.Y; r < rect.Y + rect.Height; r++)
{
- Application.Driver.Move (c, r);
- Application.Driver.AddRune (rune == default (Rune) ? new Rune (' ') : rune);
+ for (int c = rect.X; c < rect.X + rect.Width; c++)
+ {
+ Contents [r, c] = new Cell
+ {
+ Rune = (rune != default ? rune : (Rune)' '),
+ Attribute = CurrentAttribute, IsDirty = true
+ };
+ _dirtyLines [r] = true;
+ }
}
}
}
@@ -372,10 +384,13 @@ public void FillRect (Rectangle rect, Rune rune = default)
/// The column.
/// The row.
///
- /// if the coordinate is outside of the screen bounds or outside of .
+ /// if the coordinate is outside the screen bounds or outside of .
/// otherwise.
///
- public bool IsValidLocation (int col, int row) { return col >= 0 && row >= 0 && col < Cols && row < Rows && Clip.Contains (col, row); }
+ public bool IsValidLocation (int col, int row)
+ {
+ return col >= 0 && row >= 0 && col < Cols && row < Rows && Clip.Contains (col, row);
+ }
///
/// Updates and to the specified column and row in .
@@ -437,6 +452,7 @@ public virtual void Move (int col, int row)
/// Gets whether the supports TrueColor output.
public virtual bool SupportsTrueColor => true;
+ // TODO: This makes ConsoleDriver dependent on Application, which is not ideal. This should be moved to Application.
///
/// Gets or sets whether the should use 16 colors instead of the default TrueColors.
/// See to change this setting via .
@@ -466,6 +482,7 @@ public Attribute CurrentAttribute
get => _currentAttribute;
set
{
+ // TODO: This makes ConsoleDriver dependent on Application, which is not ideal. Once Attribute.PlatformColor is removed, this can be fixed.
if (Application.Driver is { })
{
_currentAttribute = new Attribute (value.Foreground, value.Background);
diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs
index 4a85804f28..22cd0b9872 100644
--- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs
+++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs
@@ -1315,7 +1315,7 @@ public override void UpdateCursor ()
{
EnsureCursorVisibility ();
- if (Col >= 0 && Col < Cols && Row >= 0 && Row < Rows)
+ if (Col >= 0 && Col < Cols && Row >= 0 && Row <= Rows)
{
SetCursorPosition (Col, Row);
SetWindowPosition (0, Row);
diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs
index 9f5e8a006f..7b67c31f7a 100644
--- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs
+++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs
@@ -18,6 +18,7 @@
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
+using System.Text;
using static Terminal.Gui.ConsoleDrivers.ConsoleKeyMapping;
namespace Terminal.Gui;
@@ -110,6 +111,7 @@ public bool WriteToConsole (Size size, ExtendedCharInfo [] charInfoBuffer, Coord
}
_stringBuilder.Append (EscSeqUtils.CSI_RestoreCursorPosition);
+ _stringBuilder.Append (EscSeqUtils.CSI_HideCursor);
var s = _stringBuilder.ToString ();
@@ -129,6 +131,11 @@ public bool WriteToConsole (Size size, ExtendedCharInfo [] charInfoBuffer, Coord
return result;
}
+ public bool WriteANSI (string ansi)
+ {
+ return WriteConsole (_screenBuffer, ansi, (uint)ansi.Length, out uint _, null);
+ }
+
public void ReadFromConsoleOutput (Size size, Coord coords, ref SmallRect window)
{
_screenBuffer = CreateConsoleScreenBuffer (
@@ -167,7 +174,10 @@ public void ReadFromConsoleOutput (Size size, Coord coords, ref SmallRect window
}
}
- public bool SetCursorPosition (Coord position) { return SetConsoleCursorPosition (_screenBuffer, position); }
+ public bool SetCursorPosition (Coord position)
+ {
+ return SetConsoleCursorPosition (_screenBuffer, position);
+ }
public void SetInitialCursorVisibility ()
{
@@ -577,14 +587,14 @@ public struct InputRecord
public readonly override string ToString ()
{
return EventType switch
- {
- EventType.Focus => FocusEvent.ToString (),
- EventType.Key => KeyEvent.ToString (),
- EventType.Menu => MenuEvent.ToString (),
- EventType.Mouse => MouseEvent.ToString (),
- EventType.WindowBufferSize => WindowBufferSizeEvent.ToString (),
- _ => "Unknown event type: " + EventType
- };
+ {
+ EventType.Focus => FocusEvent.ToString (),
+ EventType.Key => KeyEvent.ToString (),
+ EventType.Menu => MenuEvent.ToString (),
+ EventType.Mouse => MouseEvent.ToString (),
+ EventType.WindowBufferSize => WindowBufferSizeEvent.ToString (),
+ _ => "Unknown event type: " + EventType
+ };
}
}
@@ -980,7 +990,6 @@ internal class WindowsDriver : ConsoleDriver
{
private readonly bool _isWindowsTerminal;
- private CursorVisibility _cachedCursorVisibility;
private WindowsConsole.SmallRect _damageRegion;
private bool _isButtonDoubleClicked;
private bool _isButtonPressed;
@@ -1023,9 +1032,6 @@ public WindowsDriver ()
public WindowsConsole WinConsole { get; private set; }
- ///
- public override bool EnsureCursorVisibility () { return WinConsole is null || WinConsole.EnsureCursorVisibility (); }
-
public WindowsConsole.KeyEventRecord FromVKPacketToKeyEventRecord (WindowsConsole.KeyEventRecord keyEvent)
{
if (keyEvent.wVirtualKeyCode != (VK)ConsoleKey.Packet)
@@ -1072,25 +1078,12 @@ public WindowsConsole.KeyEventRecord FromVKPacketToKeyEventRecord (WindowsConsol
};
}
- ///
- public override bool GetCursorVisibility (out CursorVisibility visibility)
- {
- if (WinConsole is { })
- {
- return WinConsole.GetCursorVisibility (out visibility);
- }
-
- visibility = _cachedCursorVisibility;
-
- return true;
- }
-
public override bool IsRuneSupported (Rune rune) { return base.IsRuneSupported (rune) && rune.IsBmp; }
public override void Refresh ()
{
UpdateScreen ();
- WinConsole?.SetInitialCursorVisibility ();
+ //WinConsole?.SetInitialCursorVisibility ();
UpdateCursor ();
}
@@ -1164,13 +1157,6 @@ public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool al
}
}
- ///
- public override bool SetCursorVisibility (CursorVisibility visibility)
- {
- _cachedCursorVisibility = visibility;
-
- return WinConsole is null || WinConsole.SetCursorVisibility (visibility);
- }
#region Not Implemented
@@ -1194,9 +1180,13 @@ public WindowsConsole.ConsoleKeyInfoEx ToConsoleKeyInfoEx (WindowsConsole.KeyEve
return new WindowsConsole.ConsoleKeyInfoEx (cki, capslock, numlock, scrolllock);
}
+ #region Cursor Handling
+
+ private CursorVisibility? _cachedCursorVisibility;
+
public override void UpdateCursor ()
{
- if (Col < 0 || Row < 0 || Col > Cols || Row > Rows)
+ if (Col < 0 || Row < 0 || Col >= Cols || Row >= Rows)
{
GetCursorVisibility (out CursorVisibility cursorVisibility);
_cachedCursorVisibility = cursorVisibility;
@@ -1205,16 +1195,90 @@ public override void UpdateCursor ()
return;
}
- SetCursorVisibility (_cachedCursorVisibility);
-
var position = new WindowsConsole.Coord
{
X = (short)Col,
Y = (short)Row
};
- WinConsole?.SetCursorPosition (position);
+
+ if (Force16Colors)
+ {
+ WinConsole?.SetCursorPosition (position);
+ }
+ else
+ {
+ var sb = new StringBuilder ();
+ sb.Append (EscSeqUtils.CSI_SetCursorPosition (position.Y + 1, position.X + 1));
+ WinConsole?.WriteANSI (sb.ToString ());
+ }
+
+ if (_cachedCursorVisibility is { })
+ {
+ SetCursorVisibility (_cachedCursorVisibility.Value);
+ }
+ //EnsureCursorVisibility ();
}
+ ///
+ public override bool GetCursorVisibility (out CursorVisibility visibility)
+ {
+ if (WinConsole is { })
+ {
+ return WinConsole.GetCursorVisibility (out visibility);
+ }
+
+ visibility = _cachedCursorVisibility ?? CursorVisibility.Default;
+
+ return true;
+ }
+
+ ///
+ public override bool SetCursorVisibility (CursorVisibility visibility)
+ {
+ _cachedCursorVisibility = visibility;
+
+ if (Force16Colors)
+ {
+ return WinConsole is null || WinConsole.SetCursorVisibility (visibility);
+ }
+ else
+ {
+ var sb = new StringBuilder ();
+ sb.Append (visibility != CursorVisibility.Invisible ? EscSeqUtils.CSI_ShowCursor : EscSeqUtils.CSI_HideCursor);
+ return WinConsole?.WriteANSI (sb.ToString ()) ?? false;
+ }
+ }
+
+ ///
+ public override bool EnsureCursorVisibility ()
+ {
+ if (Force16Colors)
+ {
+ return WinConsole is null || WinConsole.EnsureCursorVisibility ();
+ }
+ else
+ {
+ var sb = new StringBuilder ();
+ sb.Append (_cachedCursorVisibility != CursorVisibility.Invisible ? EscSeqUtils.CSI_ShowCursor : EscSeqUtils.CSI_HideCursor);
+ return WinConsole?.WriteANSI (sb.ToString ()) ?? false;
+ }
+
+ if (!(Col >= 0 && Row >= 0 && Col < Cols && Row < Rows))
+ {
+ GetCursorVisibility (out CursorVisibility cursorVisibility);
+ _cachedCursorVisibility = cursorVisibility;
+ SetCursorVisibility (CursorVisibility.Invisible);
+
+ return false;
+ }
+
+ SetCursorVisibility (_cachedCursorVisibility ?? CursorVisibility.Default);
+
+ return _cachedCursorVisibility == CursorVisibility.Default;
+ }
+
+ #endregion Cursor Handling
+
public override void UpdateScreen ()
{
Size windowSize = WinConsole?.GetConsoleBufferWindow (out Point _) ?? new Size (Cols, Rows);
@@ -1226,8 +1290,8 @@ public override void UpdateScreen ()
var bufferCoords = new WindowsConsole.Coord
{
- X = (short)Clip.Width,
- Y = (short)Clip.Height
+ X = (short)Cols, //Clip.Width,
+ Y = (short)Rows, //Clip.Height
};
for (var row = 0; row < Rows; row++)
@@ -1574,13 +1638,13 @@ private KeyCode MapKey (WindowsConsole.ConsoleKeyInfoEx keyInfoEx)
// returned (e.g. on ENG OemPlus un-shifted is =, not +). This is important
// for key persistence ("Ctrl++" vs. "Ctrl+=").
mappedChar = keyInfo.Key switch
- {
- ConsoleKey.OemPeriod => '.',
- ConsoleKey.OemComma => ',',
- ConsoleKey.OemPlus => '+',
- ConsoleKey.OemMinus => '-',
- _ => mappedChar
- };
+ {
+ ConsoleKey.OemPeriod => '.',
+ ConsoleKey.OemComma => ',',
+ ConsoleKey.OemPlus => '+',
+ ConsoleKey.OemMinus => '-',
+ _ => mappedChar
+ };
}
// Return the mappedChar with they modifiers. Because mappedChar is un-shifted, if Shift was down
@@ -1745,7 +1809,10 @@ private async Task ProcessContinuousButtonPressedAsync (MouseFlags mouseFlag)
Flags = mouseFlag
};
- if (Application.WantContinuousButtonPressedView is null)
+ // TODO: This makes ConsoleDriver dependent on Application, which is not ideal. This should be moved to Application.
+ View view = Application.WantContinuousButtonPressedView;
+
+ if (view is null)
{
break;
}
@@ -1759,6 +1826,7 @@ private async Task ProcessContinuousButtonPressedAsync (MouseFlags mouseFlag)
//Debug.WriteLine($"ProcessContinuousButtonPressedAsync: {view}");
if (_isButtonPressed && (mouseFlag & MouseFlags.ReportMousePosition) == 0)
{
+ // TODO: This makes ConsoleDriver dependent on Application, which is not ideal. This should be moved to Application.
Application.Invoke (() => OnMouseEvent (me));
}
}
@@ -1813,6 +1881,7 @@ private MouseEvent ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent)
if (_isButtonDoubleClicked || _isOneFingerDoubleClicked)
{
+ // TODO: This makes ConsoleDriver dependent on Application, which is not ideal. This should be moved to Application.
Application.MainLoop.AddIdle (
() =>
{
@@ -1884,6 +1953,7 @@ private MouseEvent ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent)
if ((mouseFlag & MouseFlags.ReportMousePosition) == 0)
{
+ // TODO: This makes ConsoleDriver dependent on Application, which is not ideal. This should be moved to Application.
Application.MainLoop.AddIdle (
() =>
{
diff --git a/Terminal.Gui/Drawing/LineCanvas.cs b/Terminal.Gui/Drawing/LineCanvas.cs
index 6fe9858523..0e9fac7613 100644
--- a/Terminal.Gui/Drawing/LineCanvas.cs
+++ b/Terminal.Gui/Drawing/LineCanvas.cs
@@ -48,7 +48,7 @@ public class LineCanvas : IDisposable
// TODO: Add other resolvers
};
- private Rectangle _cachedBounds;
+ private Rectangle _cachedViewport;
/// Creates a new instance.
public LineCanvas ()
@@ -67,37 +67,37 @@ public LineCanvas ()
/// Gets the rectangle that describes the bounds of the canvas. Location is the coordinates of the line that is
/// furthest left/top and Size is defined by the line that extends the furthest right/bottom.
///
- public Rectangle Bounds
+ public Rectangle Viewport
{
get
{
- if (_cachedBounds.IsEmpty)
+ if (_cachedViewport.IsEmpty)
{
if (_lines.Count == 0)
{
- return _cachedBounds;
+ return _cachedViewport;
}
- Rectangle bounds = _lines [0].Bounds;
+ Rectangle viewport = _lines [0].Viewport;
for (var i = 1; i < _lines.Count; i++)
{
- bounds = Rectangle.Union (bounds, _lines [i].Bounds);
+ viewport = Rectangle.Union (viewport, _lines [i].Viewport);
}
- if (bounds is {Width: 0} or {Height: 0})
+ if (viewport is {Width: 0} or {Height: 0})
{
- bounds = bounds with
+ viewport = viewport with
{
- Width = Math.Clamp (bounds.Width, 1, short.MaxValue),
- Height = Math.Clamp (bounds.Height, 1, short.MaxValue)
+ Width = Math.Clamp (viewport.Width, 1, short.MaxValue),
+ Height = Math.Clamp (viewport.Height, 1, short.MaxValue)
};
}
- _cachedBounds = bounds;
+ _cachedViewport = viewport;
}
- return _cachedBounds;
+ return _cachedViewport;
}
}
@@ -134,7 +134,7 @@ public void AddLine (
Attribute? attribute = default
)
{
- _cachedBounds = Rectangle.Empty;
+ _cachedViewport = Rectangle.Empty;
_lines.Add (new StraightLine (start, length, orientation, style, attribute));
}
@@ -142,14 +142,14 @@ public void AddLine (
///
public void AddLine (StraightLine line)
{
- _cachedBounds = Rectangle.Empty;
+ _cachedViewport = Rectangle.Empty;
_lines.Add (line);
}
/// Clears all lines from the LineCanvas.
public void Clear ()
{
- _cachedBounds = Rectangle.Empty;
+ _cachedViewport = Rectangle.Empty;
_lines.Clear ();
}
@@ -157,7 +157,7 @@ public void Clear ()
/// Clears any cached states from the canvas Call this method if you make changes to lines that have already been
/// added.
///
- public void ClearCache () { _cachedBounds = Rectangle.Empty; }
+ public void ClearCache () { _cachedViewport = Rectangle.Empty; }
///
/// Evaluates the lines that have been added to the canvas and returns a map containing the glyphs and their
@@ -170,9 +170,9 @@ public Dictionary GetCellMap ()
Dictionary map = new ();
// walk through each pixel of the bitmap
- for (int y = Bounds.Y; y < Bounds.Y + Bounds.Height; y++)
+ for (int y = Viewport.Y; y < Viewport.Y + Viewport.Height; y++)
{
- for (int x = Bounds.X; x < Bounds.X + Bounds.Width; x++)
+ for (int x = Viewport.X; x < Viewport.X + Viewport.Width; x++)
{
IntersectionDefinition? [] intersects = _lines
.Select (l => l.Intersects (x, y))
@@ -232,7 +232,7 @@ public Dictionary GetMap (Rectangle inArea)
/// intersection symbols.
///
/// A map of all the points within the canvas.
- public Dictionary GetMap () { return GetMap (Bounds); }
+ public Dictionary GetMap () { return GetMap (Viewport); }
/// Merges one line canvas into this one.
///
@@ -260,13 +260,13 @@ public StraightLine RemoveLastLine ()
///
/// Returns the contents of the line canvas rendered to a string. The string will include all columns and rows,
- /// even if has negative coordinates. For example, if the canvas contains a single line that
+ /// even if has negative coordinates. For example, if the canvas contains a single line that
/// starts at (-1,-1) with a length of 2, the rendered string will have a length of 2.
///
/// The canvas rendered to a string.
public override string ToString ()
{
- if (Bounds.IsEmpty)
+ if (Viewport.IsEmpty)
{
return string.Empty;
}
@@ -275,13 +275,13 @@ public override string ToString ()
Dictionary runeMap = GetMap ();
// Create the rune canvas
- Rune [,] canvas = new Rune [Bounds.Height, Bounds.Width];
+ Rune [,] canvas = new Rune [Viewport.Height, Viewport.Width];
// Copy the rune map to the canvas, adjusting for any negative coordinates
foreach (KeyValuePair kvp in runeMap)
{
- int x = kvp.Key.X - Bounds.X;
- int y = kvp.Key.Y - Bounds.Y;
+ int x = kvp.Key.X - Viewport.X;
+ int y = kvp.Key.Y - Viewport.Y;
canvas [y, x] = kvp.Value;
}
diff --git a/Terminal.Gui/Drawing/StraightLine.cs b/Terminal.Gui/Drawing/StraightLine.cs
index 1dd7b803c4..9a2785f0f4 100644
--- a/Terminal.Gui/Drawing/StraightLine.cs
+++ b/Terminal.Gui/Drawing/StraightLine.cs
@@ -45,8 +45,8 @@ public StraightLine (
/// Gets the rectangle that describes the bounds of the canvas. Location is the coordinates of the line that is
/// furthest left/top and Size is defined by the line that extends the furthest right/bottom.
///
- // PERF: Probably better to store the rectangle rather than make a new one on every single access to Bounds.
- internal Rectangle Bounds
+ // PERF: Probably better to store the rectangle rather than make a new one on every single access to Viewport.
+ internal Rectangle Viewport
{
get
{
diff --git a/Terminal.Gui/Drawing/Thickness.cs b/Terminal.Gui/Drawing/Thickness.cs
index 26f97ffb35..ffe8b3f708 100644
--- a/Terminal.Gui/Drawing/Thickness.cs
+++ b/Terminal.Gui/Drawing/Thickness.cs
@@ -161,6 +161,7 @@ public Rectangle Draw (Rectangle rect, string label = null)
Application.Driver.FillRect (rect with { Height = Math.Min (rect.Height, Top) }, topChar);
}
+ // Draw the Left side
// Draw the Left side
if (Left > 0)
{
diff --git a/Terminal.Gui/Input/Mouse.cs b/Terminal.Gui/Input/Mouse.cs
index 2b3f2852be..7ea253a764 100644
--- a/Terminal.Gui/Input/Mouse.cs
+++ b/Terminal.Gui/Input/Mouse.cs
@@ -116,10 +116,10 @@ public class MouseEvent
/// The View at the location for the mouse event.
public View View { get; set; }
- /// The X position of the mouse in -relative coordinates.
+ /// The X position of the mouse in -relative coordinates.
public int X { get; set; }
- /// The Y position of the mouse in -relative coordinates.
+ /// The Y position of the mouse in -relative coordinates.
public int Y { get; set; }
///
@@ -133,12 +133,12 @@ public class MouseEvent
///
///
///
- /// The and properties are always -relative. When the mouse is grabbed by a view,
+ /// The and properties are always -relative. When the mouse is grabbed by a view,
/// provides the mouse position screen-relative coordinates, enabling the grabbed view to know how much the
/// mouse has moved.
///
///
- /// Calculated and processed in .
+ /// Calculated and processed in .
///
///
public Point ScreenPosition { get; set; }
diff --git a/Terminal.Gui/Terminal.Gui.csproj b/Terminal.Gui/Terminal.Gui.csproj
index c2f39edb84..3c909c250d 100644
--- a/Terminal.Gui/Terminal.Gui.csproj
+++ b/Terminal.Gui/Terminal.Gui.csproj
@@ -48,8 +48,8 @@
-
-
+
+
diff --git a/Terminal.Gui/Text/Autocomplete/AppendAutocomplete.cs b/Terminal.Gui/Text/Autocomplete/AppendAutocomplete.cs
index ba5fbed5a4..76d74f512d 100644
--- a/Terminal.Gui/Text/Autocomplete/AppendAutocomplete.cs
+++ b/Terminal.Gui/Text/Autocomplete/AppendAutocomplete.cs
@@ -117,7 +117,7 @@ public override void RenderOverlay (Point renderAt)
Suggestion suggestion = Suggestions.ElementAt (SelectedIdx);
string fragment = suggestion.Replacement.Substring (suggestion.Remove);
- int spaceAvailable = textField.Bounds.Width - textField.Text.GetColumns ();
+ int spaceAvailable = textField.Viewport.Width - textField.Text.GetColumns ();
int spaceRequired = fragment.EnumerateRunes ().Sum (c => c.GetColumns ());
if (spaceAvailable < spaceRequired)
diff --git a/Terminal.Gui/Text/Autocomplete/PopupAutocomplete.PopUp.cs b/Terminal.Gui/Text/Autocomplete/PopupAutocomplete.PopUp.cs
index 55a6140759..86fc949ecf 100644
--- a/Terminal.Gui/Text/Autocomplete/PopupAutocomplete.PopUp.cs
+++ b/Terminal.Gui/Text/Autocomplete/PopupAutocomplete.PopUp.cs
@@ -16,7 +16,7 @@ public Popup (PopupAutocomplete autoComplete)
protected internal override bool OnMouseEvent (MouseEvent mouseEvent) { return _autoComplete.OnMouseEvent (mouseEvent); }
- public override void OnDrawContent (Rectangle contentArea)
+ public override void OnDrawContent (Rectangle viewport)
{
if (!_autoComplete.LastPopupPos.HasValue)
{
diff --git a/Terminal.Gui/Text/Autocomplete/PopupAutocomplete.cs b/Terminal.Gui/Text/Autocomplete/PopupAutocomplete.cs
index 84ea483657..e76aa8a2d0 100644
--- a/Terminal.Gui/Text/Autocomplete/PopupAutocomplete.cs
+++ b/Terminal.Gui/Text/Autocomplete/PopupAutocomplete.cs
@@ -271,13 +271,13 @@ public override void RenderOverlay (Point renderAt)
if (PopupInsideContainer)
{
// don't overspill vertically
- height = Math.Min (HostControl.Bounds.Height - renderAt.Y, MaxHeight);
+ height = Math.Min (HostControl.Viewport.Height - renderAt.Y, MaxHeight);
// There is no space below, lets see if can popup on top
- if (height < Suggestions.Count && HostControl.Bounds.Height - renderAt.Y >= height)
+ if (height < Suggestions.Count && HostControl.Viewport.Height - renderAt.Y >= height)
{
// Verifies that the upper limit available is greater than the lower limit
- if (renderAt.Y > HostControl.Bounds.Height - renderAt.Y)
+ if (renderAt.Y > HostControl.Viewport.Height - renderAt.Y)
{
renderAt.Y = Math.Max (renderAt.Y - Math.Min (Suggestions.Count + 1, MaxHeight + 1), 0);
height = Math.Min (Math.Min (Suggestions.Count, MaxHeight), LastPopupPos.Value.Y - 1);
@@ -287,13 +287,13 @@ public override void RenderOverlay (Point renderAt)
else
{
// don't overspill vertically
- height = Math.Min (Math.Min (top.Bounds.Height - HostControl.Frame.Bottom, MaxHeight), Suggestions.Count);
+ height = Math.Min (Math.Min (top.Viewport.Height - HostControl.Frame.Bottom, MaxHeight), Suggestions.Count);
// There is no space below, lets see if can popup on top
if (height < Suggestions.Count && HostControl.Frame.Y - top.Frame.Y >= height)
{
// Verifies that the upper limit available is greater than the lower limit
- if (HostControl.Frame.Y > top.Bounds.Height - HostControl.Frame.Y)
+ if (HostControl.Frame.Y > top.Viewport.Height - HostControl.Frame.Y)
{
renderAt.Y = Math.Max (HostControl.Frame.Y - Math.Min (Suggestions.Count, MaxHeight), 0);
height = Math.Min (Math.Min (Suggestions.Count, MaxHeight), HostControl.Frame.Y);
@@ -323,34 +323,34 @@ public override void RenderOverlay (Point renderAt)
if (PopupInsideContainer)
{
// don't overspill horizontally, let's see if can be displayed on the left
- if (width > HostControl.Bounds.Width - renderAt.X)
+ if (width > HostControl.Viewport.Width - renderAt.X)
{
// Verifies that the left limit available is greater than the right limit
- if (renderAt.X > HostControl.Bounds.Width - renderAt.X)
+ if (renderAt.X > HostControl.Viewport.Width - renderAt.X)
{
renderAt.X -= Math.Min (width, LastPopupPos.Value.X);
width = Math.Min (width, LastPopupPos.Value.X);
}
else
{
- width = Math.Min (width, HostControl.Bounds.Width - renderAt.X);
+ width = Math.Min (width, HostControl.Viewport.Width - renderAt.X);
}
}
}
else
{
// don't overspill horizontally, let's see if can be displayed on the left
- if (width > top.Bounds.Width - (renderAt.X + HostControl.Frame.X))
+ if (width > top.Viewport.Width - (renderAt.X + HostControl.Frame.X))
{
// Verifies that the left limit available is greater than the right limit
- if (renderAt.X + HostControl.Frame.X > top.Bounds.Width - (renderAt.X + HostControl.Frame.X))
+ if (renderAt.X + HostControl.Frame.X > top.Viewport.Width - (renderAt.X + HostControl.Frame.X))
{
renderAt.X -= Math.Min (width, LastPopupPos.Value.X);
width = Math.Min (width, LastPopupPos.Value.X);
}
else
{
- width = Math.Min (width, top.Bounds.Width - renderAt.X);
+ width = Math.Min (width, top.Viewport.Width - renderAt.X);
}
}
}
diff --git a/Terminal.Gui/Text/TextFormatter.cs b/Terminal.Gui/Text/TextFormatter.cs
index 6168c53f01..db13b5930b 100644
--- a/Terminal.Gui/Text/TextFormatter.cs
+++ b/Terminal.Gui/Text/TextFormatter.cs
@@ -30,7 +30,7 @@ public TextAlignment Alignment
/// Gets or sets whether the should be automatically changed to fit the .
///
- /// Used by to resize the view's to fit .
+ /// Used by to resize the view's to fit .
///
/// AutoSize is ignored if and
/// are used.
@@ -73,8 +73,8 @@ public TextDirection Direction
}
///
- /// Determines if the bounds width will be used or only the text width will be used,
- /// If all the bounds area will be filled with whitespaces and the same background color
+ /// Determines if the viewport width will be used or only the text width will be used,
+ /// If all the viewport area will be filled with whitespaces and the same background color
/// showing a perfect rectangle.
///
public bool FillRemaining { get; set; }
@@ -202,17 +202,17 @@ public bool WordWrap
/// 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.
+ /// Specifies the screen-relative location and maximum container size.
/// The console driver currently used by the application.
///
public void Draw (
- Rectangle bounds,
+ Rectangle screen,
Attribute normalColor,
Attribute hotColor,
- Rectangle containerBounds = default,
+ Rectangle maximum = default,
ConsoleDriver driver = null
)
{
@@ -240,46 +240,46 @@ public void Draw (
}
bool isVertical = IsVerticalDirection (Direction);
- Rectangle maxBounds = bounds;
+ Rectangle maxScreen = screen;
if (driver is { })
{
// INTENT: What, exactly, is the intent of this?
- maxBounds = containerBounds == default (Rectangle)
- ? bounds
+ maxScreen = maximum == default (Rectangle)
+ ? screen
: new (
- Math.Max (containerBounds.X, bounds.X),
- Math.Max (containerBounds.Y, bounds.Y),
+ Math.Max (maximum.X, screen.X),
+ Math.Max (maximum.Y, screen.Y),
Math.Max (
- Math.Min (containerBounds.Width, containerBounds.Right - bounds.Left),
+ Math.Min (maximum.Width, maximum.Right - screen.Left),
0
),
Math.Max (
Math.Min (
- containerBounds.Height,
- containerBounds.Bottom - bounds.Top
+ maximum.Height,
+ maximum.Bottom - screen.Top
),
0
)
);
}
- if (maxBounds.Width == 0 || maxBounds.Height == 0)
+ if (maxScreen.Width == 0 || maxScreen.Height == 0)
{
return;
}
- int lineOffset = !isVertical && bounds.Y < 0 ? Math.Abs (bounds.Y) : 0;
+ int lineOffset = !isVertical && screen.Y < 0 ? Math.Abs (screen.Y) : 0;
for (int line = lineOffset; line < linesFormatted.Count; line++)
{
- if ((isVertical && line > bounds.Width) || (!isVertical && line > bounds.Height))
+ if ((isVertical && line > screen.Width) || (!isVertical && line > screen.Height))
{
continue;
}
- if ((isVertical && line >= maxBounds.Left + maxBounds.Width)
- || (!isVertical && line >= maxBounds.Top + maxBounds.Height + lineOffset))
+ if ((isVertical && line >= maxScreen.Left + maxScreen.Width)
+ || (!isVertical && line >= maxScreen.Top + maxScreen.Height + lineOffset))
{
break;
}
@@ -305,14 +305,14 @@ public void Draw (
if (isVertical)
{
int runesWidth = GetWidestLineLength (linesFormatted, line, TabWidth);
- x = bounds.Right - runesWidth;
- CursorPosition = bounds.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 = bounds.Right - runesWidth;
- CursorPosition = bounds.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 void Draw (
int runesWidth = line > 0
? GetWidestLineLength (linesFormatted, 0, line, TabWidth)
: 0;
- x = bounds.Left + runesWidth;
+ x = screen.Left + runesWidth;
}
else
{
- x = bounds.Left;
+ x = screen.Left;
}
CursorPosition = _hotKeyPos > -1 ? _hotKeyPos : 0;
@@ -336,16 +336,16 @@ public void Draw (
if (isVertical)
{
int runesWidth = GetWidestLineLength (linesFormatted, line, TabWidth);
- x = bounds.Left + line + (bounds.Width - runesWidth) / 2;
+ x = screen.Left + line + (screen.Width - runesWidth) / 2;
- CursorPosition = (bounds.Width - runesWidth) / 2 + (_hotKeyPos > -1 ? _hotKeyPos : 0);
+ CursorPosition = (screen.Width - runesWidth) / 2 + (_hotKeyPos > -1 ? _hotKeyPos : 0);
}
else
{
int runesWidth = StringExtensions.ToString (runes).GetColumns ();
- x = bounds.Left + (bounds.Width - runesWidth) / 2;
+ x = screen.Left + (screen.Width - runesWidth) / 2;
- CursorPosition = (bounds.Width - runesWidth) / 2 + (_hotKeyPos > -1 ? _hotKeyPos : 0);
+ CursorPosition = (screen.Width - runesWidth) / 2 + (_hotKeyPos > -1 ? _hotKeyPos : 0);
}
}
else
@@ -358,35 +358,35 @@ public void Draw (
{
if (isVertical)
{
- y = bounds.Bottom - runes.Length;
+ y = screen.Bottom - runes.Length;
}
else
{
- y = bounds.Bottom - linesFormatted.Count + line;
+ y = screen.Bottom - linesFormatted.Count + line;
}
}
else if (VerticalAlignment is VerticalTextAlignment.Top or VerticalTextAlignment.Justified)
{
if (isVertical)
{
- y = bounds.Top;
+ y = screen.Top;
}
else
{
- y = bounds.Top + line;
+ y = screen.Top + line;
}
}
else if (VerticalAlignment == VerticalTextAlignment.Middle)
{
if (isVertical)
{
- int s = (bounds.Height - runes.Length) / 2;
- y = bounds.Top + s;
+ int s = (screen.Height - runes.Length) / 2;
+ y = screen.Top + s;
}
else
{
- int s = (bounds.Height - linesFormatted.Count) / 2;
- y = bounds.Top + line + s;
+ int s = (screen.Height - linesFormatted.Count) / 2;
+ y = screen.Top + line + s;
}
}
else
@@ -394,9 +394,9 @@ public void Draw (
throw new ArgumentOutOfRangeException ($"{nameof (VerticalAlignment)}");
}
- int colOffset = bounds.X < 0 ? Math.Abs (bounds.X) : 0;
- int start = isVertical ? bounds.Top : bounds.Left;
- int size = isVertical ? bounds.Height : bounds.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,15 +422,15 @@ public void Draw (
break;
}
- if ((!isVertical && current - start > maxBounds.Left + maxBounds.Width - bounds.X + colOffset)
- || (isVertical && idx > maxBounds.Top + maxBounds.Height - bounds.Y))
+ if ((!isVertical && current - start > maxScreen.Left + maxScreen.Width - screen.X + colOffset)
+ || (isVertical && idx > maxScreen.Top + maxScreen.Height - screen.Y))
{
break;
}
}
- //if ((!isVertical && idx > maxBounds.Left + maxBounds.Width - bounds.X + colOffset)
- // || (isVertical && idx > maxBounds.Top + maxBounds.Height - bounds.Y))
+ //if ((!isVertical && idx > maxBounds.Left + maxBounds.Width - viewport.X + colOffset)
+ // || (isVertical && idx > maxBounds.Top + maxBounds.Height - viewport.Y))
// break;
diff --git a/Terminal.Gui/View/Adornment/Adornment.cs b/Terminal.Gui/View/Adornment/Adornment.cs
index 307f3cb3e1..585929e991 100644
--- a/Terminal.Gui/View/Adornment/Adornment.cs
+++ b/Terminal.Gui/View/Adornment/Adornment.cs
@@ -1,14 +1,14 @@
namespace Terminal.Gui;
///
-/// Adornments are a special form of that appear outside the :
+/// Adornments are a special form of that appear outside the :
/// , , and . They are defined using the
/// class, which specifies the thickness of the sides of a rectangle.
///
///
///
/// Each of , , and has slightly different
-/// behavior relative to , , keyboard input, and
+/// behavior relative to , , keyboard input, and
/// mouse input. Each can be customized by manipulating their Subviews.
///
///
@@ -89,7 +89,7 @@ public void OnThicknessChanged (Thickness previousThickness)
public override View SuperView
{
get => null;
- set => throw new NotImplementedException ();
+ set => throw new InvalidOperationException (@"Adornments can not be Subviews or have SuperViews. Use Parent instead.");
}
//internal override Adornment CreateAdornment (Type adornmentType)
@@ -107,10 +107,14 @@ internal override void LayoutAdornments ()
/// Gets the rectangle that describes the area of the Adornment. The Location is always (0,0).
/// The size is the size of the .
///
- public override Rectangle Bounds
+ ///
+ /// The Viewport of an Adornment cannot be modified. Attempting to set this property will throw an
+ /// .
+ ///
+ public override Rectangle Viewport
{
get => Frame with { Location = Point.Empty };
- set => throw new InvalidOperationException ("It makes no sense to set Bounds of a Thickness.");
+ set => throw new InvalidOperationException (@"The Viewport of an Adornment cannot be modified.");
}
///
@@ -123,10 +127,11 @@ public override Rectangle FrameToScreen ()
// Adornments are *Children* of a View, not SubViews. Thus View.FrameToScreen will not work.
// To get the screen-relative coordinates of an Adornment, we need get the parent's Frame
- // in screen coords, and add our Frame location to it.
- Rectangle parent = Parent.FrameToScreen ();
+ // in screen coords, ...
+ Rectangle parentScreen = Parent.FrameToScreen ();
- return new (new (parent.X + Frame.X, parent.Y + Frame.Y), Frame.Size);
+ // ...and add our Frame location to it.
+ return new (new (parentScreen.X + Frame.X, parentScreen.Y + Frame.Y), Frame.Size);
}
///
@@ -137,19 +142,21 @@ public override Rectangle FrameToScreen ()
public override bool OnDrawAdornments () { return false; }
/// Redraws the Adornments that comprise the .
- public override void OnDrawContent (Rectangle contentArea)
+ public override void OnDrawContent (Rectangle viewport)
{
if (Thickness == Thickness.Empty)
{
return;
}
- Rectangle screenBounds = BoundsToScreen (contentArea);
+ Rectangle prevClip = SetClip ();
+
+ Rectangle screen = ViewportToScreen (viewport);
Attribute normalAttr = GetNormalColor ();
Driver.SetAttribute (normalAttr);
// This just draws/clears the thickness, not the insides.
- Thickness.Draw (screenBounds, ToString ());
+ Thickness.Draw (screen, ToString ());
if (!string.IsNullOrEmpty (TextFormatter.Text))
{
@@ -160,11 +167,16 @@ public override void OnDrawContent (Rectangle contentArea)
}
}
- TextFormatter?.Draw (screenBounds, normalAttr, normalAttr, Rectangle.Empty);
+ TextFormatter?.Draw (screen, normalAttr, normalAttr, Rectangle.Empty);
if (Subviews.Count > 0)
{
- base.OnDrawContent (contentArea);
+ base.OnDrawContent (viewport);
+ }
+
+ if (Driver is { })
+ {
+ Driver.Clip = prevClip;
}
ClearLayoutNeeded ();
@@ -181,8 +193,8 @@ public override void OnDrawContent (Rectangle contentArea)
///
public override bool SuperViewRendersLineCanvas
{
- get => false; // throw new NotImplementedException ();
- set => throw new NotImplementedException ();
+ get => false;
+ set => throw new InvalidOperationException (@"Adornment can only render to their Parent or Parent's Superview.");
}
#endregion View Overrides
@@ -205,6 +217,7 @@ public override bool Contains (int x, int y)
{
return false;
}
+
Rectangle frame = Frame;
frame.Offset (Parent.Frame.Location);
@@ -227,43 +240,6 @@ public override bool Contains (int x, int y)
return base.OnMouseEnter (mouseEvent);
}
- /// Called when a mouse event occurs within the Adornment.
- ///
- ///
- /// The coordinates are relative to .
- ///
- ///
- /// A mouse click on the Adornment will cause the Parent to focus.
- ///
- ///
- /// A mouse drag on the Adornment will cause the Parent to move.
- ///
- ///
- ///
- /// , if the event was handled, otherwise.
- protected internal override bool OnMouseEvent (MouseEvent mouseEvent)
- {
- if (Parent is null)
- {
- return false;
- }
-
- var args = new MouseEventEventArgs (mouseEvent);
-
- if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Clicked))
- {
- if (Parent.CanFocus && !Parent.HasFocus)
- {
- Parent.SetFocus ();
- Parent.SetNeedsDisplay ();
- }
-
- return OnMouseClick (args);
- }
-
- return false;
- }
-
///
protected internal override bool OnMouseLeave (MouseEvent mouseEvent)
{
diff --git a/Terminal.Gui/View/Adornment/Border.cs b/Terminal.Gui/View/Adornment/Border.cs
index cee764c568..922fa2a19f 100644
--- a/Terminal.Gui/View/Adornment/Border.cs
+++ b/Terminal.Gui/View/Adornment/Border.cs
@@ -149,14 +149,14 @@ public override ColorScheme ColorScheme
}
}
- Rectangle GetBorderBounds (Rectangle screenBounds)
+ Rectangle GetBorderRectangle (Rectangle screenRect)
{
return new (
- screenBounds.X + Math.Max (0, Thickness.Left - 1),
- screenBounds.Y + Math.Max (0, Thickness.Top - 1),
+ screenRect.X + Math.Max (0, Thickness.Left - 1),
+ screenRect.Y + Math.Max (0, Thickness.Top - 1),
Math.Max (
0,
- screenBounds.Width
+ screenRect.Width
- Math.Max (
0,
Math.Max (0, Thickness.Left - 1)
@@ -165,7 +165,7 @@ Rectangle GetBorderBounds (Rectangle screenBounds)
),
Math.Max (
0,
- screenBounds.Height
+ screenRect.Height
- Math.Max (
0,
Math.Max (0, Thickness.Top - 1)
@@ -204,6 +204,7 @@ private void Border_Highlight (object sender, HighlightEventArgs e)
{
if (!Parent.Arrangement.HasFlag (ViewArrangement.Movable))
{
+ e.Cancel = true;
return;
}
@@ -300,7 +301,7 @@ protected internal override bool OnMouseEvent (MouseEvent mouseEvent)
_dragPosition = new Point (mouseEvent.X, mouseEvent.Y);
- Point parentLoc = Parent.SuperView?.ScreenToBounds (mouseEvent.ScreenPosition.X, mouseEvent.ScreenPosition.Y) ?? mouseEvent.ScreenPosition;
+ Point parentLoc = Parent.SuperView?.ScreenToViewport (mouseEvent.ScreenPosition.X, mouseEvent.ScreenPosition.Y) ?? mouseEvent.ScreenPosition;
GetLocationEnsuringFullVisibility (
Parent,
@@ -360,9 +361,9 @@ private void Application_UnGrabbingMouse (object sender, GrabMouseEventArgs e)
#endregion Mouse Support
///
- public override void OnDrawContent (Rectangle contentArea)
+ public override void OnDrawContent (Rectangle viewport)
{
- base.OnDrawContent (contentArea);
+ base.OnDrawContent (viewport);
if (Thickness == Thickness.Empty)
{
@@ -370,7 +371,7 @@ public override void OnDrawContent (Rectangle contentArea)
}
//Driver.SetAttribute (Colors.ColorSchemes ["Error"].Normal);
- Rectangle screenBounds = BoundsToScreen (contentArea);
+ Rectangle screenBounds = ViewportToScreen (viewport);
//OnDrawSubviews (bounds);
@@ -381,7 +382,7 @@ public override void OnDrawContent (Rectangle contentArea)
// ...thickness extends outward (border/title is always as far in as possible)
// PERF: How about a call to Rectangle.Offset?
- var borderBounds = GetBorderBounds (screenBounds);
+ var borderBounds = GetBorderRectangle (screenBounds);
int topTitleLineY = borderBounds.Y;
int titleY = borderBounds.Y;
var titleBarsLength = 0; // the little vertical thingies
@@ -432,10 +433,17 @@ public override void OnDrawContent (Rectangle contentArea)
if (canDrawBorder && Thickness.Top > 0 && maxTitleWidth > 0 && !string.IsNullOrEmpty (Parent?.Title))
{
+ var focus = Parent.GetNormalColor();
+ if (Parent.SuperView is { } && Parent.SuperView?.Subviews!.Count (s => s.CanFocus) > 1)
+ {
+ // Only use focus color if there are multiple focusable views
+ focus = Parent.GetFocusColor() ;
+ }
+
Parent.TitleTextFormatter.Draw (
new (borderBounds.X + 2, titleY, maxTitleWidth, 1),
- Parent.HasFocus ? Parent.GetFocusColor () : Parent.GetNormalColor (),
- Parent.HasFocus ? Parent.GetFocusColor () : Parent.GetHotNormalColor ());
+ Parent.HasFocus ? focus : Parent.GetNormalColor (),
+ Parent.HasFocus ? focus : Parent.GetHotNormalColor ());
}
if (canDrawBorder && LineStyle != LineStyle.None)
@@ -641,7 +649,5 @@ public override void OnDrawContent (Rectangle contentArea)
}
}
}
-
- //base.OnDrawContent (contentArea);
}
}
diff --git a/Terminal.Gui/View/Adornment/Padding.cs b/Terminal.Gui/View/Adornment/Padding.cs
index d9d373de4d..f979dcf903 100644
--- a/Terminal.Gui/View/Adornment/Padding.cs
+++ b/Terminal.Gui/View/Adornment/Padding.cs
@@ -42,7 +42,7 @@ public override ColorScheme ColorScheme
/// Called when a mouse event occurs within the Padding.
///
///
- /// The coordinates are relative to .
+ /// The coordinates are relative to .
///
///
/// A mouse click on the Padding will cause the Parent to focus.
diff --git a/Terminal.Gui/View/Layout/ViewLayout.cs b/Terminal.Gui/View/Layout/ViewLayout.cs
index dcfce11103..f593d93d2b 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;
@@ -42,18 +43,18 @@ public partial class View
/// Gets or sets the absolute location and dimension of the view.
///
/// The rectangle describing absolute location and dimension of the view, in coordinates relative to the
- /// 's .
+ /// 's Content, which is bound by .
///
///
- /// Frame is relative to the 's .
+ /// Frame is relative to the 's Content, which is bound by .
///
/// Setting Frame will set , , , and to the
/// values of the corresponding properties of the parameter.
+ /// This causes to be .
///
- /// This causes to be .
///
/// Altering the Frame will eventually (when the view hierarchy is next laid out via see
- /// cref="LayoutSubviews"/>) cause and
+ /// cref="LayoutSubviews"/>) cause and
///
/// methods to be called.
///
@@ -63,7 +64,12 @@ public Rectangle Frame
get => _frame;
set
{
- _frame = value with { Width = Math.Max (value.Width, 0), Height = Math.Max (value.Height, 0) };
+ if (_frame == value)
+ {
+ return;
+ }
+
+ SetFrame (value with { Width = Math.Max (value.Width, 0), Height = Math.Max (value.Height, 0) });
// If Frame gets set, by definition, the View is now LayoutStyle.Absolute, so
// set all Pos/Dim to Absolute values.
@@ -73,62 +79,83 @@ public Rectangle Frame
_height = _frame.Height;
// TODO: Figure out if the below can be optimized.
- if (IsInitialized /*|| LayoutStyle == LayoutStyle.Absolute*/)
+ if (IsInitialized)
{
- LayoutAdornments ();
- SetTextFormatterSize ();
- SetNeedsLayout ();
- SetNeedsDisplay ();
+ OnResizeNeeded ();
}
}
}
+ private void SetFrame (Rectangle frame)
+ {
+ Rectangle oldViewport = Rectangle.Empty;
+ if (IsInitialized)
+ {
+ oldViewport = Viewport;
+ }
+ // This is the only place where _frame should be set directly. Use Frame = or SetFrame instead.
+ _frame = frame;
+
+ OnViewportChanged (new (IsInitialized ? Viewport : Rectangle.Empty, oldViewport));
+ }
+
/// Gets the with a screen-relative location.
/// The location and size of the view in screen-relative coordinates.
public virtual Rectangle FrameToScreen ()
{
- Rectangle ret = Frame;
- View super = SuperView;
+ Rectangle screen = Frame;
+ View current = SuperView;
- while (super is { })
+ while (current is { })
{
- if (super is Adornment adornment)
+ if (current is Adornment adornment)
{
// Adornments don't have SuperViews; use Adornment.FrameToScreen override
- ret = adornment.FrameToScreen ();
- ret.Offset (Frame.X, Frame.Y);
+ // which will give us the screen coordinates of the parent
+
+ var parentScreen = adornment.FrameToScreen ();
+
+ // Now add our Frame location
+ parentScreen.Offset (screen.X, screen.Y);
- return ret;
+ return parentScreen;
}
- Point boundsOffset = super.GetBoundsOffset ();
- boundsOffset.Offset(super.Frame.X, super.Frame.Y);
- ret.X += boundsOffset.X;
- ret.Y += boundsOffset.Y;
- super = super.SuperView;
+ Point viewportOffset = current.GetViewportOffsetFromFrame ();
+ viewportOffset.Offset (current.Frame.X - current.Viewport.X, current.Frame.Y - current.Viewport.Y);
+ screen.X += viewportOffset.X;
+ screen.Y += viewportOffset.Y;
+ current = current.SuperView;
}
- return ret;
+ return screen;
}
///
/// Converts a screen-relative coordinate to a Frame-relative coordinate. Frame-relative means relative to the
- /// View's 's .
+ /// View's 's .
///
- /// The coordinate relative to the 's .
+ /// The coordinate relative to the 's .
/// Screen-relative column.
/// Screen-relative row.
public virtual Point ScreenToFrame (int x, int y)
{
- Point superViewBoundsOffset = SuperView?.GetBoundsOffset () ?? Point.Empty;
if (SuperView is null)
{
- superViewBoundsOffset.Offset (x - Frame.X, y - Frame.Y);
- return superViewBoundsOffset;
+ return new Point (x - Frame.X, y - Frame.Y);
}
- var frame = SuperView.ScreenToFrame (x - superViewBoundsOffset.X, y - superViewBoundsOffset.Y);
- frame.Offset (-Frame.X, -Frame.Y);
+ Point superViewViewportOffset = SuperView.GetViewportOffsetFromFrame ();
+ superViewViewportOffset.X -= SuperView.Viewport.X;
+ superViewViewportOffset.Y -= SuperView.Viewport.Y;
+
+ x -= superViewViewportOffset.X;
+ y -= superViewViewportOffset.Y;
+
+ Point frame = SuperView.ScreenToFrame (x, y);
+ frame.X -= Frame.X;
+ frame.Y -= Frame.Y;
+
return frame;
}
@@ -138,13 +165,16 @@ public virtual Point ScreenToFrame (int x, int y)
/// The object representing the X position.
///
///
+ /// The position is relative to the 's Content, which is bound by .
+ ///
+ ///
/// If set to a relative value (e.g. ) the value is indeterminate until the view has been
- /// initialized ( is true) and has been
+ /// initialized ( is true) and has been
/// called.
///
///
/// Changing this property will eventually (when the view is next drawn) cause the
- /// and methods to be called.
+ /// and methods to be called.
///
///
/// Changing this property will cause to be updated. If the new value is not of type
@@ -157,7 +187,13 @@ public Pos X
get => VerifyIsInitialized (_x, nameof (X));
set
{
+ if (Equals (_x, value))
+ {
+ return;
+ }
+
_x = value ?? throw new ArgumentNullException (nameof (value), @$"{nameof (X)} cannot be null");
+
OnResizeNeeded ();
}
}
@@ -168,13 +204,16 @@ public Pos X
/// The object representing the Y position.
///
///
+ /// The position is relative to the 's Content, which is bound by .
+ ///
+ ///
/// If set to a relative value (e.g. ) the value is indeterminate until the view has been
- /// initialized ( is true) and has been
+ /// initialized ( is true) and has been
/// called.
///
///
/// Changing this property will eventually (when the view is next drawn) cause the
- /// and methods to be called.
+ /// and methods to be called.
///
///
/// Changing this property will cause to be updated. If the new value is not of type
@@ -187,6 +226,11 @@ public Pos Y
get => VerifyIsInitialized (_y, nameof (Y));
set
{
+ if (Equals (_y, value))
+ {
+ return;
+ }
+
_y = value ?? throw new ArgumentNullException (nameof (value), @$"{nameof (Y)} cannot be null");
OnResizeNeeded ();
}
@@ -198,13 +242,16 @@ public Pos Y
/// The object representing the height of the view (the number of rows).
///
///
+ /// The dimension is relative to the 's Content, which is bound by .
+ ///
+ ///
/// If set to a relative value (e.g. ) the value is indeterminate until the view has
- /// been initialized ( is true) and has been
+ /// been initialized ( is true) and has been
/// called.
///
///
/// Changing this property will eventually (when the view is next drawn) cause the
- /// and methods to be called.
+ /// and methods to be called.
///
///
/// Changing this property will cause to be updated. If the new value is not of type
@@ -217,6 +264,11 @@ public Dim Height
get => VerifyIsInitialized (_height, nameof (Height));
set
{
+ if (Equals (_height, value))
+ {
+ return;
+ }
+
_height = value ?? throw new ArgumentNullException (nameof (value), @$"{nameof (Height)} cannot be null");
if (AutoSize)
@@ -247,13 +299,16 @@ public Dim Height
/// The object representing the width of the view (the number of columns).
///
///
+ /// The dimension is relative to the 's Content, which is bound by .
+ ///
+ ///
/// If set to a relative value (e.g. ) the value is indeterminate until the view has
- /// been initialized ( is true) and has been
+ /// been initialized ( is true) and has been
/// called.
///
///
/// Changing this property will eventually (when the view is next drawn) cause the
- /// and methods to be called.
+ /// and methods to be called.
///
///
/// Changing this property will cause to be updated. If the new value is not of type
@@ -266,6 +321,11 @@ public Dim Width
get => VerifyIsInitialized (_width, nameof (Width));
set
{
+ if (Equals (_width, value))
+ {
+ return;
+ }
+
_width = value ?? throw new ArgumentNullException (nameof (value), @$"{nameof (Width)} cannot be null");
if (AutoSize)
@@ -288,123 +348,13 @@ public Dim Width
#endregion Frame
- #region Bounds
-
- ///
- /// The bounds represent the View-relative rectangle used for this view; the area inside the view where
- /// subviews and content are presented.
- ///
- /// The rectangle describing the location and size of the area where the views' subviews and content are drawn.
- ///
- ///
- /// If is the value of Bounds is indeterminate until
- /// the view has been initialized ( is true) and has been
- /// called.
- ///
- ///
- /// Updates to the Bounds updates , and has the same effect as updating the
- /// .
- ///
- ///
- /// Altering the Bounds will eventually (when the view is next laid out) cause the
- /// and methods to be called.
- ///
- ///
- /// Because coordinates are relative to the upper-left corner of the , the
- /// coordinates of the upper-left corner of the rectangle returned by this property are (0,0). Use this property to
- /// obtain the size of the area of the view for tasks such as drawing the view's contents.
- ///
- ///
- public virtual Rectangle Bounds
- {
- get
- {
-#if DEBUG
- if (LayoutStyle == LayoutStyle.Computed && !IsInitialized)
- {
- Debug.WriteLine (
- $"WARNING: Bounds 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 Rectangle.Empty with { Size = Frame.Size };
- }
-
- Thickness totalThickness = GetAdornmentsThickness ();
-
- return Rectangle.Empty with
- {
- Size = new (
- Math.Max (0, Frame.Size.Width - totalThickness.Horizontal),
- Math.Max (0, Frame.Size.Height - totalThickness.Vertical))
- };
- }
- set
- {
- // TODO: Should we enforce Bounds.X/Y == 0? The code currently ignores value.X/Y which is
- // TODO: correct behavior, but is silent. Perhaps an exception?
-#if DEBUG
- if (value.Location != Point.Empty)
- {
- Debug.WriteLine (
- $"WARNING: Bounds.Location must always be 0,0. Location ({value.Location}) is ignored. {this}"
- );
- }
-#endif // DEBUG
- 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 BoundsToScreen (in Rectangle bounds)
- {
- // Translate bounds to Frame (our SuperView's Bounds-relative coordinates)
- Rectangle screen = FrameToScreen ();
- Point boundsOffset = GetBoundsOffset ();
- screen.Offset (boundsOffset.X + bounds.X, boundsOffset.Y + bounds.Y);
-
- return new (screen.Location, bounds.Size);
- }
-
- /// Converts a screen-relative coordinate to a bounds-relative coordinate.
- /// The coordinate relative to this view's .
- /// Screen-relative column.
- /// Screen-relative row.
- public Point ScreenToBounds (int x, int y)
- {
- Point boundsOffset = GetBoundsOffset ();
- Point screen = ScreenToFrame (x, y);
- screen.Offset (-boundsOffset.X, -boundsOffset.Y);
-
- return screen;
- }
-
- ///
- /// Helper to get the X and Y offset of the Bounds from the Frame. This is the sum of the Left and Top properties
- /// of , and .
- ///
- public Point GetBoundsOffset () { return Padding is null ? Point.Empty : Padding.Thickness.GetInside (Padding.Frame).Location; }
-
- #endregion Bounds
-
#region AutoSize
private bool _autoSize;
///
/// Gets or sets a flag that determines whether the View will be automatically resized to fit the
- /// within .
+ /// within .
///
/// The default is . Set to to turn on AutoSize. If
/// then and will be used if can
@@ -462,7 +412,7 @@ private bool ResizeView (bool autoSize)
if (ValidatePosDim)
{
// BUGBUG: This ain't right, obviously. We need to figure out how to handle this.
- boundsChanged = ResizeBoundsToFit (newFrameSize);
+ boundsChanged = ResizeViewportToFit (newFrameSize);
}
else
{
@@ -577,35 +527,35 @@ internal bool TrySetWidth (int desiredWidth, out int resultWidth)
/// Resizes the View to fit the specified size. Factors in the HotKey.
/// ResizeBoundsToFit can only be called when AutoSize is true (or being set to true).
///
- /// whether the Bounds was changed or not
- private bool ResizeBoundsToFit (Size size)
+ /// whether the Viewport was changed or not
+ private bool ResizeViewportToFit (Size size)
{
//if (AutoSize == false) {
- // throw new InvalidOperationException ("ResizeBoundsToFit can only be called when AutoSize is true");
+ // throw new InvalidOperationException ("ResizeViewportToFit can only be called when AutoSize is true");
//}
- var boundsChanged = false;
+ var changed = false;
bool canSizeW = TrySetWidth (size.Width - GetHotKeySpecifierLength (), out int rW);
bool canSizeH = TrySetHeight (size.Height - GetHotKeySpecifierLength (false), out int rH);
if (canSizeW)
{
- boundsChanged = true;
+ changed = true;
_width = rW;
}
if (canSizeH)
{
- boundsChanged = true;
+ changed = true;
_height = rH;
}
- if (boundsChanged)
+ if (changed)
{
- Bounds = new (Bounds.X, Bounds.Y, canSizeW ? rW : Bounds.Width, canSizeH ? rH : Bounds.Height);
+ Viewport = new (Viewport.X, Viewport.Y, canSizeW ? rW : Viewport.Width, canSizeH ? rH : Viewport.Height);
}
- return boundsChanged;
+ return changed;
}
#endregion AutoSize
@@ -651,25 +601,20 @@ public LayoutStyle LayoutStyle
#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.
@@ -682,57 +627,66 @@ public virtual bool Contains (int x, int y)
// 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, int x, int y)
{
- if (start is null || !start.Visible || !start.Contains (x, y))
+ while (start is { Visible: true } && start.Contains (x, y))
{
- return null;
- }
-
- Adornment? found = null;
+ Adornment? found = null;
- if (start.Margin.Contains (x, y))
- {
- found = start.Margin;
- }
- else if (start.Border.Contains (x, y))
- {
- found = start.Border;
- }
- else if (start.Padding.Contains (x, y))
- {
- found = start.Padding;
- }
+ if (start.Margin.Contains (x, y))
+ {
+ found = start.Margin;
+ }
+ else if (start.Border.Contains (x, y))
+ {
+ found = start.Border;
+ }
+ else if (start.Padding.Contains (x, y))
+ {
+ found = start.Padding;
+ }
- Point boundsOffset = start.GetBoundsOffset ();
+ Point viewportOffset = start.GetViewportOffsetFromFrame ();
- if (found is { })
- {
- start = found;
- boundsOffset = found.Parent.Frame.Location;
- }
+ if (found is { })
+ {
+ start = found;
+ viewportOffset = found.Parent.Frame.Location;
+ }
- if (start.InternalSubviews is { Count: > 0 })
- {
- int startOffsetX = x - (start.Frame.X + boundsOffset.X);
- int startOffsetY = y - (start.Frame.Y + boundsOffset.Y);
+ int startOffsetX = x - (start.Frame.X + viewportOffset.X);
+ int startOffsetY = y - (start.Frame.Y + viewportOffset.Y);
+ View? subview = null;
for (int i = start.InternalSubviews.Count - 1; i >= 0; i--)
{
- View nextStart = start.InternalSubviews [i];
-
- if (nextStart.Visible && nextStart.Contains (startOffsetX, startOffsetY))
+ if (start.InternalSubviews [i].Visible
+ && start.InternalSubviews [i].Contains (startOffsetX + start.Viewport.X, startOffsetY + start.Viewport.Y))
{
- // TODO: Remove recursion
- return FindDeepestView (nextStart, startOffsetX, startOffsetY) ?? nextStart;
+ subview = start.InternalSubviews [i];
+ x = startOffsetX + start.Viewport.X;
+ 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 start;
+ return null;
}
+
#nullable restore
///
- /// Gets a new location of the that is within the Bounds of the 's
+ /// Gets a new location of the that is within the Viewport of the 's
/// (e.g. for dragging a Window). The `out` parameters are the new X and Y coordinates.
///
///
@@ -761,6 +715,7 @@ out StatusBar statusBar
{
int maxDimension;
View superView;
+ statusBar = null;
if (viewToMove?.SuperView is null || viewToMove == Application.Top || viewToMove?.SuperView == Application.Top)
{
@@ -769,8 +724,8 @@ out StatusBar statusBar
}
else
{
- // Use the SuperView's Bounds, not Frame
- maxDimension = viewToMove.SuperView.Bounds.Width;
+ // Use the SuperView's Viewport, not Frame
+ maxDimension = viewToMove.SuperView.Viewport.Width;
superView = viewToMove.SuperView;
}
@@ -795,7 +750,8 @@ out StatusBar statusBar
}
//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)
{
@@ -805,12 +761,15 @@ out StatusBar statusBar
{
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)
@@ -833,13 +792,16 @@ out StatusBar statusBar
{
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)
@@ -848,7 +810,7 @@ out StatusBar statusBar
}
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)
@@ -916,8 +878,7 @@ public virtual void LayoutSubviews ()
LayoutAdornments ();
- Rectangle oldBounds = Bounds;
- OnLayoutStarted (new () { OldBounds = oldBounds });
+ OnLayoutStarted (new (ContentSize));
SetTextFormatterSize ();
@@ -929,7 +890,7 @@ public virtual void LayoutSubviews ()
foreach (View v in ordered)
{
- LayoutSubview (v, new (GetBoundsOffset (), Bounds.Size));
+ LayoutSubview (v, ContentSize);
}
// If the 'to' is rooted to 'from' and the layoutstyle is Computed it's a special-case.
@@ -938,13 +899,19 @@ public virtual void LayoutSubviews ()
{
foreach ((View from, View to) in edges)
{
- LayoutSubview (to, from.Frame);
+ LayoutSubview (to, from.ContentSize);
}
}
LayoutNeeded = false;
- OnLayoutComplete (new () { OldBounds = oldBounds });
+ OnLayoutComplete (new (ContentSize));
+ }
+ 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.
@@ -969,19 +936,19 @@ public virtual void LayoutSubviews ()
///
///
/// Determines the relative bounds of the and its s, and then calls
- /// to update the view.
+ /// to update the view.
///
///
internal void OnResizeNeeded ()
{
// TODO: Identify a real-world use-case where this API should be virtual.
// TODO: Until then leave it `internal` and non-virtual
- // First try SuperView.Bounds, then Application.Top, then Driver.Bounds.
+ // First try SuperView.Viewport, then Application.Top, then Driver.Viewport.
// Finally, if none of those are valid, use int.MaxValue (for Unit tests).
- Rectangle relativeBounds = SuperView is { IsInitialized: true } ? SuperView.Bounds :
- Application.Top is { } && Application.Top != this && Application.Top.IsInitialized ? Application.Top.Bounds :
- Application.Driver?.Bounds ?? new Rectangle (0, 0, int.MaxValue, int.MaxValue);
- SetRelativeLayout (relativeBounds);
+ Size contentSize = SuperView is { IsInitialized: true } ? SuperView.ContentSize :
+ Application.Top is { } && Application.Top != this && Application.Top.IsInitialized ? Application.Top.ContentSize :
+ Application.Driver?.Screen.Size ?? new (int.MaxValue, int.MaxValue);
+ SetRelativeLayout (contentSize);
// TODO: Determine what, if any of the below is actually needed here.
if (IsInitialized)
@@ -997,6 +964,7 @@ internal void OnResizeNeeded ()
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.
@@ -1021,15 +989,22 @@ internal void SetNeedsLayout ()
}
///
- /// Applies the view's position (, ) and dimension (, and
- /// ) to , given a rectangle describing the SuperView's Bounds (nominally the
- /// same as this.SuperView.Bounds).
+ /// Adjusts given the SuperView's ContentSize (nominally the same as
+ /// this.SuperView.ContentSize)
+ /// and the position (, ) and dimension (, and
+ /// ).
///
- ///
- /// The rectangle describing the SuperView's Bounds (nominally the same as
- /// this.SuperView.Bounds).
+ ///
+ ///
+ /// 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).
///
- internal void SetRelativeLayout (Rectangle superviewBounds)
+ internal void SetRelativeLayout (Size superviewContentSize)
{
Debug.Assert (_x is { });
Debug.Assert (_y is { });
@@ -1051,13 +1026,13 @@ internal void SetRelativeLayout (Rectangle superviewBounds)
// TODO: View.AutoSize stuff is removed.
// Returns the new dimension (width or height) and location (x or y) for the View given
- // the superview's Bounds
+ // the superview's Viewport
// the current Pos (View.X or View.Y)
// the current Dim (View.Width or View.Height)
// This method is called recursively if pos is Pos.PosCombine
(int newLocation, int newDimension) GetNewLocationAndDimension (
bool width,
- Rectangle superviewBounds,
+ Size superviewContentSize,
Pos pos,
Dim dim,
int autosizeDimension
@@ -1119,7 +1094,7 @@ int GetNewDimension (Dim d, int location, int dimension, int autosize)
}
int newDimension, newLocation;
- int superviewDimension = width ? superviewBounds.Width : superviewBounds.Height;
+ int superviewDimension = width ? superviewContentSize.Width : superviewContentSize.Height;
// Determine new location
switch (pos)
@@ -1143,7 +1118,7 @@ int GetNewDimension (Dim d, int location, int dimension, int autosize)
(left, newDimension) = GetNewLocationAndDimension (
width,
- superviewBounds,
+ superviewContentSize,
combine._left,
dim,
autosizeDimension
@@ -1151,7 +1126,7 @@ int GetNewDimension (Dim d, int location, int dimension, int autosize)
(right, newDimension) = GetNewLocationAndDimension (
width,
- superviewBounds,
+ superviewContentSize,
combine._right,
dim,
autosizeDimension
@@ -1193,10 +1168,10 @@ int GetNewDimension (Dim d, int location, int dimension, int autosize)
}
// horizontal/width
- (newX, newW) = GetNewLocationAndDimension (true, superviewBounds, _x, _width, autosize.Width);
+ (newX, newW) = GetNewLocationAndDimension (true, superviewContentSize, _x, _width, autosize.Width);
// vertical/height
- (newY, newH) = GetNewLocationAndDimension (false, superviewBounds, _y, _height, autosize.Height);
+ (newY, newH) = GetNewLocationAndDimension (false, superviewContentSize, _y, _height, autosize.Height);
Rectangle r = new (newX, newY, newW, newH);
@@ -1204,7 +1179,7 @@ int GetNewDimension (Dim d, int location, int dimension, int autosize)
{
// Set the frame. Do NOT use `Frame` as it overwrites X, Y, Width, and Height, making
// the view LayoutStyle.Absolute.
- _frame = r;
+ SetFrame (r);
if (_x is Pos.PosAbsolute)
{
@@ -1236,7 +1211,7 @@ int GetNewDimension (Dim d, int location, int dimension, int autosize)
{
// Set the frame. Do NOT use `Frame` as it overwrites X, Y, Width, and Height, making
// the view LayoutStyle.Absolute.
- _frame = _frame with { Size = autosize };
+ SetFrame (_frame with { Size = autosize });
if (autosize.Width == 0)
{
@@ -1417,17 +1392,6 @@ internal static List TopologicalSort (
return result;
} // TopologicalSort
- private void LayoutSubview (View v, Rectangle contentArea)
- {
- //if (v.LayoutStyle == LayoutStyle.Computed) {
- v.SetRelativeLayout (contentArea);
-
- //}
-
- v.LayoutSubviews ();
- v.LayoutNeeded = false;
- }
-
#region Diagnostics
// Diagnostics to highlight when Width or Height is read before the view has been initialized
@@ -1468,4 +1432,4 @@ private Pos VerifyIsInitialized (Pos pos, string member)
public bool ValidatePosDim { get; set; }
#endregion
-}
+}
\ No newline at end of file
diff --git a/Terminal.Gui/View/View.cs b/Terminal.Gui/View/View.cs
index 07235e638b..8a04e51702 100644
--- a/Terminal.Gui/View/View.cs
+++ b/Terminal.Gui/View/View.cs
@@ -52,7 +52,7 @@ namespace Terminal.Gui;
/// To create a View using Absolute layout, call a constructor that takes a Rect parameter to specify the
/// absolute position and size or simply set ). To create a View using Computed layout use
/// a constructor that does not take a Rect parameter and set the X, Y, Width and Height properties on the view to
-/// non-absolute values. Both approaches use coordinates that are relative to the of the
+/// non-absolute values. Both approaches use coordinates that are relative to the of the
/// the View is added to.
///
///
@@ -73,7 +73,7 @@ namespace Terminal.Gui;
/// a View can be accessed with the property.
///
///
-/// To flag a region of the View's to be redrawn call
+/// To flag a region of the View's to be redrawn call
/// .
/// To flag the entire view for redraw call .
///
@@ -234,7 +234,7 @@ public virtual void EndInit ()
// TODO: Move these into ViewText.cs as EndInit_Text() to consolodate.
// TODO: Verify UpdateTextDirection really needs to be called here.
- // These calls were moved from BeginInit as they access Bounds which is indeterminate until EndInit is called.
+ // These calls were moved from BeginInit as they access Viewport which is indeterminate until EndInit is called.
UpdateTextDirection (TextDirection);
UpdateTextFormatterText ();
OnResizeNeeded ();
diff --git a/Terminal.Gui/View/ViewAdornments.cs b/Terminal.Gui/View/ViewAdornments.cs
index 55445cef82..4efd92b526 100644
--- a/Terminal.Gui/View/ViewAdornments.cs
+++ b/Terminal.Gui/View/ViewAdornments.cs
@@ -39,7 +39,7 @@ private void DisposeAdornments ()
///
/// The that enables separation of a View from other SubViews of the same
- /// SuperView. The margin offsets the from the .
+ /// SuperView. The margin offsets the from the .
///
///
///
@@ -55,7 +55,7 @@ private void DisposeAdornments ()
public Margin Margin { get; private set; }
///
- /// The that offsets the from the .
+ /// The that offsets the from the .
/// The Border provides the space for a visual border (drawn using
/// line-drawing glyphs) and the Title. The Border expands inward; in other words if `Border.Thickness.Top == 2` the
/// border and title will take up the first row and the second row will be filled with spaces.
@@ -116,7 +116,7 @@ public LineStyle BorderStyle
}
///
- /// The inside of the view that offsets the
+ /// The inside of the view that offsets the
/// from the .
///
///
@@ -151,7 +151,7 @@ internal virtual void LayoutAdornments ()
if (Margin.Frame.Size != Frame.Size)
{
- Margin._frame = Rectangle.Empty with { Size = Frame.Size };
+ Margin.SetFrame (Rectangle.Empty with { Size = Frame.Size });
Margin.X = 0;
Margin.Y = 0;
Margin.Width = Frame.Size.Width;
@@ -170,7 +170,7 @@ internal virtual void LayoutAdornments ()
if (border != Border.Frame)
{
- Border._frame = border;
+ Border.SetFrame (border);
Border.X = border.Location.X;
Border.Y = border.Location.Y;
Border.Width = border.Size.Width;
@@ -189,7 +189,7 @@ internal virtual void LayoutAdornments ()
if (padding != Padding.Frame)
{
- Padding._frame = padding;
+ Padding.SetFrame (padding);
Padding.X = padding.Location.X;
Padding.Y = padding.Location.Y;
Padding.Width = padding.Size.Width;
diff --git a/Terminal.Gui/View/ViewContent.cs b/Terminal.Gui/View/ViewContent.cs
new file mode 100644
index 0000000000..90fa3ab4a5
--- /dev/null
+++ b/Terminal.Gui/View/ViewContent.cs
@@ -0,0 +1,483 @@
+using System.Diagnostics;
+
+namespace Terminal.Gui;
+
+///
+/// Settings for how the behaves relative to the View's Content area.
+///
+[Flags]
+public enum ViewportSettings
+{
+ ///
+ /// No settings.
+ ///
+ None = 0,
+
+ ///
+ /// If set, .X can be set to negative values enabling scrolling beyond the left of
+ /// the
+ /// content area.
+ ///
+ ///
+ ///
+ /// When not set, .X is constrained to positive values.
+ ///
+ ///
+ AllowNegativeX = 1,
+
+ ///
+ /// If set, .Y can be set to negative values enabling scrolling beyond the top of the
+ /// content area.
+ ///
+ ///
+ ///
+ /// When not set, .Y is constrained to positive values.
+ ///
+ ///
+ AllowNegativeY = 2,
+
+ ///
+ /// If set, .Size can be set to negative coordinates enabling scrolling beyond the
+ /// top-left of the
+ /// content area.
+ ///
+ ///
+ ///
+ /// When not set, .Size is constrained to positive coordinates.
+ ///
+ ///
+ AllowNegativeLocation = AllowNegativeX | AllowNegativeY,
+
+ ///
+ /// If set, .X can be set values greater than
+ /// .Width enabling scrolling beyond the right
+ /// of the content area.
+ ///
+ ///
+ ///
+ /// When not set, .X is constrained to
+ /// .Width - 1.
+ /// This means the last column of the content will remain visible even if there is an attempt to scroll the
+ /// Viewport past the last column.
+ ///
+ ///
+ /// The practical effect of this is that the last column of the content will always be visible.
+ ///
+ ///
+ AllowXGreaterThanContentWidth = 4,
+
+ ///
+ /// If set, .Y can be set values greater than
+ /// .Height enabling scrolling beyond the right
+ /// of the content area.
+ ///
+ ///
+ ///
+ /// When not set, .Y is constrained to
+ /// .Height - 1.
+ /// This means the last row of the content will remain visible even if there is an attempt to scroll the Viewport
+ /// past the last row.
+ ///
+ ///
+ /// The practical effect of this is that the last row of the content will always be visible.
+ ///
+ ///
+ AllowYGreaterThanContentHeight = 8,
+
+ ///
+ /// If set, .Size can be set values greater than
+ /// enabling scrolling beyond the bottom-right
+ /// of the content area.
+ ///
+ ///
+ ///
+ /// When not set, is constrained to -1.
+ /// This means the last column and row of the content will remain visible even if there is an attempt to
+ /// scroll the Viewport past the last column or row.
+ ///
+ ///
+ AllowLocationGreaterThanContentSize = AllowXGreaterThanContentWidth | AllowYGreaterThanContentHeight,
+
+ ///
+ /// By default, clipping is applied to the . Setting this flag will cause clipping to be
+ /// applied to the visible content area.
+ ///
+ ClipContentOnly = 16,
+
+ ///
+ /// If set will clear only the portion of the content
+ /// area that is visible within the . This is useful for views that have a
+ /// content area larger than the Viewport and want the area outside the content to be visually distinct.
+ ///
+ ///
+ /// must be set for this setting to work (clipping beyond the visible area must be
+ /// disabled).
+ ///
+ ClearContentOnly = 32
+}
+
+public partial class View
+{
+ #region Content Area
+
+ private Size _contentSize;
+
+ ///
+ /// Gets or sets the size of the View's content. If not set, the value will be the same as the size of ,
+ /// 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.
+ ///
+ ///
+ /// Negative sizes are not supported.
+ ///
+ ///
+ public Size ContentSize
+ {
+ get => _contentSize == Size.Empty ? Viewport.Size : _contentSize;
+ set
+ {
+ if (value.Width < 0 || value.Height < 0)
+ {
+ throw new ArgumentException (@"ContentSize cannot be negative.", nameof (value));
+ }
+
+ if (value == _contentSize)
+ {
+ return;
+ }
+
+ _contentSize = value;
+ OnContentSizeChanged (new (_contentSize));
+ }
+ }
+
+ ///
+ /// Called when changes. Invokes the event.
+ ///
+ ///
+ ///
+ protected bool? OnContentSizeChanged (SizeChangedEventArgs e)
+ {
+ ContentSizeChanged?.Invoke (this, e);
+
+ if (e.Cancel != true)
+ {
+ SetNeedsLayout ();
+ SetNeedsDisplay ();
+ }
+
+ return e.Cancel;
+ }
+
+ ///
+ /// Event raised when the changes.
+ ///
+ public event EventHandler ContentSizeChanged;
+
+ ///
+ /// Converts a Content-relative location to a Screen-relative location.
+ ///
+ /// The Content-relative location.
+ /// The Screen-relative location.
+ public Point ContentToScreen (in Point location)
+ {
+ // Subtract the ViewportOffsetFromFrame to get the Viewport-relative location.
+ Point viewportOffset = GetViewportOffsetFromFrame ();
+ Point contentRelativeToViewport = location;
+ contentRelativeToViewport.Offset (-Viewport.X, -Viewport.Y);
+
+ // Translate to Screen-Relative (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, which is
+ /// always at 0, 0.
+ ///
+ /// The Screen-relative location.
+ /// The coordinate relative to this view's Content.
+ public Point ScreenToContent (in Point location)
+ {
+ Point viewportOffset = GetViewportOffsetFromFrame ();
+ Point screen = ScreenToFrame (location.X, location.Y);
+ screen.Offset (Viewport.X - viewportOffset.X, Viewport.Y - viewportOffset.Y);
+
+ return screen;
+ }
+
+ #endregion Content Area
+
+ #region Viewport
+
+ private ViewportSettings _viewportSettings;
+
+ ///
+ /// Gets or sets how scrolling the on the View's Content Area is handled.
+ ///
+ public ViewportSettings ViewportSettings
+ {
+ get => _viewportSettings;
+ set
+ {
+ if (_viewportSettings == value)
+ {
+ return;
+ }
+
+ _viewportSettings = value;
+
+ if (IsInitialized)
+ {
+ // Force set Viewport to cause settings to be applied as needed
+ SetViewport (Viewport);
+ }
+ }
+ }
+
+ ///
+ /// The location of the viewport into the view's content (0,0) is the top-left corner of the content. The Content
+ /// area's size
+ /// is .
+ ///
+ private Point _viewportLocation;
+
+ ///
+ /// 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 .
+ /// If the viewport Size is the same as the Location will be 0, 0.
+ ///
+ ///
+ /// The rectangle describing the location and size of the viewport into the View's virtual content, described by
+ /// .
+ ///
+ ///
+ ///
+ /// Positive values for the location indicate the visible area is offset into (down-and-right) the View's virtual
+ /// . This enables scrolling down and to the right (e.g. in a .
+ ///
+ ///
+ /// Negative values for the location indicate the visible area is offset above (up-and-left) the View's virtual
+ /// . This enables scrolling up and to the left (e.g. in an image viewer that supports zoom
+ /// where the image stays centered).
+ ///
+ ///
+ /// The property controls how scrolling is handled.
+ ///
+ ///
+ /// 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 (_viewportLocation, Frame.Size);
+ }
+
+ Thickness thickness = GetAdornmentsThickness ();
+
+ return new (
+ _viewportLocation,
+ new (
+ Math.Max (0, Frame.Size.Width - thickness.Horizontal),
+ Math.Max (0, Frame.Size.Height - thickness.Vertical)
+ ));
+ }
+ set => SetViewport (value);
+ }
+
+ private void SetViewport (Rectangle viewport)
+ {
+ Rectangle oldViewport = viewport;
+ ApplySettings (ref viewport);
+
+ Thickness thickness = GetAdornmentsThickness ();
+
+ Size newSize = new (
+ viewport.Size.Width + thickness.Horizontal,
+ viewport.Size.Height + thickness.Vertical);
+
+ if (newSize == Frame.Size)
+ {
+ // The change is not changing the Frame, so we don't need to update it.
+ // Just call SetNeedsLayout to update the layout.
+ if (_viewportLocation != viewport.Location)
+ {
+ _viewportLocation = viewport.Location;
+ SetNeedsLayout ();
+ }
+
+ OnViewportChanged (new (IsInitialized ? Viewport : Rectangle.Empty, oldViewport));
+
+ return;
+ }
+
+ _viewportLocation = viewport.Location;
+
+ // Update the Frame because we made it bigger or smaller which impacts subviews.
+ Frame = Frame with
+ {
+ Size = newSize
+ };
+
+ void ApplySettings (ref Rectangle newViewport)
+ {
+ if (!ViewportSettings.HasFlag (ViewportSettings.AllowXGreaterThanContentWidth))
+ {
+ if (newViewport.X >= ContentSize.Width)
+ {
+ newViewport.X = ContentSize.Width - 1;
+ }
+ }
+
+ // IMPORTANT: Check for negative location AFTER checking for location greater than content width
+ if (!ViewportSettings.HasFlag (ViewportSettings.AllowNegativeX))
+ {
+ if (newViewport.X < 0)
+ {
+ newViewport.X = 0;
+ }
+ }
+
+ if (!ViewportSettings.HasFlag (ViewportSettings.AllowYGreaterThanContentHeight))
+ {
+ if (newViewport.Y >= ContentSize.Height)
+ {
+ newViewport.Y = ContentSize.Height - 1;
+ }
+ }
+
+ // IMPORTANT: Check for negative location AFTER checking for location greater than content width
+ if (!ViewportSettings.HasFlag (ViewportSettings.AllowNegativeY))
+ {
+ if (newViewport.Y < 0)
+ {
+ newViewport.Y = 0;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Fired when the changes. This event is fired after the has been updated.
+ ///
+ [CanBeNull]
+ public event EventHandler ViewportChanged;
+
+ ///
+ /// Called when the changes. Invokes the event.
+ ///
+ ///
+ protected virtual void OnViewportChanged (DrawEventArgs e) { ViewportChanged?.Invoke (this, e); }
+
+ ///
+ /// 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 = GetViewportOffsetFromFrame ();
+ 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 = GetViewportOffsetFromFrame ();
+ 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 GetViewportOffsetFromFrame () { 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;
+ }
+
+ 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;
+ }
+
+ Viewport = Viewport with { X = Viewport.X + cols };
+
+ return true;
+ }
+
+ #endregion Viewport
+}
diff --git a/Terminal.Gui/View/ViewDrawing.cs b/Terminal.Gui/View/ViewDrawing.cs
index 37094bbaea..cc7109a5bb 100644
--- a/Terminal.Gui/View/ViewDrawing.cs
+++ b/Terminal.Gui/View/ViewDrawing.cs
@@ -1,4 +1,6 @@
-namespace Terminal.Gui;
+using System.Drawing;
+
+namespace Terminal.Gui;
public partial class View
{
@@ -54,67 +56,108 @@ public bool NeedsDisplay
public bool SubViewNeedsDisplay { get; private set; }
///
- /// Gets or sets whether this View will use it's SuperView's for rendering any border
- /// lines. If the rendering of any borders drawn by this Frame will be done by it's parent's
+ /// 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
/// called to render the borders.
///
public virtual bool SuperViewRendersLineCanvas { get; set; } = false;
- /// Displays the specified character in the specified column and row of the View.
- /// Column (view-relative).
- /// Row (view-relative).
- /// Ch.
- public void AddRune (int col, int row, Rune ch)
+ /// Draws the specified character in the specified viewport-relative column and row of 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.
+ ///
+ /// Column (viewport-relative).
+ /// Row (viewport-relative).
+ /// The Rune.
+ public void AddRune (int col, int row, Rune rune)
{
- if (row < 0 || col < 0)
+ if (Move (col, row))
{
- return;
+ Driver.AddRune (rune);
}
+ }
- if (row > _frame.Height - 1 || col > _frame.Width - 1)
+ /// Clears with the normal background.
+ ///
+ ///
+ /// If has only
+ /// the portion of the content
+ /// area that is visible within the will be cleared. This is useful for views that have a
+ /// content area larger than the Viewport (e.g. when is
+ /// enabled) and want
+ /// the area outside the content to be visually distinct.
+ ///
+ ///
+ public void Clear ()
+ {
+ if (Driver is null)
{
return;
}
- Move (col, row);
- Driver.AddRune (ch);
- }
+ // Get screen-relative coords
+ Rectangle toClear = ViewportToScreen (Viewport with { Location = new (0, 0) });
+
+ Rectangle prevClip = Driver.Clip;
+
+ if (ViewportSettings.HasFlag (ViewportSettings.ClearContentOnly))
+ {
+ Rectangle visibleContent = ViewportToScreen (new (new (-Viewport.X, -Viewport.Y), ContentSize));
+ toClear = Rectangle.Intersect (toClear, visibleContent);
+ }
- /// Clears with the normal background.
- ///
- public void Clear () { Clear (Bounds); }
+ Attribute prev = Driver.SetAttribute (GetNormalColor());
+ Driver.FillRect (toClear);
+ Driver.SetAttribute (prev);
+
+ Driver.Clip = prevClip;
+ }
- /// Clears the specified -relative rectangle with the normal background.
- ///
- /// The Bounds-relative rectangle to clear.
- public void Clear (Rectangle contentArea)
+ /// Fills the specified -relative rectangle with the specified color.
+ /// The Viewport-relative rectangle to clear.
+ /// The color to use to fill the rectangle. If not provided, the Normal background color will be used.
+ public void FillRect (Rectangle rect, Color? color = null)
{
if (Driver is null)
{
return;
}
- Attribute prev = Driver.SetAttribute (GetNormalColor ());
+ // Get screen-relative coords
+ Rectangle toClear = ViewportToScreen (rect);
+
+ Rectangle prevClip = Driver.Clip;
+
+ Driver.Clip = Rectangle.Intersect (prevClip, ViewportToScreen (Viewport with { Location = new (0, 0) }));
- // Clamp the region to the bounds of the view
- contentArea = Rectangle.Intersect (contentArea, Bounds);
- Driver.FillRect (BoundsToScreen (contentArea));
+ Attribute prev = Driver.SetAttribute (new (color ?? GetNormalColor().Background));
+ Driver.FillRect (toClear);
Driver.SetAttribute (prev);
+
+ Driver.Clip = prevClip;
}
- /// Expands the 's clip region to include .
+ /// Sets the 's clip region to .
+ ///
+ ///
+ /// By default, the clip rectangle is set to the intersection of the current clip region and the
+ /// . This ensures that drawing is constrained to the viewport, but allows
+ /// content to be drawn beyond the viewport.
+ ///
+ ///
+ /// If has set, clipping will be
+ /// applied to just the visible content area.
+ ///
+ ///
///
/// The current screen-relative clip region, which can be then re-applied by setting
/// .
///
- ///
- ///
- /// If and do not intersect, the clip region will be set to
- /// .
- ///
- ///
- public Rectangle ClipToBounds ()
+ public Rectangle SetClip ()
{
if (Driver is null)
{
@@ -122,7 +165,18 @@ public Rectangle ClipToBounds ()
}
Rectangle previous = Driver.Clip;
- Driver.Clip = Rectangle.Intersect (previous, BoundsToScreen (Bounds));
+
+ // Clamp the Clip to the entire visible area
+ Rectangle clip = Rectangle.Intersect (ViewportToScreen (Viewport with { Location = Point.Empty }), previous);
+
+ if (ViewportSettings.HasFlag (ViewportSettings.ClipContentOnly))
+ {
+ // Clamp the Clip to the just content area that is within the viewport
+ Rectangle visibleContent = ViewportToScreen (new (new (-Viewport.X, -Viewport.Y), ContentSize));
+ clip = Rectangle.Intersect (clip, visibleContent);
+ }
+
+ Driver.Clip = clip;
return previous;
}
@@ -133,7 +187,7 @@ public Rectangle ClipToBounds ()
///
///
///
- /// Always use (view-relative) when calling , NOT
+ /// Always use (view-relative) when calling , NOT
/// (superview-relative).
///
///
@@ -142,7 +196,8 @@ public Rectangle ClipToBounds ()
///
///
/// Overrides of must ensure they do not set Driver.Clip to a clip
- /// region larger than the property, as this will cause the driver to clip the entire region.
+ /// region larger than the property, as this will cause the driver to clip the entire
+ /// region.
///
///
public void Draw ()
@@ -154,21 +209,24 @@ public void Draw ()
OnDrawAdornments ();
- Rectangle prevClip = ClipToBounds ();
-
if (ColorScheme is { })
{
//Driver.SetAttribute (HasFocus ? GetFocusColor () : GetNormalColor ());
Driver?.SetAttribute (GetNormalColor ());
}
+ // 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
+ // so via settings. SetClip honors the ViewportSettings.DisableVisibleContentClipping flag.
+ Rectangle prevClip = SetClip ();
+
// Invoke DrawContentEvent
- var dev = new DrawEventArgs (Bounds);
+ var dev = new DrawEventArgs (Viewport, Rectangle.Empty);
DrawContent?.Invoke (this, dev);
if (!dev.Cancel)
{
- OnDrawContent (Bounds);
+ OnDrawContent (Viewport);
}
if (Driver is { })
@@ -179,7 +237,7 @@ public void Draw ()
OnRenderLineCanvas ();
// Invoke DrawContentCompleteEvent
- OnDrawContentComplete (Bounds);
+ OnDrawContentComplete (Viewport);
// BUGBUG: v2 - We should be able to use View.SetClip here and not have to resort to knowing Driver details.
ClearLayoutNeeded ();
@@ -264,14 +322,13 @@ public void DrawHotString (string text, bool focused, ColorScheme scheme)
/// Determines the current based on the value.
///
- /// if is or
- /// if is . If it's
+ /// if is or
+ /// if is . If it's
/// overridden can return other values.
///
public virtual Attribute GetFocusColor ()
{
ColorScheme cs = ColorScheme;
-
if (ColorScheme is null)
{
cs = new ();
@@ -316,19 +373,33 @@ public virtual Attribute GetNormalColor ()
return Enabled ? cs.Normal : cs.Disabled;
}
- /// This moves the cursor to the specified column and row in the view.
- /// The move.
- /// The column to move to, in view-relative coordinates.
- /// the row to move to, in view-relative coordinates.
- public void Move (int col, int row)
+ /// Moves the drawing cursor to the specified -relative location 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.
+ ///
+ ///
+ /// Column (viewport-relative).
+ /// Row (viewport-relative).
+ public bool Move (int col, int row)
{
if (Driver is null || Driver?.Rows == 0)
{
- return;
+ return false;
}
- Rectangle screen = BoundsToScreen (new (col, row, 0, 0));
+ if (col < 0 || row < 0 || col >= Viewport.Width || row >= Viewport.Height)
+ {
+ return false;
+ }
+
+ Rectangle screen = ViewportToScreen (new (col, row, 0, 0));
Driver?.Move (screen.X, screen.Y);
+
+ return true;
}
// TODO: Make this cancelable
@@ -347,26 +418,51 @@ public virtual bool OnDrawAdornments ()
// Each of these renders lines to either this View's LineCanvas
// Those lines will be finally rendered in OnRenderLineCanvas
- Margin?.OnDrawContent (Margin.Bounds);
- Border?.OnDrawContent (Border.Bounds);
- Padding?.OnDrawContent (Padding.Bounds);
+ Margin?.OnDrawContent (Margin.Viewport);
+ Border?.OnDrawContent (Border.Viewport);
+ Padding?.OnDrawContent (Padding.Viewport);
return true;
}
- /// Enables overrides to draw infinitely scrolled content and/or a background behind added controls.
- ///
- /// The view-relative rectangle describing the currently visible viewport into the
- ///
+ ///
+ /// Draws the view's content, including Subviews.
+ ///
+ ///
+ ///
+ /// The parameter is provided as a convenience; it has the same values as the
+ /// property.
+ ///
+ ///
+ /// The Location and Size indicate what part of the View's content, defined
+ /// by , is visible and should be drawn. The coordinates taken by and
+ /// are relative to , thus if ViewPort.Location.Y is 5
+ /// the 6th row of the content should be drawn using MoveTo (x, 5).
+ ///
+ ///
+ /// If is larger than ViewPort.Size drawing code should use
+ /// to constrain drawing for better performance.
+ ///
+ ///
+ /// The may define smaller area than ; complex drawing code
+ /// can be more
+ /// efficient by using to constrain drawing for better performance.
+ ///
+ ///
+ /// Overrides should loop through the subviews and call .
+ ///
+ ///
+ ///
+ /// The rectangle describing the currently visible viewport into the ; has the same value as
+ /// .
///
- /// This method will be called before any subviews added with have been drawn.
- public virtual void OnDrawContent (Rectangle contentArea)
+ public virtual void OnDrawContent (Rectangle viewport)
{
if (NeedsDisplay)
{
if (SuperView is { })
{
- Clear (contentArea);
+ Clear ();
}
if (!string.IsNullOrEmpty (TextFormatter.Text))
@@ -378,8 +474,11 @@ public virtual void OnDrawContent (Rectangle contentArea)
}
// This should NOT clear
+ // TODO: If the output is not in the Viewport, do nothing
+ var drawRect = new Rectangle (ContentToScreen (Point.Empty), ContentSize);
+
TextFormatter?.Draw (
- BoundsToScreen (contentArea),
+ drawRect,
HasFocus ? GetFocusColor () : GetNormalColor (),
HasFocus ? ColorScheme.HotFocus : GetHotNormalColor (),
Rectangle.Empty
@@ -387,6 +486,7 @@ public virtual void OnDrawContent (Rectangle contentArea)
SetSubViewNeedsDisplay ();
}
+ // TODO: Move drawing of subviews to a separate OnDrawSubviews virtual method
// Draw subviews
// TODO: Implement OnDrawSubviews (cancelable);
if (_subviews is { } && SubViewNeedsDisplay)
@@ -395,39 +495,25 @@ public virtual void OnDrawContent (Rectangle contentArea)
view => view.Visible
&& (view.NeedsDisplay || view.SubViewNeedsDisplay || view.LayoutNeeded)
);
-
foreach (View view in subviewsNeedingDraw)
{
- //view.Frame.IntersectsWith (bounds)) {
- // && (view.Frame.IntersectsWith (bounds) || bounds.X < 0 || bounds.Y < 0)) {
if (view.LayoutNeeded)
{
view.LayoutSubviews ();
}
-
- // Draw the subview
- // Use the view's bounds (view-relative; Location will always be (0,0)
- //if (view.Visible && view.Frame.Width > 0 && view.Frame.Height > 0) {
view.Draw ();
-
- //}
}
}
}
///
- /// Enables overrides after completed drawing infinitely scrolled content and/or a background behind removed
- /// controls.
+ /// Called after to enable overrides.
///
- ///
- /// The view-relative rectangle describing the currently visible viewport into the
+ ///
+ /// The viewport-relative rectangle describing the currently visible viewport into the
///
///
- ///
- /// This method will be called after any subviews removed with have been completed
- /// drawing.
- ///
- public virtual void OnDrawContentComplete (Rectangle contentArea) { DrawContentComplete?.Invoke (this, new (contentArea)); }
+ public virtual void OnDrawContentComplete (Rectangle viewport) { DrawContentComplete?.Invoke (this, new (viewport, Rectangle.Empty)); }
// TODO: Make this cancelable
///
@@ -438,13 +524,13 @@ public virtual void OnDrawContent (Rectangle contentArea)
///
public virtual bool OnRenderLineCanvas ()
{
- if (!IsInitialized)
+ if (!IsInitialized || Driver is null)
{
return false;
}
// If we have a SuperView, it'll render our frames.
- if (!SuperViewRendersLineCanvas && LineCanvas.Bounds != Rectangle.Empty)
+ if (!SuperViewRendersLineCanvas && LineCanvas.Viewport != Rectangle.Empty)
{
foreach (KeyValuePair p in LineCanvas.GetCellMap ())
{
@@ -484,7 +570,7 @@ public virtual bool OnRenderLineCanvas ()
return true;
}
- /// Sets the area of this view needing to be redrawn to .
+ /// Sets the area of this view needing to be redrawn to .
///
/// If the view has not been initialized ( is ), this method
/// does nothing.
@@ -493,21 +579,28 @@ public void SetNeedsDisplay ()
{
if (IsInitialized)
{
- SetNeedsDisplay (Bounds);
+ SetNeedsDisplay (Viewport);
}
}
/// Expands the area of this view needing to be redrawn to include .
///
- /// If the view has not been initialized ( is ), the area to be
- /// redrawn will be the .
+ ///
+ /// The location of is relative to the View's content, bound by Size.Empty and
+ /// .
+ ///
+ ///
+ /// If the view has not been initialized ( is ), the area to be
+ /// redrawn will be the .
+ ///
///
- /// The Bounds-relative region that needs to be redrawn.
+ /// The content-relative region that needs to be redrawn.
public void SetNeedsDisplay (Rectangle region)
{
if (!IsInitialized)
{
_needsDisplayRect = region;
+
return;
}
@@ -572,25 +665,7 @@ protected void ClearNeedsDisplay ()
foreach (View subview in Subviews)
{
- subview.ClearNeedsDisplay();
+ subview.ClearNeedsDisplay ();
}
}
-
- // INTENT: Isn't this just intersection? It isn't used anyway.
- // Clips a rectangle in screen coordinates to the dimensions currently available on the screen
- internal Rectangle ScreenClip (Rectangle regionScreen)
- {
- int x = regionScreen.X < 0 ? 0 : regionScreen.X;
- int y = regionScreen.Y < 0 ? 0 : regionScreen.Y;
-
- int w = regionScreen.X + regionScreen.Width >= Driver.Cols
- ? Driver.Cols - regionScreen.X
- : regionScreen.Width;
-
- int h = regionScreen.Y + regionScreen.Height >= Driver.Rows
- ? Driver.Rows - regionScreen.Y
- : regionScreen.Height;
-
- return new (x, y, w, h);
- }
}
diff --git a/Terminal.Gui/View/ViewEventArgs.cs b/Terminal.Gui/View/ViewEventArgs.cs
index 3179f94ec0..a53575f2e7 100644
--- a/Terminal.Gui/View/ViewEventArgs.cs
+++ b/Terminal.Gui/View/ViewEventArgs.cs
@@ -3,8 +3,8 @@
/// Args for events that relate to specific
public class ViewEventArgs : EventArgs
{
- /// Creates a new instance of the class.
- ///
+ /// Creates a new instance of the class.
+ /// The view that the event is about.
public ViewEventArgs (View view) { View = view; }
/// The view that the event is about.
@@ -18,25 +18,40 @@ public class ViewEventArgs : EventArgs
/// Event arguments for the event.
public class LayoutEventArgs : EventArgs
{
- /// The view-relative bounds of the before it was laid out.
- public Rectangle OldBounds { get; set; }
+ /// Creates a new instance of the class.
+ /// The view that the event is about.
+ public LayoutEventArgs (Size oldContentSize) { OldContentSize = oldContentSize; }
+
+ /// The viewport of the before it was laid out.
+ public Size OldContentSize { get; set; }
}
/// Event args for draw events
public class DrawEventArgs : EventArgs
{
/// Creates a new instance of the class.
- ///
- /// Gets the view-relative rectangle describing the currently visible viewport into the
+ ///
+ /// The Content-relative rectangle describing the new visible viewport into the
+ /// .
+ ///
+ ///
+ /// The Content-relative rectangle describing the old visible viewport into the
/// .
///
- public DrawEventArgs (Rectangle rect) { Rectangle = rect; }
+ public DrawEventArgs (Rectangle newViewport, Rectangle oldViewport)
+ {
+ NewViewport = newViewport;
+ OldViewport = oldViewport;
+ }
/// If set to true, the draw operation will be canceled, if applicable.
public bool Cancel { get; set; }
- /// Gets the view-relative rectangle describing the currently visible viewport into the .
- public Rectangle Rectangle { get; }
+ /// Gets the Content-relative rectangle describing the old visible viewport into the .
+ public Rectangle OldViewport { get; }
+
+ /// Gets the Content-relative rectangle describing the currently visible viewport into the .
+ public Rectangle NewViewport { get; }
}
/// Defines the event arguments for
diff --git a/Terminal.Gui/View/ViewMouse.cs b/Terminal.Gui/View/ViewMouse.cs
index fa94be54f7..00684a3cf9 100644
--- a/Terminal.Gui/View/ViewMouse.cs
+++ b/Terminal.Gui/View/ViewMouse.cs
@@ -21,12 +21,12 @@ public enum HighlightStyle
#endif
///
- /// The mouse is pressed within the .
+ /// The mouse is pressed within the .
///
Pressed = 2,
///
- /// The mouse is pressed but moved outside the .
+ /// The mouse is pressed but moved outside the .
///
PressedOutside = 4
}
@@ -36,6 +36,10 @@ public enum HighlightStyle
///
public class HighlightEventArgs : CancelEventArgs
{
+ ///
+ /// Constructs a new instance of .
+ ///
+ ///
public HighlightEventArgs (HighlightStyle style)
{
HighlightStyle = style;
@@ -63,7 +67,7 @@ public partial class View
public virtual bool WantMousePositionReports { get; set; }
///
- /// Called by when the mouse enters . The view will
+ /// Called by when the mouse enters . The view will
/// then receive mouse events until is called indicating the mouse has left
/// the view.
///
@@ -110,7 +114,7 @@ public partial class View
}
///
- /// Called by when the mouse enters . The view will
+ /// Called by when the mouse enters . The view will
/// then receive mouse events until is called indicating the mouse has left
/// the view.
///
@@ -119,7 +123,7 @@ public partial class View
/// Override this method or subscribe to to change the default enter behavior.
///
///
- /// The coordinates are relative to .
+ /// The coordinates are relative to .
///
///
///
@@ -133,12 +137,12 @@ public partial class View
return args.Handled;
}
- /// Event fired when the mouse moves into the View's .
+ /// Event fired when the mouse moves into the View's .
public event EventHandler MouseEnter;
///
- /// Called by when the mouse leaves . The view will
+ /// Called by when the mouse leaves . The view will
/// then no longer receive mouse events.
///
///
@@ -180,15 +184,15 @@ public partial class View
return false;
}
///
- /// Called by when a mouse leaves . The view will
+ /// Called by when a mouse leaves . The view will
/// no longer receive mouse events.
///
///
///
- /// Override this method or subscribe to to change the default leave behavior.
+ /// Override this method or subscribe to to change the default leave behavior.
///
///
- /// The coordinates are relative to .
+ /// The coordinates are relative to .
///
///
///
@@ -211,7 +215,7 @@ protected internal virtual bool OnMouseLeave (MouseEvent mouseEvent)
return args.Handled;
}
- /// Event fired when the mouse leaves the View's .
+ /// Event fired when the mouse leaves the View's .
public event EventHandler MouseLeave;
///
@@ -227,7 +231,7 @@ protected internal virtual bool OnMouseLeave (MouseEvent mouseEvent)
/// mouse buttons was clicked, it calls to process the click.
///
///
- /// See and for more information.
+ /// See for more information.
///
///
/// If is , the event
@@ -307,7 +311,7 @@ protected internal virtual bool OnMouseLeave (MouseEvent mouseEvent)
///
///
///
- /// , if the event was handled, otherwise.
+ /// , if the event was handled, otherwise.
private bool HandlePressed (MouseEvent mouseEvent)
{
if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed)
@@ -325,9 +329,11 @@ private bool HandlePressed (MouseEvent mouseEvent)
// Set the focus, but don't invoke Accept
SetFocus ();
}
+
+ mouseEvent.Handled = true;
}
- if (Bounds.Contains (mouseEvent.X, mouseEvent.Y))
+ if (Viewport.Contains (mouseEvent.X, mouseEvent.Y))
{
if (SetHighlight (HighlightStyle.HasFlag (HighlightStyle.Pressed) ? HighlightStyle.Pressed : HighlightStyle.None) == true)
{
@@ -408,7 +414,7 @@ internal bool HandleClicked (MouseEvent mouseEvent)
}
// If mouse is still in bounds, click
- if (!WantContinuousButtonPressed && Bounds.Contains (mouseEvent.X, mouseEvent.Y))
+ if (!WantContinuousButtonPressed && Viewport.Contains (mouseEvent.X, mouseEvent.Y))
{
return OnMouseClick (new (mouseEvent));
}
@@ -489,9 +495,9 @@ internal bool SetHighlight (HighlightStyle style)
};
ColorScheme = cs;
}
-
- return true;
}
+ // Return false since we don't want to eat the event
+ return false;
}
@@ -526,10 +532,10 @@ internal bool SetHighlight (HighlightStyle style)
return args.Cancel;
}
- /// Called when a mouse event occurs within the view's .
+ /// Called when a mouse event occurs within the view's .
///
///
- /// The coordinates are relative to .
+ /// The coordinates are relative to .
///
///
///
@@ -546,7 +552,7 @@ protected internal virtual bool OnMouseEvent (MouseEvent mouseEvent)
/// Event fired when a mouse event occurs.
///
///
- /// The coordinates are relative to .
+ /// The coordinates are relative to .
///
///
public event EventHandler MouseEvent;
@@ -564,9 +570,7 @@ protected bool OnMouseClick (MouseEventEventArgs args)
if (!Enabled)
{
// QUESTION: Is this right? Should a disabled view eat mouse clicks?
- args.Handled = true;
-
- return true;
+ return args.Handled = true;
}
MouseClick?.Invoke (this, args);
@@ -592,7 +596,7 @@ protected bool OnMouseClick (MouseEventEventArgs args)
/// to see which button was clicked.
///
///
- /// The coordinates are relative to .
+ /// The coordinates are relative to .
///
///
public event EventHandler MouseClick;
diff --git a/Terminal.Gui/View/ViewSubViews.cs b/Terminal.Gui/View/ViewSubViews.cs
index 27570da2ca..9df4fee4d7 100644
--- a/Terminal.Gui/View/ViewSubViews.cs
+++ b/Terminal.Gui/View/ViewSubViews.cs
@@ -234,7 +234,15 @@ public virtual void Remove (View view)
}
}
- /// Removes all subviews (children) added via or from this View.
+ ///
+ /// Removes all subviews (children) added via or from this View.
+ ///
+ ///
+ ///
+ /// Normally Subviews will be disposed when this View is disposed. Removing a Subview causes ownership of the Subview's
+ /// lifecycle to be transferred to the caller; the caller must call on any Views that were added.
+ ///
+ ///
public virtual void RemoveAll ()
{
if (_subviews is null)
@@ -485,7 +493,7 @@ public virtual bool OnEnter (View view)
{
return true;
}
-
+
return false;
}
@@ -855,16 +863,13 @@ public virtual void PositionCursor ()
return;
}
- // BUGBUG: v2 - This needs to support children of Frames too
+ // BUGBUG: v2 - This needs to support Subviews of Adornments too
if (Focused is null && SuperView is { })
{
SuperView.EnsureFocus ();
}
- else if (Focused?.Visible == true
- && Focused?.Enabled == true
- && Focused?.Frame.Width > 0
- && Focused.Frame.Height > 0)
+ else if (Focused is { Visible: true, Enabled: true, Frame: { Width: > 0, Height: > 0 } })
{
Focused.PositionCursor ();
}
diff --git a/Terminal.Gui/View/ViewText.cs b/Terminal.Gui/View/ViewText.cs
index 598e2c76be..96c33e56e6 100644
--- a/Terminal.Gui/View/ViewText.cs
+++ b/Terminal.Gui/View/ViewText.cs
@@ -30,10 +30,10 @@ public virtual bool PreserveTrailingSpaces
/// and .
///
///
- /// The text will word-wrap to additional lines if it does not fit horizontally. If 's height
+ /// The text will word-wrap to additional lines if it does not fit horizontally. If 's height
/// is 1, the text will be clipped.
///
- /// If is true, the will be adjusted to fit the text.
+ /// If is true, the will be adjusted to fit the text.
/// When the text changes, the is fired.
///
public virtual string Text
@@ -80,7 +80,7 @@ public void OnTextChanged (string oldValue, string newValue)
/// redisplay the .
///
///
- /// If is true, the will be adjusted to fit the text.
+ /// If is true, the will be adjusted to fit the text.
///
/// The text alignment.
public virtual TextAlignment TextAlignment
@@ -99,7 +99,7 @@ public virtual TextAlignment TextAlignment
/// .
///
///
- /// If is true, the will be adjusted to fit the text.
+ /// If is true, the will be adjusted to fit the text.
///
/// The text alignment.
public virtual TextDirection TextDirection
@@ -120,7 +120,7 @@ public virtual TextDirection TextDirection
/// redisplay the .
///
///
- /// If is true, the will be adjusted to fit the text.
+ /// If is true, the will be adjusted to fit the text.
///
/// The text alignment.
public virtual VerticalTextAlignment VerticalTextAlignment
@@ -134,7 +134,7 @@ public virtual VerticalTextAlignment VerticalTextAlignment
}
///
- /// Gets the Frame dimensions required to fit within using the text
+ /// Gets the Frame dimensions required to fit within using the text
/// specified by the property and accounting for any
/// characters.
///
@@ -146,8 +146,8 @@ public Size GetAutoSize ()
if (IsInitialized)
{
- x = Bounds.X;
- y = Bounds.Y;
+ x = Viewport.X;
+ y = Viewport.Y;
}
Rectangle rect = TextFormatter.CalcRect (x, y, TextFormatter.Text, TextFormatter.Direction);
@@ -225,7 +225,7 @@ internal Size GetSizeNeededForTextWithoutHotKey ()
}
///
- /// Internal API. Sets .Size to the current size, adjusted for
+ /// Internal API. Sets .Size to the current size, adjusted for
/// .
///
///
@@ -244,14 +244,14 @@ internal void SetTextFormatterSize ()
if (string.IsNullOrEmpty (TextFormatter.Text))
{
- TextFormatter.Size = Bounds.Size;
+ TextFormatter.Size = ContentSize;
return;
}
TextFormatter.Size = new (
- Bounds.Size.Width + GetHotKeySpecifierLength (),
- Bounds.Size.Height + GetHotKeySpecifierLength (false)
+ ContentSize.Width + GetHotKeySpecifierLength (),
+ ContentSize.Height + GetHotKeySpecifierLength (false)
);
}
@@ -304,12 +304,12 @@ private bool SetFrameToFitText ()
throw new InvalidOperationException ("SetFrameToFitText can only be called when AutoSize is true");
}
- // BUGBUG: This API is broken - should not assume Frame.Height == Bounds.Height
+ // BUGBUG: This API is broken - should not assume Frame.Height == Viewport.Height
//
// Gets the minimum dimensions required to fit the View's , factoring in .
//
// The minimum dimensions required.
- // if the dimensions fit within the View's , otherwise.
+ // if the dimensions fit within the View's , otherwise.
//
// Always returns if is or
// if (Horizontal) or (Vertical) are not not set or zero.
@@ -324,7 +324,7 @@ bool GetMinimumSizeOfText (out Size sizeRequired)
return false;
}
- sizeRequired = Bounds.Size;
+ sizeRequired = ContentSize;
if (AutoSize || string.IsNullOrEmpty (TextFormatter.Text))
{
@@ -336,11 +336,11 @@ bool GetMinimumSizeOfText (out Size sizeRequired)
case true:
int colWidth = TextFormatter.GetWidestLineLength (new List { TextFormatter.Text }, 0, 1);
- // TODO: v2 - This uses frame.Width; it should only use Bounds
+ // TODO: v2 - This uses frame.Width; it should only use Viewport
if (_frame.Width < colWidth
- && (Width is null || (Bounds.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, Bounds.Height);
+ sizeRequired = new (colWidth, ContentSize.Height);
return true;
}
@@ -349,7 +349,7 @@ bool GetMinimumSizeOfText (out Size sizeRequired)
default:
if (_frame.Height < 1 && (Height is null || (Height is Dim.DimAbsolute && Height.Anchor (0) == 0)))
{
- sizeRequired = new (Bounds.Width, 1);
+ sizeRequired = new (ContentSize.Width, 1);
return true;
}
@@ -365,7 +365,7 @@ bool GetMinimumSizeOfText (out Size sizeRequired)
// TODO: This is a hack.
//_width = size.Width;
//_height = size.Height;
- _frame = new (_frame.Location, size);
+ SetFrame (new (_frame.Location, size));
//throw new InvalidOperationException ("This is a hack.");
return true;
@@ -392,7 +392,7 @@ private void UpdateTextDirection (TextDirection newDirection)
}
else if (AutoSize && directionChanged && IsAdded)
{
- ResizeBoundsToFit (Bounds.Size);
+ ResizeViewportToFit (Viewport.Size);
}
SetTextFormatterSize ();
diff --git a/Terminal.Gui/Views/Button.cs b/Terminal.Gui/Views/Button.cs
index a710bc2d92..53916fa178 100644
--- a/Terminal.Gui/Views/Button.cs
+++ b/Terminal.Gui/Views/Button.cs
@@ -33,9 +33,6 @@ public class Button : View
private readonly Rune _rightDefault;
private bool _isDefault;
- ///
- private bool _wantContinuousButtonPressed;
-
/// Initializes a new instance of using layout.
/// The width of the is computed based on the text length. The height will always be 1.
public Button ()
@@ -59,10 +56,10 @@ public Button ()
#endif
// Override default behavior of View
AddCommand (Command.HotKey, () =>
- {
- SetFocus ();
- return !OnAccept ();
- });
+ {
+ SetFocus ();
+ return !OnAccept ();
+ });
KeyBindings.Add (Key.Space, Command.HotKey);
KeyBindings.Add (Key.Enter, Command.HotKey);
@@ -71,6 +68,8 @@ public Button ()
MouseClick += Button_MouseClick;
}
+ private bool _wantContinuousButtonPressed;
+
///
public override bool WantContinuousButtonPressed
{
@@ -97,7 +96,7 @@ public override bool WantContinuousButtonPressed
private void Button_MouseClick (object sender, MouseEventEventArgs e)
{
- e.Handled = InvokeCommand (Command.HotKey) == true;
+ e.Handled = InvokeCommand (Command.HotKey) == true;
}
private void Button_TitleChanged (object sender, StateEventArgs e)
@@ -189,4 +188,4 @@ protected override void UpdateTextFormatterText ()
}
}
}
-}
+}
\ No newline at end of file
diff --git a/Terminal.Gui/Views/ColorPicker.cs b/Terminal.Gui/Views/ColorPicker.cs
index ece096699d..f9f492874a 100644
--- a/Terminal.Gui/Views/ColorPicker.cs
+++ b/Terminal.Gui/Views/ColorPicker.cs
@@ -43,24 +43,11 @@ private void SetInitialProperties ()
Width = _cols * BoxWidth + thickness.Vertical;
Height = _rows * BoxHeight + thickness.Horizontal;
};
-// MouseEvent += ColorPicker_MouseEvent;
+
MouseClick += ColorPicker_MouseClick;
}
// TODO: Decouple Cursor from SelectedColor so that mouse press-and-hold can show the color under the cursor.
- //private void ColorPicker_MouseEvent (object sender, MouseEventEventArgs me)
- //{
- // if (me.MouseEvent.X > Bounds.Width || me.MouseEvent.Y > Bounds.Height)
- // {
- // me.Handled = true;
-
- // return;
- // }
-
- // me.Handled = true;
-
- // return;
- //}
private void ColorPicker_MouseClick (object sender, MouseEventEventArgs me)
{
@@ -181,16 +168,16 @@ public virtual bool MoveDown ()
}
///
- public override void OnDrawContent (Rectangle contentArea)
+ public override void OnDrawContent (Rectangle viewport)
{
- base.OnDrawContent (contentArea);
+ base.OnDrawContent (viewport);
Driver.SetAttribute (HasFocus ? ColorScheme.Focus : GetNormalColor ());
var colorIndex = 0;
- for (var y = 0; y < Bounds.Height / BoxHeight; y++)
+ for (var y = 0; y < Viewport.Height / BoxHeight; y++)
{
- for (var x = 0; x < Bounds.Width / BoxWidth; x++)
+ for (var x = 0; x < Viewport.Width / BoxWidth; x++)
{
int foregroundColorIndex = y == 0 ? colorIndex + _cols : colorIndex - _cols;
Driver.SetAttribute (new Attribute ((ColorName)foregroundColorIndex, (ColorName)colorIndex));
diff --git a/Terminal.Gui/Views/ComboBox.cs b/Terminal.Gui/Views/ComboBox.cs
index b3c719b5ef..5f1d25a9ee 100644
--- a/Terminal.Gui/Views/ComboBox.cs
+++ b/Terminal.Gui/Views/ComboBox.cs
@@ -245,8 +245,8 @@ public virtual bool Expand ()
///
protected internal override bool OnMouseEvent (MouseEvent me)
{
- if (me.X == Bounds.Right - 1
- && me.Y == Bounds.Top
+ if (me.X == Viewport.Right - 1
+ && me.Y == Viewport.Top
&& me.Flags == MouseFlags.Button1Pressed
&& _autoHide)
{
@@ -284,9 +284,9 @@ protected internal override bool OnMouseEvent (MouseEvent me)
public virtual void OnCollapsed () { Collapsed?.Invoke (this, EventArgs.Empty); }
///
- public override void OnDrawContent (Rectangle contentArea)
+ public override void OnDrawContent (Rectangle viewport)
{
- base.OnDrawContent (contentArea);
+ base.OnDrawContent (viewport);
if (!_autoHide)
{
@@ -294,7 +294,7 @@ public override void OnDrawContent (Rectangle contentArea)
}
Driver.SetAttribute (ColorScheme.Focus);
- Move (Bounds.Right - 1, 0);
+ Move (Viewport.Right - 1, 0);
Driver.AddRune (Glyphs.DownArrow);
}
@@ -401,15 +401,15 @@ private bool ActivateSelected ()
///
private int CalculatetHeight ()
{
- if (!IsInitialized || Bounds.Height == 0)
+ if (!IsInitialized || Viewport.Height == 0)
{
return 0;
}
return Math.Min (
- Math.Max (Bounds.Height - 1, _minimumHeight - 1),
+ Math.Max (Viewport.Height - 1, _minimumHeight - 1),
_searchset?.Count > 0 ? _searchset.Count :
- IsShow ? Math.Max (Bounds.Height - 1, _minimumHeight - 1) : 0
+ IsShow ? Math.Max (Viewport.Height - 1, _minimumHeight - 1) : 0
);
}
@@ -491,10 +491,10 @@ private void HideList ()
}
Reset (true);
- _listview.Clear (_listview.IsInitialized ? _listview.Bounds : Rectangle.Empty);
+ _listview.Clear ();
_listview.TabStop = false;
SuperView?.SendSubviewToBack (this);
- Rectangle rect = _listview.BoundsToScreen (_listview.IsInitialized ? _listview.Bounds : Rectangle.Empty);
+ Rectangle rect = _listview.ViewportToScreen (_listview.IsInitialized ? _listview.Viewport : Rectangle.Empty);
SuperView?.SetNeedsDisplay (rect);
OnCollapsed ();
}
@@ -607,18 +607,18 @@ private bool PageUp ()
private void ProcessLayout ()
{
- if (Bounds.Height < _minimumHeight && (Height is null || Height is Dim.DimAbsolute))
+ if (Viewport.Height < _minimumHeight && (Height is null || Height is Dim.DimAbsolute))
{
Height = _minimumHeight;
}
- if ((!_autoHide && Bounds.Width > 0 && _search.Frame.Width != Bounds.Width)
- || (_autoHide && Bounds.Width > 0 && _search.Frame.Width != Bounds.Width - 1))
+ if ((!_autoHide && Viewport.Width > 0 && _search.Frame.Width != Viewport.Width)
+ || (_autoHide && Viewport.Width > 0 && _search.Frame.Width != Viewport.Width - 1))
{
- _search.Width = _listview.Width = _autoHide ? Bounds.Width - 1 : Bounds.Width;
+ _search.Width = _listview.Width = _autoHide ? Viewport.Width - 1 : Viewport.Width;
_listview.Height = CalculatetHeight ();
- _search.SetRelativeLayout (Bounds);
- _listview.SetRelativeLayout (Bounds);
+ _search.SetRelativeLayout (ContentSize);
+ _listview.SetRelativeLayout (ContentSize);
}
}
@@ -761,7 +761,7 @@ private void SetValue (object text, bool isFromSelectedItem = false)
private void ShowList ()
{
_listview.SetSource (_searchset);
- _listview.Clear (Bounds); // Ensure list shrinks in Dialog as you type
+ _listview.Clear ();
_listview.Height = CalculatetHeight ();
SuperView?.BringSubviewToFront (this);
}
@@ -839,7 +839,7 @@ protected internal override bool OnMouseEvent (MouseEvent me)
return res;
}
- public override void OnDrawContent (Rectangle contentArea)
+ public override void OnDrawContent (Rectangle viewport)
{
Attribute current = ColorScheme.Focus;
Driver.SetAttribute (current);
diff --git a/Terminal.Gui/Views/Dialog.cs b/Terminal.Gui/Views/Dialog.cs
index b22480ccc5..46787265e2 100644
--- a/Terminal.Gui/Views/Dialog.cs
+++ b/Terminal.Gui/Views/Dialog.cs
@@ -56,6 +56,7 @@ public enum ButtonAlignments
///
public Dialog ()
{
+ Arrangement = ViewArrangement.Movable;
X = Pos.Center ();
Y = Pos.Center ();
ValidatePosDim = true;
@@ -201,7 +202,7 @@ private void LayoutButtons ()
{
case ButtonAlignments.Center:
// Center Buttons
- shiftLeft = (Bounds.Width - buttonsWidth - _buttons.Count - 1) / 2 + 1;
+ shiftLeft = (Viewport.Width - buttonsWidth - _buttons.Count - 1) / 2 + 1;
for (int i = _buttons.Count - 1; i >= 0; i--)
{
@@ -214,7 +215,7 @@ private void LayoutButtons ()
}
else
{
- button.X = Bounds.Width - shiftLeft;
+ button.X = Viewport.Width - shiftLeft;
}
button.Y = Pos.AnchorEnd (1);
@@ -226,7 +227,7 @@ private void LayoutButtons ()
// Justify Buttons
// leftmost and rightmost buttons are hard against edges. The rest are evenly spaced.
- var spacing = (int)Math.Ceiling ((double)(Bounds.Width - buttonsWidth) / (_buttons.Count - 1));
+ var spacing = (int)Math.Ceiling ((double)(Viewport.Width - buttonsWidth) / (_buttons.Count - 1));
for (int i = _buttons.Count - 1; i >= 0; i--)
{
@@ -242,7 +243,7 @@ private void LayoutButtons ()
if (i == 0)
{
// first (leftmost) button
- int left = Bounds.Width;
+ int left = Viewport.Width;
button.X = Pos.AnchorEnd (left);
}
else
diff --git a/Terminal.Gui/Views/FileDialog.cs b/Terminal.Gui/Views/FileDialog.cs
index ec29e69ba7..0e4f95c2c6 100644
--- a/Terminal.Gui/Views/FileDialog.cs
+++ b/Terminal.Gui/Views/FileDialog.cs
@@ -410,23 +410,23 @@ public bool IsCompatibleWithAllowedExtensions (IFileInfo file)
}
///
- public override void OnDrawContent (Rectangle contentArea)
+ public override void OnDrawContent (Rectangle viewport)
{
- base.OnDrawContent (contentArea);
+ base.OnDrawContent (viewport);
if (!string.IsNullOrWhiteSpace (_feedback))
{
int feedbackWidth = _feedback.EnumerateRunes ().Sum (c => c.GetColumns ());
- int feedbackPadLeft = (Bounds.Width - feedbackWidth) / 2 - 1;
+ int feedbackPadLeft = (Viewport.Width - feedbackWidth) / 2 - 1;
- feedbackPadLeft = Math.Min (Bounds.Width, feedbackPadLeft);
+ feedbackPadLeft = Math.Min (Viewport.Width, feedbackPadLeft);
feedbackPadLeft = Math.Max (0, feedbackPadLeft);
- int feedbackPadRight = Bounds.Width - (feedbackPadLeft + feedbackWidth + 2);
- feedbackPadRight = Math.Min (Bounds.Width, feedbackPadRight);
+ int feedbackPadRight = Viewport.Width - (feedbackPadLeft + feedbackWidth + 2);
+ feedbackPadRight = Math.Min (Viewport.Width, feedbackPadRight);
feedbackPadRight = Math.Max (0, feedbackPadRight);
- Move (0, Bounds.Height / 2);
+ Move (0, Viewport.Height / 2);
Driver.SetAttribute (new Attribute (Color.Red, ColorScheme.Normal.Background));
Driver.AddStr (new string (' ', feedbackPadLeft));
@@ -519,7 +519,7 @@ public override void OnLoaded ()
_allowedTypeMenuBar.DrawContentComplete += (s, e) =>
{
- _allowedTypeMenuBar.Move (e.Rectangle.Width - 1, 0);
+ _allowedTypeMenuBar.Move (e.NewViewport.Width - 1, 0);
Driver.AddRune (Glyphs.DownArrow);
};
@@ -776,12 +776,12 @@ private int CalculateOkButtonPosX ()
return 0;
}
- return Bounds.Width
- - _btnOk.Bounds.Width
- - _btnCancel.Bounds.Width
+ return Viewport.Width
+ - _btnOk.Viewport.Width
+ - _btnCancel.Viewport.Width
- 1
- // TODO: Fiddle factor, seems the Bounds are wrong for someone
+ // TODO: Fiddle factor, seems the Viewport are wrong for someone
- 2;
}
diff --git a/Terminal.Gui/Views/GraphView/Annotations.cs b/Terminal.Gui/Views/GraphView/Annotations.cs
index 1a77a49496..30247d4a69 100644
--- a/Terminal.Gui/Views/GraphView/Annotations.cs
+++ b/Terminal.Gui/Views/GraphView/Annotations.cs
@@ -18,7 +18,7 @@ public interface IAnnotation
///
/// Called once after series have been rendered (or before if is true). Use
- /// to draw and to avoid drawing outside of graph
+ /// to draw and to avoid drawing outside of graph
///
///
void Render (GraphView graph);
@@ -62,7 +62,7 @@ public void Render (GraphView graph)
///
/// Draws the at the given coordinates with truncation to avoid spilling over
- /// of the
+ /// of the
///
///
/// Screen x position to start drawing string
@@ -70,7 +70,7 @@ public void Render (GraphView graph)
protected void DrawText (GraphView graph, int x, int y)
{
// the draw point is out of control bounds
- if (!graph.Bounds.Contains (new Point (x, y)))
+ if (!graph.Viewport.Contains (new Point (x, y)))
{
return;
}
@@ -83,7 +83,7 @@ protected void DrawText (GraphView graph, int x, int y)
graph.Move (x, y);
- int availableWidth = graph.Bounds.Width - x;
+ int availableWidth = graph.Viewport.Width - x;
if (availableWidth <= 0)
{
@@ -127,7 +127,7 @@ public LegendAnnotation (Rectangle legendBounds)
/// Returns false i.e. Legends render after series
public bool BeforeSeries => false;
- /// Draws the Legend and all entries into the area within
+ /// Draws the Legend and all entries into the area within
///
public void Render (GraphView graph)
{
@@ -165,13 +165,13 @@ public void Render (GraphView graph)
// add the text
Move (1, linesDrawn);
- string str = TextFormatter.ClipOrPad (entry.Item2, Bounds.Width - 1);
+ string str = TextFormatter.ClipOrPad (entry.Item2, Viewport.Width - 1);
Application.Driver.AddStr (str);
linesDrawn++;
// Legend has run out of space
- if (linesDrawn >= Bounds.Height)
+ if (linesDrawn >= Viewport.Height)
{
break;
}
@@ -182,7 +182,7 @@ public void Render (GraphView graph)
/// The symbol appearing on the graph that should appear in the legend
///
/// Text to render on this line of the legend. Will be truncated if outside of Legend
- ///
+ ///
///
public void AddEntry (GraphCellToRender graphCellToRender, string text) { _entries.Add (Tuple.Create (graphCellToRender, text)); }
}
diff --git a/Terminal.Gui/Views/GraphView/Axis.cs b/Terminal.Gui/Views/GraphView/Axis.cs
index 8d0389b371..7e7561b76d 100644
--- a/Terminal.Gui/Views/GraphView/Axis.cs
+++ b/Terminal.Gui/Views/GraphView/Axis.cs
@@ -113,7 +113,7 @@ public override void DrawAxisLabel (GraphView graph, int screenPosition, string
string toRender = text;
// this is how much space is left
- int xSpaceAvailable = graph.Bounds.Width - drawAtX;
+ int xSpaceAvailable = graph.Viewport.Width - drawAtX;
// There is no space for the label at all!
if (xSpaceAvailable <= 0)
@@ -127,7 +127,7 @@ public override void DrawAxisLabel (GraphView graph, int screenPosition, string
toRender = toRender.Substring (0, xSpaceAvailable);
}
- graph.Move (drawAtX, Math.Min (y + 1, graph.Bounds.Height - 1));
+ graph.Move (drawAtX, Math.Min (y + 1, graph.Viewport.Height - 1));
driver.AddStr (toRender);
}
}
@@ -140,9 +140,9 @@ public override void DrawAxisLabels (GraphView graph)
return;
}
- Rectangle bounds = graph.Bounds;
+ Rectangle viewport = graph.Viewport;
- IEnumerable labels = GetLabels (graph, bounds);
+ IEnumerable labels = GetLabels (graph, viewport);
foreach (AxisIncrementToRender label in labels)
{
@@ -155,12 +155,12 @@ public override void DrawAxisLabels (GraphView graph)
string toRender = Text;
// if label is too long
- if (toRender.Length > graph.Bounds.Width)
+ if (toRender.Length > graph.Viewport.Width)
{
- toRender = toRender.Substring (0, graph.Bounds.Width);
+ toRender = toRender.Substring (0, graph.Viewport.Width);
}
- graph.Move (graph.Bounds.Width / 2 - toRender.Length / 2, graph.Bounds.Height - 1);
+ graph.Move (graph.Viewport.Width / 2 - toRender.Length / 2, graph.Viewport.Height - 1);
Application.Driver.AddStr (toRender);
}
}
@@ -174,7 +174,7 @@ public override void DrawAxisLine (GraphView graph)
return;
}
- Rectangle bounds = graph.Bounds;
+ Rectangle bounds = graph.Viewport;
graph.Move (0, 0);
@@ -212,7 +212,7 @@ public int GetAxisYPosition (GraphView graph)
// float the X axis so that it accurately represents the origin of the graph
// but anchor it to top/bottom if the origin is offscreen
- return Math.Min (Math.Max (0, origin.Y), graph.Bounds.Height - ((int)graph.MarginBottom + 1));
+ return Math.Min (Math.Max (0, origin.Y), graph.Viewport.Height - ((int)graph.MarginBottom + 1));
}
/// Draws a horizontal axis line at the given , screen coordinates
@@ -225,7 +225,7 @@ protected override void DrawAxisLine (GraphView graph, int x, int y)
Application.Driver.AddRune (Glyphs.HLine);
}
- private IEnumerable GetLabels (GraphView graph, Rectangle bounds)
+ private IEnumerable GetLabels (GraphView graph, Rectangle viewport)
{
// if no labels
if (Increment == 0)
@@ -237,7 +237,7 @@ private IEnumerable GetLabels (GraphView graph, Rectangle
int y = GetAxisYPosition (graph);
RectangleF start = graph.ScreenToGraphSpace ((int)graph.MarginLeft, y);
- RectangleF end = graph.ScreenToGraphSpace (bounds.Width, y);
+ RectangleF end = graph.ScreenToGraphSpace (viewport.Width, y);
// don't draw labels below the minimum
if (Minimum.HasValue)
@@ -266,7 +266,7 @@ private IEnumerable GetLabels (GraphView graph, Rectangle
;
}
- // Label or no label definetly render it
+ // Label or no label definitely render it
yield return toRender;
current.X += Increment;
@@ -317,7 +317,7 @@ public override void DrawAxisLabels (GraphView graph)
return;
}
- Rectangle bounds = graph.Bounds;
+ Rectangle bounds = graph.Viewport;
IEnumerable labels = GetLabels (graph, bounds);
foreach (AxisIncrementToRender label in labels)
@@ -331,13 +331,13 @@ public override void DrawAxisLabels (GraphView graph)
string toRender = Text;
// if label is too long
- if (toRender.Length > graph.Bounds.Height)
+ if (toRender.Length > graph.Viewport.Height)
{
- toRender = toRender.Substring (0, graph.Bounds.Height);
+ toRender = toRender.Substring (0, graph.Viewport.Height);
}
// Draw it 1 letter at a time vertically down row 0 of the control
- int startDrawingAtY = graph.Bounds.Height / 2 - toRender.Length / 2;
+ int startDrawingAtY = graph.Viewport.Height / 2 - toRender.Length / 2;
for (var i = 0; i < toRender.Length; i++)
{
@@ -356,7 +356,7 @@ public override void DrawAxisLine (GraphView graph)
return;
}
- Rectangle bounds = graph.Bounds;
+ Rectangle bounds = graph.Viewport;
int x = GetAxisXPosition (graph);
@@ -385,7 +385,7 @@ public int GetAxisXPosition (GraphView graph)
// float the Y axis so that it accurately represents the origin of the graph
// but anchor it to left/right if the origin is offscreen
- return Math.Min (Math.Max ((int)graph.MarginLeft, origin.X), graph.Bounds.Width - 1);
+ return Math.Min (Math.Max ((int)graph.MarginLeft, origin.X), graph.Viewport.Width - 1);
}
/// Draws a vertical axis line at the given , screen coordinates
@@ -409,7 +409,7 @@ private int GetAxisYEnd (GraphView graph)
return graph.GraphSpaceToScreen (new PointF (0, Minimum.Value)).Y;
}
- return graph.Bounds.Height;
+ return graph.Viewport.Height;
}
private IEnumerable GetLabels (GraphView graph, Rectangle bounds)
diff --git a/Terminal.Gui/Views/GraphView/GraphView.cs b/Terminal.Gui/Views/GraphView/GraphView.cs
index 30b902286b..6f4f05bf16 100644
--- a/Terminal.Gui/Views/GraphView/GraphView.cs
+++ b/Terminal.Gui/Views/GraphView/GraphView.cs
@@ -192,12 +192,12 @@ public Point GraphSpaceToScreen (PointF location)
(int)((location.X - ScrollOffset.X) / CellSize.X) + (int)MarginLeft,
// screen coordinates are top down while graph coordinates are bottom up
- Bounds.Height - 1 - (int)MarginBottom - (int)((location.Y - ScrollOffset.Y) / CellSize.Y)
+ Viewport.Height - 1 - (int)MarginBottom - (int)((location.Y - ScrollOffset.Y) / CellSize.Y)
);
}
///
- public override void OnDrawContent (Rectangle contentArea)
+ public override void OnDrawContent (Rectangle viewport)
{
if (CellSize.X == 0 || CellSize.Y == 0)
{
@@ -209,10 +209,10 @@ public override void OnDrawContent (Rectangle contentArea)
Move (0, 0);
// clear all old content
- for (var i = 0; i < Bounds.Height; i++)
+ for (var i = 0; i < Viewport.Height; i++)
{
Move (0, i);
- Driver.AddStr (new string (' ', Bounds.Width));
+ Driver.AddStr (new string (' ', Viewport.Width));
}
// If there is no data do not display a graph
@@ -222,8 +222,8 @@ public override void OnDrawContent (Rectangle contentArea)
}
// The drawable area of the graph (anything that isn't in the margins)
- int graphScreenWidth = Bounds.Width - (int)MarginLeft;
- int graphScreenHeight = Bounds.Height - (int)MarginBottom;
+ int graphScreenWidth = Viewport.Width - (int)MarginLeft;
+ int graphScreenHeight = Viewport.Height - (int)MarginBottom;
// if the margins take up the full draw bounds don't render
if (graphScreenWidth < 0 || graphScreenHeight < 0)
@@ -287,10 +287,10 @@ public override bool OnEnter (View view)
}
/// Scrolls the graph down 1 page.
- public void PageDown () { Scroll (0, -1 * CellSize.Y * Bounds.Height); }
+ public void PageDown () { Scroll (0, -1 * CellSize.Y * Viewport.Height); }
/// Scrolls the graph up 1 page.
- public void PageUp () { Scroll (0, CellSize.Y * Bounds.Height); }
+ public void PageUp () { Scroll (0, CellSize.Y * Viewport.Height); }
///
/// Clears all settings configured on the graph and resets all properties to default values (
@@ -316,7 +316,7 @@ public RectangleF ScreenToGraphSpace (int col, int row)
{
return new (
ScrollOffset.X + (col - MarginLeft) * CellSize.X,
- ScrollOffset.Y + (Bounds.Height - (row + MarginBottom + 1)) * CellSize.Y,
+ ScrollOffset.Y + (Viewport.Height - (row + MarginBottom + 1)) * CellSize.Y,
CellSize.X,
CellSize.Y
);
diff --git a/Terminal.Gui/Views/GraphView/Series.cs b/Terminal.Gui/Views/GraphView/Series.cs
index 673419b161..c117bd0a24 100644
--- a/Terminal.Gui/Views/GraphView/Series.cs
+++ b/Terminal.Gui/Views/GraphView/Series.cs
@@ -192,7 +192,7 @@ public virtual void DrawSeries (GraphView graph, Rectangle drawBounds, Rectangle
screenStart.X = graph.AxisY.GetAxisXPosition (graph);
// dont draw bar off the right of the control
- screenEnd.X = Math.Min (graph.Bounds.Width - 1, screenEnd.X);
+ screenEnd.X = Math.Min (graph.Viewport.Width - 1, screenEnd.X);
// if bar is off the screen
if (screenStart.Y < 0 || screenStart.Y > drawBounds.Height - graph.MarginBottom)
diff --git a/Terminal.Gui/Views/HexView.cs b/Terminal.Gui/Views/HexView.cs
index a9a7f0aad8..2d166db3c2 100644
--- a/Terminal.Gui/Views/HexView.cs
+++ b/Terminal.Gui/Views/HexView.cs
@@ -264,7 +264,7 @@ public void ApplyEdits (Stream stream = null)
///
protected internal override bool OnMouseEvent (MouseEvent me)
{
- // BUGBUG: Test this with a border! Assumes Frame == Bounds!
+ // BUGBUG: Test this with a border! Assumes Frame == Viewport!
if (!me.Flags.HasFlag (MouseFlags.Button1Clicked)
&& !me.Flags.HasFlag (MouseFlags.Button1DoubleClicked)
@@ -351,14 +351,14 @@ protected internal override bool OnMouseEvent (MouseEvent me)
}
///
- public override void OnDrawContent (Rectangle contentArea)
+ public override void OnDrawContent (Rectangle viewport)
{
Attribute currentAttribute;
Attribute current = ColorScheme.Focus;
Driver.SetAttribute (current);
Move (0, 0);
- // BUGBUG: Bounds!!!!
+ // BUGBUG: Viewport!!!!
Rectangle frame = Frame;
int nblocks = bytesPerLine / bsize;
@@ -373,7 +373,7 @@ public override void OnDrawContent (Rectangle contentArea)
{
Rectangle lineRect = new (0, line, frame.Width, 1);
- if (!Bounds.Contains (lineRect))
+ if (!Viewport.Contains (lineRect))
{
continue;
}
@@ -611,15 +611,15 @@ private void HexView_LayoutComplete (object sender, LayoutEventArgs e)
// Small buffers will just show the position, with the bsize field value (4 bytes)
bytesPerLine = bsize;
- if (Bounds.Width - displayWidth > 17)
+ if (Viewport.Width - displayWidth > 17)
{
- bytesPerLine = bsize * ((Bounds.Width - displayWidth) / 18);
+ bytesPerLine = bsize * ((Viewport.Width - displayWidth) / 18);
}
}
private bool MoveDown (int bytes)
{
- // BUGBUG: Bounds!
+ // BUGBUG: Viewport!
RedisplayLine (position);
if (position + bytes < source.Length)
@@ -657,7 +657,7 @@ private bool MoveEnd ()
{
position = source.Length;
- // BUGBUG: Bounds!
+ // BUGBUG: Viewport!
if (position >= DisplayStart + bytesPerLine * Frame.Height)
{
SetDisplayStart (position);
@@ -744,7 +744,7 @@ private bool MoveRight ()
position++;
}
- // BUGBUG: Bounds!
+ // BUGBUG: Viewport!
if (position >= DisplayStart + bytesPerLine * Frame.Height)
{
SetDisplayStart (DisplayStart + bytesPerLine);
@@ -793,7 +793,7 @@ private void RedisplayLine (long pos)
var delta = (int)(pos - DisplayStart);
int line = delta / bytesPerLine;
- // BUGBUG: Bounds!
+ // BUGBUG: Viewport!
SetNeedsDisplay (new (0, line, Frame.Width, 1));
}
diff --git a/Terminal.Gui/Views/Line.cs b/Terminal.Gui/Views/Line.cs
index 9fe92ae7a7..8ef2940615 100644
--- a/Terminal.Gui/Views/Line.cs
+++ b/Terminal.Gui/Views/Line.cs
@@ -17,7 +17,7 @@ public Line ()
public Orientation Orientation { get; set; }
///
- public override void OnDrawContent (Rectangle contentArea)
+ public override void OnDrawContent (Rectangle viewport)
{
LineCanvas lc = LineCanvas;
@@ -26,7 +26,7 @@ public override void OnDrawContent (Rectangle contentArea)
lc = adornment.Parent.LineCanvas;
}
lc.AddLine (
- BoundsToScreen (contentArea).Location,
+ ViewportToScreen (viewport).Location,
Orientation == Orientation.Horizontal ? Frame.Width : Frame.Height,
Orientation,
BorderStyle
diff --git a/Terminal.Gui/Views/LineView.cs b/Terminal.Gui/Views/LineView.cs
index 2f43411139..a362cca10d 100644
--- a/Terminal.Gui/Views/LineView.cs
+++ b/Terminal.Gui/Views/LineView.cs
@@ -54,16 +54,16 @@ public LineView (Orientation orientation)
public Rune? StartingAnchor { get; set; }
/// Draws the line including any starting/ending anchors
- public override void OnDrawContent (Rectangle contentArea)
+ public override void OnDrawContent (Rectangle viewport)
{
- base.OnDrawContent (contentArea);
+ base.OnDrawContent (viewport);
Move (0, 0);
Driver.SetAttribute (GetNormalColor ());
int hLineWidth = Math.Max (1, Glyphs.HLine.GetColumns ());
- int dEnd = Orientation == Orientation.Horizontal ? Bounds.Width : Bounds.Height;
+ int dEnd = Orientation == Orientation.Horizontal ? Viewport.Width : Viewport.Height;
for (var d = 0; d < dEnd; d += hLineWidth)
{
diff --git a/Terminal.Gui/Views/ListView.cs b/Terminal.Gui/Views/ListView.cs
index f1de20abc1..0fb229afec 100644
--- a/Terminal.Gui/Views/ListView.cs
+++ b/Terminal.Gui/Views/ListView.cs
@@ -1,4 +1,5 @@
using System.Collections;
+using static Terminal.Gui.SpinnerStyle;
namespace Terminal.Gui;
@@ -94,7 +95,10 @@ public class ListView : View
private int _lastSelectedItem = -1;
private int _selected = -1;
private IListDataSource _source;
- private int _top, _left;
+ // TODO: ListView has been upgraded to use Viewport and ContentSize instead of the
+ // TODO: bespoke _top and _left. It was a quick & dirty port. There is now duplicate logic
+ // TODO: that could be removed.
+ //private int _top, _left;
///
/// Initializes a new instance of . Set the property to display
@@ -107,8 +111,8 @@ public ListView ()
// Things this view knows how to do
AddCommand (Command.LineUp, () => MoveUp ());
AddCommand (Command.LineDown, () => MoveDown ());
- AddCommand (Command.ScrollUp, () => ScrollUp (1));
- AddCommand (Command.ScrollDown, () => ScrollDown (1));
+ AddCommand (Command.ScrollUp, () => ScrollVertical (-1));
+ AddCommand (Command.ScrollDown, () => ScrollVertical (1));
AddCommand (Command.PageUp, () => MovePageUp ());
AddCommand (Command.PageDown, () => MovePageDown ());
AddCommand (Command.TopHome, () => MoveHome ());
@@ -117,6 +121,9 @@ public ListView ()
AddCommand (Command.OpenSelectedItem, () => OnOpenSelectedItem ());
AddCommand (Command.Select, () => MarkUnmarkRow ());
+ AddCommand (Command.ScrollLeft, () => ScrollHorizontal (-1));
+ AddCommand (Command.ScrollRight, () => ScrollHorizontal (1));
+
// Default keybindings for all ListViews
KeyBindings.Add (Key.CursorUp, Command.LineUp);
KeyBindings.Add (Key.P.WithCtrl, Command.LineUp);
@@ -199,7 +206,7 @@ public bool AllowsMultipleSelection
/// The left position.
public int LeftItem
{
- get => _left;
+ get => Viewport.X;
set
{
if (_source is null)
@@ -212,7 +219,7 @@ public int LeftItem
throw new ArgumentException ("value");
}
- _left = value;
+ Viewport = Viewport with { X = value };
SetNeedsDisplay ();
}
}
@@ -250,20 +257,34 @@ public IListDataSource Source
get => _source;
set
{
+ if (_source == value)
+
+ {
+ return;
+ }
_source = value;
+
+ ContentSize = new Size (_source?.Length ?? Viewport.Width, _source?.Count ?? Viewport.Width);
+ if (IsInitialized)
+ {
+ Viewport = Viewport with { Y = 0 };
+ }
+
KeystrokeNavigator.Collection = _source?.ToList ();
- _top = 0;
_selected = -1;
_lastSelectedItem = -1;
SetNeedsDisplay ();
}
}
- /// Gets or sets the item that is displayed at the top of the .
+ /// Gets or sets the index of the item that will appear at the top of the .
+ ///
+ /// This a helper property for accessing listView.Viewport.Y.
+ ///
/// The top item.
public int TopItem
{
- get => _top;
+ get => Viewport.Y;
set
{
if (_source is null)
@@ -271,13 +292,7 @@ public int TopItem
return;
}
- if (value < 0 || (_source.Count > 0 && value >= _source.Count))
- {
- throw new ArgumentException ("value");
- }
-
- _top = Math.Max (value, 0);
- SetNeedsDisplay ();
+ Viewport = Viewport with { Y = value };
}
}
@@ -314,13 +329,14 @@ public void EnsureSelectedItemVisible ()
{
if (SuperView?.IsInitialized == true)
{
- if (_selected < _top)
+ if (_selected < Viewport.Y)
{
- _top = Math.Max (_selected, 0);
+ // TODO: The Max check here is not needed because, by default, Viewport enforces staying w/in ContentArea (View.ScrollSettings).
+ Viewport = Viewport with { Y = _selected };
}
- else if (Bounds.Height > 0 && _selected >= _top + Bounds.Height)
+ else if (Viewport.Height > 0 && _selected >= Viewport.Y + Viewport.Height)
{
- _top = Math.Max (_selected - Bounds.Height + 1, 0);
+ Viewport = Viewport with { Y = _selected - Viewport.Height + 1};
}
LayoutStarted -= ListView_LayoutStarted;
@@ -347,7 +363,7 @@ public virtual bool MarkUnmarkRow ()
}
///
- protected internal override bool OnMouseEvent (MouseEvent me)
+ protected internal override bool OnMouseEvent (MouseEvent me)
{
if (!me.Flags.HasFlag (MouseFlags.Button1Clicked)
&& !me.Flags.HasFlag (MouseFlags.Button1DoubleClicked)
@@ -371,40 +387,40 @@ protected internal override bool OnMouseEvent (MouseEvent me)
if (me.Flags == MouseFlags.WheeledDown)
{
- ScrollDown (1);
+ ScrollVertical (1);
return true;
}
if (me.Flags == MouseFlags.WheeledUp)
{
- ScrollUp (1);
+ ScrollVertical (-1);
return true;
}
if (me.Flags == MouseFlags.WheeledRight)
{
- ScrollRight (1);
+ ScrollHorizontal (1);
return true;
}
if (me.Flags == MouseFlags.WheeledLeft)
{
- ScrollLeft (1);
+ ScrollHorizontal(-1);
return true;
}
- if (me.Y + _top >= _source.Count
- || me.Y + _top < 0
- || me.Y + _top > _top + Bounds.Height)
+ if (me.Y + Viewport.Y >= _source.Count
+ || me.Y + Viewport.Y < 0
+ || me.Y + Viewport.Y > Viewport.Y + Viewport.Height)
{
return true;
}
- _selected = _top + me.Y;
+ _selected = Viewport.Y + me.Y;
if (AllowsAll ())
{
@@ -449,13 +465,13 @@ public virtual bool MoveDown ()
//can move by down by one.
_selected++;
- if (_selected >= _top + Bounds.Height)
+ if (_selected >= Viewport.Y + Viewport.Height)
{
- _top++;
+ Viewport = Viewport with { Y = Viewport.Y + 1 };
}
- else if (_selected < _top)
+ else if (_selected < Viewport.Y)
{
- _top = Math.Max (_selected, 0);
+ Viewport = Viewport with { Y = _selected };
}
OnSelectedChanged ();
@@ -466,9 +482,9 @@ public virtual bool MoveDown ()
OnSelectedChanged ();
SetNeedsDisplay ();
}
- else if (_selected >= _top + Bounds.Height)
+ else if (_selected >= Viewport.Y + Viewport.Height)
{
- _top = Math.Max (_source.Count - Bounds.Height, 0);
+ Viewport = Viewport with { Y = _source.Count - Viewport.Height };
SetNeedsDisplay ();
}
@@ -483,9 +499,9 @@ public virtual bool MoveEnd ()
{
_selected = _source.Count - 1;
- if (_top + _selected > Bounds.Height - 1)
+ if (Viewport.Y + _selected > Viewport.Height - 1)
{
- _top = Math.Max (_selected, 0);
+ Viewport = Viewport with { Y = _selected };
}
OnSelectedChanged ();
@@ -502,7 +518,7 @@ public virtual bool MoveHome ()
if (_selected != 0)
{
_selected = 0;
- _top = Math.Max (_selected, 0);
+ Viewport = Viewport with { Y = _selected };
OnSelectedChanged ();
SetNeedsDisplay ();
}
@@ -522,7 +538,7 @@ public virtual bool MovePageDown ()
return true;
}
- int n = _selected + Bounds.Height;
+ int n = _selected + Viewport.Height;
if (n >= _source.Count)
{
@@ -533,13 +549,13 @@ public virtual bool MovePageDown ()
{
_selected = n;
- if (_source.Count >= Bounds.Height)
+ if (_source.Count >= Viewport.Height)
{
- _top = Math.Max (_selected, 0);
+ Viewport = Viewport with { Y = _selected };
}
else
{
- _top = 0;
+ Viewport = Viewport with { Y = 0 };
}
OnSelectedChanged ();
@@ -553,7 +569,7 @@ public virtual bool MovePageDown ()
///
public virtual bool MovePageUp ()
{
- int n = _selected - Bounds.Height;
+ int n = _selected - Viewport.Height;
if (n < 0)
{
@@ -563,7 +579,7 @@ public virtual bool MovePageUp ()
if (n != _selected)
{
_selected = n;
- _top = Math.Max (_selected, 0);
+ Viewport = Viewport with { Y = _selected };
OnSelectedChanged ();
SetNeedsDisplay ();
}
@@ -599,21 +615,21 @@ public virtual bool MoveUp ()
_selected = Source.Count - 1;
}
- if (_selected < _top)
+ if (_selected < Viewport.Y)
{
- _top = Math.Max (_selected, 0);
+ Viewport = Viewport with { Y = _selected };
}
- else if (_selected > _top + Bounds.Height)
+ else if (_selected > Viewport.Y + Viewport.Height)
{
- _top = Math.Max (_selected - Bounds.Height + 1, 0);
+ Viewport = Viewport with { Y = _selected - Viewport.Height + 1 };
}
OnSelectedChanged ();
SetNeedsDisplay ();
}
- else if (_selected < _top)
+ else if (_selected < Viewport.Y)
{
- _top = Math.Max (_selected, 0);
+ Viewport = Viewport with { Y = _selected };
SetNeedsDisplay ();
}
@@ -621,18 +637,18 @@ public virtual bool MoveUp ()
}
///
- public override void OnDrawContent (Rectangle contentArea)
+ public override void OnDrawContent (Rectangle viewport)
{
- base.OnDrawContent (contentArea);
+ base.OnDrawContent (viewport);
Attribute current = ColorScheme.Focus;
Driver.SetAttribute (current);
Move (0, 0);
- Rectangle f = Bounds;
- int item = _top;
+ Rectangle f = Viewport;
+ int item = Viewport.Y;
bool focused = HasFocus;
int col = _allowsMarking ? 2 : 0;
- int start = _left;
+ int start = Viewport.X;
for (var row = 0; row < f.Height; row++, item++)
{
@@ -769,57 +785,17 @@ public override void PositionCursor ()
{
if (_allowsMarking)
{
- Move (0, _selected - _top);
+ Move (0, _selected - Viewport.Y);
}
else
{
- Move (Bounds.Width - 1, _selected - _top);
+ Move (Viewport.Width - 1, _selected - Viewport.Y);
}
}
/// This event is invoked when this is being drawn before rendering.
public event EventHandler RowRender;
- /// Scrolls the view down by items.
- /// Number of items to scroll down.
- public virtual bool ScrollDown (int items)
- {
- _top = Math.Max (Math.Min (_top + items, _source.Count - 1), 0);
- SetNeedsDisplay ();
-
- return true;
- }
-
- /// Scrolls the view left.
- /// Number of columns to scroll left.
- public virtual bool ScrollLeft (int cols)
- {
- _left = Math.Max (_left - cols, 0);
- SetNeedsDisplay ();
-
- return true;
- }
-
- /// Scrolls the view right.
- /// Number of columns to scroll right.
- public virtual bool ScrollRight (int cols)
- {
- _left = Math.Max (Math.Min (_left + cols, MaxLength - 1), 0);
- SetNeedsDisplay ();
-
- return true;
- }
-
- /// Scrolls the view up by items.
- /// Number of items to scroll up.
- public virtual bool ScrollUp (int items)
- {
- _top = Math.Max (_top - items, 0);
- SetNeedsDisplay ();
-
- return true;
- }
-
/// This event is raised when the selected item in the has changed.
public event EventHandler SelectedItemChanged;
diff --git a/Terminal.Gui/Views/Menu/ContextMenu.cs b/Terminal.Gui/Views/Menu/ContextMenu.cs
index ae080a298f..cc635de003 100644
--- a/Terminal.Gui/Views/Menu/ContextMenu.cs
+++ b/Terminal.Gui/Views/Menu/ContextMenu.cs
@@ -144,12 +144,12 @@ public void Show ()
_container = Application.Current;
_container.Closing += Container_Closing;
_container.Deactivate += Container_Deactivate;
- Rectangle frame = Application.Driver.Bounds;
+ Rectangle frame = Application.Driver.Screen;
Point position = Position;
if (Host is { })
{
- Point pos = Host.BoundsToScreen (frame).Location;
+ Point pos = Host.ViewportToScreen (frame).Location;
pos.Y += Host.Frame.Height - 1;
if (position != pos)
@@ -186,7 +186,7 @@ public void Show ()
}
else
{
- Point pos = Host.BoundsToScreen (frame).Location;
+ Point pos = Host.ViewportToScreen (frame).Location;
position.Y = pos.Y - rect.Height - 1;
}
}
diff --git a/Terminal.Gui/Views/Menu/Menu.cs b/Terminal.Gui/Views/Menu/Menu.cs
index 60f7c00bf3..62d847c659 100644
--- a/Terminal.Gui/Views/Menu/Menu.cs
+++ b/Terminal.Gui/Views/Menu/Menu.cs
@@ -726,7 +726,7 @@ private void Application_RootMouseEvent (object sender, MouseEvent a)
View view = a.View ?? this;
- Point boundsPoint = view.ScreenToBounds (a.X, a.Y);
+ Point boundsPoint = view.ScreenToViewport (a.X, a.Y);
var me = new MouseEvent
{
X = boundsPoint.X,
@@ -757,7 +757,7 @@ internal Attribute DetermineColorSchemeFor (MenuItem item, int index)
return !item.IsEnabled () ? ColorScheme.Disabled : GetNormalColor ();
}
- public override void OnDrawContent (Rectangle contentArea)
+ public override void OnDrawContent (Rectangle viewport)
{
if (_barItems.Children is null)
{
@@ -771,14 +771,14 @@ public override void OnDrawContent (Rectangle contentArea)
OnDrawAdornments ();
OnRenderLineCanvas ();
- for (int i = Bounds.Y; i < _barItems.Children.Length; i++)
+ for (int i = Viewport.Y; i < _barItems.Children.Length; i++)
{
if (i < 0)
{
continue;
}
- if (BoundsToScreen (Bounds).Y + i >= Driver.Rows)
+ if (ViewportToScreen (Viewport).Y + i >= Driver.Rows)
{
break;
}
@@ -792,7 +792,8 @@ public override void OnDrawContent (Rectangle contentArea)
if (item is null && BorderStyle != LineStyle.None)
{
- Move (-1, i);
+ var s = ViewportToScreen (new (-1, i, 0, 0));
+ Driver.Move (s.X, s.Y);
Driver.AddRune (Glyphs.LeftTee);
}
else if (Frame.X < Driver.Cols)
@@ -802,7 +803,7 @@ public override void OnDrawContent (Rectangle contentArea)
Driver.SetAttribute (DetermineColorSchemeFor (item, i));
- for (int p = Bounds.X; p < Frame.Width - 2; p++)
+ for (int p = Viewport.X; p < Frame.Width - 2; p++)
{
// This - 2 is for the border
if (p < 0)
@@ -810,7 +811,7 @@ public override void OnDrawContent (Rectangle contentArea)
continue;
}
- if (BoundsToScreen (Bounds).X + p >= Driver.Cols)
+ if (ViewportToScreen (Viewport).X + p >= Driver.Cols)
{
break;
}
@@ -839,7 +840,8 @@ public override void OnDrawContent (Rectangle contentArea)
{
if (BorderStyle != LineStyle.None && SuperView?.Frame.Right - Frame.X > Frame.Width)
{
- Move (Frame.Width - 2, i);
+ var s = ViewportToScreen (new (Frame.Width - 2, i, 0, 0));
+ Driver.Move (s.X, s.Y);
Driver.AddRune (Glyphs.RightTee);
}
@@ -875,7 +877,7 @@ public override void OnDrawContent (Rectangle contentArea)
textToDraw = item.Title;
}
- Rectangle screen = BoundsToScreen (new (new (0 , i), Size.Empty));
+ Rectangle screen = ViewportToScreen (new (new (0 , i), Size.Empty));
if (screen.X < Driver.Cols)
{
Driver.Move (screen.X + 1, screen.Y);
@@ -893,10 +895,10 @@ public override void OnDrawContent (Rectangle contentArea)
// The -3 is left/right border + one space (not sure what for)
tf.Draw (
- BoundsToScreen (new (1, i, Frame.Width - 3, 1)),
+ ViewportToScreen (new (1, i, Frame.Width - 3, 1)),
i == _currentChild ? ColorScheme.Focus : GetNormalColor (),
i == _currentChild ? ColorScheme.HotFocus : ColorScheme.HotNormal,
- SuperView?.BoundsToScreen (SuperView.Bounds) ?? Rectangle.Empty
+ SuperView?.ViewportToScreen (SuperView.Viewport) ?? Rectangle.Empty
);
}
else
@@ -913,7 +915,7 @@ public override void OnDrawContent (Rectangle contentArea)
? item.Help.GetColumns ()
: item.Help.GetColumns () + item.ShortcutTag.GetColumns () + 2;
int col = Frame.Width - l - 3;
- screen = BoundsToScreen (new (new (col, i), Size.Empty));
+ screen = ViewportToScreen (new (new (col, i), Size.Empty));
if (screen.X < Driver.Cols)
{
@@ -939,7 +941,7 @@ private void Current_DrawContentComplete (object sender, DrawEventArgs e)
{
if (Visible)
{
- OnDrawContent (Bounds);
+ OnDrawContent (Viewport);
}
}
diff --git a/Terminal.Gui/Views/Menu/MenuBar.cs b/Terminal.Gui/Views/Menu/MenuBar.cs
index 07ff6912c3..805890d096 100644
--- a/Terminal.Gui/Views/Menu/MenuBar.cs
+++ b/Terminal.Gui/Views/Menu/MenuBar.cs
@@ -469,15 +469,11 @@ internal Menu openCurrentMenu
public event EventHandler MenuOpening;
///
- public override void OnDrawContent (Rectangle contentArea)
+ public override void OnDrawContent (Rectangle viewport)
{
- Move (0, 0);
Driver.SetAttribute (GetNormalColor ());
- for (var i = 0; i < Frame.Width; i++)
- {
- Driver.AddRune ((Rune)' ');
- }
+ Clear ();
var pos = 0;
@@ -824,13 +820,13 @@ internal Point GetScreenOffset ()
return Point.Empty;
}
- Rectangle superViewFrame = SuperView is null ? Driver.Bounds : SuperView.Frame;
+ Rectangle superViewFrame = SuperView is null ? Driver.Screen : SuperView.Frame;
View sv = SuperView is null ? Application.Current : SuperView;
- Point boundsOffset = sv.GetBoundsOffset ();
+ Point viewportOffset = sv.GetViewportOffsetFromFrame ();
return new (
- superViewFrame.X - sv.Frame.X - boundsOffset.X,
- superViewFrame.Y - sv.Frame.Y - boundsOffset.Y
+ superViewFrame.X - sv.Frame.X - viewportOffset.X,
+ superViewFrame.Y - sv.Frame.Y - viewportOffset.Y
);
}
@@ -841,11 +837,11 @@ internal Point GetScreenOffset ()
/// The location offset.
internal Point GetScreenOffsetFromCurrent ()
{
- Rectangle screen = Driver.Bounds;
+ Rectangle screen = Driver.Screen;
Rectangle currentFrame = Application.Current.Frame;
- Point boundsOffset = Application.Top.GetBoundsOffset ();
+ Point viewportOffset = Application.Top.GetViewportOffsetFromFrame ();
- return new (screen.X - currentFrame.X - boundsOffset.X, screen.Y - currentFrame.Y - boundsOffset.Y);
+ return new (screen.X - currentFrame.X - viewportOffset.X, screen.Y - currentFrame.Y - viewportOffset.Y);
}
internal void NextMenu (bool isSubMenu = false, bool ignoreUseSubMenusSingleFrame = false)
@@ -1319,7 +1315,7 @@ private void ProcessMenu (int i, MenuBarItem mi)
if (mi.IsTopLevel)
{
- Rectangle screen = BoundsToScreen (new (new (0, i), Size.Empty));
+ Rectangle screen = ViewportToScreen (new (new (0, i), Size.Empty));
var menu = new Menu { Host = this, X = screen.X, Y = screen.Y, BarItems = mi };
menu.Run (mi.Action);
menu.Dispose ();
@@ -1683,7 +1679,7 @@ protected internal override bool OnMouseEvent (MouseEvent me)
{
if (Menus [i].IsTopLevel)
{
- Rectangle screen = BoundsToScreen (new (new (0, i), Size.Empty));
+ Rectangle screen = ViewportToScreen (new (new (0, i), Size.Empty));
var menu = new Menu { Host = this, X = screen.X, Y = screen.Y, BarItems = Menus [i] };
menu.Run (Menus [i].Action);
menu.Dispose ();
diff --git a/Terminal.Gui/Views/MessageBox.cs b/Terminal.Gui/Views/MessageBox.cs
index 9b00337d71..ef7a33d10b 100644
--- a/Terminal.Gui/Views/MessageBox.cs
+++ b/Terminal.Gui/Views/MessageBox.cs
@@ -422,7 +422,7 @@ void Dialog_Loaded (object s, EventArgs e)
}
// TODO: replace with Dim.Fit when implemented
- Rectangle maxBounds = d.SuperView?.Bounds ?? Application.Top.Bounds;
+ Rectangle maxBounds = d.SuperView?.Viewport ?? Application.Top.Viewport;
Thickness adornmentsThickness = d.GetAdornmentsThickness ();
@@ -467,7 +467,7 @@ void Dialog_Loaded (object s, EventArgs e)
+ adornmentsThickness.Vertical);
}
- d.SetRelativeLayout (d.SuperView?.Frame ?? Application.Top.Frame);
+ d.SetRelativeLayout (d.SuperView?.ContentSize ?? Application.Top.ContentSize);
d.LayoutSubviews ();
}
}
diff --git a/Terminal.Gui/Views/ProgressBar.cs b/Terminal.Gui/Views/ProgressBar.cs
index 189533ee60..cfddca1e00 100644
--- a/Terminal.Gui/Views/ProgressBar.cs
+++ b/Terminal.Gui/Views/ProgressBar.cs
@@ -143,7 +143,7 @@ public override string Text
}
///
- public override void OnDrawContent (Rectangle contentArea)
+ public override void OnDrawContent (Rectangle viewport)
{
Driver.SetAttribute (GetHotNormalColor ());
@@ -151,7 +151,7 @@ public override void OnDrawContent (Rectangle contentArea)
if (_isActivity)
{
- for (var i = 0; i < Bounds.Width; i++)
+ for (var i = 0; i < Viewport.Width; i++)
{
if (Array.IndexOf (_activityPos, i) != -1)
{
@@ -165,15 +165,15 @@ public override void OnDrawContent (Rectangle contentArea)
}
else
{
- var mid = (int)(_fraction * Bounds.Width);
+ var mid = (int)(_fraction * Viewport.Width);
int i;
- for (i = 0; (i < mid) & (i < Bounds.Width); i++)
+ for (i = 0; (i < mid) & (i < Viewport.Width); i++)
{
Driver.AddRune (SegmentCharacter);
}
- for (; i < Bounds.Width; i++)
+ for (; i < Viewport.Width; i++)
{
Driver.AddRune ((Rune)' ');
}
@@ -190,10 +190,10 @@ public override void OnDrawContent (Rectangle contentArea)
}
tf?.Draw (
- BoundsToScreen (Bounds),
+ ViewportToScreen (Viewport),
attr,
ColorScheme.Normal,
- SuperView?.BoundsToScreen (SuperView.Bounds) ?? default (Rectangle)
+ SuperView?.ViewportToScreen (SuperView.Viewport) ?? default (Rectangle)
);
}
}
@@ -244,13 +244,13 @@ public void Pulse ()
_delta = 1;
}
- else if (_activityPos [0] >= Bounds.Width)
+ else if (_activityPos [0] >= Viewport.Width)
{
if (_bidirectionalMarquee)
{
for (var i = 0; i < _activityPos.Length; i++)
{
- _activityPos [i] = Bounds.Width + i - 2;
+ _activityPos [i] = Viewport.Width + i - 2;
}
_delta = -1;
@@ -291,7 +291,7 @@ private void ProgressBar_LayoutStarted (object sender, EventArgs e)
private void SetInitialProperties ()
{
- Height = 1; // This will be updated when Bounds is updated in ProgressBar_LayoutStarted
+ Height = 1; // This will be updated when Viewport is updated in ProgressBar_LayoutStarted
CanFocus = false;
_fraction = 0;
LayoutStarted += ProgressBar_LayoutStarted;
diff --git a/Terminal.Gui/Views/RadioGroup.cs b/Terminal.Gui/Views/RadioGroup.cs
index 6f10c3bc03..5c9d6b477c 100644
--- a/Terminal.Gui/Views/RadioGroup.cs
+++ b/Terminal.Gui/Views/RadioGroup.cs
@@ -196,9 +196,9 @@ public int SelectedItem
}
///
- public override void OnDrawContent (Rectangle contentArea)
+ public override void OnDrawContent (Rectangle viewport)
{
- base.OnDrawContent (contentArea);
+ base.OnDrawContent (viewport);
Driver.SetAttribute (GetNormalColor ());
diff --git a/Terminal.Gui/Views/ScrollBarView.cs b/Terminal.Gui/Views/ScrollBarView.cs
index e70f1e5c73..3f47a5cfa8 100644
--- a/Terminal.Gui/Views/ScrollBarView.cs
+++ b/Terminal.Gui/Views/ScrollBarView.cs
@@ -152,14 +152,14 @@ public bool KeepContentAlwaysInViewport
_keepContentAlwaysInViewport = value;
var pos = 0;
- if (value && !_vertical && _position + Host.Bounds.Width > _size)
+ if (value && !_vertical && _position + Host.Viewport.Width > _size)
{
- pos = _size - Host.Bounds.Width + (_showBothScrollIndicator ? 1 : 0);
+ pos = _size - Host.Viewport.Width + (_showBothScrollIndicator ? 1 : 0);
}
- if (value && _vertical && _position + Host.Bounds.Height > _size)
+ if (value && _vertical && _position + Host.Viewport.Height > _size)
{
- pos = _size - Host.Bounds.Height + (_showBothScrollIndicator ? 1 : 0);
+ pos = _size - Host.Viewport.Height + (_showBothScrollIndicator ? 1 : 0);
}
if (pos != 0)
@@ -204,13 +204,12 @@ public int Position
get => _position;
set
{
- _position = value;
-
- if (IsInitialized)
+ if (_position == value)
{
- // We're not initialized so we can't do anything fancy. Just cache value.
- SetPosition (value);
+ return;
}
+
+ SetPosition (value);
}
}
@@ -262,7 +261,7 @@ public int Size
if (IsInitialized)
{
- SetRelativeLayout (SuperView?.Frame ?? Host.Frame);
+ SetRelativeLayout (SuperView?.Frame.Size ?? Host.Frame.Size);
ShowHideScrollBars (false);
SetNeedsDisplay ();
}
@@ -275,7 +274,7 @@ public int Size
public event EventHandler ChangedPosition;
///
- protected internal override bool OnMouseEvent (MouseEvent mouseEvent)
+ protected internal override bool OnMouseEvent (MouseEvent mouseEvent)
{
if (mouseEvent.Flags != MouseFlags.Button1Pressed
&& mouseEvent.Flags != MouseFlags.Button1DoubleClicked
@@ -301,7 +300,7 @@ protected internal override bool OnMouseEvent (MouseEvent mouseEvent)
}
int location = _vertical ? mouseEvent.Y : mouseEvent.X;
- int barsize = _vertical ? Bounds.Height : Bounds.Width;
+ int barsize = _vertical ? Viewport.Height : Viewport.Width;
int posTopLeftTee = _vertical ? _posTopTee + 1 : _posLeftTee + 1;
int posBottomRightTee = _vertical ? _posBottomTee + 1 : _posRightTee + 1;
barsize -= 2;
@@ -449,7 +448,7 @@ protected internal override bool OnMouseEvent (MouseEvent mouseEvent)
public virtual void OnChangedPosition () { ChangedPosition?.Invoke (this, EventArgs.Empty); }
///
- public override void OnDrawContent (Rectangle contentArea)
+ public override void OnDrawContent (Rectangle viewport)
{
if (ColorScheme is null || ((!_showScrollIndicator || Size == 0) && AutoHideScrollBars && Visible))
{
@@ -461,7 +460,7 @@ public override void OnDrawContent (Rectangle contentArea)
return;
}
- if (Size == 0 || (_vertical && Bounds.Height == 0) || (!_vertical && Bounds.Width == 0))
+ if (Size == 0 || (_vertical && Viewport.Height == 0) || (!_vertical && Viewport.Width == 0))
{
return;
}
@@ -470,13 +469,13 @@ public override void OnDrawContent (Rectangle contentArea)
if (_vertical)
{
- if (Bounds.Right < Bounds.Width - 1)
+ if (Viewport.Right < Viewport.Width - 1)
{
return;
}
- int col = Bounds.Width - 1;
- int bh = Bounds.Height;
+ int col = Viewport.Width - 1;
+ int bh = Viewport.Height;
Rune special;
if (bh < 4)
@@ -486,7 +485,7 @@ public override void OnDrawContent (Rectangle contentArea)
Move (col, 0);
- if (Bounds.Height == 1)
+ if (Viewport.Height == 1)
{
Driver.AddRune (Glyphs.Diamond);
}
@@ -495,15 +494,15 @@ public override void OnDrawContent (Rectangle contentArea)
Driver.AddRune (Glyphs.UpArrow);
}
- if (Bounds.Height == 3)
+ if (Viewport.Height == 3)
{
Move (col, 1);
Driver.AddRune (Glyphs.Diamond);
}
- if (Bounds.Height > 1)
+ if (Viewport.Height > 1)
{
- Move (col, Bounds.Height - 1);
+ Move (col, Viewport.Height - 1);
Driver.AddRune (Glyphs.DownArrow);
}
}
@@ -524,8 +523,7 @@ public override void OnDrawContent (Rectangle contentArea)
by1 = Math.Max (by1 - 1, 0);
}
- Move (col, 0);
- Driver.AddRune (Glyphs.UpArrow);
+ AddRune (col, 0, Glyphs.UpArrow);
var hasTopTee = false;
var hasDiamond = false;
@@ -533,7 +531,6 @@ public override void OnDrawContent (Rectangle contentArea)
for (var y = 0; y < bh; y++)
{
- Move (col, y + 1);
if ((y < by1 || y > by2) && ((_position > 0 && !hasTopTee) || (hasTopTee && hasBottomTee)))
{
@@ -567,28 +564,26 @@ public override void OnDrawContent (Rectangle contentArea)
}
}
- Driver.AddRune (special);
+ AddRune (col, y + 1, special);
}
if (!hasTopTee)
{
- Move (col, Bounds.Height - 2);
- Driver.AddRune (Glyphs.TopTee);
+ AddRune (col, Viewport.Height - 2, Glyphs.TopTee);
}
- Move (col, Bounds.Height - 1);
- Driver.AddRune (Glyphs.DownArrow);
+ AddRune (col, Viewport.Height - 1, Glyphs.DownArrow);
}
}
else
{
- if (Bounds.Bottom < Bounds.Height - 1)
+ if (Viewport.Bottom < Viewport.Height - 1)
{
return;
}
- int row = Bounds.Height - 1;
- int bw = Bounds.Width;
+ int row = Viewport.Height - 1;
+ int bw = Viewport.Width;
Rune special;
if (bw < 4)
@@ -663,7 +658,7 @@ public override void OnDrawContent (Rectangle contentArea)
if (!hasLeftTee)
{
- Move (Bounds.Width - 2, row);
+ Move (Viewport.Width - 2, row);
Driver.AddRune (Glyphs.LeftTee);
}
@@ -685,7 +680,7 @@ public override bool OnEnter (View view)
internal bool CanScroll (int n, out int max, bool isVertical = false)
{
- if (Host?.Bounds.IsEmpty != false)
+ if (Host?.Viewport.IsEmpty != false)
{
max = 0;
@@ -706,7 +701,7 @@ internal bool CanScroll (int n, out int max, bool isVertical = false)
private bool CheckBothScrollBars (ScrollBarView scrollBarView, bool pending = false)
{
- int barsize = scrollBarView._vertical ? scrollBarView.Bounds.Height : scrollBarView.Bounds.Width;
+ int barsize = scrollBarView._vertical ? scrollBarView.Viewport.Height : scrollBarView.Viewport.Width;
if (barsize == 0 || barsize >= scrollBarView._size)
{
@@ -845,15 +840,15 @@ private void CreateBottomRightCorner (View host)
private int GetBarsize (bool isVertical)
{
- if (Host?.Bounds.IsEmpty != false)
+ if (Host?.Viewport.IsEmpty != false)
{
return 0;
}
return isVertical ? KeepContentAlwaysInViewport
- ? Host.Bounds.Height + (_showBothScrollIndicator ? -2 : -1)
+ ? Host.Viewport.Height + (_showBothScrollIndicator ? -2 : -1)
: 0 :
- KeepContentAlwaysInViewport ? Host.Bounds.Width + (_showBothScrollIndicator ? -2 : -1) : 0;
+ KeepContentAlwaysInViewport ? Host.Viewport.Width + (_showBothScrollIndicator ? -2 : -1) : 0;
}
private void Host_EnabledChanged (object sender, EventArgs e)
@@ -890,7 +885,7 @@ private void Host_VisibleChanged (object sender, EventArgs e)
private void ScrollBarView_Initialized (object sender, EventArgs e)
{
SetWidthHeight ();
- SetRelativeLayout (SuperView?.Frame ?? Host?.Frame ?? Frame);
+ SetRelativeLayout (SuperView?.Frame.Size ?? Host?.Frame.Size ?? Frame.Size);
if (OtherScrollBarView is null)
{
@@ -904,7 +899,22 @@ private void ScrollBarView_Initialized (object sender, EventArgs e)
// Helper to assist Initialized event handler
private void SetPosition (int newPosition)
{
- if (CanScroll (newPosition - _position, out int max, _vertical))
+ if (!IsInitialized)
+ {
+ // We're not initialized so we can't do anything fancy. Just cache value.
+ _position = newPosition;
+
+ return;
+ }
+
+ if (newPosition < 0)
+ {
+ _position = 0;
+ SetNeedsDisplay ();
+
+ return;
+ }
+ else if (CanScroll (newPosition - _position, out int max, _vertical))
{
if (max == newPosition - _position)
{
@@ -995,11 +1005,11 @@ private void ShowHideScrollBars (bool redraw = true)
}
SetWidthHeight ();
- SetRelativeLayout (SuperView?.Frame ?? Host.Frame);
+ SetRelativeLayout (SuperView?.Frame.Size ?? Host.Frame.Size);
if (_otherScrollBarView is { })
{
- OtherScrollBarView.SetRelativeLayout (SuperView?.Frame ?? Host.Frame);
+ OtherScrollBarView.SetRelativeLayout (SuperView?.Frame.Size ?? Host.Frame.Size);
}
if (_showBothScrollIndicator)
diff --git a/Terminal.Gui/Views/ScrollView.cs b/Terminal.Gui/Views/ScrollView.cs
index f021932ab9..a41ae12dfc 100644
--- a/Terminal.Gui/Views/ScrollView.cs
+++ b/Terminal.Gui/Views/ScrollView.cs
@@ -21,7 +21,7 @@ namespace Terminal.Gui;
///
/// The subviews that are added to this are offset by the
/// property. The view itself is a window into the space represented by the
-/// .
+/// .
///
/// Use the
///
@@ -33,7 +33,6 @@ public class ScrollView : View
private bool _autoHideScrollBars = true;
private View _contentBottomRightCorner;
private Point _contentOffset;
- private Size _contentSize;
private bool _keepContentAlwaysInViewport = true;
private bool _showHorizontalScrollIndicator;
private bool _showVerticalScrollIndicator;
@@ -85,14 +84,14 @@ public ScrollView ()
AddCommand (Command.ScrollDown, () => ScrollDown (1));
AddCommand (Command.ScrollLeft, () => ScrollLeft (1));
AddCommand (Command.ScrollRight, () => ScrollRight (1));
- AddCommand (Command.PageUp, () => ScrollUp (Bounds.Height));
- AddCommand (Command.PageDown, () => ScrollDown (Bounds.Height));
- AddCommand (Command.PageLeft, () => ScrollLeft (Bounds.Width));
- AddCommand (Command.PageRight, () => ScrollRight (Bounds.Width));
- AddCommand (Command.TopHome, () => ScrollUp (_contentSize.Height));
- AddCommand (Command.BottomEnd, () => ScrollDown (_contentSize.Height));
- AddCommand (Command.LeftHome, () => ScrollLeft (_contentSize.Width));
- AddCommand (Command.RightEnd, () => ScrollRight (_contentSize.Width));
+ AddCommand (Command.PageUp, () => ScrollUp (Viewport.Height));
+ AddCommand (Command.PageDown, () => ScrollDown (Viewport.Height));
+ AddCommand (Command.PageLeft, () => ScrollLeft (Viewport.Width));
+ AddCommand (Command.PageRight, () => ScrollRight (Viewport.Width));
+ AddCommand (Command.TopHome, () => ScrollUp (ContentSize.Height));
+ AddCommand (Command.BottomEnd, () => ScrollDown (ContentSize.Height));
+ AddCommand (Command.LeftHome, () => ScrollLeft (ContentSize.Width));
+ AddCommand (Command.RightEnd, () => ScrollRight (ContentSize.Width));
// Default keybindings for this view
KeyBindings.Add (Key.CursorUp, Command.ScrollUp);
@@ -134,6 +133,14 @@ public ScrollView ()
_vertical.ChangedPosition += delegate { ContentOffset = new Point (ContentOffset.X, _vertical.Position); };
_horizontal.ChangedPosition += delegate { ContentOffset = new Point (_horizontal.Position, ContentOffset.Y); };
};
+ ContentSizeChanged += ScrollViewContentSizeChanged;
+ }
+
+ private void ScrollViewContentSizeChanged (object sender, SizeChangedEventArgs e)
+ {
+ _contentView.Frame = new Rectangle (ContentOffset, e.Size with {Width = e.Size.Width-1, Height = e.Size.Height-1});
+ _vertical.Size = e.Size.Height;
+ _horizontal.Size = e.Size.Width;
}
private void Application_UnGrabbedMouse (object sender, ViewEventArgs e)
@@ -194,7 +201,6 @@ public Point ContentOffset
{
// 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;
}
@@ -203,23 +209,23 @@ public Point ContentOffset
}
}
- /// Represents the contents of the data shown inside the scrollview
- /// The size of the content.
- public Size ContentSize
- {
- get => _contentSize;
- set
- {
- if (_contentSize != value)
- {
- _contentSize = value;
- _contentView.Frame = new Rectangle (_contentOffset, value);
- _vertical.Size = _contentSize.Height;
- _horizontal.Size = _contentSize.Width;
- SetNeedsDisplay ();
- }
- }
- }
+ ///// Represents the contents of the data shown inside the scrollview
+ ///// The size of the content.
+ //public new Size ContentSize
+ //{
+ // get => ContentSize;
+ // set
+ // {
+ // if (ContentSize != value)
+ // {
+ // ContentSize = value;
+ // _contentView.Frame = new Rectangle (_contentOffset, value);
+ // _vertical.Size = ContentSize.Height;
+ // _horizontal.Size = ContentSize.Width;
+ // SetNeedsDisplay ();
+ // }
+ // }
+ //}
/// Get or sets if the view-port is kept always visible in the area of this
public bool KeepContentAlwaysInViewport
@@ -234,26 +240,26 @@ public bool KeepContentAlwaysInViewport
_horizontal.OtherScrollBarView.KeepContentAlwaysInViewport = value;
Point p = default;
- if (value && -_contentOffset.X + Bounds.Width > _contentSize.Width)
+ if (value && -_contentOffset.X + Viewport.Width > ContentSize.Width)
{
p = new Point (
- _contentSize.Width - Bounds.Width + (_showVerticalScrollIndicator ? 1 : 0),
+ ContentSize.Width - Viewport.Width + (_showVerticalScrollIndicator ? 1 : 0),
-_contentOffset.Y
);
}
- if (value && -_contentOffset.Y + Bounds.Height > _contentSize.Height)
+ if (value && -_contentOffset.Y + Viewport.Height > ContentSize.Height)
{
if (p == default (Point))
{
p = new Point (
-_contentOffset.X,
- _contentSize.Height - Bounds.Height + (_showHorizontalScrollIndicator ? 1 : 0)
+ ContentSize.Height - Viewport.Height + (_showHorizontalScrollIndicator ? 1 : 0)
);
}
else
{
- p.Y = _contentSize.Height - Bounds.Height + (_showHorizontalScrollIndicator ? 1 : 0);
+ p.Y = ContentSize.Height - Viewport.Height + (_showHorizontalScrollIndicator ? 1 : 0);
}
}
@@ -359,14 +365,12 @@ public override void Add (View view)
}
///
- public override void OnDrawContent (Rectangle contentArea)
+ public override void OnDrawContent (Rectangle viewport)
{
SetViewsNeedsDisplay ();
- Rectangle savedClip = ClipToBounds ();
-
// TODO: It's bad practice for views to always clear a view. It negates clipping.
- Clear (contentArea);
+ Clear ();
if (!string.IsNullOrEmpty (_contentView.Text) || _contentView.Subviews.Count > 0)
{
@@ -374,8 +378,6 @@ public override void OnDrawContent (Rectangle contentArea)
}
DrawScrollBars ();
-
- Driver.Clip = savedClip;
}
///
@@ -613,7 +615,7 @@ private void SetContentOffset (Point offset)
{
// INTENT: Unclear intent. How about a call to Offset?
_contentOffset = new Point (-Math.Abs (offset.X), -Math.Abs (offset.Y));
- _contentView.Frame = new Rectangle (_contentOffset, _contentSize);
+ _contentView.Frame = new Rectangle (_contentOffset, ContentSize);
int p = Math.Max (0, -_contentOffset.Y);
if (_vertical.Position != p)
@@ -644,7 +646,7 @@ private void ShowHideScrollBars ()
bool v = false, h = false;
var p = false;
- if (Bounds.Height == 0 || Bounds.Height > _contentSize.Height)
+ if (Viewport.Height == 0 || Viewport.Height > ContentSize.Height)
{
if (ShowVerticalScrollIndicator)
{
@@ -653,7 +655,7 @@ private void ShowHideScrollBars ()
v = false;
}
- else if (Bounds.Height > 0 && Bounds.Height == _contentSize.Height)
+ else if (Viewport.Height > 0 && Viewport.Height == ContentSize.Height)
{
p = true;
}
@@ -667,7 +669,7 @@ private void ShowHideScrollBars ()
v = true;
}
- if (Bounds.Width == 0 || Bounds.Width > _contentSize.Width)
+ if (Viewport.Width == 0 || Viewport.Width > ContentSize.Width)
{
if (ShowHorizontalScrollIndicator)
{
@@ -676,7 +678,7 @@ private void ShowHideScrollBars ()
h = false;
}
- else if (Bounds.Width > 0 && Bounds.Width == _contentSize.Width && p)
+ else if (Viewport.Width > 0 && Viewport.Width == ContentSize.Width && p)
{
if (ShowHorizontalScrollIndicator)
{
@@ -728,13 +730,13 @@ private void ShowHideScrollBars ()
if (v)
{
- _vertical.SetRelativeLayout (Bounds);
+ _vertical.SetRelativeLayout (Viewport.Size);
_vertical.Draw ();
}
if (h)
{
- _horizontal.SetRelativeLayout (Bounds);
+ _horizontal.SetRelativeLayout (Viewport.Size);
_horizontal.Draw ();
}
@@ -742,7 +744,7 @@ private void ShowHideScrollBars ()
if (v && h)
{
- _contentBottomRightCorner.SetRelativeLayout (Bounds);
+ _contentBottomRightCorner.SetRelativeLayout (Viewport.Size);
_contentBottomRightCorner.Draw ();
}
}
diff --git a/Terminal.Gui/Views/Slider.cs b/Terminal.Gui/Views/Slider.cs
index 7500037246..3d0ea3fb02 100644
--- a/Terminal.Gui/Views/Slider.cs
+++ b/Terminal.Gui/Views/Slider.cs
@@ -374,7 +374,7 @@ public bool AllowEmpty
}
///
- /// If the slider will be sized to fit the available space (the Bounds of the the
+ /// If the slider will be sized to fit the available space (the Viewport of the the
/// SuperView).
///
///
@@ -663,17 +663,17 @@ internal void CalcSpacingConfig ()
if (AutoSize)
{
- // Max size is SuperView's Bounds. Min Size is size that will fit.
+ // Max size is SuperView's Viewport. Min Size is size that will fit.
if (SuperView is { })
{
- // Calculate the size of the slider based on the size of the SuperView's Bounds.
+ // Calculate the size of the slider based on the size of the SuperView's Viewport.
if (_config._sliderOrientation == Orientation.Horizontal)
{
- size = int.Min (SuperView.Bounds.Width, CalcBestLength ());
+ size = int.Min (SuperView.Viewport.Width, CalcBestLength ());
}
else
{
- size = int.Min (SuperView.Bounds.Height, CalcBestLength ());
+ size = int.Min (SuperView.Viewport.Height, CalcBestLength ());
}
}
else
@@ -686,14 +686,14 @@ internal void CalcSpacingConfig ()
}
else
{
- // Fit Slider to the Bounds
+ // Fit Slider to the Viewport
if (_config._sliderOrientation == Orientation.Horizontal)
{
- size = Bounds.Width;
+ size = Viewport.Width;
}
else
{
- size = Bounds.Height;
+ size = Viewport.Height;
}
}
@@ -777,15 +777,15 @@ public void SetBoundsBestFit ()
if (_config._sliderOrientation == Orientation.Horizontal)
{
- Bounds = new (
- Bounds.Location,
+ Viewport = new (
+ Viewport.Location,
new (
int.Min (
- SuperView.Bounds.Width - adornmentsThickness.Horizontal,
+ SuperView.Viewport.Width - adornmentsThickness.Horizontal,
CalcBestLength ()
),
int.Min (
- SuperView.Bounds.Height - adornmentsThickness.Vertical,
+ SuperView.Viewport.Height - adornmentsThickness.Vertical,
CalcThickness ()
)
)
@@ -793,15 +793,15 @@ public void SetBoundsBestFit ()
}
else
{
- Bounds = new (
- Bounds.Location,
+ Viewport = new (
+ Viewport.Location,
new (
int.Min (
- SuperView.Bounds.Width - adornmentsThickness.Horizontal,
+ SuperView.Viewport.Width - adornmentsThickness.Horizontal,
CalcThickness ()
),
int.Min (
- SuperView.Bounds.Height - adornmentsThickness.Vertical,
+ SuperView.Viewport.Height - adornmentsThickness.Vertical,
CalcBestLength ()
)
)
@@ -988,7 +988,7 @@ public override void PositionCursor ()
if (TryGetPositionByOption (FocusedOption, out (int x, int y) position))
{
- if (IsInitialized && Bounds.Contains (position.x, position.y))
+ if (IsInitialized && Viewport.Contains (position.x, position.y))
{
Move (position.x, position.y);
}
@@ -996,7 +996,7 @@ public override void PositionCursor ()
}
///
- public override void OnDrawContent (Rectangle contentArea)
+ public override void OnDrawContent (Rectangle viewport)
{
// TODO: make this more surgical to reduce repaint
@@ -1009,9 +1009,9 @@ public override void OnDrawContent (Rectangle contentArea)
#if (DEBUG)
Driver?.SetAttribute (new Attribute (Color.White, Color.Red));
- for (var y = 0; y < contentArea.Height; y++)
+ for (var y = 0; y < viewport.Height; y++)
{
- for (var x = 0; x < contentArea.Width; x++)
+ for (var x = 0; x < viewport.Width; x++)
{
// MoveAndAdd (x, y, '·');
}
@@ -1073,7 +1073,7 @@ private string AlignText (string text, int width, TextAlignment textAlignment)
private void DrawSlider ()
{
// TODO: be more surgical on clear
- Clear (Bounds);
+ Clear ();
// Attributes
@@ -1241,7 +1241,7 @@ private void DrawSlider ()
}
}
- int remaining = isVertical ? Bounds.Height - y : Bounds.Width - x;
+ int remaining = isVertical ? Viewport.Height - y : Viewport.Width - x;
// Right Spacing
if (_config._showEndSpacing)
diff --git a/Terminal.Gui/Views/StatusBar.cs b/Terminal.Gui/Views/StatusBar.cs
index 0dfe31cf11..6dc96f663d 100644
--- a/Terminal.Gui/Views/StatusBar.cs
+++ b/Terminal.Gui/Views/StatusBar.cs
@@ -172,7 +172,7 @@ protected internal override bool OnMouseEvent (MouseEvent me)
}
///
- public override void OnDrawContent (Rectangle contentArea)
+ public override void OnDrawContent (Rectangle viewport)
{
Move (0, 0);
Driver.SetAttribute (GetNormalColor ());
diff --git a/Terminal.Gui/Views/TabView.cs b/Terminal.Gui/Views/TabView.cs
index aac2bcf94d..44375baf93 100644
--- a/Terminal.Gui/Views/TabView.cs
+++ b/Terminal.Gui/Views/TabView.cs
@@ -297,7 +297,7 @@ public void EnsureSelectedTabIsVisible ()
}
// if current viewport does not include the selected tab
- if (!CalculateViewport (Bounds).Any (r => Equals (SelectedTab, r.Tab)))
+ if (!CalculateViewport (Viewport).Any (r => Equals (SelectedTab, r.Tab)))
{
// Set scroll offset so the first tab rendered is the
TabScrollOffset = Math.Max (0, Tabs.IndexOf (SelectedTab));
@@ -311,14 +311,14 @@ public void EnsureSelectedTabIsVisible ()
public int EnsureValidScrollOffsets (int value) { return Math.Max (Math.Min (value, Tabs.Count - 1), 0); }
///
- public override void OnDrawContent (Rectangle contentArea)
+ public override void OnDrawContent (Rectangle viewport)
{
Driver.SetAttribute (GetNormalColor ());
if (Tabs.Any ())
{
- Rectangle savedClip = ClipToBounds ();
- _tabsBar.OnDrawContent (contentArea);
+ Rectangle savedClip = SetClip ();
+ _tabsBar.OnDrawContent (viewport);
_contentView.SetNeedsDisplay ();
_contentView.Draw ();
Driver.Clip = savedClip;
@@ -326,7 +326,7 @@ public override void OnDrawContent (Rectangle contentArea)
}
///
- public override void OnDrawContentComplete (Rectangle contentArea) { _tabsBar.OnDrawContentComplete (contentArea); }
+ public override void OnDrawContentComplete (Rectangle viewport) { _tabsBar.OnDrawContentComplete (viewport); }
///
/// Removes the given from . Caller is responsible for disposing the
@@ -657,12 +657,12 @@ protected internal override bool OnMouseEvent (MouseEvent me)
return false;
}
- public override void OnDrawContent (Rectangle contentArea)
+ public override void OnDrawContent (Rectangle viewport)
{
- _host._tabLocations = _host.CalculateViewport (Bounds).ToArray ();
+ _host._tabLocations = _host.CalculateViewport (Viewport).ToArray ();
// clear any old text
- Clear (contentArea);
+ Clear ();
RenderTabLine ();
@@ -670,7 +670,7 @@ public override void OnDrawContent (Rectangle contentArea)
Driver.SetAttribute (GetNormalColor ());
}
- public override void OnDrawContentComplete (Rectangle contentArea)
+ public override void OnDrawContentComplete (Rectangle viewport)
{
if (_host._tabLocations is null)
{
@@ -683,7 +683,7 @@ public override void OnDrawContentComplete (Rectangle contentArea)
for (var i = 0; i < tabLocations.Length; i++)
{
View tab = tabLocations [i].Tab;
- Rectangle vts = tab.BoundsToScreen (tab.Bounds);
+ Rectangle vts = tab.ViewportToScreen (tab.Viewport);
var lc = new LineCanvas ();
int selectedOffset = _host.Style.ShowTopLine && tabLocations [i].IsSelected ? 0 : 1;
@@ -1115,7 +1115,7 @@ public override void OnDrawContentComplete (Rectangle contentArea)
int lastSelectedTab = !_host.Style.ShowTopLine && i == selectedTab ? 1 :
_host.Style.TabsOnBottom ? 1 : 0;
- Rectangle tabsBarVts = BoundsToScreen (Bounds);
+ Rectangle tabsBarVts = ViewportToScreen (Viewport);
int lineLength = tabsBarVts.Right - vts.Right;
// Right horizontal line
@@ -1238,7 +1238,7 @@ private void RenderTabLine ()
View selected = null;
int topLine = _host.Style.ShowTopLine ? 1 : 0;
- int width = Bounds.Width;
+ int width = Viewport.Width;
foreach (TabToRender toRender in tabLocations)
{
@@ -1314,7 +1314,7 @@ private void RenderTabLine ()
}
tab.TextFormatter.Draw (
- tab.BoundsToScreen (tab.Bounds),
+ tab.ViewportToScreen (tab.Viewport),
prevAttr,
ColorScheme.HotNormal
);
@@ -1360,7 +1360,7 @@ private void RenderUnderline ()
// if there are more tabs to the right not visible
if (ShouldDrawRightScrollIndicator ())
{
- _rightScrollIndicator.X = Bounds.Width - 1;
+ _rightScrollIndicator.X = Viewport.Width - 1;
_rightScrollIndicator.Y = y;
// indicate that
diff --git a/Terminal.Gui/Views/TableView/ListTableSource.cs b/Terminal.Gui/Views/TableView/ListTableSource.cs
index 8695155019..1286e2660a 100644
--- a/Terminal.Gui/Views/TableView/ListTableSource.cs
+++ b/Terminal.Gui/Views/TableView/ListTableSource.cs
@@ -109,12 +109,12 @@ private int CalculateColumns ()
if (Style.Orientation == Orientation.Vertical != Style.ScrollParallel)
{
- float f = (float)_tableView.Bounds.Height - _tableView.GetHeaderHeight ();
+ float f = (float)_tableView.Viewport.Height - _tableView.GetHeaderHeight ();
cols = (int)Math.Ceiling (Count / f);
}
else
{
- cols = (int)Math.Ceiling (((float)_tableView.Bounds.Width - 1) / colWidth) - 2;
+ cols = (int)Math.Ceiling (((float)_tableView.Viewport.Width - 1) / colWidth) - 2;
}
return cols > 1 ? cols : 1;
@@ -179,7 +179,7 @@ private DataTable CreateTable (int cols = 1)
private void TableView_DrawContent (object sender, DrawEventArgs e)
{
- if (!_tableView.Bounds.Equals (_lastBounds)
+ if (!_tableView.Viewport.Equals (_lastBounds)
|| _tableView.MaxCellWidth != _lastMaxCellWidth
|| _tableView.MinCellWidth != _lastMinCellWidth
|| Style != _lastStyle
@@ -188,7 +188,7 @@ private void TableView_DrawContent (object sender, DrawEventArgs e)
DataTable = CreateTable (CalculateColumns ());
}
- _lastBounds = _tableView.Bounds;
+ _lastBounds = _tableView.Viewport;
_lastMinCellWidth = _tableView.MaxCellWidth;
_lastMaxCellWidth = _tableView.MaxCellWidth;
_lastStyle = Style;
diff --git a/Terminal.Gui/Views/TableView/TableView.cs b/Terminal.Gui/Views/TableView/TableView.cs
index 34d3bfbe8d..e82a006661 100644
--- a/Terminal.Gui/Views/TableView/TableView.cs
+++ b/Terminal.Gui/Views/TableView/TableView.cs
@@ -483,7 +483,7 @@ public ITableSource Table
return null;
}
- IEnumerable viewPort = CalculateViewport (Bounds);
+ IEnumerable viewPort = CalculateViewport (Viewport);
int headerHeight = GetHeaderHeightIfAny ();
@@ -502,7 +502,7 @@ public ITableSource Table
}
// the cell is way down below the scroll area and off the screen
- if (tableRow > RowOffset + (Bounds.Height - headerHeight))
+ if (tableRow > RowOffset + (Viewport.Height - headerHeight))
{
return null;
}
@@ -577,7 +577,7 @@ public void EnsureSelectedCellIsVisible ()
return;
}
- ColumnToRender [] columnsToRender = CalculateViewport (Bounds).ToArray ();
+ ColumnToRender [] columnsToRender = CalculateViewport (Viewport).ToArray ();
int headerHeight = GetHeaderHeightIfAny ();
//if we have scrolled too far to the left
@@ -595,7 +595,7 @@ public void EnsureSelectedCellIsVisible ()
while (SelectedColumn > columnsToRender.Max (r => r.Column))
{
ColumnOffset++;
- columnsToRender = CalculateViewport (Bounds).ToArray ();
+ columnsToRender = CalculateViewport (Viewport).ToArray ();
// if we are already scrolled to the last column then break
// this will prevent any theoretical infinite loop
@@ -612,9 +612,9 @@ public void EnsureSelectedCellIsVisible ()
}
//if we have scrolled too far down
- if (SelectedRow >= RowOffset + (Bounds.Height - headerHeight))
+ if (SelectedRow >= RowOffset + (Viewport.Height - headerHeight))
{
- RowOffset = SelectedRow - (Bounds.Height - headerHeight) + 1;
+ RowOffset = SelectedRow - (Viewport.Height - headerHeight) + 1;
}
//if we have scrolled too far up
@@ -896,9 +896,9 @@ protected internal override bool OnMouseEvent (MouseEvent me)
}
///
- public override void OnDrawContent (Rectangle contentArea)
+ public override void OnDrawContent (Rectangle viewport)
{
- base.OnDrawContent (contentArea);
+ base.OnDrawContent (viewport);
Move (0, 0);
@@ -906,12 +906,12 @@ public override void OnDrawContent (Rectangle contentArea)
scrollLeftPoint = null;
// What columns to render at what X offset in viewport
- ColumnToRender [] columnsToRender = CalculateViewport (Bounds).ToArray ();
+ ColumnToRender [] columnsToRender = CalculateViewport (Viewport).ToArray ();
Driver.SetAttribute (GetNormalColor ());
//invalidate current row (prevents scrolling around leaving old characters in the frame
- Driver.AddStr (new string (' ', Bounds.Width));
+ Driver.AddStr (new string (' ', Viewport.Width));
var line = 0;
@@ -925,7 +925,7 @@ public override void OnDrawContent (Rectangle contentArea)
*/
if (Style.ShowHorizontalHeaderOverline)
{
- RenderHeaderOverline (line, Bounds.Width, columnsToRender);
+ RenderHeaderOverline (line, Viewport.Width, columnsToRender);
line++;
}
@@ -937,7 +937,7 @@ public override void OnDrawContent (Rectangle contentArea)
if (Style.ShowHorizontalHeaderUnderline)
{
- RenderHeaderUnderline (line, Bounds.Width, columnsToRender);
+ RenderHeaderUnderline (line, Viewport.Width, columnsToRender);
line++;
}
}
@@ -945,9 +945,9 @@ public override void OnDrawContent (Rectangle contentArea)
int headerLinesConsumed = line;
//render the cells
- for (; line < Bounds.Height; line++)
+ for (; line < Viewport.Height; line++)
{
- ClearLine (line, Bounds.Width);
+ ClearLine (line, Viewport.Width);
//work out what Row to render
int rowToRender = RowOffset + (line - headerLinesConsumed);
@@ -963,7 +963,7 @@ public override void OnDrawContent (Rectangle contentArea)
{
if (rowToRender == Table.Rows && Style.ShowHorizontalBottomline)
{
- RenderBottomLine (line, Bounds.Width, columnsToRender);
+ RenderBottomLine (line, Viewport.Width, columnsToRender);
}
continue;
@@ -1001,7 +1001,7 @@ public override bool OnProcessKeyDown (Key keyEvent)
/// true to extend the current selection (if any) instead of replacing
public void PageDown (bool extend)
{
- ChangeSelectionByOffset (0, Bounds.Height - GetHeaderHeightIfAny (), extend);
+ ChangeSelectionByOffset (0, Viewport.Height - GetHeaderHeightIfAny (), extend);
Update ();
}
@@ -1009,7 +1009,7 @@ public void PageDown (bool extend)
/// true to extend the current selection (if any) instead of replacing
public void PageUp (bool extend)
{
- ChangeSelectionByOffset (0, -(Bounds.Height - GetHeaderHeightIfAny ()), extend);
+ ChangeSelectionByOffset (0, -(Viewport.Height - GetHeaderHeightIfAny ()), extend);
Update ();
}
@@ -1073,7 +1073,7 @@ public override void PositionCursor ()
return null;
}
- IEnumerable viewPort = CalculateViewport (Bounds);
+ IEnumerable viewPort = CalculateViewport (Viewport);
int headerHeight = GetHeaderHeightIfAny ();
@@ -1658,7 +1658,7 @@ private void RenderHeaderMidline (int row, ColumnToRender [] columnsToRender)
// Renders something like:
// │ArithmeticComparator│chi │Healthboard│Interpretation│Labnumber│
- ClearLine (row, Bounds.Width);
+ ClearLine (row, Viewport.Width);
//render start of line
if (style.ShowVerticalHeaderLines)
@@ -1688,7 +1688,7 @@ private void RenderHeaderMidline (int row, ColumnToRender [] columnsToRender)
//render end of line
if (style.ShowVerticalHeaderLines)
{
- AddRune (Bounds.Width - 1, row, Glyphs.VLine);
+ AddRune (Viewport.Width - 1, row, Glyphs.VLine);
}
}
@@ -1846,7 +1846,7 @@ private void RenderRow (int row, int rowToRender, ColumnToRender [] columnsToRen
}
Driver.SetAttribute (color);
- Driver.AddStr (new string (' ', Bounds.Width));
+ Driver.AddStr (new string (' ', Viewport.Width));
// Render cells for each visible header for the current row
for (var i = 0; i < columnsToRender.Length; i++)
@@ -1955,7 +1955,7 @@ private void RenderRow (int row, int rowToRender, ColumnToRender [] columnsToRen
//render start and end of line
AddRune (0, row, Glyphs.VLine);
- AddRune (Bounds.Width - 1, row, Glyphs.VLine);
+ AddRune (Viewport.Width - 1, row, Glyphs.VLine);
}
}
diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs
index e97a27ec51..5ee281596e 100644
--- a/Terminal.Gui/Views/TextField.cs
+++ b/Terminal.Gui/Views/TextField.cs
@@ -973,7 +973,7 @@ public void MoveEnd ()
}
///
- public override void OnDrawContent (Rectangle contentArea)
+ public override void OnDrawContent (Rectangle viewport)
{
_isDrawing = true;
@@ -1196,8 +1196,8 @@ public override void PositionCursor ()
int pos = _cursorPosition - ScrollOffset + Math.Min (Frame.X, 0);
int offB = OffSetBackground ();
- Rectangle containerFrame = SuperView?.BoundsToScreen (SuperView.Bounds) ?? default (Rectangle);
- Rectangle thisFrame = BoundsToScreen (Bounds);
+ Rectangle containerFrame = SuperView?.ViewportToScreen (SuperView.Viewport) ?? default (Rectangle);
+ Rectangle thisFrame = ViewportToScreen (Viewport);
if (pos > -1
&& col >= pos
@@ -1746,7 +1746,7 @@ private void MoveWordRightExtend ()
}
}
- // BUGBUG: This assumes Frame == Bounds. It's also not clear what the intention is. For now, changed to always return 0.
+ // BUGBUG: This assumes Frame == Viewport. It's also not clear what the intention is. For now, changed to always return 0.
private int OffSetBackground ()
{
var offB = 0;
@@ -1878,9 +1878,9 @@ private void RenderCaption ()
Move (0, 0);
string render = Caption;
- if (render.GetColumns () > Bounds.Width)
+ if (render.GetColumns () > Viewport.Width)
{
- render = render [..Bounds.Width];
+ render = render [..Viewport.Width];
}
Driver.AddStr (render);
@@ -1941,9 +1941,9 @@ private void TextField_Initialized (object sender, EventArgs e)
{
_cursorPosition = Text.GetRuneCount ();
- if (Bounds.Width > 0)
+ if (Viewport.Width > 0)
{
- ScrollOffset = _cursorPosition > Bounds.Width + 1 ? _cursorPosition - Bounds.Width + 1 : 0;
+ ScrollOffset = _cursorPosition > Viewport.Width + 1 ? _cursorPosition - Viewport.Width + 1 : 0;
}
Autocomplete.HostControl = this;
diff --git a/Terminal.Gui/Views/TextValidateField.cs b/Terminal.Gui/Views/TextValidateField.cs
index a86cd910f5..a62fa4a549 100644
--- a/Terminal.Gui/Views/TextValidateField.cs
+++ b/Terminal.Gui/Views/TextValidateField.cs
@@ -537,7 +537,7 @@ protected internal override bool OnMouseEvent (MouseEvent mouseEvent)
{
if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed))
{
- int c = _provider.Cursor (mouseEvent.X - GetMargins (Bounds.Width).left);
+ int c = _provider.Cursor (mouseEvent.X - GetMargins (Viewport.Width).left);
if (_provider.Fixed == false && TextAlignment == TextAlignment.Right && Text.Length > 0)
{
@@ -555,7 +555,7 @@ protected internal override bool OnMouseEvent (MouseEvent mouseEvent)
}
///
- public override void OnDrawContent (Rectangle contentArea)
+ public override void OnDrawContent (Rectangle viewport)
{
if (_provider is null)
{
@@ -568,7 +568,7 @@ public override void OnDrawContent (Rectangle contentArea)
Color bgcolor = !IsValid ? new Color (Color.BrightRed) : ColorScheme.Focus.Background;
var textColor = new Attribute (ColorScheme.Focus.Foreground, bgcolor);
- (int margin_left, int margin_right) = GetMargins (Bounds.Width);
+ (int margin_left, int margin_right) = GetMargins (Viewport.Width);
Move (0, 0);
@@ -642,7 +642,7 @@ public override bool OnProcessKeyDown (Key a)
///
public override void PositionCursor ()
{
- (int left, _) = GetMargins (Bounds.Width);
+ (int left, _) = GetMargins (Viewport.Width);
// Fixed = true, is for inputs that have fixed width, like masked ones.
// Fixed = false, is for normal input.
@@ -660,7 +660,7 @@ public override void PositionCursor ()
Move (curPos, 0);
}
- if (curPos < 0 || curPos >= Bounds.Width)
+ if (curPos < 0 || curPos >= Viewport.Width)
{
Application.Driver.SetCursorVisibility (CursorVisibility.Invisible);
}
diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs
index 038e1ced38..a20b4cbbc4 100644
--- a/Terminal.Gui/Views/TextView.cs
+++ b/Terminal.Gui/Views/TextView.cs
@@ -3586,7 +3586,7 @@ public virtual void OnContentsChanged ()
}
///
- public override void OnDrawContent (Rectangle contentArea)
+ public override void OnDrawContent (Rectangle viewport)
{
_isDrawing = true;
@@ -3649,7 +3649,7 @@ public override void OnDrawContent (Rectangle contentArea)
AddRune (col, row, rune);
}
- if (!TextModel.SetCol (ref col, contentArea.Right, cols))
+ if (!TextModel.SetCol (ref col, viewport.Right, cols))
{
break;
}
@@ -3672,7 +3672,7 @@ public override void OnDrawContent (Rectangle contentArea)
if (row < bottom)
{
SetNormalColor ();
- ClearRegion (contentArea.Left, row, right, bottom);
+ ClearRegion (viewport.Left, row, right, bottom);
}
PositionCursor ();
diff --git a/Terminal.Gui/Views/TileView.cs b/Terminal.Gui/Views/TileView.cs
index 8ef1c5b4aa..cbc2b9f22e 100644
--- a/Terminal.Gui/Views/TileView.cs
+++ b/Terminal.Gui/Views/TileView.cs
@@ -156,19 +156,19 @@ public override void LayoutSubviews ()
return;
}
- Rectangle contentArea = Bounds;
+ Rectangle viewport = Viewport;
if (HasBorder ())
{
- contentArea = new (
- contentArea.X + 1,
- contentArea.Y + 1,
- Math.Max (0, contentArea.Width - 2),
- Math.Max (0, contentArea.Height - 2)
- );
+ viewport = new (
+ viewport.X + 1,
+ viewport.Y + 1,
+ Math.Max (0, viewport.Width - 2),
+ Math.Max (0, viewport.Height - 2)
+ );
}
- Setup (contentArea);
+ Setup (viewport);
base.LayoutSubviews ();
}
@@ -179,12 +179,13 @@ public override void LayoutSubviews ()
public override bool OnDrawAdornments () { return false; }
///
- public override void OnDrawContent (Rectangle contentArea)
+ public override void OnDrawContent (Rectangle viewport)
{
Driver.SetAttribute (ColorScheme.Normal);
- Clear (contentArea);
- base.OnDrawContent (contentArea);
+ Clear ();
+
+ base.OnDrawContent (viewport);
var lc = new LineCanvas ();
@@ -195,19 +196,19 @@ public override void OnDrawContent (Rectangle contentArea)
{
if (HasBorder ())
{
- lc.AddLine (Point.Empty, Bounds.Width, Orientation.Horizontal, LineStyle);
- lc.AddLine (Point.Empty, Bounds.Height, Orientation.Vertical, LineStyle);
+ lc.AddLine (Point.Empty, Viewport.Width, Orientation.Horizontal, LineStyle);
+ lc.AddLine (Point.Empty, Viewport.Height, Orientation.Vertical, LineStyle);
lc.AddLine (
- new Point (Bounds.Width - 1, Bounds.Height - 1),
- -Bounds.Width,
+ new Point (Viewport.Width - 1, Viewport.Height - 1),
+ -Viewport.Width,
Orientation.Horizontal,
LineStyle
);
lc.AddLine (
- new Point (Bounds.Width - 1, Bounds.Height - 1),
- -Bounds.Height,
+ new Point (Viewport.Width - 1, Viewport.Height - 1),
+ -Viewport.Height,
Orientation.Vertical,
LineStyle
);
@@ -217,7 +218,7 @@ public override void OnDrawContent (Rectangle contentArea)
{
bool isRoot = _splitterLines.Contains (line);
- Rectangle screen = line.BoundsToScreen (Rectangle.Empty);
+ Rectangle screen = line.ViewportToScreen (Rectangle.Empty);
Point origin = ScreenToFrame (screen.X, screen.Y);
int length = line.Orientation == Orientation.Horizontal ? line.Frame.Width : line.Frame.Height;
@@ -241,7 +242,7 @@ public override void OnDrawContent (Rectangle contentArea)
Driver.SetAttribute (ColorScheme.Normal);
- foreach (KeyValuePair p in lc.GetMap (Bounds))
+ foreach (KeyValuePair p in lc.GetMap (Viewport))
{
AddRune (p.Key.X, p.Key.Y, p.Value);
}
@@ -423,7 +424,7 @@ public bool SetSplitterPos (int idx, Pos value)
);
}
- int fullSpace = _orientation == Orientation.Vertical ? Bounds.Width : Bounds.Height;
+ int fullSpace = _orientation == Orientation.Vertical ? Viewport.Width : Viewport.Height;
if (fullSpace != 0 && !IsValidNewSplitterPos (idx, value, fullSpace))
{
@@ -758,9 +759,9 @@ private bool RecursiveContains (IEnumerable haystack, View needle)
return false;
}
- private void Setup (Rectangle contentArea)
+ private void Setup (Rectangle viewport)
{
- if (contentArea.IsEmpty || contentArea.Height <= 0 || contentArea.Width <= 0)
+ if (viewport.IsEmpty || viewport.Height <= 0 || viewport.Width <= 0)
{
return;
}
@@ -803,18 +804,20 @@ private void Setup (Rectangle contentArea)
if (Orientation == Orientation.Vertical)
{
- tile.ContentView.X = i == 0 ? contentArea.X : Pos.Right (visibleSplitterLines [i - 1]);
- tile.ContentView.Y = contentArea.Y;
- tile.ContentView.Height = contentArea.Height;
- tile.ContentView.Width = GetTileWidthOrHeight (i, Bounds.Width, visibleTiles, visibleSplitterLines);
+ tile.ContentView.X = i == 0 ? viewport.X : Pos.Right (visibleSplitterLines [i - 1]);
+ tile.ContentView.Y = viewport.Y;
+ tile.ContentView.Height = viewport.Height;
+ tile.ContentView.Width = GetTileWidthOrHeight (i, Viewport.Width, visibleTiles, visibleSplitterLines);
}
else
{
- tile.ContentView.X = contentArea.X;
- tile.ContentView.Y = i == 0 ? contentArea.Y : Pos.Bottom (visibleSplitterLines [i - 1]);
- tile.ContentView.Width = contentArea.Width;
- tile.ContentView.Height = GetTileWidthOrHeight (i, Bounds.Height, visibleTiles, visibleSplitterLines);
+ tile.ContentView.X = viewport.X;
+ tile.ContentView.Y = i == 0 ? viewport.Y : Pos.Bottom (visibleSplitterLines [i - 1]);
+ tile.ContentView.Width = viewport.Width;
+ tile.ContentView.Height = GetTileWidthOrHeight (i, Viewport.Height, visibleTiles, visibleSplitterLines);
}
+ // BUGBUG: This should not be needed. If any of the pos/dim setters above actually changed values, NeedsDisplay should have already been set.
+ tile.ContentView.SetNeedsDisplay ();
}
}
@@ -837,7 +840,7 @@ public TileTitleToRender (TileView parent, Tile tile, int depth)
///
public Point GetLocalCoordinateForTitle (TileView intoCoordinateSpace)
{
- Rectangle screen = Tile.ContentView.BoundsToScreen (Rectangle.Empty);
+ Rectangle screen = Tile.ContentView.ViewportToScreen (Rectangle.Empty);
return intoCoordinateSpace.ScreenToFrame (screen.X, screen.Y - 1);
}
@@ -845,7 +848,7 @@ internal string GetTrimmedTitle ()
{
Dim spaceDim = Tile.ContentView.Width;
- int spaceAbs = spaceDim.Anchor (Parent.Bounds.Width);
+ int spaceAbs = spaceDim.Anchor (Parent.Viewport.Width);
var title = $" {Tile.Title} ";
@@ -893,13 +896,13 @@ public void DrawSplitterSymbol ()
{
if (dragPosition is { } || CanFocus)
{
- Point location = moveRuneRenderLocation ?? new Point (Bounds.Width / 2, Bounds.Height / 2);
+ Point location = moveRuneRenderLocation ?? new Point (Viewport.Width / 2, Viewport.Height / 2);
AddRune (location.X, location.Y, Glyphs.Diamond);
}
}
- protected internal override bool OnMouseEvent (MouseEvent mouseEvent)
+ protected internal override bool OnMouseEvent (MouseEvent mouseEvent)
{
if (!dragPosition.HasValue && mouseEvent.Flags == MouseFlags.Button1Pressed)
{
@@ -919,7 +922,7 @@ protected internal override bool OnMouseEvent (MouseEvent mouseEvent)
{
moveRuneRenderLocation = new Point (
0,
- Math.Max (1, Math.Min (Bounds.Height - 2, mouseEvent.Y))
+ Math.Max (1, Math.Min (Viewport.Height - 2, mouseEvent.Y))
);
}
}
@@ -943,7 +946,7 @@ protected internal override bool OnMouseEvent (MouseEvent mouseEvent)
{
int dx = mouseEvent.X - dragPosition.Value.X;
Parent.SetSplitterPos (Idx, Offset (X, dx));
- moveRuneRenderLocation = new Point (0, Math.Max (1, Math.Min (Bounds.Height - 2, mouseEvent.Y)));
+ moveRuneRenderLocation = new Point (0, Math.Max (1, Math.Min (Viewport.Height - 2, mouseEvent.Y)));
}
Parent.SetNeedsDisplay ();
@@ -969,9 +972,9 @@ protected internal override bool OnMouseEvent (MouseEvent mouseEvent)
return false;
}
- public override void OnDrawContent (Rectangle contentArea)
+ public override void OnDrawContent (Rectangle viewport)
{
- base.OnDrawContent (contentArea);
+ base.OnDrawContent (viewport);
DrawSplitterSymbol ();
}
@@ -988,7 +991,7 @@ public override void PositionCursor ()
{
base.PositionCursor ();
- Point location = moveRuneRenderLocation ?? new Point (Bounds.Width / 2, Bounds.Height / 2);
+ Point location = moveRuneRenderLocation ?? new Point (Viewport.Width / 2, Viewport.Height / 2);
Move (location.X, location.Y);
}
@@ -1032,10 +1035,10 @@ private bool FinalisePosition (Pos oldValue, Pos newValue)
{
if (Orientation == Orientation.Horizontal)
{
- return Parent.SetSplitterPos (Idx, ConvertToPosFactor (newValue, Parent.Bounds.Height));
+ return Parent.SetSplitterPos (Idx, ConvertToPosFactor (newValue, Parent.Viewport.Height));
}
- return Parent.SetSplitterPos (Idx, ConvertToPosFactor (newValue, Parent.Bounds.Width));
+ return Parent.SetSplitterPos (Idx, ConvertToPosFactor (newValue, Parent.Viewport.Width));
}
return Parent.SetSplitterPos (Idx, newValue);
@@ -1071,8 +1074,8 @@ private Pos Offset (Pos pos, int delta)
{
int posAbsolute = pos.Anchor (
Orientation == Orientation.Horizontal
- ? Parent.Bounds.Height
- : Parent.Bounds.Width
+ ? Parent.Viewport.Height
+ : Parent.Viewport.Width
);
return posAbsolute + delta;
diff --git a/Terminal.Gui/Views/Toplevel.cs b/Terminal.Gui/Views/Toplevel.cs
index 69bcb2d4db..e5f4b64b14 100644
--- a/Terminal.Gui/Views/Toplevel.cs
+++ b/Terminal.Gui/Views/Toplevel.cs
@@ -29,7 +29,7 @@ public partial class Toplevel : View
///
public Toplevel ()
{
- Arrangement = ViewArrangement.Movable;
+ Arrangement = ViewArrangement.Fixed;
Width = Dim.Fill ();
Height = Dim.Fill ();
@@ -249,7 +249,7 @@ public virtual void OnAlternateForwardKeyChanged (KeyChangedEventArgs e)
}
///
- public override void OnDrawContent (Rectangle contentArea)
+ public override void OnDrawContent (Rectangle viewport)
{
if (!Visible)
{
@@ -268,12 +268,12 @@ public override void OnDrawContent (Rectangle contentArea)
{
foreach (Toplevel top in Application.OverlappedChildren.AsEnumerable ().Reverse ())
{
- if (top.Frame.IntersectsWith (Bounds))
+ if (top.Frame.IntersectsWith (Viewport))
{
if (top != this && !top.IsCurrentTop && !OutsideTopFrame (top) && top.Visible)
{
top.SetNeedsLayout ();
- top.SetNeedsDisplay (top.Bounds);
+ top.SetNeedsDisplay (top.Viewport);
top.Draw ();
top.OnRenderLineCanvas ();
}
@@ -284,20 +284,20 @@ public override void OnDrawContent (Rectangle contentArea)
// This should not be here, but in base
foreach (View view in Subviews)
{
- if (view.Frame.IntersectsWith (Bounds) && !OutsideTopFrame (this))
+ if (view.Frame.IntersectsWith (Viewport) && !OutsideTopFrame (this))
{
//view.SetNeedsLayout ();
- view.SetNeedsDisplay (view.Bounds);
+ view.SetNeedsDisplay ();
view.SetSubViewNeedsDisplay ();
}
}
- base.OnDrawContent (contentArea);
+ base.OnDrawContent (viewport);
// This is causing the menus drawn incorrectly if UseSubMenusSingleFrame is true
//if (this.MenuBar is { } && this.MenuBar.IsMenuOpen && this.MenuBar.openMenu is { }) {
// // TODO: Hack until we can get compositing working right.
- // this.MenuBar.openMenu.Redraw (this.MenuBar.openMenu.Bounds);
+ // this.MenuBar.openMenu.Redraw (this.MenuBar.openMenu.Viewport);
//}
}
}
@@ -380,6 +380,7 @@ public override void PositionCursor ()
/// The Toplevel to adjust.
public virtual void PositionToplevel (Toplevel top)
{
+
View superView = GetLocationEnsuringFullVisibility (
top,
top.Frame.X,
@@ -547,7 +548,7 @@ public virtual void RequestStop ()
/// to dispose objects after calling .
///
public event EventHandler Unloaded;
-
+
internal void AddMenuStatusBar (View view)
{
if (view is MenuBar)
diff --git a/Terminal.Gui/Views/TreeView/TreeView.cs b/Terminal.Gui/Views/TreeView/TreeView.cs
index 941948737b..2eb67267c1 100644
--- a/Terminal.Gui/Views/TreeView/TreeView.cs
+++ b/Terminal.Gui/Views/TreeView/TreeView.cs
@@ -742,10 +742,10 @@ public void EnsureVisible (T model)
//if user has scrolled up too far to see their selection
ScrollOffsetVertical = idx;
}
- else if (idx >= ScrollOffsetVertical + Bounds.Height - leaveSpace)
+ else if (idx >= ScrollOffsetVertical + Viewport.Height - leaveSpace)
{
//if user has scrolled off bottom of visible tree
- ScrollOffsetVertical = Math.Max (0, idx + 1 - (Bounds.Height - leaveSpace));
+ ScrollOffsetVertical = Math.Max (0, idx + 1 - (Viewport.Height - leaveSpace));
}
}
@@ -868,12 +868,12 @@ public int GetContentWidth (bool visible)
}
// If control has no height to it then there is no visible area for content
- if (Bounds.Height == 0)
+ if (Viewport.Height == 0)
{
return 0;
}
- return map.Skip (ScrollOffsetVertical).Take (Bounds.Height).Max (b => b.GetWidth (Driver));
+ return map.Skip (ScrollOffsetVertical).Take (Viewport.Height).Max (b => b.GetWidth (Driver));
}
return map.Max (b => b.GetWidth (Driver));
@@ -886,13 +886,13 @@ public int GetContentWidth (bool visible)
/// If you have screen coordinates then use to translate these into the client area of
/// the .
///
- /// The row of the of the .
+ /// The row of the of the .
/// The object currently displayed on this row or null.
public T GetObjectOnRow (int row) { return HitTest (row)?.Model; }
///
///
- /// Returns the Y coordinate within the of the tree at which
+ /// Returns the Y coordinate within the of the tree at which
/// would be displayed or null if it is not currently exposed (e.g. its parent is collapsed).
///
///
@@ -967,7 +967,7 @@ public void GoTo (T toSelect)
public void GoToEnd ()
{
IReadOnlyCollection> map = BuildLineMap ();
- ScrollOffsetVertical = Math.Max (0, map.Count - Bounds.Height + 1);
+ ScrollOffsetVertical = Math.Max (0, map.Count - Viewport.Height + 1);
SelectedObject = map.LastOrDefault ()?.Model;
SetNeedsDisplay ();
@@ -1129,12 +1129,12 @@ protected internal override bool OnMouseEvent (MouseEvent me)
/// Moves the selection down by the height of the control (1 page).
/// True if the navigation should add the covered nodes to the selected current selection.
///
- public void MovePageDown (bool expandSelection = false) { AdjustSelection (Bounds.Height, expandSelection); }
+ public void MovePageDown (bool expandSelection = false) { AdjustSelection (Viewport.Height, expandSelection); }
/// Moves the selection up by the height of the control (1 page).
/// True if the navigation should add the covered nodes to the selected current selection.
///
- public void MovePageUp (bool expandSelection = false) { AdjustSelection (-Bounds.Height, expandSelection); }
+ public void MovePageUp (bool expandSelection = false) { AdjustSelection (-Viewport.Height, expandSelection); }
///
/// This event is raised when an object is activated e.g. by double clicking or pressing
@@ -1143,7 +1143,7 @@ protected internal override bool OnMouseEvent (MouseEvent me)
public event EventHandler> ObjectActivated;
///
- public override void OnDrawContent (Rectangle contentArea)
+ public override void OnDrawContent (Rectangle viewport)
{
if (roots is null)
{
@@ -1160,7 +1160,7 @@ public override void OnDrawContent (Rectangle contentArea)
IReadOnlyCollection> map = BuildLineMap ();
- for (var line = 0; line < Bounds.Height; line++)
+ for (var line = 0; line < Viewport.Height; line++)
{
int idxToRender = ScrollOffsetVertical + line;
@@ -1168,14 +1168,14 @@ public override void OnDrawContent (Rectangle contentArea)
if (idxToRender < map.Count)
{
// Render the line
- map.ElementAt (idxToRender).Draw (Driver, ColorScheme, line, Bounds.Width);
+ map.ElementAt (idxToRender).Draw (Driver, ColorScheme, line, Viewport.Width);
}
else
{
// Else clear the line to prevent stale symbols due to scrolling etc
Move (0, line);
Driver.SetAttribute (GetNormalColor ());
- Driver.AddStr (new string (' ', Bounds.Width));
+ Driver.AddStr (new string (' ', Viewport.Width));
}
}
}
@@ -1247,7 +1247,7 @@ public override void PositionCursor ()
int idx = map.IndexOf (b => b.Model.Equals (SelectedObject));
// if currently selected line is visible
- if (idx - ScrollOffsetVertical >= 0 && idx - ScrollOffsetVertical < Bounds.Height)
+ if (idx - ScrollOffsetVertical >= 0 && idx - ScrollOffsetVertical < Viewport.Height)
{
Move (0, idx - ScrollOffsetVertical);
}
diff --git a/Terminal.sln.DotSettings b/Terminal.sln.DotSettings
index d04aeb7c3e..3bc1fab5d4 100644
--- a/Terminal.sln.DotSettings
+++ b/Terminal.sln.DotSettings
@@ -386,10 +386,15 @@
<Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" />
<Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" />
<Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" />
+ <Policy><Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static readonly fields (private)"><ElementKinds><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /></Policy>
+ <Policy><Descriptor Staticness="Instance" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Instance fields (not private)"><ElementKinds><Kind Name="FIELD" /><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /></Policy>
+ <Policy><Descriptor Staticness="Static" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Static fields (not private)"><ElementKinds><Kind Name="FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /></Policy>
+ <Policy><Descriptor Staticness="Static" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Static readonly fields (not private)"><ElementKinds><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /></Policy>
True
True
True
True
+ True
True
#FFCF9D32
True
diff --git a/UICatalog/Scenarios/ASCIICustomButton.cs b/UICatalog/Scenarios/ASCIICustomButton.cs
index 8051e08ea6..a1c5695332 100644
--- a/UICatalog/Scenarios/ASCIICustomButton.cs
+++ b/UICatalog/Scenarios/ASCIICustomButton.cs
@@ -86,14 +86,14 @@ public void CustomInitialize ()
var fillText = new StringBuilder ();
- for (var i = 0; i < Bounds.Height; i++)
+ for (var i = 0; i < Viewport.Height; i++)
{
if (i > 0)
{
fillText.AppendLine ("");
}
- for (var j = 0; j < Bounds.Width; j++)
+ for (var j = 0; j < Viewport.Width; j++)
{
fillText.Append ("█");
}
diff --git a/UICatalog/Scenarios/AdornmentExperiments.cs b/UICatalog/Scenarios/AdornmentExperiments.cs
deleted file mode 100644
index a41c959613..0000000000
--- a/UICatalog/Scenarios/AdornmentExperiments.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-using Terminal.Gui;
-
-namespace UICatalog.Scenarios;
-
-[ScenarioMetadata ("Adornment Experiments", "Playground for Adornment experiments")]
-[ScenarioCategory ("Controls")]
-public class AdornmentExperiments : Scenario
-{
- private ViewDiagnosticFlags _diagnosticFlags;
-
- private View _frameView;
-
- public override void Init ()
- {
- Application.Init ();
- ConfigurationManager.Themes.Theme = Theme;
- ConfigurationManager.Apply ();
- Top = new ();
- Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme];
-
- _diagnosticFlags = View.Diagnostics;
- //View.Diagnostics = ViewDiagnosticFlags.MouseEnter;
-
- _frameView = new View ()
- {
- Title = "Frame View",
- X = 0,
- Y = 0,
- Width = Dim.Percent (90),
- Height = Dim.Percent (90),
- CanFocus = true,
- };
- Top.Add (_frameView);
- _frameView.Initialized += FrameView_Initialized;
-
- Top.Closed += (s, e) => View.Diagnostics = _diagnosticFlags;
- }
-
- private void FrameView_Initialized (object sender, System.EventArgs e)
- {
- _frameView.Border.Thickness = new (1, 1, 1, 1);
- _frameView.Padding.Thickness = new (0, 10, 0, 0);
- _frameView.Padding.ColorScheme = Colors.ColorSchemes ["Error"];
-
- var label = new Label ()
- {
- Text = "In Padding",
- X = Pos.Center (),
- Y = 0,
- BorderStyle = LineStyle.Dashed
- };
- _frameView.Padding.Add (label);
- }
-
-}
diff --git a/UICatalog/Scenarios/Adornments.cs b/UICatalog/Scenarios/Adornments.cs
index 302eda6236..27aefbddc7 100644
--- a/UICatalog/Scenarios/Adornments.cs
+++ b/UICatalog/Scenarios/Adornments.cs
@@ -13,15 +13,30 @@ public class Adornments : Scenario
{
private ViewDiagnosticFlags _diagnosticFlags;
- public override void Init ()
+ public override void Main ()
{
Application.Init ();
- ConfigurationManager.Themes.Theme = Theme;
- ConfigurationManager.Apply ();
- Top = new ();
- Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme];
- var view = new Window { Title = "The _Window" };
+ _diagnosticFlags = View.Diagnostics;
+
+ Window app = new ()
+ {
+ Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}",
+ };
+
+ var editor = new AdornmentsEditor ();
+ app.Add (editor);
+
+ var window = new Window
+ {
+ Title = "The _Window",
+ Arrangement = ViewArrangement.Movable,
+ X = Pos.Right(editor),
+ Width = Dim.Percent (60),
+ Height = Dim.Percent (80),
+ };
+ app.Add (window);
+
var tf1 = new TextField { Width = 10, Text = "TextField" };
var color = new ColorPicker { Title = "BG", BoxHeight = 1, BoxWidth = 1, X = Pos.AnchorEnd (11) };
color.BorderStyle = LineStyle.RoundedDotted;
@@ -40,7 +55,7 @@ public override void Init ()
var button = new Button { X = Pos.Center (), Y = Pos.Center (), Text = "Press me!" };
button.Accept += (s, e) =>
- MessageBox.Query (20, 7, "Hi", $"Am I a {view.GetType ().Name}?", "Yes", "No");
+ MessageBox.Query (20, 7, "Hi", $"Am I a {window.GetType ().Name}?", "Yes", "No");
var label = new TextView
{
@@ -64,45 +79,39 @@ public override void Init ()
Text = "Label\nY=AnchorEnd(3),Height=Dim.Fill()"
};
- view.Margin.Data = "Margin";
- view.Margin.Thickness = new (3);
+ window.Margin.Data = "Margin";
+ window.Margin.Thickness = new (3);
- view.Border.Data = "Border";
- view.Border.Thickness = new (3);
+ window.Border.Data = "Border";
+ window.Border.Thickness = new (3);
- view.Padding.Data = "Padding";
- view.Padding.Thickness = new (3);
+ window.Padding.Data = "Padding";
+ window.Padding.Thickness = new (3);
- view.Add (tf1, color, button, label, btnButtonInWindow, tv);
-
- var editor = new AdornmentsEditor
+ var longLabel = new Label ()
{
- Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}",
- ColorScheme = Colors.ColorSchemes [TopLevelColorScheme]
+ X = 40, Y = 5, Title = "This is long text (in a label) that should clip.",
- //BorderStyle = LineStyle.None,
};
- view.X = 36;
- view.Y = 0;
- view.Width = Dim.Percent (60);
- view.Height = Dim.Percent (80);
+ longLabel.TextFormatter.WordWrap = true;
+ window.Add (tf1, color, button, label, btnButtonInWindow, tv, longLabel);
- editor.Initialized += (s, e) => { editor.ViewToEdit = view; };
+ editor.Initialized += (s, e) => { editor.ViewToEdit = window; };
- view.Initialized += (s, e) =>
+ window.Initialized += (s, e) =>
{
- var labelInPadding = new Label () { X = 1, Y = 0, Title = "_Text:" };
- view.Padding.Add (labelInPadding);
+ var labelInPadding = new Label () { X = 1, Y = 0, Title = "_Text:" };
+ window.Padding.Add (labelInPadding);
var textFieldInPadding = new TextField () { X = Pos.Right (labelInPadding) + 1, Y = Pos.Top (labelInPadding), Width = 15, Text = "some text" };
textFieldInPadding.Accept += (s, e) => MessageBox.Query (20, 7, "TextField", textFieldInPadding.Text, "Ok");
- view.Padding.Add (textFieldInPadding);
+ window.Padding.Add (textFieldInPadding);
var btnButtonInPadding = new Button { X = Pos.Center (), Y = 0, Text = "_Button in Padding" };
btnButtonInPadding.Accept += (s, e) => MessageBox.Query (20, 7, "Hi", "Button in Padding Pressed!", "Ok");
btnButtonInPadding.BorderStyle = LineStyle.Dashed;
- btnButtonInPadding.Border.Thickness = new (1,1,1,1);
- view.Padding.Add (btnButtonInPadding);
+ btnButtonInPadding.Border.Thickness = new (1, 1, 1, 1);
+ window.Padding.Add (btnButtonInPadding);
#if SUBVIEW_BASED_BORDER
btnButtonInPadding.Border.CloseButton.Visible = true;
@@ -118,16 +127,17 @@ public override void Init ()
#endif
};
- Top.Closed += (s, e) => View.Diagnostics = _diagnosticFlags;
+ app.Closed += (s, e) => View.Diagnostics = _diagnosticFlags;
- Application.Run (editor);
- editor.Dispose ();
+ Application.Run (app);
+ app.Dispose ();
Application.Shutdown ();
}
- public override void Run () { }
-
+ ///
+ /// Provides a composable UI for editing the settings of an Adornment.
+ ///
public class AdornmentEditor : View
{
private readonly ColorPicker _backgroundColorPicker = new ()
@@ -148,12 +158,12 @@ public class AdornmentEditor : View
SuperViewRendersLineCanvas = true
};
- private TextField _bottomEdit;
- private bool _isUpdating;
- private TextField _leftEdit;
- private TextField _rightEdit;
+ private Buttons.NumericUpDown _topEdit;
+ private Buttons.NumericUpDown _leftEdit;
+ private Buttons.NumericUpDown _bottomEdit;
+ private Buttons.NumericUpDown _rightEdit;
private Thickness _thickness;
- private TextField _topEdit;
+ private bool _isUpdating;
public AdornmentEditor ()
{
@@ -161,7 +171,6 @@ public AdornmentEditor ()
BorderStyle = LineStyle.Double;
Initialized += AdornmentEditor_Initialized;
}
-
public Attribute Color
{
get => new (_foregroundColorPicker.SelectedColor, _backgroundColorPicker.SelectedColor);
@@ -183,31 +192,15 @@ public Thickness Thickness
}
_thickness = value;
- ThicknessChanged?.Invoke (this, new() { Thickness = Thickness });
+ ThicknessChanged?.Invoke (this, new () { Thickness = Thickness });
if (IsInitialized)
{
_isUpdating = true;
-
- if (_topEdit.Text != _thickness.Top.ToString ())
- {
- _topEdit.Text = _thickness.Top.ToString ();
- }
-
- if (_leftEdit.Text != _thickness.Left.ToString ())
- {
- _leftEdit.Text = _thickness.Left.ToString ();
- }
-
- if (_rightEdit.Text != _thickness.Right.ToString ())
- {
- _rightEdit.Text = _thickness.Right.ToString ();
- }
-
- if (_bottomEdit.Text != _thickness.Bottom.ToString ())
- {
- _bottomEdit.Text = _thickness.Bottom.ToString ();
- }
+ _topEdit.Value = _thickness.Top;
+ _leftEdit.Value = _thickness.Left;
+ _rightEdit.Value = _thickness.Right;
+ _bottomEdit.Value = _thickness.Bottom;
_isUpdating = false;
}
@@ -219,49 +212,44 @@ public Thickness Thickness
private void AdornmentEditor_Initialized (object sender, EventArgs e)
{
- var editWidth = 3;
-
- _topEdit = new() { X = Pos.Center (), Y = 0, Width = editWidth };
+ _topEdit = new ()
+ {
+ X = Pos.Center (), Y = 0
+ };
- _topEdit.Accept += Edit_Accept;
+ _topEdit.ValueChanging += Top_ValueChanging;
Add (_topEdit);
- _leftEdit = new()
+ _leftEdit = new ()
{
- X = Pos.Left (_topEdit) - editWidth, Y = Pos.Bottom (_topEdit), Width = editWidth
+ X = Pos.Left (_topEdit) - Pos.Function (() => _topEdit.Digits) - 2, Y = Pos.Bottom (_topEdit)
};
- _leftEdit.Accept += Edit_Accept;
+ _leftEdit.ValueChanging += Left_ValueChanging;
Add (_leftEdit);
- _rightEdit = new() { X = Pos.Right (_topEdit), Y = Pos.Bottom (_topEdit), Width = editWidth };
+ _rightEdit = new () { X = Pos.Right (_leftEdit) + 5, Y = Pos.Bottom (_topEdit) };
- _rightEdit.Accept += Edit_Accept;
+ _rightEdit.ValueChanging += Right_ValueChanging;
Add (_rightEdit);
- _bottomEdit = new() { X = Pos.Center (), Y = Pos.Bottom (_leftEdit), Width = editWidth };
+ _bottomEdit = new () { X = Pos.Center (), Y = Pos.Bottom (_leftEdit) };
- _bottomEdit.Accept += Edit_Accept;
+ _bottomEdit.ValueChanging += Bottom_ValueChanging;
Add (_bottomEdit);
- var copyTop = new Button { X = Pos.Center () + 1, Y = Pos.Bottom (_bottomEdit), Text = "Cop_y Top" };
+ var copyTop = new Button { X = Pos.Center (), Y = Pos.Bottom (_bottomEdit), Text = "Cop_y Top" };
copyTop.Accept += (s, e) =>
{
Thickness = new (Thickness.Top);
-
- if (string.IsNullOrEmpty (_topEdit.Text))
- {
- _topEdit.Text = "0";
- }
-
- _bottomEdit.Text = _leftEdit.Text = _rightEdit.Text = _topEdit.Text;
+ _leftEdit.Value = _rightEdit.Value = _bottomEdit.Value = _topEdit.Value;
};
Add (copyTop);
// Foreground ColorPicker.
_foregroundColorPicker.X = -1;
- _foregroundColorPicker.Y = Pos.Bottom (copyTop) + 1;
+ _foregroundColorPicker.Y = Pos.Bottom (copyTop);
_foregroundColorPicker.SelectedColor = Color.Foreground.GetClosestNamedColor ();
_foregroundColorPicker.ColorChanged += (o, a) =>
@@ -289,29 +277,69 @@ private void AdornmentEditor_Initialized (object sender, EventArgs e)
);
Add (_backgroundColorPicker);
- _topEdit.Text = $"{Thickness.Top}";
- _leftEdit.Text = $"{Thickness.Left}";
- _rightEdit.Text = $"{Thickness.Right}";
- _bottomEdit.Text = $"{Thickness.Bottom}";
+ _topEdit.Value = Thickness.Top;
+ _leftEdit.Value = Thickness.Left;
+ _rightEdit.Value = Thickness.Right;
+ _bottomEdit.Value = Thickness.Bottom;
LayoutSubviews ();
- Height = GetAdornmentsThickness ().Vertical + 4 + 4;
+ Height = GetAdornmentsThickness ().Vertical + 4 + 3;
Width = GetAdornmentsThickness ().Horizontal + _foregroundColorPicker.Frame.Width * 2 - 3;
}
- private void Edit_Accept (object sender, CancelEventArgs e)
+ private void Top_ValueChanging (object sender, StateEventArgs e)
{
- e.Cancel = true;
+ if (e.NewValue < 0)
+ {
+ e.Cancel = true;
- Thickness = new (
- int.Parse (_leftEdit.Text),
- int.Parse (_topEdit.Text),
- int.Parse (_rightEdit.Text),
- int.Parse (_bottomEdit.Text));
+ return;
+ }
+
+ Thickness.Top = e.NewValue;
+ }
+
+ private void Left_ValueChanging (object sender, StateEventArgs e)
+ {
+ if (e.NewValue < 0)
+ {
+ e.Cancel = true;
+
+ return;
+ }
+
+ Thickness.Left = e.NewValue;
+ }
+
+ private void Right_ValueChanging (object sender, StateEventArgs e)
+ {
+ if (e.NewValue < 0)
+ {
+ e.Cancel = true;
+
+ return;
+ }
+
+ Thickness.Right = e.NewValue;
+ }
+
+ private void Bottom_ValueChanging (object sender, StateEventArgs e)
+ {
+ if (e.NewValue < 0)
+ {
+ e.Cancel = true;
+
+ return;
+ }
+
+ Thickness.Bottom = e.NewValue;
}
}
- public class AdornmentsEditor : Window
+ ///
+ /// Provides an editor UI for the Margin, Border, and Padding of a View.
+ ///
+ public class AdornmentsEditor : View
{
private AdornmentEditor _borderEditor;
private CheckBox _diagCheckBox;
@@ -320,6 +348,15 @@ public class AdornmentsEditor : Window
private AdornmentEditor _paddingEditor;
private View _viewToEdit;
+ public AdornmentsEditor ()
+ {
+ ColorScheme = Colors.ColorSchemes ["Dialog"];
+
+ // TOOD: Use Dim.Auto
+ Width = 36;
+ Height = Dim.Fill ();
+ }
+
public View ViewToEdit
{
get => _viewToEdit;
@@ -328,26 +365,26 @@ public View ViewToEdit
_origTitle = value.Title;
_viewToEdit = value;
- _marginEditor = new()
+ _marginEditor = new ()
{
X = 0,
Y = 0,
Title = "_Margin",
Thickness = _viewToEdit.Margin.Thickness,
- Color = new (_viewToEdit.Margin.ColorScheme.Normal),
+ Color = new (_viewToEdit.Margin.ColorScheme?.Normal ?? ColorScheme.Normal),
SuperViewRendersLineCanvas = true
};
_marginEditor.ThicknessChanged += Editor_ThicknessChanged;
_marginEditor.AttributeChanged += Editor_AttributeChanged;
Add (_marginEditor);
- _borderEditor = new()
+ _borderEditor = new ()
{
X = Pos.Left (_marginEditor),
Y = Pos.Bottom (_marginEditor),
Title = "B_order",
Thickness = _viewToEdit.Border.Thickness,
- Color = new (_viewToEdit.Border.ColorScheme.Normal),
+ Color = new (_viewToEdit.Border.ColorScheme?.Normal ?? ColorScheme.Normal),
SuperViewRendersLineCanvas = true
};
_borderEditor.ThicknessChanged += Editor_ThicknessChanged;
@@ -411,7 +448,7 @@ public View ViewToEdit
{
if (ckbTitle.Checked == true)
{
- _viewToEdit.Title = _origTitle;
+ //_viewToEdit.Title = _origTitle;
}
else
{
@@ -420,13 +457,13 @@ public View ViewToEdit
};
Add (ckbTitle);
- _paddingEditor = new()
+ _paddingEditor = new ()
{
X = Pos.Left (_borderEditor),
Y = Pos.Bottom (rbBorderStyle),
Title = "_Padding",
Thickness = _viewToEdit.Padding.Thickness,
- Color = new (_viewToEdit.Padding.ColorScheme.Normal),
+ Color = new (_viewToEdit.Padding.ColorScheme?.Normal ?? ColorScheme.Normal),
SuperViewRendersLineCanvas = true
};
_paddingEditor.ThicknessChanged += Editor_ThicknessChanged;
@@ -450,7 +487,6 @@ public View ViewToEdit
};
Add (_diagCheckBox);
- Add (_viewToEdit);
_viewToEdit.LayoutComplete += (s, e) =>
{
diff --git a/UICatalog/Scenarios/Animation.cs b/UICatalog/Scenarios/Animation.cs
index e1a7391122..80bebdec2f 100644
--- a/UICatalog/Scenarios/Animation.cs
+++ b/UICatalog/Scenarios/Animation.cs
@@ -176,16 +176,16 @@ private class ImageView : View
private Rectangle oldSize = Rectangle.Empty;
public void NextFrame () { currentFrame = (currentFrame + 1) % frameCount; }
- public override void OnDrawContent (Rectangle contentArea)
+ public override void OnDrawContent (Rectangle viewport)
{
- base.OnDrawContent (contentArea);
+ base.OnDrawContent (viewport);
- if (oldSize != Bounds)
+ if (oldSize != Viewport)
{
// Invalidate cached images now size has changed
matchSizes = new Image [frameCount];
brailleCache = new string [frameCount];
- oldSize = Bounds;
+ oldSize = Viewport;
}
Image imgScaled = matchSizes [currentFrame];
@@ -196,7 +196,7 @@ public override void OnDrawContent (Rectangle contentArea)
Image imgFull = fullResImages [currentFrame];
// keep aspect ratio
- int newSize = Math.Min (Bounds.Width, Bounds.Height);
+ int newSize = Math.Min (Viewport.Width, Viewport.Height);
// generate one
matchSizes [currentFrame] = imgScaled = imgFull.Clone (
diff --git a/UICatalog/Scenarios/BackgroundWorkerCollection.cs b/UICatalog/Scenarios/BackgroundWorkerCollection.cs
index e6f51d6f94..e2d4b8c780 100644
--- a/UICatalog/Scenarios/BackgroundWorkerCollection.cs
+++ b/UICatalog/Scenarios/BackgroundWorkerCollection.cs
@@ -36,6 +36,7 @@ private class OverlappedMain : Toplevel
public OverlappedMain ()
{
+ Arrangement = ViewArrangement.Movable;
Data = "OverlappedMain";
IsOverlappedContainer = true;
@@ -258,6 +259,8 @@ public StagingUIController (Staging staging, List list) : this ()
public StagingUIController ()
{
+ Arrangement = ViewArrangement.Movable;
+
X = Pos.Center ();
Y = Pos.Center ();
Width = Dim.Percent (85);
@@ -303,7 +306,7 @@ public StagingUIController ()
LayoutStarted += (s, e) =>
{
int btnsWidth = _start.Frame.Width + _close.Frame.Width + 2 - 1;
- int shiftLeft = Math.Max ((Bounds.Width - btnsWidth) / 2 - 2, 0);
+ int shiftLeft = Math.Max ((Viewport.Width - btnsWidth) / 2 - 2, 0);
shiftLeft += _close.Frame.Width + 1;
_close.X = Pos.AnchorEnd (shiftLeft);
@@ -338,6 +341,8 @@ private class WorkerApp : Toplevel
public WorkerApp ()
{
+ Arrangement = ViewArrangement.Movable;
+
Data = "WorkerApp";
Title = "Worker collection Log";
diff --git a/UICatalog/Scenarios/Buttons.cs b/UICatalog/Scenarios/Buttons.cs
index 068c15a7c2..f405e063c7 100644
--- a/UICatalog/Scenarios/Buttons.cs
+++ b/UICatalog/Scenarios/Buttons.cs
@@ -1,5 +1,8 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
using System.Text;
+using JetBrains.Annotations;
using Terminal.Gui;
namespace UICatalog.Scenarios;
@@ -13,8 +16,9 @@ public override void Main ()
{
Window main = new ()
{
- Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}",
+ Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}"
};
+
// Add a label & text field so we can demo IsDefault
var editLabel = new Label { X = 0, Y = 0, TabStop = true, Text = "TextField (to demo IsDefault):" };
main.Add (editLabel);
@@ -32,19 +36,19 @@ public override void Main ()
var swapButton = new Button { X = 50, Text = "S_wap Default (Absolute Layout)" };
swapButton.Accept += (s, e) =>
- {
- defaultButton.IsDefault = !defaultButton.IsDefault;
- swapButton.IsDefault = !swapButton.IsDefault;
- };
+ {
+ defaultButton.IsDefault = !defaultButton.IsDefault;
+ swapButton.IsDefault = !swapButton.IsDefault;
+ };
main.Add (swapButton);
static void DoMessage (Button button, string txt)
{
button.Accept += (s, e) =>
- {
- string btnText = button.Text;
- MessageBox.Query ("Message", $"Did you click {txt}?", "Yes", "No");
- };
+ {
+ string btnText = button.Text;
+ MessageBox.Query ("Message", $"Did you click {txt}?", "Yes", "No");
+ };
}
var colorButtonsLabel = new Label { X = 0, Y = Pos.Bottom (editLabel) + 1, Text = "Color Buttons:" };
@@ -75,20 +79,20 @@ static void DoMessage (Button button, string txt)
Button button;
main.Add (
- button = new Button
- {
- X = 2,
- Y = Pos.Bottom (colorButtonsLabel) + 1,
- Text =
- "A super l_öng Button that will probably expose a bug in clipping or wrapping of text. Will it?"
- }
- );
+ button = new ()
+ {
+ X = 2,
+ Y = Pos.Bottom (colorButtonsLabel) + 1,
+ Text =
+ "A super l_öng Button that will probably expose a bug in clipping or wrapping of text. Will it?"
+ }
+ );
DoMessage (button, button.Text);
// Note the 'N' in 'Newline' will be the hotkey
main.Add (
- button = new Button { X = 2, Y = Pos.Bottom (button) + 1, Text = "a Newline\nin the button" }
- );
+ button = new () { X = 2, Y = Pos.Bottom (button) + 1, Text = "a Newline\nin the button" }
+ );
button.Accept += (s, e) => MessageBox.Query ("Message", "Question?", "Yes", "No");
var textChanger = new Button { X = 2, Y = Pos.Bottom (button) + 1, Text = "Te_xt Changer" };
@@ -96,13 +100,13 @@ static void DoMessage (Button button, string txt)
textChanger.Accept += (s, e) => textChanger.Text += "!";
main.Add (
- button = new Button
- {
- X = Pos.Right (textChanger) + 2,
- Y = Pos.Y (textChanger),
- Text = "Lets see if this will move as \"Text Changer\" grows"
- }
- );
+ button = new ()
+ {
+ X = Pos.Right (textChanger) + 2,
+ Y = Pos.Y (textChanger),
+ Text = "Lets see if this will move as \"Text Changer\" grows"
+ }
+ );
var removeButton = new Button
{
@@ -112,12 +116,12 @@ static void DoMessage (Button button, string txt)
// This in interesting test case because `moveBtn` and below are laid out relative to this one!
removeButton.Accept += (s, e) =>
- {
- // Now this throw a InvalidOperationException on the TopologicalSort method as is expected.
- //main.Remove (removeButton);
+ {
+ // Now this throw a InvalidOperationException on the TopologicalSort method as is expected.
+ //main.Remove (removeButton);
- removeButton.Visible = false;
- };
+ removeButton.Visible = false;
+ };
var computedFrame = new FrameView
{
@@ -142,12 +146,12 @@ static void DoMessage (Button button, string txt)
};
moveBtn.Accept += (s, e) =>
- {
- moveBtn.X = moveBtn.Frame.X + 5;
+ {
+ moveBtn.X = moveBtn.Frame.X + 5;
- // This is already fixed with the call to SetNeedDisplay() in the Pos Dim.
- //computedFrame.LayoutSubviews (); // BUGBUG: This call should not be needed. View.X is not causing relayout correctly
- };
+ // This is already fixed with the call to SetNeedDisplay() in the Pos Dim.
+ //computedFrame.LayoutSubviews (); // BUGBUG: This call should not be needed. View.X is not causing relayout correctly
+ };
computedFrame.Add (moveBtn);
// Demonstrates how changing the View.Frame property can SIZE Views (#583)
@@ -163,11 +167,11 @@ static void DoMessage (Button button, string txt)
};
sizeBtn.Accept += (s, e) =>
- {
- sizeBtn.Width = sizeBtn.Frame.Width + 5;
+ {
+ sizeBtn.Width = sizeBtn.Frame.Width + 5;
- //computedFrame.LayoutSubviews (); // FIXED: This call should not be needed. View.X is not causing relayout correctly
- };
+ //computedFrame.LayoutSubviews (); // FIXED: This call should not be needed. View.X is not causing relayout correctly
+ };
computedFrame.Add (sizeBtn);
var absoluteFrame = new FrameView
@@ -184,14 +188,14 @@ static void DoMessage (Button button, string txt)
var moveBtnA = new Button { ColorScheme = Colors.ColorSchemes ["Error"], Text = "Move This Button via Frame" };
moveBtnA.Accept += (s, e) =>
- {
- moveBtnA.Frame = new Rectangle (
- moveBtnA.Frame.X + 5,
- moveBtnA.Frame.Y,
- moveBtnA.Frame.Width,
- moveBtnA.Frame.Height
- );
- };
+ {
+ moveBtnA.Frame = new (
+ moveBtnA.Frame.X + 5,
+ moveBtnA.Frame.Y,
+ moveBtnA.Frame.Width,
+ moveBtnA.Frame.Height
+ );
+ };
absoluteFrame.Add (moveBtnA);
// Demonstrates how changing the View.Frame property can SIZE Views (#583)
@@ -201,14 +205,14 @@ static void DoMessage (Button button, string txt)
};
sizeBtnA.Accept += (s, e) =>
- {
- sizeBtnA.Frame = new Rectangle (
- sizeBtnA.Frame.X,
- sizeBtnA.Frame.Y,
- sizeBtnA.Frame.Width + 5,
- sizeBtnA.Frame.Height
- );
- };
+ {
+ sizeBtnA.Frame = new (
+ sizeBtnA.Frame.X,
+ sizeBtnA.Frame.Y,
+ sizeBtnA.Frame.Width + 5,
+ sizeBtnA.Frame.Height
+ );
+ };
absoluteFrame.Add (sizeBtnA);
var label = new Label
@@ -289,154 +293,273 @@ string MoveHotkey (string txt)
main.Add (moveUnicodeHotKeyBtn);
radioGroup.SelectedItemChanged += (s, args) =>
- {
- switch (args.SelectedItem)
- {
- case 0:
- moveBtn.TextAlignment = TextAlignment.Left;
- sizeBtn.TextAlignment = TextAlignment.Left;
- moveBtnA.TextAlignment = TextAlignment.Left;
- sizeBtnA.TextAlignment = TextAlignment.Left;
- moveHotKeyBtn.TextAlignment = TextAlignment.Left;
- moveUnicodeHotKeyBtn.TextAlignment = TextAlignment.Left;
-
- break;
- case 1:
- moveBtn.TextAlignment = TextAlignment.Right;
- sizeBtn.TextAlignment = TextAlignment.Right;
- moveBtnA.TextAlignment = TextAlignment.Right;
- sizeBtnA.TextAlignment = TextAlignment.Right;
- moveHotKeyBtn.TextAlignment = TextAlignment.Right;
- moveUnicodeHotKeyBtn.TextAlignment = TextAlignment.Right;
-
- break;
- case 2:
- moveBtn.TextAlignment = TextAlignment.Centered;
- sizeBtn.TextAlignment = TextAlignment.Centered;
- moveBtnA.TextAlignment = TextAlignment.Centered;
- sizeBtnA.TextAlignment = TextAlignment.Centered;
- moveHotKeyBtn.TextAlignment = TextAlignment.Centered;
- moveUnicodeHotKeyBtn.TextAlignment = TextAlignment.Centered;
-
- break;
- case 3:
- moveBtn.TextAlignment = TextAlignment.Justified;
- sizeBtn.TextAlignment = TextAlignment.Justified;
- moveBtnA.TextAlignment = TextAlignment.Justified;
- sizeBtnA.TextAlignment = TextAlignment.Justified;
- moveHotKeyBtn.TextAlignment = TextAlignment.Justified;
- moveUnicodeHotKeyBtn.TextAlignment = TextAlignment.Justified;
-
- break;
- }
- };
-
- label = new Label ()
+ {
+ switch (args.SelectedItem)
+ {
+ case 0:
+ moveBtn.TextAlignment = TextAlignment.Left;
+ sizeBtn.TextAlignment = TextAlignment.Left;
+ moveBtnA.TextAlignment = TextAlignment.Left;
+ sizeBtnA.TextAlignment = TextAlignment.Left;
+ moveHotKeyBtn.TextAlignment = TextAlignment.Left;
+ moveUnicodeHotKeyBtn.TextAlignment = TextAlignment.Left;
+
+ break;
+ case 1:
+ moveBtn.TextAlignment = TextAlignment.Right;
+ sizeBtn.TextAlignment = TextAlignment.Right;
+ moveBtnA.TextAlignment = TextAlignment.Right;
+ sizeBtnA.TextAlignment = TextAlignment.Right;
+ moveHotKeyBtn.TextAlignment = TextAlignment.Right;
+ moveUnicodeHotKeyBtn.TextAlignment = TextAlignment.Right;
+
+ break;
+ case 2:
+ moveBtn.TextAlignment = TextAlignment.Centered;
+ sizeBtn.TextAlignment = TextAlignment.Centered;
+ moveBtnA.TextAlignment = TextAlignment.Centered;
+ sizeBtnA.TextAlignment = TextAlignment.Centered;
+ moveHotKeyBtn.TextAlignment = TextAlignment.Centered;
+ moveUnicodeHotKeyBtn.TextAlignment = TextAlignment.Centered;
+
+ break;
+ case 3:
+ moveBtn.TextAlignment = TextAlignment.Justified;
+ sizeBtn.TextAlignment = TextAlignment.Justified;
+ moveBtnA.TextAlignment = TextAlignment.Justified;
+ sizeBtnA.TextAlignment = TextAlignment.Justified;
+ moveHotKeyBtn.TextAlignment = TextAlignment.Justified;
+ moveUnicodeHotKeyBtn.TextAlignment = TextAlignment.Justified;
+
+ break;
+ }
+ };
+
+ label = new ()
{
X = 0,
Y = Pos.Bottom (moveUnicodeHotKeyBtn) + 1,
Title = "_Numeric Up/Down (press-and-hold):",
};
- var downButton = new Button ()
- {
- CanFocus = false,
- AutoSize = false,
- X = Pos.Right(label)+1,
- Y = Pos.Top (label),
- Height = 1,
- Width = 1,
- NoPadding = true,
- NoDecorations = true,
- Title = $"{CM.Glyphs.DownArrow}",
- WantContinuousButtonPressed = true,
- };
- var numericEdit = new TextField ()
+ var numericUpDown = new NumericUpDown
{
- Text = "1966",
- X = Pos.Right (downButton),
- Y = Pos.Top (downButton),
+ Value = 69,
+ X = Pos.Right (label) + 1,
+ Y = Pos.Top (label),
Width = 5,
- Height = 1,
+ Height = 1
};
- var upButton = new Button ()
- {
- CanFocus = false,
- AutoSize = false,
- X = Pos.Right (numericEdit),
- Y = Pos.Top (numericEdit),
- Height = 1,
- Width = 1,
- NoPadding = true,
- NoDecorations = true,
- Title = $"{CM.Glyphs.UpArrow}",
- WantContinuousButtonPressed = true,
- };
- downButton.Accept += (s, e) =>
- {
- numericEdit.Text = $"{int.Parse(numericEdit.Text) - 1}";
- };
- upButton.Accept += (s, e) =>
- {
- numericEdit.Text = $"{int.Parse (numericEdit.Text) + 1}";
- };
+ numericUpDown.ValueChanged += NumericUpDown_ValueChanged;
- main.Add (label, downButton, numericEdit, upButton);
+ void NumericUpDown_ValueChanged (object sender, StateEventArgs e) { }
- label = new Label ()
+ main.Add (label, numericUpDown);
+
+ label = new ()
{
X = 0,
- Y = Pos.Bottom (label) + 1,
- Title = "_No Repeat:",
+ Y = Pos.Bottom (numericUpDown) + 1,
+ Title = "_No Repeat:"
};
- int noRepeatAcceptCount = 0;
- var noRepeatButton = new Button ()
+ var noRepeatAcceptCount = 0;
+
+ var noRepeatButton = new Button
{
X = Pos.Right (label) + 1,
Y = Pos.Top (label),
Title = $"Accept Cou_nt: {noRepeatAcceptCount}",
- WantContinuousButtonPressed = false,
+ WantContinuousButtonPressed = false
};
- noRepeatButton.Accept += (s, e) =>
- {
- noRepeatButton.Title = $"Accept Cou_nt: {++noRepeatAcceptCount}";
- };
- main.Add(label, noRepeatButton);
+ noRepeatButton.Accept += (s, e) => { noRepeatButton.Title = $"Accept Cou_nt: {++noRepeatAcceptCount}"; };
+ main.Add (label, noRepeatButton);
- label = new Label ()
+ label = new ()
{
X = 0,
Y = Pos.Bottom (label) + 1,
- Title = "_Repeat (press-and-hold):",
+ Title = "_Repeat (press-and-hold):"
};
- int acceptCount = 0;
- var repeatButton = new Button ()
+ var acceptCount = 0;
+
+ var repeatButton = new Button
{
X = Pos.Right (label) + 1,
Y = Pos.Top (label),
Title = $"Accept Co_unt: {acceptCount}",
- WantContinuousButtonPressed = true,
+ WantContinuousButtonPressed = true
};
- repeatButton.Accept += (s, e) =>
- {
- repeatButton.Title = $"Accept Co_unt: {++acceptCount}";
- };
+ repeatButton.Accept += (s, e) => { repeatButton.Title = $"Accept Co_unt: {++acceptCount}"; };
- var enableCB = new CheckBox ()
+ var enableCB = new CheckBox
{
X = Pos.Right (repeatButton) + 1,
Y = Pos.Top (repeatButton),
Title = "Enabled",
- Checked = true,
+ Checked = true
};
- enableCB.Toggled += (s, e) =>
- {
- repeatButton.Enabled = !repeatButton.Enabled;
- };
- main.Add(label, repeatButton, enableCB);
+ enableCB.Toggled += (s, e) => { repeatButton.Enabled = !repeatButton.Enabled; };
+ main.Add (label, repeatButton, enableCB);
main.Ready += (s, e) => radioGroup.Refresh ();
Application.Run (main);
main.Dispose ();
}
+
+ ///
+ /// Enables the user to increase or decrease a value by clicking on the up or down buttons.
+ ///
+ ///
+ /// Supports the following types: , , , , .
+ /// Supports only one digit of precision.
+ ///
+ public class NumericUpDown : View
+ {
+ private readonly Button _down;
+ // TODO: Use a TextField instead of a Label
+ private readonly View _number;
+ private readonly Button _up;
+
+ public NumericUpDown ()
+ {
+ Type type = typeof (T);
+ if (!(type == typeof (int) || type == typeof (long) || type == typeof (float) || type == typeof (double) || type == typeof (decimal)))
+ {
+ throw new InvalidOperationException ("T must be a numeric type that supports addition and subtraction.");
+ }
+
+ // TODO: Use Dim.Auto for the Width and Height
+ Height = 1;
+ Width = Dim.Function (() => Digits + 2); // button + 3 for number + button
+
+ _down = new ()
+ {
+ AutoSize = false,
+ Height = 1,
+ Width = 1,
+ NoPadding = true,
+ NoDecorations = true,
+ Title = $"{CM.Glyphs.DownArrow}",
+ WantContinuousButtonPressed = true,
+ CanFocus = false,
+ };
+
+ _number = new ()
+ {
+ Text = Value.ToString (),
+ AutoSize = false,
+ X = Pos.Right (_down),
+ Y = Pos.Top (_down),
+ Width = Dim.Function (() => Digits),
+ Height = 1,
+ TextAlignment = TextAlignment.Centered,
+ CanFocus = true
+ };
+
+ _up = new ()
+ {
+ AutoSize = false,
+ X = Pos.AnchorEnd (1),
+ Y = Pos.Top (_number),
+ Height = 1,
+ Width = 1,
+ NoPadding = true,
+ NoDecorations = true,
+ Title = $"{CM.Glyphs.UpArrow}",
+ WantContinuousButtonPressed = true,
+ CanFocus = false,
+ };
+
+ CanFocus = true;
+
+ _down.Accept += OnDownButtonOnAccept;
+ _up.Accept += OnUpButtonOnAccept;
+
+ Add (_down, _number, _up);
+
+
+ AddCommand (Command.ScrollUp, () =>
+ {
+ Value = (dynamic)Value + 1;
+ _number.Text = Value.ToString ();
+
+ return true;
+ });
+ AddCommand (Command.ScrollDown, () =>
+ {
+ Value = (dynamic)Value - 1;
+ _number.Text = Value.ToString ();
+
+ return true;
+ });
+
+ KeyBindings.Add (Key.CursorUp, Command.ScrollUp);
+ KeyBindings.Add (Key.CursorDown, Command.ScrollDown);
+
+ return;
+
+ void OnDownButtonOnAccept (object s, CancelEventArgs e)
+ {
+ InvokeCommand (Command.ScrollDown);
+ }
+
+ void OnUpButtonOnAccept (object s, CancelEventArgs e)
+ {
+ InvokeCommand (Command.ScrollUp);
+ }
+ }
+
+ private void _up_Enter (object sender, FocusEventArgs e)
+ {
+ throw new NotImplementedException ();
+ }
+
+ private T _value;
+
+ ///
+ /// The value that will be incremented or decremented.
+ ///
+ public T Value
+ {
+ get => _value;
+ set
+ {
+ if (_value.Equals (value))
+ {
+ return;
+ }
+
+ T oldValue = value;
+ StateEventArgs args = new StateEventArgs (_value, value);
+ ValueChanging?.Invoke (this, args);
+
+ if (args.Cancel)
+ {
+ return;
+ }
+
+ _value = value;
+ _number.Text = _value.ToString ();
+ ValueChanged?.Invoke (this, new (oldValue, _value));
+ }
+ }
+
+ ///
+ /// Fired when the value is about to change. Set to true to prevent the change.
+ ///
+ [CanBeNull]
+ public event EventHandler> ValueChanging;
+
+ ///
+ /// Fired when the value has changed.
+ ///
+ [CanBeNull]
+ public event EventHandler> ValueChanged;
+
+ ///
+ /// The number of digits to display. The will be resized to fit this number of characters plus the buttons. The default is 3.
+ ///
+ public int Digits { get; set; } = 3;
+ }
}
+
diff --git a/UICatalog/Scenarios/CharacterMap.cs b/UICatalog/Scenarios/CharacterMap.cs
index c6ff8d9c27..b0d7a03ca5 100644
--- a/UICatalog/Scenarios/CharacterMap.cs
+++ b/UICatalog/Scenarios/CharacterMap.cs
@@ -1,3 +1,5 @@
+#define OTHER_CONTROLS
+
using System;
using System.Collections.Generic;
using System.ComponentModel;
@@ -17,12 +19,15 @@ namespace UICatalog.Scenarios;
///
/// This Scenario demonstrates building a custom control (a class deriving from View) that: - Provides a
/// "Character Map" application (like Windows' charmap.exe). - Helps test unicode character rendering in Terminal.Gui -
-/// Illustrates how to use ScrollView to do infinite scrolling
+/// Illustrates how to do infinite scrolling
///
-[ScenarioMetadata ("Character Map", "Unicode viewer demonstrating the ScrollView control.")]
+[ScenarioMetadata ("Character Map", "Unicode viewer demonstrating infinite content, scrolling, and Unicode.")]
[ScenarioCategory ("Text and Formatting")]
+[ScenarioCategory ("Drawing")]
[ScenarioCategory ("Controls")]
-[ScenarioCategory ("ScrollView")]
+[ScenarioCategory ("Layout")]
+[ScenarioCategory ("Scrolling")]
+
public class CharacterMap : Scenario
{
public Label _errorLabel;
@@ -30,17 +35,26 @@ public class CharacterMap : Scenario
private CharMap _charMap;
// Don't create a Window, just return the top-level view
- public override void Init ()
+ public override void Main ()
{
Application.Init ();
- Top = new ();
- Top.ColorScheme = Colors.ColorSchemes ["Base"];
- }
- public override void Setup ()
- {
- _charMap = new() { X = 0, Y = 1, Height = Dim.Fill () };
- Top.Add (_charMap);
+ var top = new Window
+ {
+ BorderStyle = LineStyle.None
+ };
+
+ _charMap = new ()
+ {
+ X = 0,
+ Y = 0,
+ Width = Dim.Fill (),
+ Height = Dim.Fill ()
+ };
+ top.Add (_charMap);
+
+#if OTHER_CONTROLS
+ _charMap.Y = 1;
var jumpLabel = new Label
{
@@ -49,19 +63,19 @@ public override void Setup ()
HotKeySpecifier = (Rune)'_',
Text = "_Jump To Code Point:"
};
- Top.Add (jumpLabel);
+ top.Add (jumpLabel);
var jumpEdit = new TextField
{
X = Pos.Right (jumpLabel) + 1, Y = Pos.Y (_charMap), Width = 10, Caption = "e.g. 01BE3"
};
- Top.Add (jumpEdit);
+ top.Add (jumpEdit);
- _errorLabel = new()
+ _errorLabel = new ()
{
X = Pos.Right (jumpEdit) + 1, Y = Pos.Y (_charMap), ColorScheme = Colors.ColorSchemes ["error"], Text = "err"
};
- Top.Add (_errorLabel);
+ top.Add (_errorLabel);
#if TEXT_CHANGED_TO_JUMP
jumpEdit.TextChanged += JumpEdit_TextChanged;
@@ -76,8 +90,7 @@ void JumpEditOnAccept (object sender, CancelEventArgs e)
e.Cancel = true;
}
#endif
- _categoryList = new() { X = Pos.Right (_charMap), Y = Pos.Bottom (jumpLabel), Height = Dim.Fill () };
-
+ _categoryList = new () { X = Pos.Right (_charMap), Y = Pos.Bottom (jumpLabel), Height = Dim.Fill () };
_categoryList.FullRowSelect = true;
//jumpList.Style.ShowHeaders = false;
@@ -120,10 +133,10 @@ void JumpEditOnAccept (object sender, CancelEventArgs e)
_categoryList.Style.ColumnStyles.Add (
0,
- new() { MaxWidth = longestName, MinWidth = longestName, MinAcceptableWidth = longestName }
+ new () { MaxWidth = longestName, MinWidth = longestName, MinAcceptableWidth = longestName }
);
- _categoryList.Style.ColumnStyles.Add (1, new() { MaxWidth = 1, MinWidth = 6 });
- _categoryList.Style.ColumnStyles.Add (2, new() { MaxWidth = 1, MinWidth = 6 });
+ _categoryList.Style.ColumnStyles.Add (1, new () { MaxWidth = 1, MinWidth = 6 });
+ _categoryList.Style.ColumnStyles.Add (2, new () { MaxWidth = 1, MinWidth = 6 });
_categoryList.Width = _categoryList.Style.ColumnStyles.Sum (c => c.Value.MinWidth) + 4;
@@ -133,10 +146,7 @@ void JumpEditOnAccept (object sender, CancelEventArgs e)
_charMap.StartCodePoint = table.Data.ToArray () [args.NewRow].Start;
};
- Top.Add (_categoryList);
-
- _charMap.SelectedCodePoint = 0;
- _charMap.SetFocus ();
+ top.Add (_categoryList);
// TODO: Replace this with Dim.Auto when that's ready
_categoryList.Initialized += _categoryList_Initialized;
@@ -162,7 +172,14 @@ void JumpEditOnAccept (object sender, CancelEventArgs e)
)
]
};
- Top.Add (menu);
+ top.Add (menu);
+#endif // OTHER_CONTROLS
+
+ _charMap.SelectedCodePoint = 0;
+ _charMap.SetFocus ();
+
+ Application.Run (top);
+ top.Dispose ();
}
private void _categoryList_Initialized (object sender, EventArgs e) { _charMap.Width = Dim.Fill () - _categoryList.Width; }
@@ -203,7 +220,7 @@ private EnumerableTableSource CreateCategoryTable (int sortByColum
return new (
sortedRanges,
- new()
+ new ()
{
{ $"Category{categorySort}", s => s.Category },
{ $"Start{startSort}", s => $"{s.Start:x5}" },
@@ -296,7 +313,7 @@ private void JumpEdit_TextChanged (object sender, StateEventArgs e)
}
}
-internal class CharMap : ScrollView
+internal class CharMap : View
{
private const CursorVisibility _cursor = CursorVisibility.Default;
private const int COLUMN_WIDTH = 3;
@@ -311,10 +328,7 @@ public CharMap ()
ColorScheme = Colors.ColorSchemes ["Dialog"];
CanFocus = true;
- ContentSize = new (
- RowWidth,
- (MaxCodePoint / 16 + (ShowHorizontalScrollIndicator ? 2 : 1)) * _rowHeight
- );
+ ContentSize = new (RowWidth, (MaxCodePoint / 16 + 2) * _rowHeight);
AddCommand (
Command.ScrollUp,
@@ -325,6 +339,8 @@ public CharMap ()
SelectedCodePoint -= 16;
}
+ ScrollVertical (-_rowHeight);
+
return true;
}
);
@@ -333,11 +349,16 @@ public CharMap ()
Command.ScrollDown,
() =>
{
- if (SelectedCodePoint < MaxCodePoint - 16)
+ if (SelectedCodePoint <= MaxCodePoint - 16)
{
SelectedCodePoint += 16;
}
+ if (Cursor.Y >= Viewport.Height)
+ {
+ ScrollVertical (_rowHeight);
+ }
+
return true;
}
);
@@ -351,6 +372,11 @@ public CharMap ()
SelectedCodePoint--;
}
+ if (Cursor.X > RowLabelWidth + 1)
+ {
+ ScrollHorizontal (-COLUMN_WIDTH);
+ }
+
return true;
}
);
@@ -364,6 +390,11 @@ public CharMap ()
SelectedCodePoint++;
}
+ if (Cursor.X >= Viewport.Width)
+ {
+ ScrollHorizontal (COLUMN_WIDTH);
+ }
+
return true;
}
);
@@ -372,8 +403,9 @@ public CharMap ()
Command.PageUp,
() =>
{
- int page = (Bounds.Height / _rowHeight - 1) * 16;
+ int page = (Viewport.Height - 1 / _rowHeight) * 16;
SelectedCodePoint -= Math.Min (page, SelectedCodePoint);
+ Viewport = Viewport with { Y = SelectedCodePoint / 16 * _rowHeight };
return true;
}
@@ -383,8 +415,9 @@ public CharMap ()
Command.PageDown,
() =>
{
- int page = (Bounds.Height / _rowHeight - 1) * 16;
+ int page = (Viewport.Height - 1 / _rowHeight) * 16;
SelectedCodePoint += Math.Min (page, MaxCodePoint - SelectedCodePoint);
+ Viewport = Viewport with { Y = SelectedCodePoint / 16 * _rowHeight };
return true;
}
@@ -405,11 +438,11 @@ public CharMap ()
() =>
{
SelectedCodePoint = MaxCodePoint;
+ Viewport = Viewport with { Y = SelectedCodePoint / 16 * _rowHeight };
return true;
}
);
- KeyBindings.Add (Key.Enter, Command.Accept);
AddCommand (
Command.Accept,
@@ -421,7 +454,116 @@ public CharMap ()
}
);
+ KeyBindings.Add (Key.Enter, Command.Accept);
+ 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);
+
MouseClick += Handle_MouseClick;
+ MouseEvent += Handle_MouseEvent;
+
+ // Prototype scrollbars
+ Padding.Thickness = new (0, 0, 1, 1);
+
+ var up = new Button
+ {
+ AutoSize = false,
+ X = Pos.AnchorEnd (1),
+ Y = 0,
+ Height = 1,
+ Width = 1,
+ NoPadding = true,
+ NoDecorations = true,
+ Title = CM.Glyphs.UpArrow.ToString (),
+ WantContinuousButtonPressed = true,
+ CanFocus = false
+ };
+ up.Accept += (sender, args) => { args.Cancel = ScrollVertical (-1) == true; };
+
+ var down = new Button
+ {
+ AutoSize = false,
+ X = Pos.AnchorEnd (1),
+ Y = Pos.AnchorEnd (2),
+ Height = 1,
+ Width = 1,
+ NoPadding = true,
+ NoDecorations = true,
+ Title = CM.Glyphs.DownArrow.ToString (),
+ WantContinuousButtonPressed = true,
+ CanFocus = false
+ };
+ down.Accept += (sender, args) => { ScrollVertical (1); };
+
+ var left = new Button
+ {
+ AutoSize = false,
+ X = 0,
+ Y = Pos.AnchorEnd (1),
+ Height = 1,
+ Width = 1,
+ NoPadding = true,
+ NoDecorations = true,
+ Title = CM.Glyphs.LeftArrow.ToString (),
+ WantContinuousButtonPressed = true,
+ CanFocus = false
+ };
+ left.Accept += (sender, args) => { ScrollHorizontal (-1); };
+
+ var right = new Button
+ {
+ AutoSize = false,
+ X = Pos.AnchorEnd (2),
+ Y = Pos.AnchorEnd (1),
+ Height = 1,
+ Width = 1,
+ NoPadding = true,
+ NoDecorations = true,
+ Title = CM.Glyphs.RightArrow.ToString (),
+ WantContinuousButtonPressed = true,
+ CanFocus = false
+ };
+ right.Accept += (sender, args) => { ScrollHorizontal (1); };
+
+ Padding.Add (up, down, left, right);
+ }
+
+ private void Handle_MouseEvent (object sender, MouseEventEventArgs e)
+ {
+ if (e.MouseEvent.Flags == MouseFlags.WheeledDown)
+ {
+ ScrollVertical (1);
+ e.Handled = true;
+
+ return;
+ }
+
+ if (e.MouseEvent.Flags == MouseFlags.WheeledUp)
+ {
+ ScrollVertical (-1);
+ e.Handled = true;
+
+ return;
+ }
+
+ if (e.MouseEvent.Flags == MouseFlags.WheeledRight)
+ {
+ ScrollHorizontal (1);
+ e.Handled = true;
+
+ return;
+ }
+
+ if (e.MouseEvent.Flags == MouseFlags.WheeledLeft)
+ {
+ ScrollHorizontal (-1);
+ e.Handled = true;
+ }
}
/// Gets the coordinates of the Cursor based on the SelectedCodePoint in screen coordinates
@@ -429,16 +571,16 @@ public Point Cursor
{
get
{
- int row = SelectedCodePoint / 16 * _rowHeight + ContentOffset.Y + 1;
+ int row = SelectedCodePoint / 16 * _rowHeight - Viewport.Y + 1;
- int col = SelectedCodePoint % 16 * COLUMN_WIDTH + ContentOffset.X + RowLabelWidth + 1; // + 1 for padding
+ int col = SelectedCodePoint % 16 * COLUMN_WIDTH - Viewport.X + RowLabelWidth + 1; // + 1 for padding between label and first column
return new (col, row);
}
set => throw new NotImplementedException ();
}
- public static int MaxCodePoint => 0x10FFFF;
+ public static int MaxCodePoint = UnicodeRange.Ranges.Max (r => r.End);
///
/// Specifies the starting offset for the character map. The default is 0x2500 which is the Box Drawing
@@ -449,6 +591,11 @@ public int SelectedCodePoint
get => _selected;
set
{
+ if (_selected == value)
+ {
+ return;
+ }
+
_selected = value;
if (IsInitialized)
@@ -456,36 +603,28 @@ public int SelectedCodePoint
int row = SelectedCodePoint / 16 * _rowHeight;
int col = SelectedCodePoint % 16 * COLUMN_WIDTH;
- int height = Bounds.Height - (ShowHorizontalScrollIndicator ? 2 : 1);
-
- if (row + ContentOffset.Y < 0)
+ if (row - Viewport.Y < 0)
{
// Moving up.
- ContentOffset = new (ContentOffset.X, row);
+ Viewport = Viewport with { Y = row };
}
- else if (row + ContentOffset.Y >= height)
+ else if (row - Viewport.Y >= Viewport.Height)
{
// Moving down.
- ContentOffset = new (
- ContentOffset.X,
- Math.Min (row, row - height + _rowHeight)
- );
+ Viewport = Viewport with { Y = row - Viewport.Height };
}
- int width = Bounds.Width / COLUMN_WIDTH * COLUMN_WIDTH - (ShowVerticalScrollIndicator ? RowLabelWidth + 1 : RowLabelWidth);
+ int width = Viewport.Width / COLUMN_WIDTH * COLUMN_WIDTH - RowLabelWidth;
- if (col + ContentOffset.X < 0)
+ if (col - Viewport.X < 0)
{
// Moving left.
- ContentOffset = new (col, ContentOffset.Y);
+ Viewport = Viewport with { X = col };
}
- else if (col + ContentOffset.X >= width)
+ else if (col - Viewport.X >= width)
{
// Moving right.
- ContentOffset = new (
- Math.Min (col, col - width + COLUMN_WIDTH),
- ContentOffset.Y
- );
+ Viewport = Viewport with { X = col - width };
}
}
@@ -515,6 +654,7 @@ public int StartCodePoint
{
_start = value;
SelectedCodePoint = value;
+ Viewport = Viewport with { Y = SelectedCodePoint / 16 * _rowHeight };
SetNeedsDisplay ();
}
}
@@ -523,98 +663,71 @@ public int StartCodePoint
private static int RowWidth => RowLabelWidth + COLUMN_WIDTH * 16;
public event EventHandler Hover;
- public override void OnDrawContent (Rectangle contentArea)
+ public override void OnDrawContent (Rectangle viewport)
{
- if (contentArea.Height == 0 || contentArea.Width == 0)
+ if (viewport.Height == 0 || viewport.Width == 0)
{
return;
}
- // Call the base (ScrollView) to draw the scrollbars. Do this ahead of our own drawing so that
- // any wide or tall glyphs actually render over the scrollbars (on platforms like Windows Terminal) that
- // does this correctly.
- base.OnDrawContent (contentArea);
-
- Rectangle viewport = new (
- ContentOffset,
- new (
- Math.Max (Bounds.Width - (ShowVerticalScrollIndicator ? 1 : 0), 0),
- Math.Max (Bounds.Height - (ShowHorizontalScrollIndicator ? 1 : 0), 0)
- )
- );
-
- Rectangle oldClip = ClipToBounds ();
-
- if (ShowHorizontalScrollIndicator)
- {
- // ClipToBounds doesn't know about the scroll indicators, so if off, subtract one from height
- Driver.Clip = new (Driver.Clip.Location, new (Driver.Clip.Size.Width, Driver.Clip.Size.Height - 1));
- }
-
- if (ShowVerticalScrollIndicator)
- {
- // ClipToBounds doesn't know about the scroll indicators, so if off, subtract one from width
- Driver.Clip = new (Driver.Clip.Location, new (Driver.Clip.Size.Width - 1, Driver.Clip.Size.Height));
- }
+ Clear ();
- int cursorCol = Cursor.X - ContentOffset.X - RowLabelWidth - 1;
- int cursorRow = Cursor.Y - ContentOffset.Y - 1;
+ int cursorCol = Cursor.X + Viewport.X - RowLabelWidth - 1;
+ int cursorRow = Cursor.Y + Viewport.Y - 1;
Driver.SetAttribute (GetHotNormalColor ());
Move (0, 0);
Driver.AddStr (new (' ', RowLabelWidth + 1));
+ int firstColumnX = RowLabelWidth - Viewport.X;
+
+ // Header
for (var hexDigit = 0; hexDigit < 16; hexDigit++)
{
- int x = ContentOffset.X + RowLabelWidth + hexDigit * COLUMN_WIDTH;
+ int x = firstColumnX + hexDigit * COLUMN_WIDTH;
if (x > RowLabelWidth - 2)
{
Move (x, 0);
Driver.SetAttribute (GetHotNormalColor ());
Driver.AddStr (" ");
-
- Driver.SetAttribute (
- HasFocus && cursorCol + ContentOffset.X + RowLabelWidth == x
- ? ColorScheme.HotFocus
- : GetHotNormalColor ()
- );
+ Driver.SetAttribute (HasFocus && cursorCol + firstColumnX == x ? ColorScheme.HotFocus : GetHotNormalColor ());
Driver.AddStr ($"{hexDigit:x}");
Driver.SetAttribute (GetHotNormalColor ());
Driver.AddStr (" ");
}
}
- int firstColumnX = viewport.X + RowLabelWidth;
-
// Even though the Clip is set to prevent us from drawing on the row potentially occupied by the horizontal
// scroll bar, we do the smart thing and not actually draw that row if not necessary.
- for (var y = 1; y < Bounds.Height - (ShowHorizontalScrollIndicator ? 1 : 0); y++)
+ for (var y = 1; y < Viewport.Height; y++)
{
// What row is this?
- int row = (y - ContentOffset.Y - 1) / _rowHeight;
+ int row = (y + Viewport.Y - 1) / _rowHeight;
int val = row * 16;
if (val > MaxCodePoint)
{
- continue;
+ break;
}
Move (firstColumnX + COLUMN_WIDTH, y);
Driver.SetAttribute (GetNormalColor ());
- // Note, this code naïvely draws all columns, even if the viewport is smaller than
- // the needed width. We rely on Clip to ensure we don't draw past the viewport.
- // If we were *really* worried about performance, we'd optimize this code to only draw the
- // parts of the row that are actually visible in the viewport.
for (var col = 0; col < 16; col++)
{
int x = firstColumnX + COLUMN_WIDTH * col + 1;
+ if (x < 0 || x > Viewport.Width - 1)
+ {
+ continue;
+ }
+
Move (x, y);
- if (cursorRow + ContentOffset.Y + 1 == y && cursorCol + ContentOffset.X + firstColumnX + 1 == x && !HasFocus)
+ // If we're at the cursor position, and we don't have focus, invert the colors.
+ if (row == cursorRow && x == cursorCol && !HasFocus)
{
Driver.SetAttribute (GetFocusColor ());
}
@@ -629,9 +742,9 @@ public override void OnDrawContent (Rectangle contentArea)
int width = rune.GetColumns ();
- // are we at first row of the row?
- if (!ShowGlyphWidths || (y - ContentOffset.Y) % _rowHeight > 0)
+ if (!ShowGlyphWidths || (y + Viewport.Y) % _rowHeight > 0)
{
+ // Draw the rune
if (width > 0)
{
Driver.AddRune (rune);
@@ -666,25 +779,24 @@ public override void OnDrawContent (Rectangle contentArea)
}
else
{
+ // Draw the width of the rune
Driver.SetAttribute (ColorScheme.HotNormal);
Driver.AddStr ($"{width}");
}
- if (cursorRow + ContentOffset.Y + 1 == y && cursorCol + ContentOffset.X + firstColumnX + 1 == x && !HasFocus)
+ // If we're at the cursor position, and we don't have focus, revert the colors to normal
+ if (row == cursorRow && x == cursorCol && !HasFocus)
{
Driver.SetAttribute (GetNormalColor ());
}
}
+ // Draw row label (U+XXXX_)
Move (0, y);
- Driver.SetAttribute (
- HasFocus && cursorRow + ContentOffset.Y + 1 == y
- ? ColorScheme.HotFocus
- : ColorScheme.HotNormal
- );
+ Driver.SetAttribute (HasFocus && y + Viewport.Y - 1 == cursorRow ? ColorScheme.HotFocus : ColorScheme.HotNormal);
- if (!ShowGlyphWidths || (y - ContentOffset.Y) % _rowHeight > 0)
+ if (!ShowGlyphWidths || (y + Viewport.Y) % _rowHeight > 0)
{
Driver.AddStr ($"U+{val / 16:x5}_ ");
}
@@ -693,8 +805,6 @@ public override void OnDrawContent (Rectangle contentArea)
Driver.AddStr (new (' ', RowLabelWidth));
}
}
-
- Driver.Clip = oldClip;
}
public override bool OnEnter (View view)
@@ -718,9 +828,9 @@ public override void PositionCursor ()
{
if (HasFocus
&& Cursor.X >= RowLabelWidth
- && Cursor.X < Bounds.Width - (ShowVerticalScrollIndicator ? 1 : 0)
+ && Cursor.X < Viewport.Width
&& Cursor.Y > 0
- && Cursor.Y < Bounds.Height - (ShowHorizontalScrollIndicator ? 1 : 0))
+ && Cursor.Y < Viewport.Height)
{
Driver.SetCursorVisibility (_cursor);
Move (Cursor.X, Cursor.Y);
@@ -760,6 +870,8 @@ private void Handle_MouseClick (object sender, MouseEventEventArgs args)
return;
}
+ args.Handled = true;
+
if (me.Y == 0)
{
me.Y = Cursor.Y;
@@ -773,8 +885,8 @@ private void Handle_MouseClick (object sender, MouseEventEventArgs args)
me.X = Cursor.X;
}
- int row = (me.Y - 1 - ContentOffset.Y) / _rowHeight; // -1 for header
- int col = (me.X - RowLabelWidth - ContentOffset.X) / COLUMN_WIDTH;
+ int row = (me.Y - 1 - -Viewport.Y) / _rowHeight; // -1 for header
+ int col = (me.X - RowLabelWidth - -Viewport.X) / COLUMN_WIDTH;
if (col > 15)
{
@@ -812,7 +924,7 @@ private void Handle_MouseClick (object sender, MouseEventEventArgs args)
{
SelectedCodePoint = val;
- _contextMenu = new()
+ _contextMenu = new ()
{
Position = new (me.X + 1, me.Y + 1),
MenuItems = new (
@@ -846,6 +958,7 @@ private void ShowDetails ()
{
var client = new UcdApiClient ();
var decResponse = string.Empty;
+ var getCodePointError = string.Empty;
var waitIndicator = new Dialog
{
@@ -854,7 +967,7 @@ private void ShowDetails ()
Y = Pos.Center (),
Height = 7,
Width = 50,
- Buttons = [new() { Text = "Cancel" }]
+ Buttons = [new () { Text = "Cancel" }]
};
var errorLabel = new Label
@@ -877,12 +990,13 @@ private void ShowDetails ()
try
{
decResponse = await client.GetCodepointDec (SelectedCodePoint);
+ Application.Invoke (() => waitIndicator.RequestStop ());
}
catch (HttpRequestException e)
{
+ getCodePointError = errorLabel.Text = e.Message;
Application.Invoke (() => waitIndicator.RequestStop ());
}
-
};
Application.Run (waitIndicator);
waitIndicator.Dispose ();
@@ -939,46 +1053,46 @@ private void ShowDetails ()
var label = new Label { Text = "IsAscii: ", X = 0, Y = 0 };
dlg.Add (label);
- label = new() { Text = $"{rune.IsAscii}", X = Pos.Right (label), Y = Pos.Top (label) };
+ label = new () { Text = $"{rune.IsAscii}", X = Pos.Right (label), Y = Pos.Top (label) };
dlg.Add (label);
- label = new() { Text = ", Bmp: ", X = Pos.Right (label), Y = Pos.Top (label) };
+ label = new () { Text = ", Bmp: ", X = Pos.Right (label), Y = Pos.Top (label) };
dlg.Add (label);
- label = new() { Text = $"{rune.IsBmp}", X = Pos.Right (label), Y = Pos.Top (label) };
+ label = new () { Text = $"{rune.IsBmp}", X = Pos.Right (label), Y = Pos.Top (label) };
dlg.Add (label);
- label = new() { Text = ", CombiningMark: ", X = Pos.Right (label), Y = Pos.Top (label) };
+ label = new () { Text = ", CombiningMark: ", X = Pos.Right (label), Y = Pos.Top (label) };
dlg.Add (label);
- label = new() { Text = $"{rune.IsCombiningMark ()}", X = Pos.Right (label), Y = Pos.Top (label) };
+ label = new () { Text = $"{rune.IsCombiningMark ()}", X = Pos.Right (label), Y = Pos.Top (label) };
dlg.Add (label);
- label = new() { Text = ", SurrogatePair: ", X = Pos.Right (label), Y = Pos.Top (label) };
+ label = new () { Text = ", SurrogatePair: ", X = Pos.Right (label), Y = Pos.Top (label) };
dlg.Add (label);
- label = new() { Text = $"{rune.IsSurrogatePair ()}", X = Pos.Right (label), Y = Pos.Top (label) };
+ label = new () { Text = $"{rune.IsSurrogatePair ()}", X = Pos.Right (label), Y = Pos.Top (label) };
dlg.Add (label);
- label = new() { Text = ", Plane: ", X = Pos.Right (label), Y = Pos.Top (label) };
+ label = new () { Text = ", Plane: ", X = Pos.Right (label), Y = Pos.Top (label) };
dlg.Add (label);
- label = new() { Text = $"{rune.Plane}", X = Pos.Right (label), Y = Pos.Top (label) };
+ label = new () { Text = $"{rune.Plane}", X = Pos.Right (label), Y = Pos.Top (label) };
dlg.Add (label);
- label = new() { Text = "Columns: ", X = 0, Y = Pos.Bottom (label) };
+ label = new () { Text = "Columns: ", X = 0, Y = Pos.Bottom (label) };
dlg.Add (label);
- label = new() { Text = $"{rune.GetColumns ()}", X = Pos.Right (label), Y = Pos.Top (label) };
+ label = new () { Text = $"{rune.GetColumns ()}", X = Pos.Right (label), Y = Pos.Top (label) };
dlg.Add (label);
- label = new() { Text = ", Utf16SequenceLength: ", X = Pos.Right (label), Y = Pos.Top (label) };
+ label = new () { Text = ", Utf16SequenceLength: ", X = Pos.Right (label), Y = Pos.Top (label) };
dlg.Add (label);
- label = new() { Text = $"{rune.Utf16SequenceLength}", X = Pos.Right (label), Y = Pos.Top (label) };
+ label = new () { Text = $"{rune.Utf16SequenceLength}", X = Pos.Right (label), Y = Pos.Top (label) };
dlg.Add (label);
- label = new()
+ label = new ()
{
Text =
$"Code Point Information from {UcdApiClient.BaseUrl}codepoint/dec/{SelectedCodePoint}:",
diff --git a/UICatalog/Scenarios/Clipping.cs b/UICatalog/Scenarios/Clipping.cs
index 4cdaff4bba..0aba6ece9a 100644
--- a/UICatalog/Scenarios/Clipping.cs
+++ b/UICatalog/Scenarios/Clipping.cs
@@ -4,6 +4,8 @@ namespace UICatalog.Scenarios;
[ScenarioMetadata ("Clipping", "Used to test that things clip correctly")]
[ScenarioCategory ("Tests")]
+[ScenarioCategory ("Drawing")]
+[ScenarioCategory ("Scrolling")]
public class Clipping : Scenario
{
public override void Init ()
@@ -30,8 +32,9 @@ public override void Setup ()
scrollView.ContentSize = new (200, 100);
//ContentOffset = Point.Empty,
- //scrollView.ShowVerticalScrollIndicator = true;
- //scrollView.ShowHorizontalScrollIndicator = true;
+ scrollView.AutoHideScrollBars = true;
+ scrollView.ShowVerticalScrollIndicator = true;
+ scrollView.ShowHorizontalScrollIndicator = true;
var embedded1 = new View
{
diff --git a/UICatalog/Scenarios/ColorPicker.cs b/UICatalog/Scenarios/ColorPicker.cs
index 32fdd26d3c..8ae76c5dc6 100644
--- a/UICatalog/Scenarios/ColorPicker.cs
+++ b/UICatalog/Scenarios/ColorPicker.cs
@@ -44,8 +44,8 @@ public override void Setup ()
backgroundColorPicker = new ColorPicker
{
Title = "Background Color",
- Y = Pos.Center (),
- X = Pos.Center (),
+ // TODO: Replace with Pos.AnchorEnd () when #2900 is done
+ X = Pos.AnchorEnd ((8 * 4) + 2), // 8 box * 4 width + 2 for border
BoxHeight = 1,
BoxWidth = 4,
BorderStyle = LineStyle.Single
diff --git a/UICatalog/Scenarios/ComputedLayout.cs b/UICatalog/Scenarios/ComputedLayout.cs
index 682e89a5b7..d51311c90c 100644
--- a/UICatalog/Scenarios/ComputedLayout.cs
+++ b/UICatalog/Scenarios/ComputedLayout.cs
@@ -57,12 +57,12 @@ public override void Setup ()
Top.LayoutComplete += (s, a) =>
{
horizontalRuler.Text =
- rule.Repeat ((int)Math.Ceiling (horizontalRuler.Bounds.Width / (double)rule.Length)) [
- ..horizontalRuler.Bounds.Width];
+ rule.Repeat ((int)Math.Ceiling (horizontalRuler.Viewport.Width / (double)rule.Length)) [
+ ..horizontalRuler.Viewport.Width];
verticalRuler.Text =
- vrule.Repeat ((int)Math.Ceiling (verticalRuler.Bounds.Height * 2 / (double)rule.Length))
- [..(verticalRuler.Bounds.Height * 2)];
+ vrule.Repeat ((int)Math.Ceiling (verticalRuler.Viewport.Height * 2 / (double)rule.Length))
+ [..(verticalRuler.Viewport.Height * 2)];
};
Top.Add (verticalRuler);
diff --git a/UICatalog/Scenarios/ContentScrolling.cs b/UICatalog/Scenarios/ContentScrolling.cs
new file mode 100644
index 0000000000..67f61d3c8b
--- /dev/null
+++ b/UICatalog/Scenarios/ContentScrolling.cs
@@ -0,0 +1,402 @@
+using System;
+using System.ComponentModel;
+using System.Linq;
+using Terminal.Gui;
+using static UICatalog.Scenarios.Adornments;
+
+namespace UICatalog.Scenarios;
+
+[ScenarioMetadata ("Content Scrolling", "Demonstrates using View.Viewport and View.ContentSize to scroll content.")]
+[ScenarioCategory ("Layout")]
+[ScenarioCategory ("Drawing")]
+[ScenarioCategory ("Scrolling")]
+public class ContentScrolling : Scenario
+{
+ private ViewDiagnosticFlags _diagnosticFlags;
+
+ public class ScrollingDemoView : FrameView
+ {
+ public ScrollingDemoView ()
+ {
+ Width = Dim.Fill ();
+ Height = Dim.Fill ();
+ ColorScheme = Colors.ColorSchemes ["Base"];
+ Text = "Text (ScrollingDemoView.Text). This is long text.\nThe second line.\n3\n4\n5th line\nLine 6. This is a longer line that should wrap automatically.";
+ CanFocus = true;
+ BorderStyle = LineStyle.Rounded;
+ Arrangement = ViewArrangement.Fixed;
+
+ ContentSize = new (60, 40);
+ ViewportSettings |= ViewportSettings.ClearContentOnly;
+ ViewportSettings |= ViewportSettings.ClipContentOnly;
+
+ // 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));
+
+ // 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);
+
+ // Add a status label to the border that shows Viewport and ContentSize values. Bit of a hack.
+ // TODO: Move to Padding with controls
+ Border.Add (new Label { AutoSize = false, X = 20 });
+ 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);
+ }
+ }
+
+ private void VirtualDemoView_LayoutComplete (object sender, LayoutEventArgs e)
+ {
+ Label status = Border.Subviews.OfType