diff --git a/Terminal.Gui/ConsoleDrivers/ConsoleKeyMapping.cs b/Terminal.Gui/ConsoleDrivers/ConsoleKeyMapping.cs
index 566d7e1f3e..f18420c424 100644
--- a/Terminal.Gui/ConsoleDrivers/ConsoleKeyMapping.cs
+++ b/Terminal.Gui/ConsoleDrivers/ConsoleKeyMapping.cs
@@ -58,6 +58,9 @@ public static class ConsoleKeyMapping {
/// the function returns 0.
public static uint MapVKtoChar (VK vk)
{
+ if (Environment.OSVersion.Platform != PlatformID.Win32NT) {
+ return 0;
+ }
var tid = GetWindowThreadProcessId (GetForegroundWindow (), 0);
var hkl = GetKeyboardLayout (tid);
return MapVirtualKeyEx (vk, 2, hkl);
@@ -85,7 +88,18 @@ public static uint MapVKtoChar (VK vk)
///
///
[DllImport ("user32.dll")]
- public extern static bool GetKeyboardLayoutName ([Out] StringBuilder pwszKLID);
+ extern static bool GetKeyboardLayoutName ([Out] StringBuilder pwszKLID);
+
+ public static string GetKeyboardLayoutName ()
+ {
+ if (Environment.OSVersion.Platform != PlatformID.Win32NT) {
+ return "none";
+ }
+
+ StringBuilder klidSB = new StringBuilder ();
+ GetKeyboardLayoutName (klidSB);
+ return klidSB.ToString ();
+ }
class ScanCodeMapping : IEquatable {
public uint ScanCode;
diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs
index 591b49447b..58eae6264f 100644
--- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs
+++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs
@@ -3,14 +3,16 @@
//
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
-using Terminal.Gui.ConsoleDrivers;
+using static Terminal.Gui.ConsoleDrivers.ConsoleKeyMapping;
using static Terminal.Gui.NetEvents;
+using static Terminal.Gui.WindowsConsole;
namespace Terminal.Gui;
@@ -585,6 +587,35 @@ public struct InputResult {
public WindowSizeEvent WindowSizeEvent;
public WindowPositionEvent WindowPositionEvent;
public RequestResponseEvent RequestResponseEvent;
+
+ public override readonly string ToString ()
+ {
+ return EventType switch {
+ EventType.Key => ToString (ConsoleKeyInfo),
+ EventType.Mouse => MouseEvent.ToString (),
+ //EventType.WindowSize => WindowSize.ToString (),
+ //EventType.RequestResponse => RequestResponse.ToString (),
+ _ => "Unknown event type: " + EventType
+ };
+ }
+
+ ///
+ /// Prints a ConsoleKeyInfoEx structure
+ ///
+ ///
+ ///
+ public readonly string ToString (ConsoleKeyInfo cki)
+ {
+ var ke = new Key ((KeyCode)cki.KeyChar);
+ var sb = new StringBuilder ();
+ sb.Append ($"Key: {(KeyCode)cki.Key} ({cki.Key})");
+ sb.Append ((cki.Modifiers & ConsoleModifiers.Shift) != 0 ? " | Shift" : string.Empty);
+ sb.Append ((cki.Modifiers & ConsoleModifiers.Control) != 0 ? " | Control" : string.Empty);
+ sb.Append ((cki.Modifiers & ConsoleModifiers.Alt) != 0 ? " | Alt" : string.Empty);
+ sb.Append ($", KeyChar: {ke.AsRune.MakePrintable ()} ({(uint)cki.KeyChar}) ");
+ var s = sb.ToString ().TrimEnd (',').TrimEnd (' ');
+ return $"[ConsoleKeyInfo({s})]";
+ }
}
void HandleKeyboardEvent (ConsoleKeyInfo cki)
@@ -877,7 +908,7 @@ void WriteToConsole (StringBuilder output, ref int lastCol, int row, ref int out
);
// Dictionary for mapping ConsoleColor values to the values used by System.Net.Console.
- static Dictionary colorMap = new() {
+ static Dictionary colorMap = new () {
{ ConsoleColor.Black, COLOR_BLACK },
{ ConsoleColor.DarkBlue, COLOR_BLUE },
{ ConsoleColor.DarkGreen, COLOR_GREEN },
@@ -1100,7 +1131,7 @@ ConsoleKeyInfo FromVKPacketToKConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo)
bool alt = (mod & ConsoleModifiers.Alt) != 0;
bool control = (mod & ConsoleModifiers.Control) != 0;
- var cKeyInfo = ConsoleKeyMapping.DecodeVKPacketToKConsoleKeyInfo (consoleKeyInfo);
+ var cKeyInfo = DecodeVKPacketToKConsoleKeyInfo (consoleKeyInfo);
return new ConsoleKeyInfo (cKeyInfo.KeyChar, cKeyInfo.Key, shift, alt, control);
}
@@ -1108,37 +1139,11 @@ ConsoleKeyInfo FromVKPacketToKConsoleKeyInfo (ConsoleKeyInfo consoleKeyInfo)
KeyCode MapKey (ConsoleKeyInfo keyInfo)
{
switch (keyInfo.Key) {
- case ConsoleKey.Escape:
- return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.Esc);
- case ConsoleKey.Tab:
- return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.Tab);
- case ConsoleKey.Home:
- return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.Home);
- case ConsoleKey.End:
- return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.End);
- case ConsoleKey.LeftArrow:
- return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.CursorLeft);
- case ConsoleKey.RightArrow:
- return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.CursorRight);
- case ConsoleKey.UpArrow:
- return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.CursorUp);
- case ConsoleKey.DownArrow:
- return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.CursorDown);
- case ConsoleKey.PageUp:
- return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.PageUp);
- case ConsoleKey.PageDown:
- return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.PageDown);
- case ConsoleKey.Enter:
- return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.Enter);
- case ConsoleKey.Spacebar:
- return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.Space);
- case ConsoleKey.Backspace:
- return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.Backspace);
- case ConsoleKey.Delete:
- return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.Delete);
- case ConsoleKey.Insert:
- return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.Insert);
-
+ case ConsoleKey.OemPeriod:
+ case ConsoleKey.OemComma:
+ case ConsoleKey.OemPlus:
+ case ConsoleKey.OemMinus:
+ case ConsoleKey.Packet:
case ConsoleKey.Oem1:
case ConsoleKey.Oem2:
case ConsoleKey.Oem3:
@@ -1148,107 +1153,85 @@ KeyCode MapKey (ConsoleKeyInfo keyInfo)
case ConsoleKey.Oem7:
case ConsoleKey.Oem8:
case ConsoleKey.Oem102:
- var ret = ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)(uint)keyInfo.KeyChar);
- if (ret.HasFlag (KeyCode.ShiftMask)) {
- ret &= ~KeyCode.ShiftMask;
+ if (keyInfo.KeyChar == 0) {
+ // If the keyChar is 0, keyInfo.Key value is not a printable character.
+
+ return KeyCode.Null;// MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode)keyInfo.Key);
+ } else {
+ if (keyInfo.Modifiers != ConsoleModifiers.Shift) {
+ // If Shift wasn't down we don't need to do anything but return the keyInfo.KeyChar
+ return MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)(keyInfo.KeyChar));
+ }
+
+ // Strip off Shift - We got here because they KeyChar from Windows is the shifted char (e.g. "Ç")
+ // and passing on Shift would be redundant.
+ return MapToKeyCodeModifiers (keyInfo.Modifiers & ~ConsoleModifiers.Shift, (KeyCode)keyInfo.KeyChar);
}
- return ret;
+ break;
+
- case ConsoleKey.OemPeriod:
- case ConsoleKey.OemComma:
- case ConsoleKey.OemPlus:
- case ConsoleKey.OemMinus:
return (KeyCode)(uint)keyInfo.KeyChar;
}
var key = keyInfo.Key;
- if (key is >= ConsoleKey.A and <= ConsoleKey.Z) {
- int delta = key - ConsoleKey.A;
- if (keyInfo.Modifiers == ConsoleModifiers.Control) {
- return (KeyCode)((uint)KeyCode.CtrlMask | (uint)KeyCode.A + delta);
- }
- if (keyInfo.Modifiers == ConsoleModifiers.Alt) {
- return (KeyCode)((uint)KeyCode.AltMask | (uint)KeyCode.A + delta);
- }
- if (keyInfo.Modifiers == (ConsoleModifiers.Shift | ConsoleModifiers.Alt)) {
- return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)((uint)KeyCode.A + delta));
- }
- if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
- if (keyInfo.KeyChar == 0 || keyInfo.KeyChar != 0 && keyInfo.KeyChar >= 1 && keyInfo.KeyChar <= 26) {
- return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)((uint)KeyCode.A + delta));
- } else if (keyInfo.KeyChar != 0) {
- return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)keyInfo.KeyChar);
- }
+ // A..Z are special cased:
+ // - Alone, they represent lowercase a...z
+ // - With ShiftMask they are A..Z
+ // - If CapsLock is on the above is reversed.
+ // - If Alt and/or Ctrl are present, treat as upper case
+ if (keyInfo.Key is >= ConsoleKey.A and <= ConsoleKey.Z) {
+ if (keyInfo.Modifiers.HasFlag (ConsoleModifiers.Alt) || keyInfo.Modifiers.HasFlag (ConsoleModifiers.Control)) {
+ return MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)(uint)keyInfo.Key);
}
- if (keyInfo.Modifiers == ConsoleModifiers.Shift /*^ (keyInfoEx.CapsLock)*/) {
- if (keyInfo.KeyChar <= (uint)KeyCode.Z) {
- return (KeyCode)((uint)KeyCode.A + delta) | KeyCode.ShiftMask;
- }
- }
- // This is buggy because is converting a lower case to a upper case and mustn't
- //if (((KeyCode)((uint)keyInfo.KeyChar) & KeyCode.Space) == KeyCode.Space) {
- // return (KeyCode)((uint)keyInfo.KeyChar) & ~KeyCode.Space;
- //}
- return (KeyCode)(uint)keyInfo.KeyChar;
- }
- if (key is >= ConsoleKey.D0 and <= ConsoleKey.D9) {
- int delta = key - ConsoleKey.D0;
- if (keyInfo.Modifiers == ConsoleModifiers.Alt) {
- return (KeyCode)((uint)KeyCode.AltMask | (uint)KeyCode.D0 + delta);
- }
- if (keyInfo.Modifiers == ConsoleModifiers.Control) {
- return (KeyCode)((uint)KeyCode.CtrlMask | (uint)KeyCode.D0 + delta);
- }
- if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
- if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30 || keyInfo.KeyChar == (uint)KeyCode.D0 + delta) {
- return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)((uint)KeyCode.D0 + delta));
+ if (keyInfo.Modifiers == ConsoleModifiers.Shift) {
+ // If ShiftMask is on add the ShiftMask
+ if (char.IsUpper (keyInfo.KeyChar)) {
+ return (KeyCode)((uint)keyInfo.Key) | KeyCode.ShiftMask;
}
}
return (KeyCode)(uint)keyInfo.KeyChar;
}
- if (key is >= ConsoleKey.F1 and <= ConsoleKey.F12) {
- int delta = key - ConsoleKey.F1;
- if ((keyInfo.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
- return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)((uint)KeyCode.F1 + delta));
- }
- return (KeyCode)((uint)KeyCode.F1 + delta);
+ // Handle control keys whose VK codes match the related ASCII value (those below ASCII 33) like ESC
+ if (keyInfo.Key != ConsoleKey.None && Enum.IsDefined (typeof (KeyCode), (uint)keyInfo.Key)) {
+ return MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)(keyInfo.Key));
}
- // Is it a key between a..z?
- if ((char)keyInfo.KeyChar is >= 'a' and <= 'z') {
- // 'a' should be Key.A
- return (KeyCode)(uint)keyInfo.KeyChar & ~KeyCode.Space;
+ // Handle control keys (e.g. CursorUp)
+ if (keyInfo.Key != ConsoleKey.None && Enum.IsDefined (typeof (KeyCode), ((uint)keyInfo.Key + (uint)KeyCode.MaxCodePoint))) {
+ return MapToKeyCodeModifiers (keyInfo.Modifiers, (KeyCode)((uint)keyInfo.Key + (uint)KeyCode.MaxCodePoint));
}
- // Is it a key between A..Z?
- if (((KeyCode)(uint)keyInfo.KeyChar & ~KeyCode.Space) is >= KeyCode.A and <= KeyCode.Z) {
- // It's Key.A...Z. Make it Key.A | Key.ShiftMask
- return (KeyCode)(uint)keyInfo.KeyChar & ~KeyCode.Space | KeyCode.ShiftMask;
- }
return (KeyCode)(uint)keyInfo.KeyChar;
}
#endregion Keyboard Handling
-
+
void ProcessInput (InputResult inputEvent)
{
switch (inputEvent.EventType) {
- case EventType.Key:
+ case NetEvents.EventType.Key:
var consoleKeyInfo = inputEvent.ConsoleKeyInfo;
- if (consoleKeyInfo.Key == ConsoleKey.Packet) {
- consoleKeyInfo = FromVKPacketToKConsoleKeyInfo (consoleKeyInfo);
- }
+ //if (consoleKeyInfo.Key == ConsoleKey.Packet) {
+ // consoleKeyInfo = FromVKPacketToKConsoleKeyInfo (consoleKeyInfo);
+ //}
+
+ //Debug.WriteLine ($"event: {inputEvent}");
+
var map = MapKey (consoleKeyInfo);
+ if (map == KeyCode.Null) {
+ break;
+ }
+
OnKeyDown (new Key (map));
OnKeyUp (new Key (map));
break;
- case EventType.Mouse:
+ case NetEvents.EventType.Mouse:
OnMouseEvent (new MouseEventEventArgs (ToDriverMouse (inputEvent.MouseEvent)));
break;
- case EventType.WindowSize:
+ case NetEvents.EventType.WindowSize:
_winSizeChanging = true;
Top = 0;
Left = 0;
@@ -1260,12 +1243,9 @@ void ProcessInput (InputResult inputEvent)
_winSizeChanging = false;
OnSizeChanged (new SizeChangedEventArgs (new Size (Cols, Rows)));
break;
- case EventType.RequestResponse:
- // BUGBUG: What is this for? It does not seem to be used anywhere.
- // It is also not clear what it does. View.Data is documented as "This property is not used internally"
- Application.Top.Data = inputEvent.RequestResponseEvent.ResultTuple;
+ case NetEvents.EventType.RequestResponse:
break;
- case EventType.WindowPosition:
+ case NetEvents.EventType.WindowPosition:
break;
default:
throw new ArgumentOutOfRangeException ();
@@ -1275,7 +1255,7 @@ void ProcessInput (InputResult inputEvent)
public override void SendKeys (char keyChar, ConsoleKey key, bool shift, bool alt, bool control)
{
var input = new InputResult {
- EventType = EventType.Key,
+ EventType = NetEvents.EventType.Key,
ConsoleKeyInfo = new ConsoleKeyInfo (keyChar, key, shift, alt, control)
};
diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs
index eb1563403b..c6baab686b 100644
--- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs
+++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs
@@ -1085,9 +1085,7 @@ internal void ProcessInput (WindowsConsole.InputRecord inputEvent)
inputEvent.KeyEvent = FromVKPacketToKeyEventRecord (inputEvent.KeyEvent);
}
var keyInfo = ToConsoleKeyInfoEx (inputEvent.KeyEvent);
- StringBuilder klidSB = new StringBuilder ();
- GetKeyboardLayoutName (klidSB);
- Debug.WriteLine ($"event: KBD: {klidSB} {inputEvent.ToString ()} {keyInfo.ToString (keyInfo)}");
+ //Debug.WriteLine ($"event: KBD: {GetKeyboardLayoutName()} {inputEvent.ToString ()} {keyInfo.ToString (keyInfo)}");
var map = MapKey (keyInfo);
diff --git a/Terminal.Gui/Input/Responder.cs b/Terminal.Gui/Input/Responder.cs
index bdf8ec1ead..e42f6e764e 100644
--- a/Terminal.Gui/Input/Responder.cs
+++ b/Terminal.Gui/Input/Responder.cs
@@ -47,6 +47,11 @@ public Responder ()
}
#endif
+ ///
+ /// Event raised when has been called to signal that this object is being disposed.
+ ///
+ public event EventHandler Disposing;
+
///
/// Gets or sets a value indicating whether this can focus.
///
@@ -186,6 +191,7 @@ protected virtual void Dispose (bool disposing)
public void Dispose ()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
+ Disposing?.Invoke (this, EventArgs.Empty);
Dispose (disposing: true);
GC.SuppressFinalize (this);
#if DEBUG_IDISPOSABLE
diff --git a/Terminal.Gui/View/Layout/ViewLayout.cs b/Terminal.Gui/View/Layout/ViewLayout.cs
index 0ca320c307..c57de26049 100644
--- a/Terminal.Gui/View/Layout/ViewLayout.cs
+++ b/Terminal.Gui/View/Layout/ViewLayout.cs
@@ -30,22 +30,31 @@ public partial class View {
Rect _frame;
///
- /// Gets or sets the frame for the view. The frame is relative to the 's .
+ /// Gets or sets location and size of the view. The frame is relative to the 's .
///
- /// The frame.
+ /// The rectangle describing the location and size of the view, in coordinates relative to the .
///
///
- /// Change the Frame when using the layout style to move or resize views.
+ /// Change the Frame when using the layout style to move or resize views.
///
///
- /// Altering the Frame of a view will trigger the redrawing of the
- /// view as well as the redrawing of the affected regions of the .
+ /// Altering the Frame will change to .
+ /// Additionally, , , , and will be set
+ /// to the values of the Frame (using and ).
+ ///
+ ///
+ /// Altering the Frame will eventually (when the view is next drawn) cause the
+ /// and methods to be called.
///
///
public virtual Rect Frame {
get => _frame;
set {
_frame = new Rect (value.X, value.Y, Math.Max (value.Width, 0), Math.Max (value.Height, 0));
+ //X = _frame.X;
+ //Y = _frame.Y;
+ //Width = _frame.Width;
+ //Height = _frame.Height;
if (IsInitialized || LayoutStyle == LayoutStyle.Absolute) {
LayoutFrames ();
TextFormatter.Size = GetTextFormatterSizeNeededForTextAndHotKey ();
@@ -161,7 +170,7 @@ public Thickness GetFramesThickness ()
/// 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 () => new Point (Padding?.Thickness.GetInside (Padding.Frame).X ?? 0, Padding?.Thickness.GetInside (Padding.Frame).Y ?? 0);
+ public Point GetBoundsOffset () => new (Padding?.Thickness.GetInside (Padding.Frame).X ?? 0, Padding?.Thickness.GetInside (Padding.Frame).Y ?? 0);
///
/// Creates the view's objects. This internal method is overridden by Frame to do nothing
@@ -171,7 +180,9 @@ internal virtual void CreateFrames ()
{
void ThicknessChangedHandler (object sender, EventArgs e)
{
- LayoutFrames ();
+ if (IsInitialized) {
+ LayoutFrames ();
+ }
SetNeedsLayout ();
SetNeedsDisplay ();
}
@@ -207,41 +218,74 @@ void ThicknessChangedHandler (object sender, EventArgs e)
///
/// Controls how the View's is computed during . If the style is set to
- /// ,
- /// LayoutSubviews does not change the . If the style is
- /// the is updated using
+ /// , LayoutSubviews does not change the .
+ /// If the style is the is updated using
/// the , , , and properties.
///
+ ///
+ ///
+ /// Setting this property to will cause to determine the
+ /// size and position of the view. and will be set to using .
+ ///
+ ///
+ /// Setting this property to will cause the view to use the method to
+ /// size and position of the view. If either of the and properties are `null` they will be set to using
+ /// the current value of .
+ /// If either of the and properties are `null` they will be set to using .
+ ///
+ ///
/// The layout style.
public LayoutStyle LayoutStyle {
- get => _layoutStyle;
+ get {
+ return _layoutStyle;
+ //if ((X == null || X is Pos.PosAbsolute) && (Y == null || Y is Pos.PosAbsolute) &&
+ //(Width == null || Width is Dim.DimAbsolute) && (Height == null || Height is Dim.DimAbsolute)) {
+ // return LayoutStyle.Absolute;
+ //} else {
+ // return LayoutStyle.Computed;
+ //}
+ }
set {
_layoutStyle = value;
+ //switch (_layoutStyle) {
+ //case LayoutStyle.Absolute:
+ // X = Frame.X;
+ // Y = Frame.Y;
+ // Width = Frame.Width;
+ // Height = Frame.Height;
+ // break;
+
+ //case LayoutStyle.Computed:
+ // X ??= Frame.X;
+ // Y ??= Frame.Y;
+ // Width ??= Frame.Width;
+ // Height ??= Frame.Height;
+ // break;
+ //}
SetNeedsLayout ();
}
}
///
- /// The view's content area.
- ///
- /// SubViews are positioned relative to Bounds.
- ///
+ /// The bounds represent the View-relative rectangle used for this view; the area inside of 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.
+ ///
///
- /// Drawing is clipped to Bounds ( clips drawing to Bounds.Size).
+ /// If is the value of Bounds is indeterminate until the
+ /// view has been initialized ( is true) and has been called.
///
///
- /// Mouse events are reported relative to Bounds.
+ /// Updates to the Bounds updates , and has the same side effects as updating the .
///
- ///
- /// The view's content area.
- ///
///
- /// The of Bounds is always (0, 0). To obtain the offset of the Bounds from the Frame use
- /// .
+ /// Altering the Bounds will eventually (when the view is next drawn) cause the
+ /// and methods to be called.
///
///
- /// When using , Bounds is not valid until after the view has been initialized (after has been called and
- /// has fired). Accessing this property before the view is initialized is considered an error./>
+ /// 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 Rect Bounds {
@@ -249,7 +293,6 @@ public virtual Rect Bounds {
#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}");
- Debug.WriteLine ($"The Frame is set before the View has been initialized. So it isn't a bug.Is by design.");
}
#endif // DEBUG
//var frameRelativeBounds = Padding?.Thickness.GetInside (Padding.Frame) ?? new Rect (default, Frame.Size);
@@ -280,19 +323,28 @@ Rect FrameGetInsideBounds ()
Pos _x, _y;
///
- /// Gets or sets the X position for the view (the column). Only used if the is .
+ /// Gets or sets the X position for the view (the column).
///
- /// The X Position.
+ /// The object representing the X position.
///
///
- /// If is changing this property has no effect and its value is indeterminate.
+ /// If is the value is indeterminate until the
+ /// view 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.
+ ///
+ ///
+ /// If is changing this property will cause the to be updated. If
+ /// the new value is not of type the will change to .
///
///
/// is the same as Pos.Absolute(0).
///
///
public Pos X {
- get => VerifyIsInitialized (_x);
+ get => VerifyIsInitialized (_x, nameof(X));
set {
// BUGBUG: null is the sames a Pos.Absolute(0). Should we be explicit and set it?
@@ -306,21 +358,29 @@ public Pos X {
}
}
-
///
- /// Gets or sets the Y position for the view (the row). Only used if the is .
+ /// Gets or sets the Y position for the view (the row).
///
- /// The X Position.
+ /// The object representing the Y position.
///
///
- /// If is changing this property has no effect and its value is indeterminate.
+ /// If is the value is indeterminate until the
+ /// view 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.
+ ///
+ ///
+ /// If is changing this property will cause the to be updated. If
+ /// the new value is not of type the will change to .
///
///
/// is the same as Pos.Absolute(0).
///
///
public Pos Y {
- get => VerifyIsInitialized (_y);
+ get => VerifyIsInitialized (_y, nameof(Y));
set {
// BUGBUG: null is the sames a Pos.Absolute(0). Should we be explicit and set it?
@@ -333,23 +393,29 @@ public Pos Y {
OnResizeNeeded ();
}
}
+
Dim _width, _height;
///
- /// Gets or sets the width of the view. Only used when is .
+ /// Gets or sets the width of the view.
///
- /// The width.
+ /// The object representing the width of the view (the number of columns).
///
///
- /// If is changing this property
- /// has no effect and its value is indeterminate.
+ /// If is the value is indeterminate until the
+ /// view has been initialized ( is true) and has been called.
///
///
- /// is the same as Dim.Fill (0).
+ /// Changing this property will eventually (when the view is next drawn) cause the
+ /// and methods to be called.
+ ///
+ ///
+ /// If is changing this property will cause the to be updated. If
+ /// the new value is not of type the will change to .
///
///
public Dim Width {
- get => VerifyIsInitialized (_width);
+ get => VerifyIsInitialized (_width, nameof (Width));
set {
// BUGBUG: null is the sames a Dim.Fill(0). Should we be explicit and set it?
if (ValidatePosDim) {
@@ -372,20 +438,25 @@ public Dim Width {
}
///
- /// Gets or sets the height of the view. Only used when is .
+ /// Gets or sets the height of the view.
///
- /// The width.
+ /// The object representing the height of the view (the number of rows).
///
///
- /// If is changing this property
- /// has no effect and its value is indeterminate.
+ /// If is the value is indeterminate until the
+ /// view has been initialized ( is true) and has been called.
///
///
- /// is the same as Dim.Fill (0).
+ /// Changing this property will eventually (when the view is next drawn) cause the
+ /// and methods to be called.
+ ///
+ ///
+ /// If is changing this property will cause the to be updated. If
+ /// the new value is not of type the will change to .
///
///
public Dim Height {
- get => VerifyIsInitialized (_height);
+ get => VerifyIsInitialized (_height, nameof (Height));
set {
// BUGBUG: null is the sames a Dim.Fill(0). Should we be explicit and set it?
if (ValidatePosDim) {
@@ -408,22 +479,22 @@ public Dim Height {
}
// Diagnostics to highlight when X or Y is read before the view has been initialized
- Pos VerifyIsInitialized (Pos pos)
+ Pos VerifyIsInitialized (Pos pos, string member)
{
#if DEBUG
if (LayoutStyle == LayoutStyle.Computed && !IsInitialized) {
- Debug.WriteLine ($"WARNING: \"{this}\" has not been initialized; position is indeterminate {pos}. This is likely a bug.");
+ Debug.WriteLine ($"WARNING: \"{this}\" has not been initialized; {member} is indeterminate {pos}. This is potentially a bug.");
}
#endif // DEBUG
return pos;
}
// Diagnostics to highlight when Width or Height is read before the view has been initialized
- Dim VerifyIsInitialized (Dim dim)
+ Dim VerifyIsInitialized (Dim dim, string member)
{
#if DEBUG
if (LayoutStyle == LayoutStyle.Computed && !IsInitialized) {
- Debug.WriteLine ($"WARNING: \"{this}\" has not been initialized; dimension is indeterminate: {dim}. This is likely a bug.");
+ Debug.WriteLine ($"WARNING: \"{this}\" has not been initialized; {member} is indeterminate: {dim}. This is potentially a bug.");
}
#endif // DEBUG
return dim;
@@ -658,7 +729,7 @@ int GetNewDimension (Dim d, int location, int dimension, int autosize)
newDimension = d.Anchor (dimension);
newDimension = AutoSize && autosize > newDimension ? autosize : newDimension;
break;
-
+
case Dim.DimFill:
default:
newDimension = Math.Max (d.Anchor (dimension - location), 0);
@@ -803,7 +874,7 @@ internal void CollectAll (View from, ref HashSet nNodes, ref HashSet<(View
// BUGBUG: This should really only work on initialized subviews
foreach (var v in from.InternalSubviews /*.Where(v => v.IsInitialized)*/) {
nNodes.Add (v);
- if (v._layoutStyle != LayoutStyle.Computed) {
+ if (v.LayoutStyle != LayoutStyle.Computed) {
continue;
}
CollectPos (v.X, v, ref nNodes, ref nEdges);
@@ -896,7 +967,6 @@ internal virtual void LayoutFrames ()
Margin.Width = Frame.Size.Width;
Margin.Height = Frame.Size.Height;
Margin.SetNeedsLayout ();
- Margin.LayoutSubviews ();
Margin.SetNeedsDisplay ();
}
@@ -908,7 +978,6 @@ internal virtual void LayoutFrames ()
Border.Width = border.Size.Width;
Border.Height = border.Size.Height;
Border.SetNeedsLayout ();
- Border.LayoutSubviews ();
Border.SetNeedsDisplay ();
}
@@ -920,7 +989,6 @@ internal virtual void LayoutFrames ()
Padding.Width = padding.Size.Width;
Padding.Height = padding.Size.Height;
Padding.SetNeedsLayout ();
- Padding.LayoutSubviews ();
Padding.SetNeedsDisplay ();
}
}
@@ -930,7 +998,13 @@ internal virtual void LayoutFrames ()
/// response to the container view or terminal resizing.
///
///
+ ///
+ /// The position and dimensions of the view are indeterminate until the view has been initialized. Therefore,
+ /// the behavior of this method is indeterminate if is .
+ ///
+ ///
/// Raises the event) before it returns.
+ ///
///
public virtual void LayoutSubviews ()
{
@@ -1027,8 +1101,6 @@ bool ResizeView (bool autoSize)
Width = newFrameSize.Width;
}
}
- // BUGBUG: This call may be redundant
- TextFormatter.Size = GetTextFormatterSizeNeededForTextAndHotKey ();
return boundsChanged;
}
diff --git a/Terminal.Gui/View/View.cs b/Terminal.Gui/View/View.cs
index a22e60d948..d6e798c03d 100644
--- a/Terminal.Gui/View/View.cs
+++ b/Terminal.Gui/View/View.cs
@@ -1,527 +1,514 @@
using System;
-using System.Collections.Generic;
using System.ComponentModel;
-using System.Linq;
-using System.Text;
-namespace Terminal.Gui {
- #region API Docs
+namespace Terminal.Gui;
+
+#region API Docs
+///
+/// View is the base class for all views on the screen and represents a visible element that can render itself and
+/// contains zero or more nested views, called SubViews. View provides basic functionality for layout, positioning,
+/// and drawing. In addition, View provides keyboard and mouse event handling.
+///
+///
+///
+///
+/// TermDefinition
+///
+///
+/// SubViewA View that is contained in another view and will be rendered as part of the containing view's ContentArea.
+/// SubViews are added to another view via the ` method. A View may only be a SubView of a single View.
+///
+///
+/// SuperViewThe View that is a container for SubViews.
+///
+///
+///
+/// Focus is a concept that is used to describe which View is currently receiving user input. Only Views that are
+/// , , and will receive focus.
+///
+///
+/// Views that are focusable should implement the to make sure that
+/// the cursor is placed in a location that makes sense. Unix terminals do not have
+/// a way of hiding the cursor, so it can be distracting to have the cursor left at
+/// the last focused view. So views should make sure that they place the cursor
+/// in a visually sensible place.
+///
+///
+/// The View defines the base functionality for user interface elements in Terminal.Gui. Views
+/// can contain one or more subviews, can respond to user input and render themselves on the screen.
+///
+///
+/// Views supports two layout styles: or .
+/// The choice as to which layout style is used by the View
+/// is determined when the View is initialized. To create a View using Absolute layout, call a constructor that takes a
+/// Rect parameter to specify the absolute position and size (the View.). 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. Both approaches use coordinates that are relative to the container they are being added to.
+///
+///
+/// To switch between Absolute and Computed layout, use the property.
+///
+///
+/// Computed layout is more flexible and supports dynamic console apps where controls adjust layout
+/// as the terminal resizes or other Views change size or position. The X, Y, Width and Height
+/// properties are Dim and Pos objects that dynamically update the position of a view.
+/// The X and Y properties are of type
+/// and you can use either absolute positions, percentages or anchor
+/// points. The Width and Height properties are of type
+/// and can use absolute position,
+/// percentages and anchors. These are useful as they will take
+/// care of repositioning views when view's frames are resized or
+/// if the terminal size changes.
+///
+///
+/// Absolute layout requires specifying coordinates and sizes of Views explicitly, and the
+/// View will typically stay in a fixed position and size. To change the position and size use the
+/// property.
+///
+///
+/// Subviews (child views) can be added to a View by calling the method.
+/// The container of a View can be accessed with the property.
+///
+///
+/// To flag a region of the View's to be redrawn call .
+/// To flag the entire view for redraw call .
+///
+///
+/// The method is invoked when the size or layout of a view has
+/// changed. The default processing system will keep the size and dimensions
+/// for views that use the , and will recompute the
+/// frames for the vies that use .
+///
+///
+/// Views have a property that defines the default colors that subviews
+/// should use for rendering. This ensures that the views fit in the context where
+/// they are being used, and allows for themes to be plugged in. For example, the
+/// default colors for windows and Toplevels uses a blue background, while it uses
+/// a white background for dialog boxes and a red background for errors.
+///
+///
+/// Subclasses should not rely on being
+/// set at construction time. If a is not set on a view, the view will inherit the
+/// value from its and the value might only be valid once a view has been
+/// added to a SuperView.
+///
+///
+/// By using applications will work both
+/// in color as well as black and white displays.
+///
+///
+/// Views can also opt-in to more sophisticated initialization
+/// by implementing overrides to and
+/// which will be called
+/// when the view is added to a .
+///
+///
+/// If first-run-only initialization is preferred, overrides to
+/// can be implemented, in which case the
+/// methods will only be called if
+/// is . This allows proper inheritance hierarchies
+/// to override base class layout code optimally by doing so only on first run,
+/// instead of on every run.
+///
+///
+/// See for an overview of View keyboard handling.
+/// ///
+#endregion API Docs
+
+public partial class View : Responder, ISupportInitializeNotification {
+ #region Constructors and Initialization
///
- /// View is the base class for all views on the screen and represents a visible element that can render itself and
- /// contains zero or more nested views, called SubViews. View provides basic functionality for layout, positioning,
- /// and drawing. In addition, View provides keyboard and mouse event handling.
+ /// Initializes a new instance of a class with the absolute
+ /// dimensions specified in the parameter.
+ ///
+ /// The region covered by this view.
+ ///
+ /// This constructor initialize a View with a of .
+ /// Use to initialize a View with of
+ ///
+ public View (Rect frame) : this (frame, null) { }
+
+ ///
+ /// Initializes a new instance of using layout.
///
///
- ///
- ///
- /// TermDefinition
- ///
- ///
- /// SubViewA View that is contained in another view and will be rendered as part of the containing view's ContentArea.
- /// SubViews are added to another view via the ` method. A View may only be a SubView of a single View.
- ///
- ///
- /// SuperViewThe View that is a container for SubViews.
- ///
- ///
- ///
- /// Focus is a concept that is used to describe which View is currently receiving user input. Only Views that are
- /// , , and will receive focus.
- ///
- ///
- /// Views that are focusable should implement the to make sure that
- /// the cursor is placed in a location that makes sense. Unix terminals do not have
- /// a way of hiding the cursor, so it can be distracting to have the cursor left at
- /// the last focused view. So views should make sure that they place the cursor
- /// in a visually sensible place.
- ///
- ///
- /// The View defines the base functionality for user interface elements in Terminal.Gui. Views
- /// can contain one or more subviews, can respond to user input and render themselves on the screen.
- ///
///
- /// Views supports two layout styles: or .
- /// The choice as to which layout style is used by the View
- /// is determined when the View is initialized. To create a View using Absolute layout, call a constructor that takes a
- /// Rect parameter to specify the absolute position and size (the View.). 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. Both approaches use coordinates that are relative to the container they are being added to.
+ /// Use , , , and properties to dynamically control the size and location of the view.
+ /// The will be created using
+ /// coordinates. The initial size () will be
+ /// adjusted to fit the contents of , including newlines ('\n') for multiple lines.
///
///
- /// To switch between Absolute and Computed layout, use the property.
+ /// If is greater than one, word wrapping is provided.
///
///
- /// Computed layout is more flexible and supports dynamic console apps where controls adjust layout
- /// as the terminal resizes or other Views change size or position. The X, Y, Width and Height
- /// properties are Dim and Pos objects that dynamically update the position of a view.
- /// The X and Y properties are of type
- /// and you can use either absolute positions, percentages or anchor
- /// points. The Width and Height properties are of type
- /// and can use absolute position,
- /// percentages and anchors. These are useful as they will take
- /// care of repositioning views when view's frames are resized or
- /// if the terminal size changes.
+ /// This constructor initialize a View with a of .
+ /// Use , , , and properties to dynamically control the size and location of the view.
///
+ ///
+ public View () : this (string.Empty, TextDirection.LeftRight_TopBottom) { }
+
+ ///
+ /// Initializes a new instance of using layout.
+ ///
+ ///
///
- /// Absolute layout requires specifying coordinates and sizes of Views explicitly, and the
- /// View will typically stay in a fixed position and size. To change the position and size use the
- /// property.
+ /// The will be created at the given
+ /// coordinates with the given string. The size () will be
+ /// adjusted to fit the contents of , including newlines ('\n') for multiple lines.
///
///
- /// Subviews (child views) can be added to a View by calling the method.
- /// The container of a View can be accessed with the property.
+ /// No line wrapping is provided.
///
+ ///
+ /// column to locate the View.
+ /// row to locate the View.
+ /// text to initialize the property with.
+ public View (int x, int y, string text) : this (TextFormatter.CalcRect (x, y, text), text) { }
+
+ ///
+ /// Initializes a new instance of using layout.
+ ///
+ ///
///
- /// To flag a region of the View's to be redrawn call .
- /// To flag the entire view for redraw call .
+ /// The will be created at the given
+ /// coordinates with the given string. The initial size () will be
+ /// adjusted to fit the contents of , including newlines ('\n') for multiple lines.
///
///
- /// The method is invoked when the size or layout of a view has
- /// changed. The default processing system will keep the size and dimensions
- /// for views that use the , and will recompute the
- /// frames for the vies that use .
+ /// If rect.Height is greater than one, word wrapping is provided.
///
+ ///
+ /// Location.
+ /// text to initialize the property with.
+ public View (Rect rect, string text) => SetInitialProperties (text, rect, LayoutStyle.Absolute, TextDirection.LeftRight_TopBottom);
+
+ ///
+ /// Initializes a new instance of using layout.
+ ///
+ ///
///
- /// Views have a property that defines the default colors that subviews
- /// should use for rendering. This ensures that the views fit in the context where
- /// they are being used, and allows for themes to be plugged in. For example, the
- /// default colors for windows and Toplevels uses a blue background, while it uses
- /// a white background for dialog boxes and a red background for errors.
+ /// The will be created using
+ /// coordinates with the given string. The initial size () will be
+ /// adjusted to fit the contents of , including newlines ('\n') for multiple lines.
///
///
- /// Subclasses should not rely on being
- /// set at construction time. If a is not set on a view, the view will inherit the
- /// value from its and the value might only be valid once a view has been
- /// added to a SuperView.
+ /// If is greater than one, word wrapping is provided.
///
+ ///
+ /// text to initialize the property with.
+ /// The text direction.
+ public View (string text, TextDirection direction = TextDirection.LeftRight_TopBottom) => SetInitialProperties (text, Rect.Empty, LayoutStyle.Computed, direction);
+
+ // TODO: v2 - Remove constructors with parameters
+ ///
+ /// Private helper to set the initial properties of the View that were provided via constructors.
+ ///
+ ///
+ ///
+ ///
+ ///
+ void SetInitialProperties (string text, Rect rect, LayoutStyle layoutStyle = LayoutStyle.Computed,
+ TextDirection direction = TextDirection.LeftRight_TopBottom)
+ {
+ TextFormatter = new TextFormatter ();
+ TextFormatter.HotKeyChanged += TextFormatter_HotKeyChanged;
+ TextDirection = direction;
+
+ CanFocus = false;
+ TabIndex = -1;
+ TabStop = false;
+ LayoutStyle = layoutStyle;
+
+ Text = text == null ? string.Empty : text;
+ LayoutStyle = layoutStyle;
+ Frame = rect.IsEmpty ? TextFormatter.CalcRect (0, 0, text, direction) : rect;
+ OnResizeNeeded ();
+
+ AddCommands ();
+
+ CreateFrames ();
+ }
+
+ ///
+ /// Get or sets if the has been initialized (via
+ /// and ).
+ ///
///
- /// By using applications will work both
- /// in color as well as black and white displays.
- ///
+ /// If first-run-only initialization is preferred, overrides to
+ /// can be implemented, in which case the
+ /// methods will only be called if
+ /// is . This allows proper inheritance hierarchies
+ /// to override base class layout code optimally by doing so only on first run,
+ /// instead of on every run.
+ ///
+ public virtual bool IsInitialized { get; set; }
+
+ ///
+ /// Signals the View that initialization is starting. See .
+ ///
+ ///
///
- /// Views can also opt-in to more sophisticated initialization
+ /// Views can opt-in to more sophisticated initialization
/// by implementing overrides to and
/// which will be called
/// when the view is added to a .
///
///
/// If first-run-only initialization is preferred, overrides to
- /// can be implemented, in which case the
+ /// can be implemented too, in which case the
/// methods will only be called if
/// is . This allows proper inheritance hierarchies
/// to override base class layout code optimally by doing so only on first run,
/// instead of on every run.
///
- ///
- /// See for an overview of View keyboard handling.
- /// ///
- #endregion API Docs
- public partial class View : Responder, ISupportInitializeNotification {
-
- #region Constructors and Initialization
-
- ///
- /// Initializes a new instance of a class with the absolute
- /// dimensions specified in the parameter.
- ///
- /// The region covered by this view.
- ///
- /// This constructor initialize a View with a of .
- /// Use to initialize a View with of
- ///
- public View (Rect frame) : this (frame, null) { }
-
- ///
- /// Initializes a new instance of using layout.
- ///
- ///
- ///
- /// Use , , , and properties to dynamically control the size and location of the view.
- /// The will be created using
- /// coordinates. The initial size () will be
- /// adjusted to fit the contents of , including newlines ('\n') for multiple lines.
- ///
- ///
- /// If is greater than one, word wrapping is provided.
- ///
- ///
- /// This constructor initialize a View with a of .
- /// Use , , , and properties to dynamically control the size and location of the view.
- ///
- ///
- public View () : this (text: string.Empty, direction: TextDirection.LeftRight_TopBottom) { }
-
- ///
- /// Initializes a new instance of using layout.
- ///
- ///
- ///
- /// The will be created at the given
- /// coordinates with the given string. The size () will be
- /// adjusted to fit the contents of , including newlines ('\n') for multiple lines.
- ///
- ///
- /// No line wrapping is provided.
- ///
- ///
- /// column to locate the View.
- /// row to locate the View.
- /// text to initialize the property with.
- public View (int x, int y, string text) : this (TextFormatter.CalcRect (x, y, text), text) { }
-
- ///
- /// Initializes a new instance of using layout.
- ///
- ///
- ///
- /// The will be created at the given
- /// coordinates with the given string. The initial size () will be
- /// adjusted to fit the contents of , including newlines ('\n') for multiple lines.
- ///
- ///
- /// If rect.Height is greater than one, word wrapping is provided.
- ///
- ///
- /// Location.
- /// text to initialize the property with.
- public View (Rect rect, string text)
- {
- SetInitialProperties (text, rect, LayoutStyle.Absolute, TextDirection.LeftRight_TopBottom);
- }
+ ///
+ public virtual void BeginInit ()
+ {
+ if (!IsInitialized) {
+ _oldCanFocus = CanFocus;
+ _oldTabIndex = _tabIndex;
- ///
- /// Initializes a new instance of using layout.
- ///
- ///
- ///
- /// The will be created using
- /// coordinates with the given string. The initial size () will be
- /// adjusted to fit the contents of , including newlines ('\n') for multiple lines.
- ///
- ///
- /// If is greater than one, word wrapping is provided.
- ///
- ///
- /// text to initialize the property with.
- /// The text direction.
- public View (string text, TextDirection direction = TextDirection.LeftRight_TopBottom)
- {
- SetInitialProperties (text, Rect.Empty, LayoutStyle.Computed, direction);
- }
- // TODO: v2 - Remove constructors with parameters
- ///
- /// Private helper to set the initial properties of the View that were provided via constructors.
- ///
- ///
- ///
- ///
- ///
- void SetInitialProperties (string text, Rect rect, LayoutStyle layoutStyle = LayoutStyle.Computed,
- TextDirection direction = TextDirection.LeftRight_TopBottom)
- {
- TextFormatter = new TextFormatter ();
- TextFormatter.HotKeyChanged += TextFormatter_HotKeyChanged;
- TextDirection = direction;
-
- CanFocus = false;
- TabIndex = -1;
- TabStop = false;
- LayoutStyle = layoutStyle;
-
- Text = text == null ? string.Empty : text;
- LayoutStyle = layoutStyle;
- Frame = rect.IsEmpty ? TextFormatter.CalcRect (0, 0, text, direction) : rect;
- OnResizeNeeded ();
-
- AddCommands ();
-
- CreateFrames ();
-
- LayoutFrames ();
- }
+ // TODO: Figure out why ScrollView and other tests fail if this call is put here
+ // instead of the constructor.
+ //InitializeFrames ();
- ///
- /// Get or sets if the has been initialized (via
- /// and ).
- ///
- ///
- /// If first-run-only initialization is preferred, overrides to
- /// can be implemented, in which case the
- /// methods will only be called if
- /// is . This allows proper inheritance hierarchies
- /// to override base class layout code optimally by doing so only on first run,
- /// instead of on every run.
- ///
- public virtual bool IsInitialized { get; set; }
-
- ///
- /// Signals the View that initialization is starting. See .
- ///
- ///
- ///
- /// Views can opt-in to more sophisticated initialization
- /// by implementing overrides to and
- /// which will be called
- /// when the view is added to a .
- ///
- ///
- /// If first-run-only initialization is preferred, overrides to
- /// can be implemented too, in which case the
- /// methods will only be called if
- /// is . This allows proper inheritance hierarchies
- /// to override base class layout code optimally by doing so only on first run,
- /// instead of on every run.
- ///
- ///
- public virtual void BeginInit ()
- {
- if (!IsInitialized) {
- _oldCanFocus = CanFocus;
- _oldTabIndex = _tabIndex;
-
- // BUGBUG: These should move to EndInit as they access Bounds causing debug spew.
- UpdateTextDirection (TextDirection);
- UpdateTextFormatterText ();
- SetHotKey ();
-
- // TODO: Figure out why ScrollView and other tests fail if this call is put here
- // instead of the constructor.
- //InitializeFrames ();
-
- } else {
- //throw new InvalidOperationException ("The view is already initialized.");
+ } else {
+ //throw new InvalidOperationException ("The view is already initialized.");
- }
+ }
- if (_subviews?.Count > 0) {
- foreach (var view in _subviews) {
- if (!view.IsInitialized) {
- view.BeginInit ();
- }
+ if (_subviews?.Count > 0) {
+ foreach (var view in _subviews) {
+ if (!view.IsInitialized) {
+ view.BeginInit ();
}
}
}
+ }
- ///
- /// Signals the View that initialization is ending. See .
- ///
- public void EndInit ()
- {
- IsInitialized = true;
- OnResizeNeeded ();
- if (_subviews != null) {
- foreach (var view in _subviews) {
- if (!view.IsInitialized) {
- view.EndInit ();
- }
+ ///
+ /// Signals the View that initialization is ending. See .
+ ///
+ public void EndInit ()
+ {
+ IsInitialized = true;
+ // These calls were moved from BeginInit as they access Bounds which is indeterminate until EndInit is called.
+ UpdateTextDirection (TextDirection);
+ UpdateTextFormatterText ();
+ SetHotKey ();
+
+ OnResizeNeeded ();
+ if (_subviews != null) {
+ foreach (var view in _subviews) {
+ if (!view.IsInitialized) {
+ view.EndInit ();
}
}
- Initialized?.Invoke (this, EventArgs.Empty);
}
- #endregion Constructors and Initialization
-
- ///
- /// Points to the current driver in use by the view, it is a convenience property
- /// for simplifying the development of new views.
- ///
- public static ConsoleDriver Driver => Application.Driver;
-
- ///
- /// Gets or sets arbitrary data for the view.
- ///
- /// This property is not used internally.
- public object Data { get; set; }
-
- ///
- /// Gets or sets an identifier for the view;
- ///
- /// The identifier.
- /// The id should be unique across all Views that share a SuperView.
- public string Id { get; set; } = "";
-
- string _title = string.Empty;
- ///
- /// The title to be displayed for this . The title will be displayed if .
- /// is greater than 0.
- ///
- /// The title.
- public string Title {
- get => _title;
- set {
- if (!OnTitleChanging (_title, value)) {
- var old = _title;
- _title = value;
- SetNeedsDisplay ();
+ Initialized?.Invoke (this, EventArgs.Empty);
+ }
+ #endregion Constructors and Initialization
+
+ ///
+ /// Points to the current driver in use by the view, it is a convenience property
+ /// for simplifying the development of new views.
+ ///
+ public static ConsoleDriver Driver => Application.Driver;
+
+ ///
+ /// Gets or sets arbitrary data for the view.
+ ///
+ /// This property is not used internally.
+ public object Data { get; set; }
+
+ ///
+ /// Gets or sets an identifier for the view;
+ ///
+ /// The identifier.
+ /// The id should be unique across all Views that share a SuperView.
+ public string Id { get; set; } = "";
+
+ string _title = string.Empty;
+
+ ///
+ /// The title to be displayed for this . The title will be displayed if .
+ /// is greater than 0.
+ ///
+ /// The title.
+ public string Title {
+ get => _title;
+ set {
+ if (!OnTitleChanging (_title, value)) {
+ string old = _title;
+ _title = value;
+ SetNeedsDisplay ();
#if DEBUG
- if (_title != null && string.IsNullOrEmpty (Id)) {
- Id = _title.ToString ();
- }
-#endif // DEBUG
- OnTitleChanged (old, _title);
+ if (_title != null && string.IsNullOrEmpty (Id)) {
+ Id = _title.ToString ();
}
+#endif // DEBUG
+ OnTitleChanged (old, _title);
}
}
+ }
- ///
- /// Called before the changes. Invokes the event, which can be cancelled.
- ///
- /// The that is/has been replaced.
- /// The new to be replaced.
- /// `true` if an event handler canceled the Title change.
- public virtual bool OnTitleChanging (string oldTitle, string newTitle)
- {
- var args = new TitleEventArgs (oldTitle, newTitle);
- TitleChanging?.Invoke (this, args);
- return args.Cancel;
- }
+ ///
+ /// Called before the changes. Invokes the event, which can be cancelled.
+ ///
+ /// The that is/has been replaced.
+ /// The new to be replaced.
+ /// `true` if an event handler canceled the Title change.
+ public virtual bool OnTitleChanging (string oldTitle, string newTitle)
+ {
+ var args = new TitleEventArgs (oldTitle, newTitle);
+ TitleChanging?.Invoke (this, args);
+ return args.Cancel;
+ }
- ///
- /// Event fired when the is changing. Set to
- /// `true` to cancel the Title change.
- ///
- public event EventHandler TitleChanging;
-
- ///
- /// Called when the has been changed. Invokes the event.
- ///
- /// The that is/has been replaced.
- /// The new to be replaced.
- public virtual void OnTitleChanged (string oldTitle, string newTitle)
- {
- var args = new TitleEventArgs (oldTitle, newTitle);
- TitleChanged?.Invoke (this, args);
- }
+ ///
+ /// Event fired when the is changing. Set to
+ /// `true` to cancel the Title change.
+ ///
+ public event EventHandler TitleChanging;
- ///
- /// Event fired after the has been changed.
- ///
- public event EventHandler TitleChanged;
-
- ///
- /// Event fired when the value is being changed.
- ///
- public event EventHandler EnabledChanged;
-
- ///
- public override void OnEnabledChanged () => EnabledChanged?.Invoke (this, EventArgs.Empty);
-
- bool _oldEnabled;
-
- ///
- public override bool Enabled {
- get => base.Enabled;
- set {
- if (base.Enabled != value) {
- if (value) {
- if (SuperView == null || SuperView?.Enabled == true) {
- base.Enabled = value;
- }
- } else {
+ ///
+ /// Called when the has been changed. Invokes the event.
+ ///
+ /// The that is/has been replaced.
+ /// The new to be replaced.
+ public virtual void OnTitleChanged (string oldTitle, string newTitle)
+ {
+ var args = new TitleEventArgs (oldTitle, newTitle);
+ TitleChanged?.Invoke (this, args);
+ }
+
+ ///
+ /// Event fired after the has been changed.
+ ///
+ public event EventHandler TitleChanged;
+
+ ///
+ /// Event fired when the value is being changed.
+ ///
+ public event EventHandler EnabledChanged;
+
+ ///
+ public override void OnEnabledChanged () => EnabledChanged?.Invoke (this, EventArgs.Empty);
+
+ bool _oldEnabled;
+
+ ///
+ public override bool Enabled {
+ get => base.Enabled;
+ set {
+ if (base.Enabled != value) {
+ if (value) {
+ if (SuperView == null || SuperView?.Enabled == true) {
base.Enabled = value;
}
- if (!value && HasFocus) {
- SetHasFocus (false, this);
- }
- OnEnabledChanged ();
- SetNeedsDisplay ();
-
- if (_subviews != null) {
- foreach (var view in _subviews) {
- if (!value) {
- view._oldEnabled = view.Enabled;
- view.Enabled = false;
- } else {
- view.Enabled = view._oldEnabled;
- view._addingView = false;
- }
+ } else {
+ base.Enabled = value;
+ }
+ if (!value && HasFocus) {
+ SetHasFocus (false, this);
+ }
+ OnEnabledChanged ();
+ SetNeedsDisplay ();
+
+ if (_subviews != null) {
+ foreach (var view in _subviews) {
+ if (!value) {
+ view._oldEnabled = view.Enabled;
+ view.Enabled = false;
+ } else {
+ view.Enabled = view._oldEnabled;
+ view._addingView = false;
}
}
}
}
}
+ }
- ///
- /// Event fired when the value is being changed.
- ///
- public event EventHandler VisibleChanged;
-
- ///
- public override void OnVisibleChanged () => VisibleChanged?.Invoke (this, EventArgs.Empty);
-
- ///
- /// Gets or sets whether a view is cleared if the property is .
- ///
- public bool ClearOnVisibleFalse { get; set; } = true;
-
- /// >
- public override bool Visible {
- get => base.Visible;
- set {
- if (base.Visible != value) {
- base.Visible = value;
- if (!value) {
- if (HasFocus) {
- SetHasFocus (false, this);
- }
- if (ClearOnVisibleFalse) {
- Clear ();
- }
+ ///
+ /// Event fired when the value is being changed.
+ ///
+ public event EventHandler VisibleChanged;
+
+ ///
+ public override void OnVisibleChanged () => VisibleChanged?.Invoke (this, EventArgs.Empty);
+
+ ///
+ /// Gets or sets whether a view is cleared if the property is .
+ ///
+ public bool ClearOnVisibleFalse { get; set; } = true;
+
+ /// >
+ public override bool Visible {
+ get => base.Visible;
+ set {
+ if (base.Visible != value) {
+ base.Visible = value;
+ if (!value) {
+ if (HasFocus) {
+ SetHasFocus (false, this);
+ }
+ if (ClearOnVisibleFalse) {
+ Clear ();
}
- OnVisibleChanged ();
- SetNeedsDisplay ();
}
+ OnVisibleChanged ();
+ SetNeedsDisplay ();
}
}
+ }
- bool CanBeVisible (View view)
- {
- if (!view.Visible) {
+ bool CanBeVisible (View view)
+ {
+ if (!view.Visible) {
+ return false;
+ }
+ for (var c = view.SuperView; c != null; c = c.SuperView) {
+ if (!c.Visible) {
return false;
}
- for (var c = view.SuperView; c != null; c = c.SuperView) {
- if (!c.Visible) {
- return false;
- }
- }
-
- return true;
}
- ///
- /// Pretty prints the View
- ///
- ///
- public override string ToString ()
- {
- return $"{GetType ().Name}({Id}){Frame}";
- }
-
- ///
- protected override void Dispose (bool disposing)
- {
- LineCanvas.Dispose ();
-
- Margin?.Dispose ();
- Margin = null;
- Border?.Dispose ();
- Border = null;
- Padding?.Dispose ();
- Padding = null;
-
- _height = null;
- _width = null;
- _x = null;
- _y = null;
-
- for (var i = InternalSubviews.Count - 1; i >= 0; i--) {
- var subview = InternalSubviews [i];
- Remove (subview);
- subview.Dispose ();
- }
+ return true;
+ }
- base.Dispose (disposing);
- System.Diagnostics.Debug.Assert (InternalSubviews.Count == 0);
+ ///
+ /// Pretty prints the View
+ ///
+ ///
+ public override string ToString () => $"{GetType ().Name}({Id}){Frame}";
+
+ ///
+ protected override void Dispose (bool disposing)
+ {
+ LineCanvas.Dispose ();
+
+ Margin?.Dispose ();
+ Margin = null;
+ Border?.Dispose ();
+ Border = null;
+ Padding?.Dispose ();
+ Padding = null;
+
+ _height = null;
+ _width = null;
+ _x = null;
+ _y = null;
+
+ for (int i = InternalSubviews.Count - 1; i >= 0; i--) {
+ var subview = InternalSubviews [i];
+ Remove (subview);
+ subview.Dispose ();
}
+
+ base.Dispose (disposing);
+ System.Diagnostics.Debug.Assert (InternalSubviews.Count == 0);
}
-}
+}
\ No newline at end of file
diff --git a/Terminal.Gui/View/ViewDrawing.cs b/Terminal.Gui/View/ViewDrawing.cs
index 4e5ed8206e..f622d80e37 100644
--- a/Terminal.Gui/View/ViewDrawing.cs
+++ b/Terminal.Gui/View/ViewDrawing.cs
@@ -3,509 +3,517 @@
using System.Linq;
using System.Text;
-namespace Terminal.Gui {
- public partial class View {
-
- ColorScheme _colorScheme;
-
- ///
- /// The color scheme for this view, if it is not defined, it returns the 's
- /// color scheme.
- ///
- public virtual ColorScheme ColorScheme {
- get {
- if (_colorScheme == null) {
- return SuperView?.ColorScheme;
- }
- return _colorScheme;
- }
- set {
- if (_colorScheme != value) {
- _colorScheme = value;
- SetNeedsDisplay ();
- }
+namespace Terminal.Gui;
+
+public partial class View {
+ ColorScheme _colorScheme;
+
+ ///
+ /// The color scheme for this view, if it is not defined, it returns the 's
+ /// color scheme.
+ ///
+ public virtual ColorScheme ColorScheme {
+ get {
+ if (_colorScheme == null) {
+ return SuperView?.ColorScheme;
}
+ return _colorScheme;
}
-
- ///
- /// Determines the current based on the value.
- ///
- /// if is
- /// or if is .
- /// If it's overridden can return other values.
- public virtual Attribute GetNormalColor ()
- {
- ColorScheme cs = ColorScheme;
- if (ColorScheme == null) {
- cs = new ColorScheme ();
+ set {
+ if (_colorScheme != value) {
+ _colorScheme = value;
+ SetNeedsDisplay ();
}
- return Enabled ? cs.Normal : cs.Disabled;
}
+ }
- ///
- /// Determines the current based on the value.
- ///
- /// if is
- /// or if is .
- /// If it's overridden can return other values.
- public virtual Attribute GetFocusColor ()
- {
- return Enabled ? ColorScheme.Focus : ColorScheme.Disabled;
+ ///
+ /// Determines the current based on the value.
+ ///
+ /// if is
+ /// or if is .
+ /// If it's overridden can return other values.
+ public virtual Attribute GetNormalColor ()
+ {
+ var cs = ColorScheme;
+ if (ColorScheme == null) {
+ cs = new ColorScheme ();
}
+ return Enabled ? cs.Normal : cs.Disabled;
+ }
- ///
- /// Determines the current based on the value.
- ///
- /// if is
- /// or if is .
- /// If it's overridden can return other values.
- public virtual Attribute GetHotNormalColor ()
- {
- return Enabled ? ColorScheme.HotNormal : ColorScheme.Disabled;
+ ///
+ /// Determines the current based on the value.
+ ///
+ /// if is
+ /// or if is .
+ /// If it's overridden can return other values.
+ public virtual Attribute GetFocusColor () => Enabled ? ColorScheme.Focus : ColorScheme.Disabled;
+
+ ///
+ /// Determines the current based on the value.
+ ///
+ /// if is
+ /// or if is .
+ /// If it's overridden can return other values.
+ public virtual Attribute GetHotNormalColor () => Enabled ? ColorScheme.HotNormal : ColorScheme.Disabled;
+
+ ///
+ /// 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)
+ {
+ if (row < 0 || col < 0) {
+ return;
}
-
- ///
- /// 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)
- {
- if (row < 0 || col < 0) {
- return;
- }
- if (row > _frame.Height - 1 || col > _frame.Width - 1) {
- return;
- }
- Move (col, row);
- Driver.AddRune (ch);
+ if (row > _frame.Height - 1 || col > _frame.Width - 1) {
+ return;
}
+ Move (col, row);
+ Driver.AddRune (ch);
+ }
- ///
- /// Clears and .
- ///
- protected void ClearNeedsDisplay ()
- {
- _needsDisplayRect = Rect.Empty;
- _subViewNeedsDisplay = false;
- }
+ ///
+ /// Clears and .
+ ///
+ protected void ClearNeedsDisplay ()
+ {
+ _needsDisplayRect = Rect.Empty;
+ _subViewNeedsDisplay = false;
+ }
- // The view-relative region that needs to be redrawn. Marked internal for unit tests.
- internal Rect _needsDisplayRect = Rect.Empty;
-
- ///
- /// Gets or sets whether the view needs to be redrawn.
- ///
- public bool NeedsDisplay {
- get => _needsDisplayRect != Rect.Empty;
- set {
- if (value) {
- SetNeedsDisplay ();
- } else {
- ClearNeedsDisplay ();
- }
+ // The view-relative region that needs to be redrawn. Marked internal for unit tests.
+ internal Rect _needsDisplayRect = Rect.Empty;
+
+ ///
+ /// Gets or sets whether the view needs to be redrawn.
+ ///
+ public bool NeedsDisplay {
+ get => _needsDisplayRect != Rect.Empty;
+ set {
+ if (value) {
+ SetNeedsDisplay ();
+ } else {
+ ClearNeedsDisplay ();
}
}
+ }
- ///
- /// Sets the area of this view needing to be redrawn to .
- ///
- public void SetNeedsDisplay ()
- {
- if (!IsInitialized) {
- return;
- }
+ ///
+ /// Sets the area of this view needing to be redrawn to .
+ ///
+ ///
+ /// If the view has not been initialized ( is ),
+ /// this method does nothing.
+ ///
+ public void SetNeedsDisplay ()
+ {
+ if (IsInitialized) {
SetNeedsDisplay (Bounds);
}
+ }
- ///
- /// Expands the area of this view needing to be redrawn to include .
- ///
- /// The view-relative region that needs to be redrawn.
- public void SetNeedsDisplay (Rect region)
- {
- if (_needsDisplayRect.IsEmpty) {
- _needsDisplayRect = region;
- } else {
- var x = Math.Min (_needsDisplayRect.X, region.X);
- var y = Math.Min (_needsDisplayRect.Y, region.Y);
- var w = Math.Max (_needsDisplayRect.Width, region.Width);
- var h = Math.Max (_needsDisplayRect.Height, region.Height);
- _needsDisplayRect = new Rect (x, y, w, h);
- }
- _superView?.SetSubViewNeedsDisplay ();
-
- if (_needsDisplayRect.X < Bounds.X ||
- _needsDisplayRect.Y < Bounds.Y ||
- _needsDisplayRect.Width > Bounds.Width ||
- _needsDisplayRect.Height > Bounds.Height) {
- Margin?.SetNeedsDisplay (Margin.Bounds);
- Border?.SetNeedsDisplay (Border.Bounds);
- Padding?.SetNeedsDisplay (Padding.Bounds);
- }
+ ///
+ /// 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 Bounds-relative region that needs to be redrawn.
+ public void SetNeedsDisplay (Rect region)
+ {
+ if (!IsInitialized) {
+ _needsDisplayRect = region;
+ return;
+ }
+ if (_needsDisplayRect.IsEmpty) {
+ _needsDisplayRect = region;
+ } else {
+ int x = Math.Min (_needsDisplayRect.X, region.X);
+ int y = Math.Min (_needsDisplayRect.Y, region.Y);
+ int w = Math.Max (_needsDisplayRect.Width, region.Width);
+ int h = Math.Max (_needsDisplayRect.Height, region.Height);
+ _needsDisplayRect = new Rect (x, y, w, h);
+ }
+ _superView?.SetSubViewNeedsDisplay ();
+
+ if (_needsDisplayRect.X < Bounds.X ||
+ _needsDisplayRect.Y < Bounds.Y ||
+ _needsDisplayRect.Width > Bounds.Width ||
+ _needsDisplayRect.Height > Bounds.Height) {
+ Margin?.SetNeedsDisplay (Margin.Bounds);
+ Border?.SetNeedsDisplay (Border.Bounds);
+ Padding?.SetNeedsDisplay (Padding.Bounds);
+ }
- if (_subviews == null) {
- return;
- }
+ if (_subviews == null) {
+ return;
+ }
- foreach (var subview in _subviews) {
- if (subview.Frame.IntersectsWith (region)) {
- var subviewRegion = Rect.Intersect (subview.Frame, region);
- subviewRegion.X -= subview.Frame.X;
- subviewRegion.Y -= subview.Frame.Y;
- subview.SetNeedsDisplay (subviewRegion);
- }
+ foreach (var subview in _subviews) {
+ if (subview.Frame.IntersectsWith (region)) {
+ var subviewRegion = Rect.Intersect (subview.Frame, region);
+ subviewRegion.X -= subview.Frame.X;
+ subviewRegion.Y -= subview.Frame.Y;
+ subview.SetNeedsDisplay (subviewRegion);
}
}
+ }
- ///
- /// Gets whether any Subviews need to be redrawn.
- ///
- public bool SubViewNeedsDisplay {
- get => _subViewNeedsDisplay;
+ ///
+ /// Gets whether any Subviews need to be redrawn.
+ ///
+ public bool SubViewNeedsDisplay => _subViewNeedsDisplay;
+
+ bool _subViewNeedsDisplay;
+
+ ///
+ /// Indicates that any Subviews (in the list) need to be repainted.
+ ///
+ public void SetSubViewNeedsDisplay ()
+ {
+ _subViewNeedsDisplay = true;
+ if (_superView != null && !_superView._subViewNeedsDisplay) {
+ _superView.SetSubViewNeedsDisplay ();
}
+ }
- bool _subViewNeedsDisplay;
+ ///
+ /// Clears the with the normal background color.
+ ///
+ ///
+ ///
+ /// This clears the Bounds used by this view.
+ ///
+ ///
+ public void Clear ()
+ {
+ if (IsInitialized) {
+ Clear (BoundsToScreen (Bounds));
+ }
- ///
- /// Indicates that any Subviews (in the list) need to be repainted.
- ///
- public void SetSubViewNeedsDisplay ()
- {
- _subViewNeedsDisplay = true;
- if (_superView != null && !_superView._subViewNeedsDisplay) {
- _superView.SetSubViewNeedsDisplay ();
- }
+ }
+
+ // BUGBUG: This version of the Clear API should be removed. We should have a tenet that says
+ // "View APIs only deal with View-relative coords". This is only used by ComboBox which can
+ // be refactored to use the View-relative version.
+ ///
+ /// Clears the specified screen-relative rectangle with the normal background.
+ ///
+ ///
+ ///
+ /// The screen-relative rectangle to clear.
+ public void Clear (Rect regionScreen)
+ {
+ if (Driver == null) {
+ return;
}
+ var prev = Driver.SetAttribute (GetNormalColor ());
+ Driver.FillRect (regionScreen);
+ Driver.SetAttribute (prev);
+ }
+
+ // Clips a rectangle in screen coordinates to the dimensions currently available on the screen
+ internal Rect ScreenClip (Rect 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 Rect (x, y, w, h);
+ }
+
+ ///
+ /// Expands the 's clip region to include .
+ ///
+ /// 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 Rect ClipToBounds ()
+ {
+ var previous = Driver.Clip;
+ Driver.Clip = Rect.Intersect (previous, BoundsToScreen (Bounds));
+ return previous;
+ }
- ///
- /// Clears the with the normal background color.
- ///
- ///
- ///
- /// This clears the Bounds used by this view.
- ///
- ///
- public void Clear () => Clear (BoundsToScreen(Bounds));
-
- // BUGBUG: This version of the Clear API should be removed. We should have a tenet that says
- // "View APIs only deal with View-relative coords". This is only used by ComboBox which can
- // be refactored to use the View-relative version.
- ///
- /// Clears the specified screen-relative rectangle with the normal background.
- ///
- ///
- ///
- /// The screen-relative rectangle to clear.
- public void Clear (Rect regionScreen)
- {
- if (Driver == null) {
- return;
+ ///
+ /// Utility function to draw strings that contain a hotkey.
+ ///
+ /// String to display, the hotkey specifier before a letter flags the next letter as the hotkey.
+ /// Hot color.
+ /// Normal color.
+ ///
+ /// The hotkey is any character following the hotkey specifier, which is the underscore ('_') character by default.
+ /// The hotkey specifier can be changed via
+ ///
+ public void DrawHotString (string text, Attribute hotColor, Attribute normalColor)
+ {
+ var hotkeySpec = HotKeySpecifier == (Rune)0xffff ? (Rune)'_' : HotKeySpecifier;
+ Application.Driver.SetAttribute (normalColor);
+ foreach (char rune in text) {
+ if (rune == hotkeySpec.Value) {
+ Application.Driver.SetAttribute (hotColor);
+ continue;
}
- var prev = Driver.SetAttribute (GetNormalColor ());
- Driver.FillRect (regionScreen);
- Driver.SetAttribute (prev);
+ Application.Driver.AddRune ((Rune)rune);
+ Application.Driver.SetAttribute (normalColor);
}
+ }
- // Clips a rectangle in screen coordinates to the dimensions currently available on the screen
- internal Rect ScreenClip (Rect regionScreen)
- {
- var x = regionScreen.X < 0 ? 0 : regionScreen.X;
- var y = regionScreen.Y < 0 ? 0 : regionScreen.Y;
- var w = regionScreen.X + regionScreen.Width >= Driver.Cols ? Driver.Cols - regionScreen.X : regionScreen.Width;
- var h = regionScreen.Y + regionScreen.Height >= Driver.Rows ? Driver.Rows - regionScreen.Y : regionScreen.Height;
-
- return new Rect (x, y, w, h);
- }
-
- ///
- /// Expands the 's clip region to include .
- ///
- /// 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 Rect ClipToBounds ()
- {
- var previous = Driver.Clip;
- Driver.Clip = Rect.Intersect (previous, BoundsToScreen (Bounds));
- return previous;
+ ///
+ /// Utility function to draw strings that contains a hotkey using a and the "focused" state.
+ ///
+ /// String to display, the underscore before a letter flags the next letter as the hotkey.
+ /// If set to this uses the focused colors from the color scheme, otherwise the regular ones.
+ /// The color scheme to use.
+ public void DrawHotString (string text, bool focused, ColorScheme scheme)
+ {
+ if (focused) {
+ DrawHotString (text, scheme.HotFocus, scheme.Focus);
+ } else {
+ DrawHotString (text, Enabled ? scheme.HotNormal : scheme.Disabled, Enabled ? scheme.Normal : scheme.Disabled);
}
+ }
- ///
- /// Utility function to draw strings that contain a hotkey.
- ///
- /// String to display, the hotkey specifier before a letter flags the next letter as the hotkey.
- /// Hot color.
- /// Normal color.
- ///
- /// The hotkey is any character following the hotkey specifier, which is the underscore ('_') character by default.
- /// The hotkey specifier can be changed via
- ///
- public void DrawHotString (string text, Attribute hotColor, Attribute normalColor)
- {
- var hotkeySpec = HotKeySpecifier == (Rune)0xffff ? (Rune)'_' : HotKeySpecifier;
- Application.Driver.SetAttribute (normalColor);
- foreach (var rune in text) {
- if (rune == hotkeySpec.Value) {
- Application.Driver.SetAttribute (hotColor);
- continue;
- }
- Application.Driver.AddRune ((Rune)rune);
- Application.Driver.SetAttribute (normalColor);
- }
+ ///
+ /// 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)
+ {
+ if (Driver.Rows == 0) {
+ return;
}
- ///
- /// Utility function to draw strings that contains a hotkey using a and the "focused" state.
- ///
- /// String to display, the underscore before a letter flags the next letter as the hotkey.
- /// If set to this uses the focused colors from the color scheme, otherwise the regular ones.
- /// The color scheme to use.
- public void DrawHotString (string text, bool focused, ColorScheme scheme)
- {
- if (focused)
- DrawHotString (text, scheme.HotFocus, scheme.Focus);
- else
- DrawHotString (text, Enabled ? scheme.HotNormal : scheme.Disabled, Enabled ? scheme.Normal : scheme.Disabled);
- }
+ BoundsToScreen (col, row, out int rCol, out int rRow, false);
+ Driver.Move (rCol, rRow);
+ }
- ///
- /// 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)
- {
- if (Driver.Rows == 0) {
- return;
- }
-
- BoundsToScreen (col, row, out var rCol, out var rRow, false);
- Driver.Move (rCol, rRow);
+ ///
+ /// The canvas that any line drawing that is to be shared by subviews of this view should add lines to.
+ ///
+ /// adds border lines to this LineCanvas.
+ public LineCanvas LineCanvas { get; } = new ();
+
+ ///
+ /// 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 SuperView. If (the default)
+ /// this View's method will be called to render the borders.
+ ///
+ public virtual bool SuperViewRendersLineCanvas { get; set; } = false;
+
+ // TODO: Make this cancelable
+ ///
+ /// Prepares . If is true, only the of
+ /// this view's subviews will be rendered. If is false (the default), this
+ /// method will cause the be prepared to be rendered.
+ ///
+ ///
+ public virtual bool OnDrawFrames ()
+ {
+ if (!IsInitialized) {
+ return false;
}
- ///
- /// The canvas that any line drawing that is to be shared by subviews of this view should add lines to.
- ///
- /// adds border lines to this LineCanvas.
- public LineCanvas LineCanvas { get; } = new LineCanvas ();
-
- ///
- /// 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 SuperView. If (the default)
- /// this View's method will be called to render the borders.
- ///
- public virtual bool SuperViewRendersLineCanvas { get; set; } = false;
-
- // TODO: Make this cancelable
- ///
- /// Prepares . If is true, only the of
- /// this view's subviews will be rendered. If is false (the default), this
- /// method will cause the be prepared to be rendered.
- ///
- ///
- public virtual bool OnDrawFrames ()
- {
- if (!IsInitialized) {
- return false;
- }
- // 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);
+ // 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);
- return true;
+ return true;
+ }
+
+ ///
+ /// Draws the view. Causes the following virtual methods to be called (along with their related events):
+ /// , .
+ ///
+ ///
+ ///
+ /// Always use (view-relative) when calling , NOT (superview-relative).
+ ///
+ ///
+ /// Views should set the color that they want to use on entry, as otherwise this will inherit
+ /// the last color that was set globally on the driver.
+ ///
+ ///
+ /// 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.
+ ///
+ ///
+ public void Draw ()
+ {
+ if (!CanBeVisible (this)) {
+ return;
}
+ OnDrawFrames ();
- ///
- /// Draws the view. Causes the following virtual methods to be called (along with their related events):
- /// , .
- ///
- ///
- ///
- /// Always use (view-relative) when calling , NOT (superview-relative).
- ///
- ///
- /// Views should set the color that they want to use on entry, as otherwise this will inherit
- /// the last color that was set globally on the driver.
- ///
- ///
- /// 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.
- ///
- ///
- public void Draw ()
- {
- if (!CanBeVisible (this)) {
- return;
- }
- OnDrawFrames ();
+ var prevClip = ClipToBounds ();
- var prevClip = ClipToBounds ();
+ if (ColorScheme != null) {
+ //Driver.SetAttribute (HasFocus ? GetFocusColor () : GetNormalColor ());
+ Driver.SetAttribute (GetNormalColor ());
+ }
- if (ColorScheme != null) {
- //Driver.SetAttribute (HasFocus ? GetFocusColor () : GetNormalColor ());
- Driver.SetAttribute (GetNormalColor ());
- }
+ // Invoke DrawContentEvent
+ var dev = new DrawEventArgs (Bounds);
+ DrawContent?.Invoke (this, dev);
- // Invoke DrawContentEvent
- var dev = new DrawEventArgs (Bounds);
- DrawContent?.Invoke (this, dev);
+ if (!dev.Cancel) {
+ OnDrawContent (Bounds);
+ }
- if (!dev.Cancel) {
- OnDrawContent (Bounds);
- }
+ Driver.Clip = prevClip;
- Driver.Clip = prevClip;
+ OnRenderLineCanvas ();
+ // Invoke DrawContentCompleteEvent
+ OnDrawContentComplete (Bounds);
- OnRenderLineCanvas ();
- // Invoke DrawContentCompleteEvent
- OnDrawContentComplete (Bounds);
+ // BUGBUG: v2 - We should be able to use View.SetClip here and not have to resort to knowing Driver details.
+ ClearLayoutNeeded ();
+ ClearNeedsDisplay ();
+ }
- // BUGBUG: v2 - We should be able to use View.SetClip here and not have to resort to knowing Driver details.
- ClearLayoutNeeded ();
- ClearNeedsDisplay ();
+ // TODO: Make this cancelable
+ ///
+ /// Renders . If is true, only the of
+ /// this view's subviews will be rendered. If is false (the default), this
+ /// method will cause the to be rendered.
+ ///
+ ///
+ public virtual bool OnRenderLineCanvas ()
+ {
+ if (!IsInitialized) {
+ return false;
}
- // TODO: Make this cancelable
- ///
- /// Renders . If is true, only the of
- /// this view's subviews will be rendered. If is false (the default), this
- /// method will cause the to be rendered.
- ///
- ///
- public virtual bool OnRenderLineCanvas ()
- {
- if (!IsInitialized) {
- return false;
+ // If we have a SuperView, it'll render our frames.
+ if (!SuperViewRendersLineCanvas && LineCanvas.Bounds != Rect.Empty) {
+ foreach (var p in LineCanvas.GetCellMap ()) {
+ // Get the entire map
+ Driver.SetAttribute (p.Value.Attribute ?? ColorScheme.Normal);
+ Driver.Move (p.Key.X, p.Key.Y);
+ // TODO: #2616 - Support combining sequences that don't normalize
+ Driver.AddRune (p.Value.Rune);
}
+ LineCanvas.Clear ();
+ }
- // If we have a SuperView, it'll render our frames.
- if (!SuperViewRendersLineCanvas && LineCanvas.Bounds != Rect.Empty) {
- foreach (var p in LineCanvas.GetCellMap ()) { // Get the entire map
- Driver.SetAttribute (p.Value.Attribute ?? ColorScheme.Normal);
- Driver.Move (p.Key.X, p.Key.Y);
- // TODO: #2616 - Support combining sequences that don't normalize
- Driver.AddRune (p.Value.Rune);
- }
- LineCanvas.Clear ();
+ if (Subviews.Any (s => s.SuperViewRendersLineCanvas)) {
+ foreach (var subview in Subviews.Where (s => s.SuperViewRendersLineCanvas == true)) {
+ // Combine the LineCanvas'
+ LineCanvas.Merge (subview.LineCanvas);
+ subview.LineCanvas.Clear ();
}
- if (Subviews.Any (s => s.SuperViewRendersLineCanvas)) {
- foreach (var subview in Subviews.Where (s => s.SuperViewRendersLineCanvas == true)) {
- // Combine the LineCanvas'
- LineCanvas.Merge (subview.LineCanvas);
- subview.LineCanvas.Clear ();
- }
-
- foreach (var p in LineCanvas.GetCellMap ()) { // Get the entire map
- Driver.SetAttribute (p.Value.Attribute ?? ColorScheme.Normal);
- Driver.Move (p.Key.X, p.Key.Y);
- // TODO: #2616 - Support combining sequences that don't normalize
- Driver.AddRune (p.Value.Rune);
- }
- LineCanvas.Clear ();
+ foreach (var p in LineCanvas.GetCellMap ()) {
+ // Get the entire map
+ Driver.SetAttribute (p.Value.Attribute ?? ColorScheme.Normal);
+ Driver.Move (p.Key.X, p.Key.Y);
+ // TODO: #2616 - Support combining sequences that don't normalize
+ Driver.AddRune (p.Value.Rune);
}
-
- return true;
+ LineCanvas.Clear ();
}
- ///
- /// Event invoked when the content area of the View is to be drawn.
- ///
- ///
- ///
- /// Will be invoked before any subviews added with have been drawn.
- ///
- ///
- /// Rect provides the view-relative rectangle describing the currently visible viewport into the .
- ///
- ///
- public event EventHandler DrawContent;
-
- ///
- /// 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
- ///
- /// This method will be called before any subviews added with have been drawn.
- ///
- public virtual void OnDrawContent (Rect contentArea)
- {
- if (NeedsDisplay) {
- if (SuperView != null) {
- Clear (BoundsToScreen (Bounds));
- }
+ return true;
+ }
- if (!string.IsNullOrEmpty (TextFormatter.Text)) {
- if (TextFormatter != null) {
- TextFormatter.NeedsFormat = true;
- }
- }
- // This should NOT clear
- TextFormatter?.Draw (BoundsToScreen (Bounds), HasFocus ? GetFocusColor () : GetNormalColor (),
- HasFocus ? ColorScheme.HotFocus : GetHotNormalColor (),
- Rect.Empty, false);
- SetSubViewNeedsDisplay ();
+ ///
+ /// Event invoked when the content area of the View is to be drawn.
+ ///
+ ///
+ ///
+ /// Will be invoked before any subviews added with have been drawn.
+ ///
+ ///
+ /// Rect provides the view-relative rectangle describing the currently visible viewport into the .
+ ///
+ ///
+ public event EventHandler DrawContent;
+
+ ///
+ /// 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
+ ///
+ /// This method will be called before any subviews added with have been drawn.
+ ///
+ public virtual void OnDrawContent (Rect contentArea)
+ {
+ if (NeedsDisplay) {
+ if (SuperView != null) {
+ Clear (BoundsToScreen (Bounds));
}
- // Draw subviews
- // TODO: Implement OnDrawSubviews (cancelable);
- if (_subviews != null && SubViewNeedsDisplay) {
- var subviewsNeedingDraw = _subviews.Where (
- view => view.Visible &&
- (view.NeedsDisplay ||
- view.SubViewNeedsDisplay ||
- view.LayoutNeeded)
- );
-
- foreach (var 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 ();
- //}
+ if (!string.IsNullOrEmpty (TextFormatter.Text)) {
+ if (TextFormatter != null) {
+ TextFormatter.NeedsFormat = true;
}
}
+ // This should NOT clear
+ TextFormatter?.Draw (BoundsToScreen (Bounds), HasFocus ? GetFocusColor () : GetNormalColor (),
+ HasFocus ? ColorScheme.HotFocus : GetHotNormalColor (),
+ Rect.Empty, false);
+ SetSubViewNeedsDisplay ();
}
- ///
- /// Event invoked when the content area of the View is completed drawing.
- ///
- ///
- ///
- /// Will be invoked after any subviews removed with have been completed drawing.
- ///
- ///
- /// Rect provides the view-relative rectangle describing the currently visible viewport into the .
- ///
- ///
- public event EventHandler DrawContentComplete;
-
- ///
- /// Enables overrides after completed drawing infinitely scrolled content and/or a background behind removed controls.
- ///
- /// The view-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 (Rect contentArea)
- {
- DrawContentComplete?.Invoke (this, new DrawEventArgs (contentArea));
- }
+ // Draw subviews
+ // TODO: Implement OnDrawSubviews (cancelable);
+ if (_subviews != null && SubViewNeedsDisplay) {
+ var subviewsNeedingDraw = _subviews.Where (
+ view => view.Visible &&
+ (view.NeedsDisplay ||
+ view.SubViewNeedsDisplay ||
+ view.LayoutNeeded)
+ );
+
+ foreach (var 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 ();
+ //}
+ }
+ }
}
+
+ ///
+ /// Event invoked when the content area of the View is completed drawing.
+ ///
+ ///
+ ///
+ /// Will be invoked after any subviews removed with have been completed drawing.
+ ///
+ ///
+ /// Rect provides the view-relative rectangle describing the currently visible viewport into the .
+ ///
+ ///
+ public event EventHandler DrawContentComplete;
+
+ ///
+ /// Enables overrides after completed drawing infinitely scrolled content and/or a background behind removed controls.
+ ///
+ /// The view-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 (Rect contentArea) => DrawContentComplete?.Invoke (this, new DrawEventArgs (contentArea));
}
\ No newline at end of file
diff --git a/Terminal.Gui/View/ViewSubViews.cs b/Terminal.Gui/View/ViewSubViews.cs
index a04d60b049..c546200ed4 100644
--- a/Terminal.Gui/View/ViewSubViews.cs
+++ b/Terminal.Gui/View/ViewSubViews.cs
@@ -82,14 +82,15 @@ public virtual void Add (View view)
view._oldEnabled = true;
view.Enabled = false;
}
- SetNeedsLayout ();
- SetNeedsDisplay ();
OnAdded (new SuperViewChangedEventArgs (this, view));
if (IsInitialized && !view.IsInitialized) {
view.BeginInit ();
view.EndInit ();
}
+
+ SetNeedsLayout ();
+ SetNeedsDisplay ();
}
///
diff --git a/Terminal.Gui/View/ViewText.cs b/Terminal.Gui/View/ViewText.cs
index 73f191358c..8b3eb0e9d6 100644
--- a/Terminal.Gui/View/ViewText.cs
+++ b/Terminal.Gui/View/ViewText.cs
@@ -1,260 +1,256 @@
-using System.Text;
-using System;
+using System;
using System.Collections.Generic;
-namespace Terminal.Gui {
+namespace Terminal.Gui;
- public partial class View {
- string _text;
+public partial class View {
+ string _text;
- ///
- /// The text displayed by the .
- ///
- ///
- ///
- /// The text will be drawn before any subviews are drawn.
- ///
- ///
- /// The text will be drawn starting at the view origin (0, 0) and will be formatted according
- /// to and .
- ///
- ///
- /// The text will word-wrap to additional lines if it does not fit horizontally. If 's height
- /// is 1, the text will be clipped.
- ///
- ///
- /// Set the to enable hotkey support. To disable hotkey support set to
- /// (Rune)0xffff.
- ///
- ///
- public virtual string Text {
- get => _text;
- set {
- _text = value;
- SetHotKey ();
- UpdateTextFormatterText ();
- //TextFormatter.Format ();
- OnResizeNeeded ();
+ ///
+ /// The text displayed by the .
+ ///
+ ///
+ ///
+ /// The text will be drawn before any subviews are drawn.
+ ///
+ ///
+ /// The text will be drawn starting at the view origin (0, 0) and will be formatted according
+ /// to and .
+ ///
+ ///
+ /// The text will word-wrap to additional lines if it does not fit horizontally. If 's height
+ /// is 1, the text will be clipped.
+ ///
+ ///
+ /// Set the to enable hotkey support. To disable hotkey support set to
+ /// (Rune)0xffff.
+ ///
+ ///
+ public virtual string Text {
+ get => _text;
+ set {
+ _text = value;
+ SetHotKey ();
+ UpdateTextFormatterText ();
+ //TextFormatter.Format ();
+ OnResizeNeeded ();
#if DEBUG
- if (_text != null && string.IsNullOrEmpty (Id)) {
- Id = _text;
- }
-#endif
+ if (_text != null && string.IsNullOrEmpty (Id)) {
+ Id = _text;
}
+#endif
}
+ }
- ///
- /// Gets or sets the used to format .
- ///
- public TextFormatter TextFormatter { get; set; }
+ ///
+ /// Gets or sets the used to format .
+ ///
+ public TextFormatter TextFormatter { get; set; }
- ///
- /// Can be overridden if the has
- /// different format than the default.
- ///
- protected virtual void UpdateTextFormatterText ()
- {
- if (TextFormatter != null) {
- TextFormatter.Text = _text;
- }
+ ///
+ /// Can be overridden if the has
+ /// different format than the default.
+ ///
+ protected virtual void UpdateTextFormatterText ()
+ {
+ if (TextFormatter != null) {
+ TextFormatter.Text = _text;
}
+ }
- ///
- /// Gets or sets whether trailing spaces at the end of word-wrapped lines are preserved
- /// or not when is enabled.
- /// If trailing spaces at the end of wrapped lines will be removed when
- /// is formatted for display. The default is .
- ///
- public virtual bool PreserveTrailingSpaces {
- get => TextFormatter.PreserveTrailingSpaces;
- set {
- if (TextFormatter.PreserveTrailingSpaces != value) {
- TextFormatter.PreserveTrailingSpaces = value;
- TextFormatter.NeedsFormat = true;
- }
+ ///
+ /// Gets or sets whether trailing spaces at the end of word-wrapped lines are preserved
+ /// or not when is enabled.
+ /// If trailing spaces at the end of wrapped lines will be removed when
+ /// is formatted for display. The default is .
+ ///
+ public virtual bool PreserveTrailingSpaces {
+ get => TextFormatter.PreserveTrailingSpaces;
+ set {
+ if (TextFormatter.PreserveTrailingSpaces != value) {
+ TextFormatter.PreserveTrailingSpaces = value;
+ TextFormatter.NeedsFormat = true;
}
}
+ }
- ///
- /// Gets or sets how the View's is aligned horizontally when drawn. Changing this property will redisplay the .
- ///
- /// The text alignment.
- public virtual TextAlignment TextAlignment {
- get => TextFormatter.Alignment;
- set {
- TextFormatter.Alignment = value;
- UpdateTextFormatterText ();
- OnResizeNeeded ();
- }
+ ///
+ /// Gets or sets how the View's is aligned horizontally when drawn. Changing this property will redisplay the .
+ ///
+ /// The text alignment.
+ public virtual TextAlignment TextAlignment {
+ get => TextFormatter.Alignment;
+ set {
+ TextFormatter.Alignment = value;
+ UpdateTextFormatterText ();
+ OnResizeNeeded ();
}
+ }
- ///
- /// Gets or sets how the View's is aligned vertically when drawn. Changing this property will redisplay the .
- ///
- /// The text alignment.
- public virtual VerticalTextAlignment VerticalTextAlignment {
- get => TextFormatter.VerticalAlignment;
- set {
- TextFormatter.VerticalAlignment = value;
- SetNeedsDisplay ();
- }
+ ///
+ /// Gets or sets how the View's is aligned vertically when drawn. Changing this property will redisplay the .
+ ///
+ /// The text alignment.
+ public virtual VerticalTextAlignment VerticalTextAlignment {
+ get => TextFormatter.VerticalAlignment;
+ set {
+ TextFormatter.VerticalAlignment = value;
+ SetNeedsDisplay ();
}
+ }
- ///
- /// Gets or sets the direction of the View's . Changing this property will redisplay the .
- ///
- /// The text alignment.
- public virtual TextDirection TextDirection {
- get => TextFormatter.Direction;
- set {
- UpdateTextDirection (value);
- TextFormatter.Direction = value;
- }
+ ///
+ /// Gets or sets the direction of the View's . Changing this property will redisplay the .
+ ///
+ /// The text alignment.
+ public virtual TextDirection TextDirection {
+ get => TextFormatter.Direction;
+ set {
+ UpdateTextDirection (value);
+ TextFormatter.Direction = value;
}
+ }
- private void UpdateTextDirection (TextDirection newDirection)
- {
- var directionChanged = TextFormatter.IsHorizontalDirection (TextFormatter.Direction)
- != TextFormatter.IsHorizontalDirection (newDirection);
- TextFormatter.Direction = newDirection;
+ void UpdateTextDirection (TextDirection newDirection)
+ {
+ bool directionChanged = TextFormatter.IsHorizontalDirection (TextFormatter.Direction)
+ != TextFormatter.IsHorizontalDirection (newDirection);
+ TextFormatter.Direction = newDirection;
- var isValidOldAutoSize = AutoSize && IsValidAutoSize (out var _);
+ bool isValidOldAutoSize = AutoSize && IsValidAutoSize (out var _);
- UpdateTextFormatterText ();
+ UpdateTextFormatterText ();
- if ((!ValidatePosDim && directionChanged && AutoSize)
- || (ValidatePosDim && directionChanged && AutoSize && isValidOldAutoSize)) {
- OnResizeNeeded ();
- } else if (directionChanged && IsAdded) {
- ResizeBoundsToFit (Bounds.Size);
- // BUGBUG: I think this call is redundant.
- SetFrameToFitText ();
- } else {
- SetFrameToFitText ();
- }
- TextFormatter.Size = GetTextFormatterSizeNeededForTextAndHotKey ();
- SetNeedsDisplay ();
+ if (!ValidatePosDim && directionChanged && AutoSize
+ || ValidatePosDim && directionChanged && AutoSize && isValidOldAutoSize) {
+ OnResizeNeeded ();
+ } else if (directionChanged && IsAdded) {
+ ResizeBoundsToFit (Bounds.Size);
+ // BUGBUG: I think this call is redundant.
+ SetFrameToFitText ();
+ } else {
+ SetFrameToFitText ();
}
+ TextFormatter.Size = GetTextFormatterSizeNeededForTextAndHotKey ();
+ SetNeedsDisplay ();
+ }
- ///
- /// Sets the size of the View to the minimum width or height required to fit .
- ///
- /// if the size was changed; if == or
- /// will not fit.
- ///
- /// Always returns if is or
- /// if (Horizontal) or (Vertical) are not not set or zero.
- /// Does not take into account word wrapping.
- ///
- bool SetFrameToFitText ()
+ ///
+ /// Sets the size of the View to the minimum width or height required to fit .
+ ///
+ /// if the size was changed; if == or
+ /// will not fit.
+ ///
+ /// Always returns if is or
+ /// if (Horizontal) or (Vertical) are not not set or zero.
+ /// Does not take into account word wrapping.
+ ///
+ bool SetFrameToFitText ()
+ {
+ // BUGBUG: This API is broken - should not assume Frame.Height == Bounds.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.
+ //
+ // Always returns if is or
+ // if (Horizontal) or (Vertical) are not not set or zero.
+ // Does not take into account word wrapping.
+ //
+ bool GetMinimumSizeOfText (out Size sizeRequired)
{
- // BUGBUG: This API is broken - should not assume Frame.Height == Bounds.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.
- //
- // Always returns if is or
- // if (Horizontal) or (Vertical) are not not set or zero.
- // Does not take into account word wrapping.
- //
- bool GetMinimumSizeOfText (out Size sizeRequired)
- {
- if (!IsInitialized) {
- sizeRequired = new Size (0, 0);
- return false;
- }
- sizeRequired = Bounds.Size;
-
- if (!AutoSize && !string.IsNullOrEmpty (TextFormatter.Text)) {
- switch (TextFormatter.IsVerticalDirection (TextDirection)) {
- case true:
- int colWidth = TextFormatter.GetSumMaxCharWidth (new List { TextFormatter.Text }, 0, 1);
- // TODO: v2 - This uses frame.Width; it should only use Bounds
- if (_frame.Width < colWidth &&
- (Width == null ||
- Bounds.Width >= 0 &&
- Width is Dim.DimAbsolute &&
- Width.Anchor (0) >= 0 &&
- Width.Anchor (0) < colWidth)) {
- sizeRequired = new Size (colWidth, Bounds.Height);
- return true;
- }
- break;
- default:
- if (_frame.Height < 1 &&
- (Height == null ||
- Height is Dim.DimAbsolute &&
- Height.Anchor (0) == 0)) {
- sizeRequired = new Size (Bounds.Width, 1);
- return true;
- }
- break;
- }
- }
+ if (!IsInitialized) {
+ sizeRequired = new Size (0, 0);
return false;
}
+ sizeRequired = Bounds.Size;
- if (GetMinimumSizeOfText (out var size)) {
- _frame = new Rect (_frame.Location, size);
- return true;
+ if (!AutoSize && !string.IsNullOrEmpty (TextFormatter.Text)) {
+ switch (TextFormatter.IsVerticalDirection (TextDirection)) {
+ case true:
+ int colWidth = TextFormatter.GetSumMaxCharWidth (new List { TextFormatter.Text }, 0, 1);
+ // TODO: v2 - This uses frame.Width; it should only use Bounds
+ if (_frame.Width < colWidth &&
+ (Width == null ||
+ Bounds.Width >= 0 &&
+ Width is Dim.DimAbsolute &&
+ Width.Anchor (0) >= 0 &&
+ Width.Anchor (0) < colWidth)) {
+ sizeRequired = new Size (colWidth, Bounds.Height);
+ return true;
+ }
+ break;
+ default:
+ if (_frame.Height < 1 &&
+ (Height == null ||
+ Height is Dim.DimAbsolute &&
+ Height.Anchor (0) == 0)) {
+ sizeRequired = new Size (Bounds.Width, 1);
+ return true;
+ }
+ break;
+ }
}
return false;
}
- ///
- /// Gets the width or height of the characters
- /// in the property.
- ///
- ///
- /// Only the first hotkey specifier found in is supported.
- ///
- /// If (the default) the width required for the hotkey specifier is returned. Otherwise the height is returned.
- /// The number of characters required for the . If the text direction specified
- /// by does not match the parameter, 0 is returned.
- public int GetHotKeySpecifierLength (bool isWidth = true)
- {
- if (isWidth) {
- return TextFormatter.IsHorizontalDirection (TextDirection) &&
- TextFormatter.Text?.Contains ((char)HotKeySpecifier.Value) == true
- ? Math.Max (HotKeySpecifier.GetColumns (), 0) : 0;
- } else {
- return TextFormatter.IsVerticalDirection (TextDirection) &&
- TextFormatter.Text?.Contains ((char)HotKeySpecifier.Value) == true
- ? Math.Max (HotKeySpecifier.GetColumns (), 0) : 0;
- }
+ if (GetMinimumSizeOfText (out var size)) {
+ _frame = new Rect (_frame.Location, size);
+ return true;
}
+ return false;
+ }
- ///
- /// Gets the dimensions required for ignoring a .
- ///
- ///
- public Size GetSizeNeededForTextWithoutHotKey ()
- {
- return new Size (TextFormatter.Size.Width - GetHotKeySpecifierLength (),
- TextFormatter.Size.Height - GetHotKeySpecifierLength (false));
+ ///
+ /// Gets the width or height of the characters
+ /// in the property.
+ ///
+ ///
+ /// Only the first hotkey specifier found in is supported.
+ ///
+ /// If (the default) the width required for the hotkey specifier is returned. Otherwise the height is returned.
+ /// The number of characters required for the . If the text direction specified
+ /// by does not match the parameter, 0 is returned.
+ public int GetHotKeySpecifierLength (bool isWidth = true)
+ {
+ if (isWidth) {
+ return TextFormatter.IsHorizontalDirection (TextDirection) &&
+ TextFormatter.Text?.Contains ((char)HotKeySpecifier.Value) == true
+ ? Math.Max (HotKeySpecifier.GetColumns (), 0) : 0;
+ } else {
+ return TextFormatter.IsVerticalDirection (TextDirection) &&
+ TextFormatter.Text?.Contains ((char)HotKeySpecifier.Value) == true
+ ? Math.Max (HotKeySpecifier.GetColumns (), 0) : 0;
}
+ }
- ///
- /// Gets the dimensions required for accounting for a .
- ///
- ///
- public Size GetTextFormatterSizeNeededForTextAndHotKey ()
- {
- if (string.IsNullOrEmpty (TextFormatter.Text)) {
-
- if (!IsInitialized) return Size.Empty;
+ ///
+ /// Gets the dimensions required for ignoring a .
+ ///
+ ///
+ public Size GetSizeNeededForTextWithoutHotKey () => new (TextFormatter.Size.Width - GetHotKeySpecifierLength (),
+ TextFormatter.Size.Height - GetHotKeySpecifierLength (false));
- return Bounds.Size;
- }
+ ///
+ /// Gets the dimensions required for accounting for a .
+ ///
+ ///
+ public Size GetTextFormatterSizeNeededForTextAndHotKey ()
+ {
+ if (!IsInitialized) {
+ return Size.Empty;
+ }
- // BUGBUG: This IGNORES what Text is set to, using on only the current View size. This doesn't seem to make sense.
- // BUGBUG: This uses Frame; in v2 it should be Bounds
- return new Size (Bounds.Size.Width + GetHotKeySpecifierLength (),
- Bounds.Size.Height + GetHotKeySpecifierLength (false));
+ if (string.IsNullOrEmpty (TextFormatter.Text)) {
+ return Bounds.Size;
}
+
+ // BUGBUG: This IGNORES what Text is set to, using on only the current View size. This doesn't seem to make sense.
+ // BUGBUG: This uses Frame; in v2 it should be Bounds
+ return new Size (Bounds.Size.Width + GetHotKeySpecifierLength (),
+ Bounds.Size.Height + GetHotKeySpecifierLength (false));
}
}
\ No newline at end of file
diff --git a/Terminal.Gui/Views/ColorPicker.cs b/Terminal.Gui/Views/ColorPicker.cs
index 0c9b92da49..a19b9f758d 100644
--- a/Terminal.Gui/Views/ColorPicker.cs
+++ b/Terminal.Gui/Views/ColorPicker.cs
@@ -51,7 +51,9 @@ public int BoxWidth {
set {
if (_boxWidth != value) {
_boxWidth = value;
- Bounds = new Rect (Bounds.Location, new Size (_cols * BoxWidth, _rows * BoxHeight));
+ if (IsInitialized) {
+ Bounds = new Rect (Bounds.Location, new Size (_cols * BoxWidth, _rows * BoxHeight));
+ }
}
}
}
@@ -65,7 +67,9 @@ public int BoxHeight {
set {
if (_boxHeight != value) {
_boxHeight = value;
- Bounds = new Rect (Bounds.Location, new Size (_cols * BoxWidth, _rows * BoxHeight));
+ if (IsInitialized) {
+ Bounds = new Rect (Bounds.Location, new Size (_cols * BoxWidth, _rows * BoxHeight));
+ }
}
}
}
diff --git a/Terminal.Gui/Views/ComboBox.cs b/Terminal.Gui/Views/ComboBox.cs
index ddf24086db..851719c243 100644
--- a/Terminal.Gui/Views/ComboBox.cs
+++ b/Terminal.Gui/Views/ComboBox.cs
@@ -10,878 +10,853 @@
using System.Collections.Generic;
using System.Text;
-namespace Terminal.Gui {
- ///
- /// Provides a drop-down list of items the user can select from.
- ///
- public class ComboBox : View {
+namespace Terminal.Gui;
- private class ComboListView : ListView {
- private int highlighted = -1;
- private bool isFocusing;
- private ComboBox container;
- private bool hideDropdownListOnClick;
+///
+/// Provides a drop-down list of items the user can select from.
+///
+public class ComboBox : View {
+ class ComboListView : ListView {
+ int _highlighted = -1;
+ bool _isFocusing;
+ ComboBox _container;
+ bool _hideDropdownListOnClick;
- public ComboListView (ComboBox container, bool hideDropdownListOnClick)
- {
- Initialize (container, hideDropdownListOnClick);
- }
+ public ComboListView (ComboBox container, bool hideDropdownListOnClick) => SetInitialProperties (container, hideDropdownListOnClick);
- public ComboListView (ComboBox container, Rect rect, IList source, bool hideDropdownListOnClick) : base (rect, source)
- {
- Initialize (container, hideDropdownListOnClick);
- }
+ public ComboListView (ComboBox container, Rect rect, IList source, bool hideDropdownListOnClick) : base (rect, source) => SetInitialProperties (container, hideDropdownListOnClick);
- public ComboListView (ComboBox container, IList source, bool hideDropdownListOnClick) : base (source)
- {
- Initialize (container, hideDropdownListOnClick);
- }
+ public ComboListView (ComboBox container, IList source, bool hideDropdownListOnClick) : base (source) => SetInitialProperties (container, hideDropdownListOnClick);
- private void Initialize (ComboBox container, bool hideDropdownListOnClick)
- {
- this.container = container ?? throw new ArgumentNullException (nameof (container), "ComboBox container cannot be null.");
- HideDropdownListOnClick = hideDropdownListOnClick;
- }
+ void SetInitialProperties (ComboBox container, bool hideDropdownListOnClick)
+ {
+ _container = container ?? throw new ArgumentNullException (nameof (container), "ComboBox container cannot be null.");
+ HideDropdownListOnClick = hideDropdownListOnClick;
+ }
- public bool HideDropdownListOnClick {
- get => hideDropdownListOnClick;
- set => hideDropdownListOnClick = WantContinuousButtonPressed = value;
- }
+ public bool HideDropdownListOnClick {
+ get => _hideDropdownListOnClick;
+ set => _hideDropdownListOnClick = WantContinuousButtonPressed = value;
+ }
- public override bool MouseEvent (MouseEvent me)
- {
- var res = false;
- var isMousePositionValid = IsMousePositionValid (me);
+ public override bool MouseEvent (MouseEvent me)
+ {
+ bool res = false;
+ bool isMousePositionValid = IsMousePositionValid (me);
+ if (isMousePositionValid) {
+ res = base.MouseEvent (me);
+ }
+
+ if (HideDropdownListOnClick && me.Flags == MouseFlags.Button1Clicked) {
+ if (!isMousePositionValid && !_isFocusing) {
+ _container._isShow = false;
+ _container.HideList ();
+ } else if (isMousePositionValid) {
+ OnOpenSelectedItem ();
+ } else {
+ _isFocusing = false;
+ }
+ return true;
+ } else if (me.Flags == MouseFlags.ReportMousePosition && HideDropdownListOnClick) {
if (isMousePositionValid) {
- res = base.MouseEvent (me);
+ _highlighted = Math.Min (TopItem + me.Y, Source.Count);
+ SetNeedsDisplay ();
}
+ _isFocusing = false;
+ return true;
+ }
- if (HideDropdownListOnClick && me.Flags == MouseFlags.Button1Clicked) {
- if (!isMousePositionValid && !isFocusing) {
- container.isShow = false;
- container.HideList ();
- } else if (isMousePositionValid) {
- OnOpenSelectedItem ();
- } else {
- isFocusing = false;
- }
- return true;
- } else if (me.Flags == MouseFlags.ReportMousePosition && HideDropdownListOnClick) {
- if (isMousePositionValid) {
- highlighted = Math.Min (TopItem + me.Y, Source.Count);
- SetNeedsDisplay ();
- }
- isFocusing = false;
- return true;
- }
+ return res;
+ }
- return res;
+ bool IsMousePositionValid (MouseEvent me)
+ {
+ if (me.X >= 0 && me.X < Frame.Width && me.Y >= 0 && me.Y < Frame.Height) {
+ return true;
}
+ return false;
+ }
- private bool IsMousePositionValid (MouseEvent me)
- {
- if (me.X >= 0 && me.X < Frame.Width && me.Y >= 0 && me.Y < Frame.Height) {
- return true;
+ public override void OnDrawContent (Rect contentArea)
+ {
+ var current = ColorScheme.Focus;
+ Driver.SetAttribute (current);
+ Move (0, 0);
+ var f = Frame;
+ int item = TopItem;
+ bool focused = HasFocus;
+ int col = AllowsMarking ? 2 : 0;
+ int start = LeftItem;
+
+ for (int row = 0; row < f.Height; row++, item++) {
+ bool isSelected = item == _container.SelectedItem;
+ bool isHighlighted = _hideDropdownListOnClick && item == _highlighted;
+
+ Attribute newcolor;
+ if (isHighlighted || isSelected && !_hideDropdownListOnClick) {
+ newcolor = focused ? ColorScheme.Focus : ColorScheme.HotNormal;
+ } else if (isSelected && _hideDropdownListOnClick) {
+ newcolor = focused ? ColorScheme.HotFocus : ColorScheme.HotNormal;
+ } else {
+ newcolor = focused ? GetNormalColor () : GetNormalColor ();
}
- return false;
- }
- public override void OnDrawContent (Rect contentArea)
- {
- var current = ColorScheme.Focus;
- Driver.SetAttribute (current);
- Move (0, 0);
- var f = Frame;
- var item = TopItem;
- bool focused = HasFocus;
- int col = AllowsMarking ? 2 : 0;
- int start = LeftItem;
-
- for (int row = 0; row < f.Height; row++, item++) {
- bool isSelected = item == container.SelectedItem;
- bool isHighlighted = hideDropdownListOnClick && item == highlighted;
-
- Attribute newcolor;
- if (isHighlighted || (isSelected && !hideDropdownListOnClick)) {
- newcolor = focused ? ColorScheme.Focus : ColorScheme.HotNormal;
- } else if (isSelected && hideDropdownListOnClick) {
- newcolor = focused ? ColorScheme.HotFocus : ColorScheme.HotNormal;
- } else {
- newcolor = focused ? GetNormalColor () : GetNormalColor ();
- }
+ if (newcolor != current) {
+ Driver.SetAttribute (newcolor);
+ current = newcolor;
+ }
- if (newcolor != current) {
- Driver.SetAttribute (newcolor);
- current = newcolor;
+ Move (0, row);
+ if (Source == null || item >= Source.Count) {
+ for (int c = 0; c < f.Width; c++) {
+ Driver.AddRune ((Rune)' ');
}
-
- Move (0, row);
- if (Source == null || item >= Source.Count) {
- for (int c = 0; c < f.Width; c++)
- Driver.AddRune ((Rune)' ');
- } else {
- var rowEventArgs = new ListViewRowEventArgs (item);
- OnRowRender (rowEventArgs);
- if (rowEventArgs.RowAttribute != null && current != rowEventArgs.RowAttribute) {
- current = (Attribute)rowEventArgs.RowAttribute;
- Driver.SetAttribute (current);
- }
- if (AllowsMarking) {
- Driver.AddRune (Source.IsMarked (item) ? (AllowsMultipleSelection ? CM.Glyphs.Checked : CM.Glyphs.Selected) : (AllowsMultipleSelection ? CM.Glyphs.UnChecked : CM.Glyphs.UnSelected));
- Driver.AddRune ((Rune)' ');
- }
- Source.Render (this, Driver, isSelected, item, col, row, f.Width - col, start);
+ } else {
+ var rowEventArgs = new ListViewRowEventArgs (item);
+ OnRowRender (rowEventArgs);
+ if (rowEventArgs.RowAttribute != null && current != rowEventArgs.RowAttribute) {
+ current = (Attribute)rowEventArgs.RowAttribute;
+ Driver.SetAttribute (current);
}
+ if (AllowsMarking) {
+ Driver.AddRune (Source.IsMarked (item) ? AllowsMultipleSelection ? Glyphs.Checked : Glyphs.Selected : AllowsMultipleSelection ? Glyphs.UnChecked : Glyphs.UnSelected);
+ Driver.AddRune ((Rune)' ');
+ }
+ Source.Render (this, Driver, isSelected, item, col, row, f.Width - col, start);
}
}
+ }
- public override bool OnEnter (View view)
- {
- if (hideDropdownListOnClick) {
- isFocusing = true;
- highlighted = container.SelectedItem;
- Application.GrabMouse (this);
- }
-
- return base.OnEnter (view);
+ public override bool OnEnter (View view)
+ {
+ if (_hideDropdownListOnClick) {
+ _isFocusing = true;
+ _highlighted = _container.SelectedItem;
+ Application.GrabMouse (this);
}
- public override bool OnLeave (View view)
- {
- if (hideDropdownListOnClick) {
- isFocusing = false;
- highlighted = container.SelectedItem;
- Application.UngrabMouse ();
- }
+ return base.OnEnter (view);
+ }
- return base.OnLeave (view);
+ public override bool OnLeave (View view)
+ {
+ if (_hideDropdownListOnClick) {
+ _isFocusing = false;
+ _highlighted = _container.SelectedItem;
+ Application.UngrabMouse ();
}
- public override bool OnSelectedChanged ()
- {
- var res = base.OnSelectedChanged ();
+ return base.OnLeave (view);
+ }
- highlighted = SelectedItem;
+ public override bool OnSelectedChanged ()
+ {
+ bool res = base.OnSelectedChanged ();
- return res;
- }
- }
+ _highlighted = SelectedItem;
- IListDataSource source;
- ///
- /// Gets or sets the backing this , enabling custom rendering.
- ///
- /// The source.
- ///
- /// Use to set a new source.
- ///
- public IListDataSource Source {
- get => source;
- set {
- source = value;
-
- // Only need to refresh list if its been added to a container view
- if (SuperView != null && SuperView.Subviews.Contains (this)) {
- SelectedItem = -1;
- search.Text = "";
- Search_Changed (this, new TextChangedEventArgs (""));
- SetNeedsDisplay ();
- }
- }
+ return res;
}
+ }
- ///
- /// Sets the source of the to an .
- ///
- /// An object implementing the IList interface.
- ///
- /// Use the property to set a new source and use custome rendering.
- ///
- public void SetSource (IList source)
- {
- if (source == null) {
- Source = null;
- } else {
- listview.SetSource (source);
- Source = listview.Source;
+ IListDataSource _source;
+
+ ///
+ /// Gets or sets the backing this , enabling custom rendering.
+ ///
+ /// The source.
+ ///
+ /// Use to set a new source.
+ ///
+ public IListDataSource Source {
+ get => _source;
+ set {
+ _source = value;
+
+ // Only need to refresh list if its been added to a container view
+ if (SuperView != null && SuperView.Subviews.Contains (this)) {
+ SelectedItem = -1;
+ _search.Text = "";
+ Search_Changed (this, new TextChangedEventArgs (""));
+ SetNeedsDisplay ();
}
}
+ }
- ///
- /// This event is raised when the selected item in the has changed.
- ///
- public event EventHandler SelectedItemChanged;
-
- ///
- /// This event is raised when the drop-down list is expanded.
- ///
- public event EventHandler Expanded;
-
- ///
- /// This event is raised when the drop-down list is collapsed.
- ///
- public event EventHandler Collapsed;
-
- ///
- /// This event is raised when the user Double Clicks on an item or presses ENTER to open the selected item.
- ///
- public event EventHandler OpenSelectedItem;
-
- readonly IList searchset = new List
public void EnsureSelectedTabIsVisible ()
{
- if (SelectedTab == null) {
+ if (!IsInitialized || SelectedTab == null) {
return;
}
diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs
index a416706763..acaf46fdbb 100644
--- a/Terminal.Gui/Views/TextField.cs
+++ b/Terminal.Gui/Views/TextField.cs
@@ -390,6 +390,9 @@ public virtual int CursorPosition {
///
public override void PositionCursor ()
{
+ if (!IsInitialized) {
+ return;
+ }
ProcessAutocomplete ();
var col = 0;
diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs
index c0718dd174..0626c6ea73 100644
--- a/Terminal.Gui/Views/TextView.cs
+++ b/Terminal.Gui/Views/TextView.cs
@@ -1609,175 +1609,58 @@ void SetInitialProperties ()
Initialized += TextView_Initialized!;
- // Things this view knows how to do
- AddCommand (Command.PageDown, () => {
- ProcessPageDown ();
- return true;
- });
- AddCommand (Command.PageDownExtend, () => {
- ProcessPageDownExtend ();
- return true;
- });
- AddCommand (Command.PageUp, () => {
- ProcessPageUp ();
- return true;
- });
- AddCommand (Command.PageUpExtend, () => {
- ProcessPageUpExtend ();
- return true;
- });
- AddCommand (Command.LineDown, () => {
- ProcessMoveDown ();
- return true;
- });
- AddCommand (Command.LineDownExtend, () => {
- ProcessMoveDownExtend ();
- return true;
- });
- AddCommand (Command.LineUp, () => {
- ProcessMoveUp ();
- return true;
- });
- AddCommand (Command.LineUpExtend, () => {
- ProcessMoveUpExtend ();
- return true;
- });
- AddCommand (Command.Right, () => ProcessMoveRight ());
- AddCommand (Command.RightExtend, () => {
- ProcessMoveRightExtend ();
- return true;
- });
- AddCommand (Command.Left, () => ProcessMoveLeft ());
- AddCommand (Command.LeftExtend, () => {
- ProcessMoveLeftExtend ();
- return true;
- });
- AddCommand (Command.DeleteCharLeft, () => {
- ProcessDeleteCharLeft ();
- return true;
- });
- AddCommand (Command.StartOfLine, () => {
- ProcessMoveStartOfLine ();
- return true;
- });
- AddCommand (Command.StartOfLineExtend, () => {
- ProcessMoveStartOfLineExtend ();
- return true;
- });
- AddCommand (Command.DeleteCharRight, () => {
- ProcessDeleteCharRight ();
- return true;
- });
- AddCommand (Command.EndOfLine, () => {
- ProcessMoveEndOfLine ();
- return true;
- });
- AddCommand (Command.EndOfLineExtend, () => {
- ProcessMoveEndOfLineExtend ();
- return true;
- });
- AddCommand (Command.CutToEndLine, () => {
- KillToEndOfLine ();
- return true;
- });
- AddCommand (Command.CutToStartLine, () => {
- KillToStartOfLine ();
- return true;
- });
- AddCommand (Command.Paste, () => {
- ProcessPaste ();
- return true;
- });
- AddCommand (Command.ToggleExtend, () => {
- ToggleSelecting ();
- return true;
- });
- AddCommand (Command.Copy, () => {
- ProcessCopy ();
- return true;
- });
- AddCommand (Command.Cut, () => {
- ProcessCut ();
- return true;
- });
- AddCommand (Command.WordLeft, () => {
- ProcessMoveWordBackward ();
- return true;
- });
- AddCommand (Command.WordLeftExtend, () => {
- ProcessMoveWordBackwardExtend ();
- return true;
- });
- AddCommand (Command.WordRight, () => {
- ProcessMoveWordForward ();
- return true;
- });
- AddCommand (Command.WordRightExtend, () => {
- ProcessMoveWordForwardExtend ();
- return true;
- });
- AddCommand (Command.KillWordForwards, () => {
- ProcessKillWordForward ();
- return true;
- });
- AddCommand (Command.KillWordBackwards, () => {
- ProcessKillWordBackward ();
- return true;
- });
- AddCommand (Command.NewLine, () => ProcessReturn ());
- AddCommand (Command.BottomEnd, () => {
- MoveBottomEnd ();
- return true;
- });
- AddCommand (Command.BottomEndExtend, () => {
- MoveBottomEndExtend ();
- return true;
- });
- AddCommand (Command.TopHome, () => {
- MoveTopHome ();
- return true;
- });
- AddCommand (Command.TopHomeExtend, () => {
- MoveTopHomeExtend ();
- return true;
- });
- AddCommand (Command.SelectAll, () => {
- ProcessSelectAll ();
- return true;
- });
- AddCommand (Command.ToggleOverwrite, () => {
- ProcessSetOverwrite ();
- return true;
- });
- AddCommand (Command.EnableOverwrite, () => {
- SetOverwrite (true);
- return true;
- });
- AddCommand (Command.DisableOverwrite, () => {
- SetOverwrite (false);
- return true;
- });
- AddCommand (Command.Tab, () => ProcessTab ());
- AddCommand (Command.BackTab, () => ProcessBackTab ());
- AddCommand (Command.NextView, () => ProcessMoveNextView ());
- AddCommand (Command.PreviousView, () => ProcessMovePreviousView ());
- AddCommand (Command.Undo, () => {
- Undo ();
- return true;
- });
- AddCommand (Command.Redo, () => {
- Redo ();
- return true;
- });
- AddCommand (Command.DeleteAll, () => {
- DeleteAll ();
- return true;
- });
- AddCommand (Command.Accept, () => {
- ContextMenu!.Position = new Point (CursorPosition.X - _leftColumn + 2, CursorPosition.Y - _topRow + 2);
- ShowContextMenu ();
- return true;
- });
+ // Things this view knows how to do
+ AddCommand (Command.PageDown, () => { ProcessPageDown (); return true; });
+ AddCommand (Command.PageDownExtend, () => { ProcessPageDownExtend (); return true; });
+ AddCommand (Command.PageUp, () => { ProcessPageUp (); return true; });
+ AddCommand (Command.PageUpExtend, () => { ProcessPageUpExtend (); return true; });
+ AddCommand (Command.LineDown, () => { ProcessMoveDown (); return true; });
+ AddCommand (Command.LineDownExtend, () => { ProcessMoveDownExtend (); return true; });
+ AddCommand (Command.LineUp, () => { ProcessMoveUp (); return true; });
+ AddCommand (Command.LineUpExtend, () => { ProcessMoveUpExtend (); return true; });
+ AddCommand (Command.Right, () => ProcessMoveRight ());
+ AddCommand (Command.RightExtend, () => { ProcessMoveRightExtend (); return true; });
+ AddCommand (Command.Left, () => ProcessMoveLeft ());
+ AddCommand (Command.LeftExtend, () => { ProcessMoveLeftExtend (); return true; });
+ AddCommand (Command.DeleteCharLeft, () => { ProcessDeleteCharLeft (); return true; });
+ AddCommand (Command.StartOfLine, () => { ProcessMoveStartOfLine (); return true; });
+ AddCommand (Command.StartOfLineExtend, () => { ProcessMoveStartOfLineExtend (); return true; });
+ AddCommand (Command.DeleteCharRight, () => { ProcessDeleteCharRight (); return true; });
+ AddCommand (Command.EndOfLine, () => { ProcessMoveEndOfLine (); return true; });
+ AddCommand (Command.EndOfLineExtend, () => { ProcessMoveEndOfLineExtend (); return true; });
+ AddCommand (Command.CutToEndLine, () => { KillToEndOfLine (); return true; });
+ AddCommand (Command.CutToStartLine, () => { KillToStartOfLine (); return true; });
+ AddCommand (Command.Paste, () => { ProcessPaste (); return true; });
+ AddCommand (Command.ToggleExtend, () => { ToggleSelecting (); return true; });
+ AddCommand (Command.Copy, () => { ProcessCopy (); return true; });
+ AddCommand (Command.Cut, () => { ProcessCut (); return true; });
+ AddCommand (Command.WordLeft, () => { ProcessMoveWordBackward (); return true; });
+ AddCommand (Command.WordLeftExtend, () => { ProcessMoveWordBackwardExtend (); return true; });
+ AddCommand (Command.WordRight, () => { ProcessMoveWordForward (); return true; });
+ AddCommand (Command.WordRightExtend, () => { ProcessMoveWordForwardExtend (); return true; });
+ AddCommand (Command.KillWordForwards, () => { ProcessKillWordForward (); return true; });
+ AddCommand (Command.KillWordBackwards, () => { ProcessKillWordBackward (); return true; });
+ AddCommand (Command.NewLine, () => ProcessReturn ());
+ AddCommand (Command.BottomEnd, () => { MoveBottomEnd (); return true; });
+ AddCommand (Command.BottomEndExtend, () => { MoveBottomEndExtend (); return true; });
+ AddCommand (Command.TopHome, () => { MoveTopHome (); return true; });
+ AddCommand (Command.TopHomeExtend, () => { MoveTopHomeExtend (); return true; });
+ AddCommand (Command.SelectAll, () => { ProcessSelectAll (); return true; });
+ AddCommand (Command.ToggleOverwrite, () => { ProcessSetOverwrite (); return true; });
+ AddCommand (Command.EnableOverwrite, () => { SetOverwrite (true); return true; });
+ AddCommand (Command.DisableOverwrite, () => { SetOverwrite (false); return true; });
+ AddCommand (Command.Tab, () => ProcessTab ());
+ AddCommand (Command.BackTab, () => ProcessBackTab ());
+ AddCommand (Command.NextView, () => ProcessMoveNextView ());
+ AddCommand (Command.PreviousView, () => ProcessMovePreviousView ());
+ AddCommand (Command.Undo, () => { Undo (); return true; });
+ AddCommand (Command.Redo, () => { Redo (); return true; });
+ AddCommand (Command.DeleteAll, () => { DeleteAll (); return true; });
+ AddCommand (Command.ShowContextMenu, () => {
+ ContextMenu!.Position = new Point (CursorPosition.X - _leftColumn + 2, CursorPosition.Y - _topRow + 2);
+ ShowContextMenu ();
+ return true;
+ });
// Default keybindings for this view
KeyBindings.Add (KeyCode.PageDown, Command.PageDown);
@@ -1881,8 +1764,8 @@ void SetInitialProperties ()
ContextMenu = new ContextMenu () { MenuItems = BuildContextMenuBarItem () };
ContextMenu.KeyChanged += ContextMenu_KeyChanged!;
- KeyBindings.Add ((KeyCode)ContextMenu.Key, KeyBindingScope.HotKey, Command.Accept);
- }
+ KeyBindings.Add ((KeyCode)ContextMenu.Key, KeyBindingScope.HotKey, Command.ShowContextMenu);
+ }
MenuBarItem BuildContextMenuBarItem () => new (new MenuItem [] {
new (Strings.ctxSelectAll, "", () => SelectAll (), null, null, (KeyCode)KeyBindings.GetKeyFromCommands (Command.SelectAll)),
diff --git a/UICatalog/Scenarios/ASCIICustomButton.cs b/UICatalog/Scenarios/ASCIICustomButton.cs
index cb2250a1f2..595af572dc 100644
--- a/UICatalog/Scenarios/ASCIICustomButton.cs
+++ b/UICatalog/Scenarios/ASCIICustomButton.cs
@@ -77,6 +77,7 @@ private void CustomInitialize (string id, string text, Pos x, Pos y, int width,
};
AutoSize = false;
+ LayoutStyle = LayoutStyle.Absolute;
var fillText = new System.Text.StringBuilder ();
for (int i = 0; i < Bounds.Height; i++) {
diff --git a/UICatalog/Scenarios/AllViewsTester.cs b/UICatalog/Scenarios/AllViewsTester.cs
index d113fe2bac..e5ca76be7f 100644
--- a/UICatalog/Scenarios/AllViewsTester.cs
+++ b/UICatalog/Scenarios/AllViewsTester.cs
@@ -1,438 +1,407 @@
using System;
-using System.Collections;
using System.Collections.Generic;
-using System.Diagnostics;
using System.Linq;
using System.Reflection;
-using System.Text;
using Terminal.Gui;
-namespace UICatalog.Scenarios {
- [ScenarioMetadata (Name: "All Views Tester", Description: "Provides a test UI for all classes derived from View.")]
- [ScenarioCategory ("Layout")]
- [ScenarioCategory ("Tests")]
- [ScenarioCategory ("Top Level Windows")]
- public class AllViewsTester : Scenario {
- FrameView _leftPane;
- ListView _classListView;
- FrameView _hostPane;
-
- Dictionary _viewClasses;
- View _curView = null;
-
- // Settings
- FrameView _settingsPane;
- CheckBox _computedCheckBox;
- FrameView _locationFrame;
- RadioGroup _xRadioGroup;
- TextField _xText;
- int _xVal = 0;
- RadioGroup _yRadioGroup;
- TextField _yText;
- int _yVal = 0;
-
- FrameView _sizeFrame;
- RadioGroup _wRadioGroup;
- TextField _wText;
- int _wVal = 0;
- RadioGroup _hRadioGroup;
- TextField _hText;
- int _hVal = 0;
-
- public override void Init ()
- {
- // Don't create a sub-win (Scenario.Win); just use Application.Top
- Application.Init ();
- ConfigurationManager.Themes.Theme = Theme;
- ConfigurationManager.Apply ();
- Application.Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme];
- }
+namespace UICatalog.Scenarios;
+
+[ScenarioMetadata ("All Views Tester", "Provides a test UI for all classes derived from View.")]
+[ScenarioCategory ("Layout")]
+[ScenarioCategory ("Tests")]
+[ScenarioCategory ("Top Level Windows")]
+public class AllViewsTester : Scenario {
+ FrameView _leftPane;
+ ListView _classListView;
+ FrameView _hostPane;
+
+ Dictionary _viewClasses;
+ View _curView = null;
+
+ // Settings
+ FrameView _settingsPane;
+ CheckBox _computedCheckBox;
+ FrameView _locationFrame;
+ RadioGroup _xRadioGroup;
+ TextField _xText;
+ int _xVal = 0;
+ RadioGroup _yRadioGroup;
+ TextField _yText;
+ int _yVal = 0;
+
+ FrameView _sizeFrame;
+ RadioGroup _wRadioGroup;
+ TextField _wText;
+ int _wVal = 0;
+ RadioGroup _hRadioGroup;
+ TextField _hText;
+ int _hVal = 0;
+
+ public override void Init ()
+ {
+ // Don't create a sub-win (Scenario.Win); just use Application.Top
+ Application.Init ();
+ ConfigurationManager.Themes.Theme = Theme;
+ ConfigurationManager.Apply ();
+ Application.Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme];
+ }
- public override void Setup ()
- {
- var statusBar = new StatusBar (new StatusItem [] {
- new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit()),
- new StatusItem(KeyCode.F2, "~F2~ Toggle Frame Ruler", () => {
- ConsoleDriver.Diagnostics ^= ConsoleDriver.DiagnosticFlags.FrameRuler;
- Application.Top.SetNeedsDisplay ();
- }),
- new StatusItem(KeyCode.F3, "~F3~ Toggle Frame Padding", () => {
- ConsoleDriver.Diagnostics ^= ConsoleDriver.DiagnosticFlags.FramePadding;
- Application.Top.SetNeedsDisplay ();
- }),
- });
- Application.Top.Add (statusBar);
-
- _viewClasses = GetAllViewClassesCollection ()
+ public override void Setup ()
+ {
+ var statusBar = new StatusBar (new StatusItem [] {
+ new (Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit ()),
+ new (KeyCode.F2, "~F2~ Toggle Frame Ruler", () => {
+ ConsoleDriver.Diagnostics ^= ConsoleDriver.DiagnosticFlags.FrameRuler;
+ Application.Top.SetNeedsDisplay ();
+ }),
+ new (KeyCode.F3, "~F3~ Toggle Frame Padding", () => {
+ ConsoleDriver.Diagnostics ^= ConsoleDriver.DiagnosticFlags.FramePadding;
+ Application.Top.SetNeedsDisplay ();
+ })
+ });
+ Application.Top.Add (statusBar);
+
+ _viewClasses = GetAllViewClassesCollection ()
.OrderBy (t => t.Name)
.Select (t => new KeyValuePair (t.Name, t))
.ToDictionary (t => t.Key, t => t.Value);
- _leftPane = new FrameView ("Classes") {
- X = 0,
- Y = 0,
- Width = 15,
- Height = Dim.Fill (1), // for status bar
- CanFocus = false,
- ColorScheme = Colors.TopLevel,
- };
-
- _classListView = new ListView (_viewClasses.Keys.ToList ()) {
- X = 0,
- Y = 0,
- Width = Dim.Fill (0),
- Height = Dim.Fill (0),
- AllowsMarking = false,
- ColorScheme = Colors.TopLevel,
- SelectedItem = 0
- };
- _classListView.OpenSelectedItem += (s, a) => {
- _settingsPane.SetFocus ();
- };
- _classListView.SelectedItemChanged += (s,args) => {
- // Remove existing class, if any
- if (_curView != null) {
- _curView.LayoutComplete -= LayoutCompleteHandler;
- _hostPane.Remove (_curView);
- _curView.Dispose ();
- _curView = null;
- _hostPane.Clear ();
- }
- _curView = CreateClass (_viewClasses.Values.ToArray () [_classListView.SelectedItem]);
- };
- _leftPane.Add (_classListView);
-
- _settingsPane = new FrameView ("Settings") {
- X = Pos.Right (_leftPane),
- Y = 0, // for menu
- Width = Dim.Fill (),
- Height = 10,
- CanFocus = false,
- ColorScheme = Colors.TopLevel,
- };
- _computedCheckBox = new CheckBox ("Computed Layout", true) { X = 0, Y = 0 };
- _computedCheckBox.Toggled += (s,e) => {
- if (_curView != null) {
- _curView.LayoutStyle = e.OldValue == true ? LayoutStyle.Absolute : LayoutStyle.Computed;
- _hostPane.LayoutSubviews ();
- }
- };
- _settingsPane.Add (_computedCheckBox);
-
- var radioItems = new string [] { "Percent(x)", "AnchorEnd(x)", "Center", "At(x)" };
- _locationFrame = new FrameView ("Location (Pos)") {
- X = Pos.Left (_computedCheckBox),
- Y = Pos.Bottom (_computedCheckBox),
- Height = 3 + radioItems.Length,
- Width = 36,
- };
- _settingsPane.Add (_locationFrame);
-
- var label = new Label ("x:") { X = 0, Y = 0 };
- _locationFrame.Add (label);
- _xRadioGroup = new RadioGroup (radioItems) {
- X = 0,
- Y = Pos.Bottom (label),
- };
- _xRadioGroup.SelectedItemChanged += (s, selected) => DimPosChanged (_curView);
- _xText = new TextField ($"{_xVal}") { X = Pos.Right (label) + 1, Y = 0, Width = 4 };
- _xText.TextChanged += (s, args) => {
- try {
- _xVal = int.Parse (_xText.Text);
- DimPosChanged (_curView);
- } catch {
-
- }
- };
- _locationFrame.Add (_xText);
-
- _locationFrame.Add (_xRadioGroup);
-
- radioItems = new string [] { "Percent(y)", "AnchorEnd(y)", "Center", "At(y)" };
- label = new Label ("y:") { X = Pos.Right (_xRadioGroup) + 1, Y = 0 };
- _locationFrame.Add (label);
- _yText = new TextField ($"{_yVal}") { X = Pos.Right (label) + 1, Y = 0, Width = 4 };
- _yText.TextChanged += (s,args) => {
- try {
- _yVal = int.Parse (_yText.Text);
- DimPosChanged (_curView);
- } catch {
-
- }
- };
- _locationFrame.Add (_yText);
- _yRadioGroup = new RadioGroup (radioItems) {
- X = Pos.X (label),
- Y = Pos.Bottom (label),
- };
- _yRadioGroup.SelectedItemChanged += (s, selected) => DimPosChanged (_curView);
- _locationFrame.Add (_yRadioGroup);
-
- _sizeFrame = new FrameView ("Size (Dim)") {
- X = Pos.Right (_locationFrame),
- Y = Pos.Y (_locationFrame),
- Height = 3 + radioItems.Length,
- Width = 40,
- };
-
- radioItems = new string [] { "Percent(width)", "Fill(width)", "Sized(width)" };
- label = new Label ("width:") { X = 0, Y = 0 };
- _sizeFrame.Add (label);
- _wRadioGroup = new RadioGroup (radioItems) {
- X = 0,
- Y = Pos.Bottom (label),
- };
- _wRadioGroup.SelectedItemChanged += (s, selected) => DimPosChanged (_curView);
- _wText = new TextField ($"{_wVal}") { X = Pos.Right (label) + 1, Y = 0, Width = 4 };
- _wText.TextChanged += (s,args) => {
- try {
- switch (_wRadioGroup.SelectedItem) {
- case 0:
- _wVal = Math.Min (int.Parse (_wText.Text), 100);
- break;
- case 1:
- case 2:
- _wVal = int.Parse (_wText.Text);
- break;
- }
- DimPosChanged (_curView);
- } catch {
-
- }
- };
- _sizeFrame.Add (_wText);
- _sizeFrame.Add (_wRadioGroup);
-
- radioItems = new string [] { "Percent(height)", "Fill(height)", "Sized(height)" };
- label = new Label ("height:") { X = Pos.Right (_wRadioGroup) + 1, Y = 0 };
- _sizeFrame.Add (label);
- _hText = new TextField ($"{_hVal}") { X = Pos.Right (label) + 1, Y = 0, Width = 4 };
- _hText.TextChanged += (s, args) => {
- try {
- switch (_hRadioGroup.SelectedItem) {
- case 0:
- _hVal = Math.Min (int.Parse (_hText.Text), 100);
- break;
- case 1:
- case 2:
- _hVal = int.Parse (_hText.Text);
- break;
- }
- DimPosChanged (_curView);
- } catch {
-
- }
- };
- _sizeFrame.Add (_hText);
-
- _hRadioGroup = new RadioGroup (radioItems) {
- X = Pos.X (label),
- Y = Pos.Bottom (label),
- };
- _hRadioGroup.SelectedItemChanged += (s, selected) => DimPosChanged (_curView);
- _sizeFrame.Add (_hRadioGroup);
-
- _settingsPane.Add (_sizeFrame);
-
- _hostPane = new FrameView ("") {
- X = Pos.Right (_leftPane),
- Y = Pos.Bottom (_settingsPane),
- Width = Dim.Fill (),
- Height = Dim.Fill (1), // + 1 for status bar
- ColorScheme = Colors.Dialog,
- };
-
- Application.Top.Add (_leftPane, _settingsPane, _hostPane);
-
- _curView = CreateClass (_viewClasses.First ().Value);
- }
-
- void DimPosChanged (View view)
- {
- if (view == null) {
- return;
+ _leftPane = new FrameView ("Classes") {
+ X = 0,
+ Y = 0,
+ Width = 15,
+ Height = Dim.Fill (1), // for status bar
+ CanFocus = false,
+ ColorScheme = Colors.TopLevel
+ };
+
+ _classListView = new ListView (_viewClasses.Keys.ToList ()) {
+ X = 0,
+ Y = 0,
+ Width = Dim.Fill (0),
+ Height = Dim.Fill (0),
+ AllowsMarking = false,
+ ColorScheme = Colors.TopLevel,
+ SelectedItem = 0
+ };
+ _classListView.OpenSelectedItem += (s, a) => {
+ _settingsPane.SetFocus ();
+ };
+ _classListView.SelectedItemChanged += (s, args) => {
+ // Remove existing class, if any
+ if (_curView != null) {
+ _curView.LayoutComplete -= LayoutCompleteHandler;
+ _hostPane.Remove (_curView);
+ _curView.Dispose ();
+ _curView = null;
+ _hostPane.Clear ();
}
-
- var layout = view.LayoutStyle;
-
+ _curView = CreateClass (_viewClasses.Values.ToArray () [_classListView.SelectedItem]);
+ };
+ _leftPane.Add (_classListView);
+
+ _settingsPane = new FrameView ("Settings") {
+ X = Pos.Right (_leftPane),
+ Y = 0, // for menu
+ Width = Dim.Fill (),
+ Height = 10,
+ CanFocus = false,
+ ColorScheme = Colors.TopLevel
+ };
+ _computedCheckBox = new CheckBox ("Computed Layout", true) { X = 0, Y = 0 };
+ _computedCheckBox.Toggled += (s, e) => {
+ if (_curView != null) {
+ _curView.LayoutStyle = e.OldValue == true ? LayoutStyle.Absolute : LayoutStyle.Computed;
+ _hostPane.LayoutSubviews ();
+ }
+ };
+ _settingsPane.Add (_computedCheckBox);
+
+ string [] radioItems = new string [] { "Percent(x)", "AnchorEnd(x)", "Center", "At(x)" };
+ _locationFrame = new FrameView ("Location (Pos)") {
+ X = Pos.Left (_computedCheckBox),
+ Y = Pos.Bottom (_computedCheckBox),
+ Height = 3 + radioItems.Length,
+ Width = 36
+ };
+ _settingsPane.Add (_locationFrame);
+
+ var label = new Label ("x:") { X = 0, Y = 0 };
+ _locationFrame.Add (label);
+ _xRadioGroup = new RadioGroup (radioItems) {
+ X = 0,
+ Y = Pos.Bottom (label)
+ };
+ _xRadioGroup.SelectedItemChanged += (s, selected) => DimPosChanged (_curView);
+ _xText = new TextField ($"{_xVal}") { X = Pos.Right (label) + 1, Y = 0, Width = 4 };
+ _xText.TextChanged += (s, args) => {
+ try {
+ _xVal = int.Parse (_xText.Text);
+ DimPosChanged (_curView);
+ } catch { }
+ };
+ _locationFrame.Add (_xText);
+
+ _locationFrame.Add (_xRadioGroup);
+
+ radioItems = new string [] { "Percent(y)", "AnchorEnd(y)", "Center", "At(y)" };
+ label = new Label ("y:") { X = Pos.Right (_xRadioGroup) + 1, Y = 0 };
+ _locationFrame.Add (label);
+ _yText = new TextField ($"{_yVal}") { X = Pos.Right (label) + 1, Y = 0, Width = 4 };
+ _yText.TextChanged += (s, args) => {
+ try {
+ _yVal = int.Parse (_yText.Text);
+ DimPosChanged (_curView);
+ } catch { }
+ };
+ _locationFrame.Add (_yText);
+ _yRadioGroup = new RadioGroup (radioItems) {
+ X = Pos.X (label),
+ Y = Pos.Bottom (label)
+ };
+ _yRadioGroup.SelectedItemChanged += (s, selected) => DimPosChanged (_curView);
+ _locationFrame.Add (_yRadioGroup);
+
+ _sizeFrame = new FrameView ("Size (Dim)") {
+ X = Pos.Right (_locationFrame),
+ Y = Pos.Y (_locationFrame),
+ Height = 3 + radioItems.Length,
+ Width = 40
+ };
+
+ radioItems = new string [] { "Percent(width)", "Fill(width)", "Sized(width)" };
+ label = new Label ("width:") { X = 0, Y = 0 };
+ _sizeFrame.Add (label);
+ _wRadioGroup = new RadioGroup (radioItems) {
+ X = 0,
+ Y = Pos.Bottom (label)
+ };
+ _wRadioGroup.SelectedItemChanged += (s, selected) => DimPosChanged (_curView);
+ _wText = new TextField ($"{_wVal}") { X = Pos.Right (label) + 1, Y = 0, Width = 4 };
+ _wText.TextChanged += (s, args) => {
try {
- view.LayoutStyle = LayoutStyle.Absolute;
-
- switch (_xRadioGroup.SelectedItem) {
- case 0:
- view.X = Pos.Percent (_xVal);
- break;
- case 1:
- view.X = Pos.AnchorEnd (_xVal);
- break;
- case 2:
- view.X = Pos.Center ();
- break;
- case 3:
- view.X = Pos.At (_xVal);
- break;
- }
-
- switch (_yRadioGroup.SelectedItem) {
- case 0:
- view.Y = Pos.Percent (_yVal);
- break;
- case 1:
- view.Y = Pos.AnchorEnd (_yVal);
- break;
- case 2:
- view.Y = Pos.Center ();
- break;
- case 3:
- view.Y = Pos.At (_yVal);
- break;
- }
-
switch (_wRadioGroup.SelectedItem) {
case 0:
- view.Width = Dim.Percent (_wVal);
+ _wVal = Math.Min (int.Parse (_wText.Text), 100);
break;
case 1:
- view.Width = Dim.Fill (_wVal);
- break;
case 2:
- view.Width = Dim.Sized (_wVal);
+ _wVal = int.Parse (_wText.Text);
break;
}
-
+ DimPosChanged (_curView);
+ } catch { }
+ };
+ _sizeFrame.Add (_wText);
+ _sizeFrame.Add (_wRadioGroup);
+
+ radioItems = new string [] { "Percent(height)", "Fill(height)", "Sized(height)" };
+ label = new Label ("height:") { X = Pos.Right (_wRadioGroup) + 1, Y = 0 };
+ _sizeFrame.Add (label);
+ _hText = new TextField ($"{_hVal}") { X = Pos.Right (label) + 1, Y = 0, Width = 4 };
+ _hText.TextChanged += (s, args) => {
+ try {
switch (_hRadioGroup.SelectedItem) {
case 0:
- view.Height = Dim.Percent (_hVal);
+ _hVal = Math.Min (int.Parse (_hText.Text), 100);
break;
case 1:
- view.Height = Dim.Fill (_hVal);
- break;
case 2:
- view.Height = Dim.Sized (_hVal);
+ _hVal = int.Parse (_hText.Text);
break;
}
- } catch (Exception e) {
- MessageBox.ErrorQuery ("Exception", e.Message, "Ok");
- } finally {
- view.LayoutStyle = layout;
- }
- UpdateTitle (view);
- }
+ DimPosChanged (_curView);
+ } catch { }
+ };
+ _sizeFrame.Add (_hText);
+
+ _hRadioGroup = new RadioGroup (radioItems) {
+ X = Pos.X (label),
+ Y = Pos.Bottom (label)
+ };
+ _hRadioGroup.SelectedItemChanged += (s, selected) => DimPosChanged (_curView);
+ _sizeFrame.Add (_hRadioGroup);
+
+ _settingsPane.Add (_sizeFrame);
+
+ _hostPane = new FrameView ("") {
+ X = Pos.Right (_leftPane),
+ Y = Pos.Bottom (_settingsPane),
+ Width = Dim.Fill (),
+ Height = Dim.Fill (1), // + 1 for status bar
+ ColorScheme = Colors.Dialog
+ };
+
+ Application.Top.Add (_leftPane, _settingsPane, _hostPane);
+
+ _curView = CreateClass (_viewClasses.First ().Value);
+ }
- List posNames = new List { "Factor", "AnchorEnd", "Center", "Absolute" };
- List dimNames = new List { "Factor", "Fill", "Absolute" };
-
- void UpdateSettings (View view)
- {
- var x = view.X.ToString ();
- var y = view.Y.ToString ();
- _xRadioGroup.SelectedItem = posNames.IndexOf (posNames.Where (s => x.Contains (s)).First ());
- _yRadioGroup.SelectedItem = posNames.IndexOf (posNames.Where (s => y.Contains (s)).First ());
- _xText.Text = $"{view.Frame.X}";
- _yText.Text = $"{view.Frame.Y}";
-
- var w = view.Width.ToString ();
- var h = view.Height.ToString ();
- _wRadioGroup.SelectedItem = dimNames.IndexOf (dimNames.Where (s => w.Contains (s)).First ());
- _hRadioGroup.SelectedItem = dimNames.IndexOf (dimNames.Where (s => h.Contains (s)).First ());
- _wText.Text = $"{view.Frame.Width}";
- _hText.Text = $"{view.Frame.Height}";
+ void DimPosChanged (View view)
+ {
+ if (view == null) {
+ return;
}
- void UpdateTitle (View view)
- {
- _hostPane.Title = $"{view.GetType ().Name} - {view.X}, {view.Y}, {view.Width}, {view.Height}";
- }
+ var layout = view.LayoutStyle;
- List GetAllViewClassesCollection ()
- {
- List types = new List ();
- foreach (Type type in typeof (View).Assembly.GetTypes ()
- .Where (myType => myType.IsClass && !myType.IsAbstract && myType.IsPublic && myType.IsSubclassOf (typeof (View)))) {
- types.Add (type);
- }
- types.Add (typeof (View));
- return types;
+ try {
+ view.LayoutStyle = LayoutStyle.Absolute;
+
+ view.X = _xRadioGroup.SelectedItem switch {
+ 0 => Pos.Percent (_xVal),
+ 1 => Pos.AnchorEnd (_xVal),
+ 2 => Pos.Center (),
+ 3 => Pos.At (_xVal),
+ _ => view.X
+ };
+
+ view.Y = _yRadioGroup.SelectedItem switch {
+ 0 => Pos.Percent (_yVal),
+ 1 => Pos.AnchorEnd (_yVal),
+ 2 => Pos.Center (),
+ 3 => Pos.At (_yVal),
+ _ => view.Y
+ };
+
+ view.Width = _wRadioGroup.SelectedItem switch {
+ 0 => Dim.Percent (_wVal),
+ 1 => Dim.Fill (_wVal),
+ 2 => Dim.Sized (_wVal),
+ _ => view.Width
+ };
+
+ view.Height = _hRadioGroup.SelectedItem switch {
+ 0 => Dim.Percent (_hVal),
+ 1 => Dim.Fill (_hVal),
+ 2 => Dim.Sized (_hVal),
+ _ => view.Height
+ };
+ } catch (Exception e) {
+ MessageBox.ErrorQuery ("Exception", e.Message, "Ok");
+ } finally {
+ view.LayoutStyle = layout;
}
-
+ UpdateTitle (view);
+ }
- View CreateClass (Type type)
- {
- // If we are to create a generic Type
- if (type.IsGenericType) {
+ // TODO: This is missing some
+ List _posNames = new() { "Factor", "AnchorEnd", "Center", "Absolute" };
+ List _dimNames = new() { "Factor", "Fill", "Absolute" };
+
+ void UpdateSettings (View view)
+ {
+ string x = view.X.ToString ();
+ string y = view.Y.ToString ();
+ _xRadioGroup.SelectedItem = _posNames.IndexOf (_posNames.Where (s => x.Contains (s)).First ());
+ _yRadioGroup.SelectedItem = _posNames.IndexOf (_posNames.Where (s => y.Contains (s)).First ());
+ _xText.Text = $"{view.Frame.X}";
+ _yText.Text = $"{view.Frame.Y}";
+
+ string w = view.Width.ToString ();
+ string h = view.Height.ToString ();
+ _wRadioGroup.SelectedItem = _dimNames.IndexOf (_dimNames.Where (s => w.Contains (s)).First ());
+ _hRadioGroup.SelectedItem = _dimNames.IndexOf (_dimNames.Where (s => h.Contains (s)).First ());
+ _wText.Text = $"{view.Frame.Width}";
+ _hText.Text = $"{view.Frame.Height}";
+ }
- // For each of the arguments
- List typeArguments = new List ();
+ void UpdateTitle (View view) => _hostPane.Title = $"{view.GetType ().Name} - {view.X}, {view.Y}, {view.Width}, {view.Height}";
- // use
- foreach (var arg in type.GetGenericArguments ()) {
- typeArguments.Add (typeof (object));
- }
+ List GetAllViewClassesCollection ()
+ {
+ var types = new List ();
+ foreach (var type in typeof (View).Assembly.GetTypes ()
+ .Where (myType => myType.IsClass && !myType.IsAbstract && myType.IsPublic && myType.IsSubclassOf (typeof (View)))) {
+ types.Add (type);
+ }
+ types.Add (typeof (View));
+ return types;
+ }
- // And change what type we are instantiating from MyClass to MyClass
- type = type.MakeGenericType (typeArguments.ToArray ());
- }
- // Instantiate view
- var view = (View)Activator.CreateInstance (type);
+ // TODO: Add Command.Default handler (pop a message box?)
+ View CreateClass (Type type)
+ {
+ // If we are to create a generic Type
+ if (type.IsGenericType) {
- //_curView.X = Pos.Center ();
- //_curView.Y = Pos.Center ();
- view.Width = Dim.Percent (75);
- view.Height = Dim.Percent (75);
+ // For each of the arguments
+ var typeArguments = new List ();
- // Set the colorscheme to make it stand out if is null by default
- if (view.ColorScheme == null) {
- view.ColorScheme = Colors.Base;
+ // use
+ foreach (var arg in type.GetGenericArguments ()) {
+ typeArguments.Add (typeof (object));
}
- // If the view supports a Text property, set it so we have something to look at
- if (view.GetType ().GetProperty ("Text") != null) {
- try {
- view.GetType ().GetProperty ("Text")?.GetSetMethod ()?.Invoke (view, new [] { "Test Text" });
- } catch (TargetInvocationException e) {
- MessageBox.ErrorQuery ("Exception", e.InnerException.Message, "Ok");
- view = null;
- }
- }
+ // And change what type we are instantiating from MyClass to MyClass
+ type = type.MakeGenericType (typeArguments.ToArray ());
+ }
+ // Instantiate view
+ var view = (View)Activator.CreateInstance (type);
- // If the view supports a Title property, set it so we have something to look at
- if (view != null && view.GetType ().GetProperty ("Title") != null) {
- if (view.GetType ().GetProperty ("Title").PropertyType == typeof (string)) {
- view?.GetType ().GetProperty ("Title")?.GetSetMethod ()?.Invoke (view, new [] { "Test Title" });
- } else {
- view?.GetType ().GetProperty ("Title")?.GetSetMethod ()?.Invoke (view, new [] { "Test Title" });
- }
+ // Set the colorscheme to make it stand out if is null by default
+ if (view.ColorScheme == null) {
+ view.ColorScheme = Colors.Base;
+ }
+
+ // If the view supports a Text property, set it so we have something to look at
+ if (view.GetType ().GetProperty ("Text") != null) {
+ try {
+ view.GetType ().GetProperty ("Text")?.GetSetMethod ()?.Invoke (view, new [] { "Test Text" });
+ } catch (TargetInvocationException e) {
+ MessageBox.ErrorQuery ("Exception", e.InnerException.Message, "Ok");
+ view = null;
}
+ }
- // If the view supports a Source property, set it so we have something to look at
- if (view != null && view.GetType ().GetProperty ("Source") != null && view.GetType ().GetProperty ("Source").PropertyType == typeof (Terminal.Gui.IListDataSource)) {
- var source = new ListWrapper (new List () { "Test Text #1", "Test Text #2", "Test Text #3" });
- view?.GetType ().GetProperty ("Source")?.GetSetMethod ()?.Invoke (view, new [] { source });
+ // If the view supports a Title property, set it so we have something to look at
+ if (view != null && view.GetType ().GetProperty ("Title") != null) {
+ if (view.GetType ().GetProperty ("Title").PropertyType == typeof (string)) {
+ view?.GetType ().GetProperty ("Title")?.GetSetMethod ()?.Invoke (view, new [] { "Test Title" });
+ } else {
+ view?.GetType ().GetProperty ("Title")?.GetSetMethod ()?.Invoke (view, new [] { "Test Title" });
}
+ }
- // Set Settings
- _computedCheckBox.Checked = view.LayoutStyle == LayoutStyle.Computed;
+ // If the view supports a Source property, set it so we have something to look at
+ if (view != null && view.GetType ().GetProperty ("Source") != null && view.GetType ().GetProperty ("Source").PropertyType == typeof (IListDataSource)) {
+ var source = new ListWrapper (new List () { "Test Text #1", "Test Text #2", "Test Text #3" });
+ view?.GetType ().GetProperty ("Source")?.GetSetMethod ()?.Invoke (view, new [] { source });
+ }
- // Add
- _hostPane.Add (view);
- _hostPane.Clear ();
- _hostPane.SetNeedsDisplay ();
- UpdateSettings (view);
- UpdateTitle (view);
+ // Set Settings
+ _computedCheckBox.Checked = view.LayoutStyle == LayoutStyle.Computed;
- view.LayoutComplete += LayoutCompleteHandler;
+ view.Initialized += View_Initialized;
- return view;
- }
+ // Add
+ _hostPane.Add (view);
+ _hostPane.SetNeedsDisplay ();
- void LayoutCompleteHandler (object sender, LayoutEventArgs args)
- {
- UpdateTitle (_curView);
- }
+ return view;
+ }
+
+ void View_Initialized (object sender, EventArgs e)
+ {
+ var view = sender as View;
- private void Quit ()
- {
- Application.RequestStop ();
+ //view.X = Pos.Center ();
+ //view.Y = Pos.Center ();
+ if (view.Width == null || view.Frame.Width == 0) {
+ view.Width = Dim.Fill();
+ }
+ if (view.Height == null || view.Frame.Height == 0) {
+ view.Height = Dim.Fill();
}
+ UpdateSettings (view);
+ UpdateTitle (view);
}
+
+ void LayoutCompleteHandler (object sender, LayoutEventArgs args)
+ {
+ UpdateSettings (_curView);
+ UpdateTitle (_curView);
+ }
+
+ void Quit () => Application.RequestStop ();
}
\ No newline at end of file
diff --git a/UICatalog/Scenarios/CharacterMap.cs b/UICatalog/Scenarios/CharacterMap.cs
index 762017c2b2..7dc7574a34 100644
--- a/UICatalog/Scenarios/CharacterMap.cs
+++ b/UICatalog/Scenarios/CharacterMap.cs
@@ -114,10 +114,9 @@ public override void Setup ()
Application.Top.Add (_categoryList);
_charMap.SelectedCodePoint = 0;
- //jumpList.Refresh ();
_charMap.SetFocus ();
-
- _charMap.Width = Dim.Fill () - _categoryList.Width;
+ // TODO: Replace this with Dim.Auto when that's ready
+ _categoryList.Initialized += _categoryList_Initialized;
var menu = new MenuBar (new MenuBarItem [] {
new ("_File", new MenuItem [] {
@@ -128,12 +127,11 @@ public override void Setup ()
})
});
Application.Top.Add (menu);
-
- //_charMap.Hover += (s, a) => {
- // _errorLabel.Text = $"U+{a.Item:x5} {(Rune)a.Item}";
- //};
+
}
+ private void _categoryList_Initialized (object sender, EventArgs e) => _charMap.Width = Dim.Fill () - _categoryList.Width;
+
MenuItem CreateMenuShowWidth ()
{
var item = new MenuItem {
@@ -255,24 +253,26 @@ public int SelectedCodePoint {
get => _selected;
set {
_selected = value;
- int row = SelectedCodePoint / 16 * _rowHeight;
- int col = SelectedCodePoint % 16 * COLUMN_WIDTH;
-
- int height = Bounds.Height - (ShowHorizontalScrollIndicator ? 2 : 1);
- if (row + ContentOffset.Y < 0) {
- // Moving up.
- ContentOffset = new Point (ContentOffset.X, row);
- } else if (row + ContentOffset.Y >= height) {
- // Moving down.
- ContentOffset = new Point (ContentOffset.X, Math.Min (row, row - height + _rowHeight));
- }
- int width = Bounds.Width / COLUMN_WIDTH * COLUMN_WIDTH - (ShowVerticalScrollIndicator ? RowLabelWidth + 1 : RowLabelWidth);
- if (col + ContentOffset.X < 0) {
- // Moving left.
- ContentOffset = new Point (col, ContentOffset.Y);
- } else if (col + ContentOffset.X >= width) {
- // Moving right.
- ContentOffset = new Point (Math.Min (col, col - width + COLUMN_WIDTH), ContentOffset.Y);
+ if (IsInitialized) {
+ int row = SelectedCodePoint / 16 * _rowHeight;
+ int col = SelectedCodePoint % 16 * COLUMN_WIDTH;
+
+ int height = Bounds.Height - (ShowHorizontalScrollIndicator ? 2 : 1);
+ if (row + ContentOffset.Y < 0) {
+ // Moving up.
+ ContentOffset = new Point (ContentOffset.X, row);
+ } else if (row + ContentOffset.Y >= height) {
+ // Moving down.
+ ContentOffset = new Point (ContentOffset.X, Math.Min (row, row - height + _rowHeight));
+ }
+ int width = Bounds.Width / COLUMN_WIDTH * COLUMN_WIDTH - (ShowVerticalScrollIndicator ? RowLabelWidth + 1 : RowLabelWidth);
+ if (col + ContentOffset.X < 0) {
+ // Moving left.
+ ContentOffset = new Point (col, ContentOffset.Y);
+ } else if (col + ContentOffset.X >= width) {
+ // Moving right.
+ ContentOffset = new Point (Math.Min (col, col - width + COLUMN_WIDTH), ContentOffset.Y);
+ }
}
SetNeedsDisplay ();
SelectedCodePointChanged?.Invoke (this, new ListViewItemEventArgs (SelectedCodePoint, null));
diff --git a/UICatalog/Scenarios/ClassExplorer.cs b/UICatalog/Scenarios/ClassExplorer.cs
index 79992b3072..60eda61951 100644
--- a/UICatalog/Scenarios/ClassExplorer.cs
+++ b/UICatalog/Scenarios/ClassExplorer.cs
@@ -59,7 +59,6 @@ public override void Setup ()
Win.Title = this.GetName ();
Win.Y = 1; // menu
Win.Height = Dim.Fill (1); // status bar
- Application.Top.LayoutSubviews ();
var menu = new MenuBar (new MenuBarItem [] {
new MenuBarItem ("_File", new MenuItem [] {
diff --git a/UICatalog/Scenarios/ColorPicker.cs b/UICatalog/Scenarios/ColorPicker.cs
index 9533726687..241c07f1b0 100644
--- a/UICatalog/Scenarios/ColorPicker.cs
+++ b/UICatalog/Scenarios/ColorPicker.cs
@@ -91,6 +91,7 @@ public override void Setup ()
// Set default colors.
foregroundColorPicker.SelectedColor = _demoView.SuperView.ColorScheme.Normal.Foreground.ColorName;
backgroundColorPicker.SelectedColor = _demoView.SuperView.ColorScheme.Normal.Background.ColorName;
+ Win.Initialized += (s, e) => Win.LayoutSubviews ();
}
///
diff --git a/UICatalog/Scenarios/ComputedLayout.cs b/UICatalog/Scenarios/ComputedLayout.cs
index 19a9fde2c8..c1f24a17c8 100644
--- a/UICatalog/Scenarios/ComputedLayout.cs
+++ b/UICatalog/Scenarios/ComputedLayout.cs
@@ -79,7 +79,9 @@ public override void Setup ()
Width = Dim.Fill (margin),
Height = 7
};
- subWin.Title = $"{subWin.GetType ().Name} {{X={subWin.X},Y={subWin.Y},Width={subWin.Width},Height={subWin.Height}}}";
+ subWin.Initialized += (s, a) => {
+ subWin.Title = $"{subWin.GetType ().Name} {{X={subWin.X},Y={subWin.Y},Width={subWin.Width},Height={subWin.Height}}}";
+ };
Application.Top.Add (subWin);
int i = 1;
@@ -98,7 +100,10 @@ public override void Setup ()
Width = 30,
Height = 7
};
- frameView.Title = $"{frameView.GetType ().Name} {{X={frameView.X},Y={frameView.Y},Width={frameView.Width},Height={frameView.Height}}}";
+ frameView.Initialized += (sender, args) => {
+ var fv = sender as FrameView;
+ fv.Title = $"{frameView.GetType ().Name} {{X={fv.X},Y={fv.Y},Width={fv.Width},Height={fv.Height}}}";
+ };
i = 1;
labelList = new List