diff --git a/Terminal.Gui/Text/Autocomplete/AppendAutocomplete.cs b/Terminal.Gui/Text/Autocomplete/AppendAutocomplete.cs
index 9f8392d67..c20bed711 100644
--- a/Terminal.Gui/Text/Autocomplete/AppendAutocomplete.cs
+++ b/Terminal.Gui/Text/Autocomplete/AppendAutocomplete.cs
@@ -83,6 +83,7 @@ namespace Terminal.Gui {
public override void GenerateSuggestions (AutocompleteContext context)
{
if (_suspendSuggestions) {
+ _suspendSuggestions = false;
return;
}
base.GenerateSuggestions (context);
diff --git a/Terminal.Gui/Text/TextFormatter.cs b/Terminal.Gui/Text/TextFormatter.cs
index 47390077e..eb93d90dd 100644
--- a/Terminal.Gui/Text/TextFormatter.cs
+++ b/Terminal.Gui/Text/TextFormatter.cs
@@ -1102,7 +1102,7 @@ namespace Terminal.Gui {
_text = EnableNeedsFormat (value);
if ((AutoSize && Alignment != TextAlignment.Justified && VerticalAlignment != VerticalTextAlignment.Justified) || (textWasNull && Size.IsEmpty)) {
- Size = CalcRect (0, 0, _text, _textDirection, TabWidth).Size;
+ Size = CalcRect (0, 0, _text, Direction, TabWidth).Size;
}
//if (_text != null && _text.GetRuneCount () > 0 && (Size.Width == 0 || Size.Height == 0 || Size.Width != _text.GetColumns ())) {
@@ -1129,7 +1129,7 @@ namespace Terminal.Gui {
set {
_autoSize = EnableNeedsFormat (value);
if (_autoSize && Alignment != TextAlignment.Justified && VerticalAlignment != VerticalTextAlignment.Justified) {
- Size = CalcRect (0, 0, Text, _textDirection, TabWidth).Size;
+ Size = CalcRect (0, 0, _text, Direction, TabWidth).Size;
}
}
}
@@ -1169,7 +1169,12 @@ namespace Terminal.Gui {
/// The text vertical alignment.
public TextDirection Direction {
get => _textDirection;
- set => _textDirection = EnableNeedsFormat (value);
+ set {
+ _textDirection = EnableNeedsFormat (value);
+ if (AutoSize && Alignment != TextAlignment.Justified && VerticalAlignment != VerticalTextAlignment.Justified) {
+ Size = CalcRect (0, 0, Text, Direction, TabWidth).Size;
+ }
+ }
}
///
@@ -1250,7 +1255,7 @@ namespace Terminal.Gui {
get => _size;
set {
if (AutoSize && Alignment != TextAlignment.Justified && VerticalAlignment != VerticalTextAlignment.Justified) {
- _size = EnableNeedsFormat (CalcRect (0, 0, Text, _textDirection, TabWidth).Size);
+ _size = EnableNeedsFormat (CalcRect (0, 0, Text, Direction, TabWidth).Size);
} else {
_size = EnableNeedsFormat (value);
}
@@ -1329,7 +1334,7 @@ namespace Terminal.Gui {
shown_text = ReplaceHotKeyWithTag (shown_text, _hotKeyPos);
}
- if (IsVerticalDirection (_textDirection)) {
+ if (IsVerticalDirection (Direction)) {
var colsWidth = GetSumMaxCharWidth (shown_text, 0, 1, TabWidth);
_lines = Format (shown_text, Size.Height, VerticalAlignment == VerticalTextAlignment.Justified, Size.Width > colsWidth && WordWrap,
PreserveTrailingSpaces, TabWidth, Direction, MultiLine);
@@ -1434,7 +1439,7 @@ namespace Terminal.Gui {
// Use "Lines" to ensure a Format (don't use "lines"))
var linesFormated = Lines;
- switch (_textDirection) {
+ switch (Direction) {
case TextDirection.TopBottom_RightLeft:
case TextDirection.LeftRight_BottomTop:
case TextDirection.RightLeft_BottomTop:
@@ -1443,7 +1448,7 @@ namespace Terminal.Gui {
break;
}
- var isVertical = IsVerticalDirection (_textDirection);
+ var isVertical = IsVerticalDirection (Direction);
var maxBounds = bounds;
if (driver != null) {
maxBounds = containerBounds == default
@@ -1475,7 +1480,7 @@ namespace Terminal.Gui {
var runes = _lines [line].ToRunes ();
- switch (_textDirection) {
+ switch (Direction) {
case TextDirection.RightLeft_BottomTop:
case TextDirection.RightLeft_TopBottom:
case TextDirection.BottomTop_LeftRight:
@@ -1488,7 +1493,7 @@ namespace Terminal.Gui {
int x, y;
// Horizontal Alignment
- if (_textAlignment == TextAlignment.Right || (_textAlignment == TextAlignment.Justified && !IsLeftToRight (_textDirection))) {
+ if (_textAlignment == TextAlignment.Right || (_textAlignment == TextAlignment.Justified && !IsLeftToRight (Direction))) {
if (isVertical) {
var runesWidth = GetSumMaxCharWidth (Lines, line, TabWidth);
x = bounds.Right - runesWidth;
@@ -1521,7 +1526,7 @@ namespace Terminal.Gui {
}
// Vertical Alignment
- if (_textVerticalAlignment == VerticalTextAlignment.Bottom || (_textVerticalAlignment == VerticalTextAlignment.Justified && !IsTopToBottom (_textDirection))) {
+ if (_textVerticalAlignment == VerticalTextAlignment.Bottom || (_textVerticalAlignment == VerticalTextAlignment.Justified && !IsTopToBottom (Direction))) {
if (isVertical) {
y = bounds.Bottom - runes.Length;
} else {
diff --git a/Terminal.Gui/View/Layout/ViewLayout.cs b/Terminal.Gui/View/Layout/ViewLayout.cs
index 1d42fea10..21e4231ea 100644
--- a/Terminal.Gui/View/Layout/ViewLayout.cs
+++ b/Terminal.Gui/View/Layout/ViewLayout.cs
@@ -569,14 +569,14 @@ public partial class View {
///
///
///
- /// Sets the .
- ///
- ///
- /// Can be overridden if the view resize behavior is different than the default.
+ /// Determines the relative bounds of the and its s, and then calls
+ /// to update the view.
///
///
- protected virtual void OnResizeNeeded ()
+ internal void OnResizeNeeded ()
{
+ // TODO: Identify a real-world use-case where this API should be virtual.
+ // TODO: Until then leave it `internal` and non-virtual
// First try SuperView.Bounds, then Application.Top, then Driver.Bounds.
// Finally, if none of those are valid, use int.MaxValue (for Unit tests).
var relativeBounds = SuperView is { IsInitialized: true } ? SuperView.Bounds :
diff --git a/Terminal.Gui/View/ViewDrawing.cs b/Terminal.Gui/View/ViewDrawing.cs
index 94d8cad84..aa79c479c 100644
--- a/Terminal.Gui/View/ViewDrawing.cs
+++ b/Terminal.Gui/View/ViewDrawing.cs
@@ -1,5 +1,4 @@
using System;
-using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -8,9 +7,12 @@ namespace Terminal.Gui;
public partial class View {
ColorScheme _colorScheme;
+ // The view-relative region that needs to be redrawn. Marked internal for unit tests.
+ internal Rect _needsDisplayRect = Rect.Empty;
+
///
/// The color scheme for this view, if it is not defined, it returns the 's
- /// color scheme.
+ /// color scheme.
///
public virtual ColorScheme ColorScheme {
get {
@@ -27,12 +29,47 @@ public partial class View {
}
}
+ ///
+ /// Gets or sets whether the view needs to be redrawn.
+ ///
+ public bool NeedsDisplay {
+ get => _needsDisplayRect != Rect.Empty;
+ set {
+ if (value) {
+ SetNeedsDisplay ();
+ } else {
+ ClearNeedsDisplay ();
+ }
+ }
+ }
+
+ ///
+ /// Gets whether any Subviews need to be redrawn.
+ ///
+ public bool SubViewNeedsDisplay { get; private set; }
+
+ ///
+ /// 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;
+
///
/// Determines the current based on the value.
///
- /// if is
+ ///
+ /// if is
/// or if is .
- /// If it's overridden can return other values.
+ /// If it's overridden can return other values.
+ ///
public virtual Attribute GetNormalColor ()
{
var cs = ColorScheme;
@@ -45,17 +82,21 @@ public partial class View {
///
/// Determines the current based on the value.
///
- /// if is
+ ///
+ /// if is
/// or if is .
- /// If it's overridden can return other values.
+ /// 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
+ ///
+ /// if is
/// or if is .
- /// If it's overridden can return other values.
+ /// If it's overridden can return other values.
+ ///
public virtual Attribute GetHotNormalColor () => Enabled ? ColorScheme.HotNormal : ColorScheme.Disabled;
///
@@ -81,25 +122,8 @@ public partial class View {
///
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 ();
- }
- }
+ _needsDisplayRect = Rect.Empty;
+ SubViewNeedsDisplay = false;
}
///
@@ -133,18 +157,18 @@ public partial class View {
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);
+ 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) {
+ _needsDisplayRect.Y < Bounds.Y ||
+ _needsDisplayRect.Width > Bounds.Width ||
+ _needsDisplayRect.Height > Bounds.Height) {
Margin?.SetNeedsDisplay (Margin.Bounds);
Border?.SetNeedsDisplay (Border.Bounds);
Padding?.SetNeedsDisplay (Padding.Bounds);
@@ -164,31 +188,24 @@ public partial class View {
}
}
- ///
- /// 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) {
+ SubViewNeedsDisplay = true;
+ if (_superView != null && !_superView.SubViewNeedsDisplay) {
_superView.SetSubViewNeedsDisplay ();
}
}
///
- /// Clears the with the normal background color.
+ /// Clears the with the normal background color.
///
///
- ///
- /// This clears the Bounds used by this view.
- ///
+ ///
+ /// This clears the Bounds used by this view.
+ ///
///
public void Clear ()
{
@@ -202,7 +219,7 @@ public partial class View {
// "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.
+ /// Clears the specified screen-relative rectangle with the normal background.
///
///
///
@@ -220,10 +237,10 @@ public partial class View {
// 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;
+ 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);
}
@@ -231,11 +248,15 @@ public partial class View {
///
/// Expands the 's clip region to include .
///
- /// The current screen-relative clip region, which can be then re-applied by setting .
+ ///
+ /// 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 .
- ///
+ ///
+ /// If and do not intersect, the clip region will be set to
+ /// .
+ ///
///
public Rect ClipToBounds ()
{
@@ -251,14 +272,17 @@ public partial class View {
/// 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
+ ///
+ /// 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) {
+ foreach (var rune in text) {
if (rune == hotkeySpec.Value) {
Application.Driver.SetAttribute (hotColor);
continue;
@@ -272,7 +296,10 @@ public partial class View {
/// 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.
+ ///
+ /// 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)
{
@@ -295,28 +322,15 @@ public partial class View {
return;
}
- BoundsToScreen (col, row, out int rCol, out int rRow, false);
+ 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
+ /// 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.
///
///
@@ -336,21 +350,22 @@ public partial class View {
}
///
- /// Draws the view. Causes the following virtual methods to be called (along with their related events):
+ /// 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.
- ///
+ ///
+ /// 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 ()
{
@@ -387,8 +402,9 @@ public partial class View {
// TODO: Make this cancelable
///
- /// Renders . If is true, only the of
- /// this view's subviews will be rendered. If is false (the default), this
+ /// 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.
///
///
@@ -411,7 +427,7 @@ public partial class View {
}
if (Subviews.Any (s => s.SuperViewRendersLineCanvas)) {
- foreach (var subview in Subviews.Where (s => s.SuperViewRendersLineCanvas == true)) {
+ foreach (var subview in Subviews.Where (s => s.SuperViewRendersLineCanvas)) {
// Combine the LineCanvas'
LineCanvas.Merge (subview.LineCanvas);
subview.LineCanvas.Clear ();
@@ -434,21 +450,25 @@ public partial class View {
/// 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 .
- ///
+ ///
+ /// 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.
+ /// 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
+ ///
+ /// The view-relative rectangle describing the currently visible viewport into the
+ ///
+ ///
///
- /// This method will be called before any subviews added with have been drawn.
+ /// This method will be called before any subviews added with have been drawn.
///
public virtual void OnDrawContent (Rect contentArea)
{
@@ -475,8 +495,8 @@ public partial class View {
var subviewsNeedingDraw = _subviews.Where (
view => view.Visible &&
(view.NeedsDisplay ||
- view.SubViewNeedsDisplay ||
- view.LayoutNeeded)
+ view.SubViewNeedsDisplay ||
+ view.LayoutNeeded)
);
foreach (var view in subviewsNeedingDraw) {
@@ -499,19 +519,23 @@ public partial class View {
/// 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 .
- ///
+ ///
+ /// 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
+ ///
+ /// 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.
///
diff --git a/Terminal.Gui/View/ViewSubViews.cs b/Terminal.Gui/View/ViewSubViews.cs
index 36eecb52e..6c67882d8 100644
--- a/Terminal.Gui/View/ViewSubViews.cs
+++ b/Terminal.Gui/View/ViewSubViews.cs
@@ -1,724 +1,725 @@
using System;
using System.Collections.Generic;
-using System.ComponentModel;
-using System.Linq;
-using System.Text;
-namespace Terminal.Gui {
- public partial class View {
- static readonly IList _empty = new List (0).AsReadOnly ();
+namespace Terminal.Gui;
- View _superView = null;
+public partial class View {
+ static readonly IList _empty = new List (0).AsReadOnly ();
- ///
- /// Returns the container for this view, or null if this view has not been added to a container.
- ///
- /// The super view.
- public virtual View SuperView {
- get {
- return _superView;
+ internal bool _addingView;
+
+ List _subviews; // This is null, and allocated on demand.
+
+ View _superView;
+
+ ///
+ /// Returns the container for this view, or null if this view has not been added to a container.
+ ///
+ /// The super view.
+ public virtual View SuperView {
+ get => _superView;
+ set => throw new NotImplementedException ();
+ }
+
+ ///
+ /// This returns a list of the subviews contained by this view.
+ ///
+ /// The subviews.
+ public IList Subviews => _subviews?.AsReadOnly () ?? _empty;
+
+ // Internally, we use InternalSubviews rather than subviews, as we do not expect us
+ // to make the same mistakes our users make when they poke at the Subviews.
+ internal IList InternalSubviews => _subviews ?? _empty;
+
+ ///
+ /// Returns a value indicating if this View is currently on Top (Active)
+ ///
+ public bool IsCurrentTop => Application.Current == this;
+
+ ///
+ /// Indicates whether the view was added to .
+ ///
+ public bool IsAdded { get; private set; }
+
+ ///
+ /// Event fired when this view is added to another.
+ ///
+ public event EventHandler Added;
+
+ ///
+ /// Adds a subview (child) to this view.
+ ///
+ ///
+ /// The Views that have been added to this view can be retrieved via the property.
+ /// See also
+ ///
+ public virtual void Add (View view)
+ {
+ if (view == null) {
+ return;
+ }
+ if (_subviews == null) {
+ _subviews = new List ();
+ }
+ if (_tabIndexes == null) {
+ _tabIndexes = new List ();
+ }
+ _subviews.Add (view);
+ _tabIndexes.Add (view);
+ view._superView = this;
+ if (view.CanFocus) {
+ _addingView = true;
+ if (SuperView?.CanFocus == false) {
+ SuperView._addingView = true;
+ SuperView.CanFocus = true;
+ SuperView._addingView = false;
}
- set {
- throw new NotImplementedException ();
+ CanFocus = true;
+ view._tabIndex = _tabIndexes.IndexOf (view);
+ _addingView = false;
+ }
+ if (view.Enabled && !Enabled) {
+ view._oldEnabled = true;
+ view.Enabled = false;
+ }
+
+ OnAdded (new SuperViewChangedEventArgs (this, view));
+ if (IsInitialized && !view.IsInitialized) {
+ view.BeginInit ();
+ view.EndInit ();
+ }
+
+ SetNeedsLayout ();
+ SetNeedsDisplay ();
+ }
+
+ ///
+ /// Adds the specified views (children) to the view.
+ ///
+ /// Array of one or more views (can be optional parameter).
+ ///
+ /// The Views that have been added to this view can be retrieved via the property.
+ /// See also
+ ///
+ public void Add (params View [] views)
+ {
+ if (views == null) {
+ return;
+ }
+ foreach (var view in views) {
+ Add (view);
+ }
+ }
+
+ ///
+ /// Method invoked when a subview is being added to this view.
+ ///
+ /// Event where is the subview being added.
+ public virtual void OnAdded (SuperViewChangedEventArgs e)
+ {
+ var view = e.Child;
+ view.IsAdded = true;
+ view.OnResizeNeeded ();
+ view.Added?.Invoke (this, e);
+ }
+
+ ///
+ /// Event fired when this view is removed from another.
+ ///
+ public event EventHandler Removed;
+
+ ///
+ /// Removes all subviews (children) added via or from this View.
+ ///
+ public virtual void RemoveAll ()
+ {
+ if (_subviews == null) {
+ return;
+ }
+
+ while (_subviews.Count > 0) {
+ Remove (_subviews [0]);
+ }
+ }
+
+ ///
+ /// Removes a subview added via or from this View.
+ ///
+ ///
+ ///
+ public virtual void Remove (View view)
+ {
+ if (view == null || _subviews == null) {
+ return;
+ }
+
+ var touched = view.Frame;
+ _subviews.Remove (view);
+ _tabIndexes.Remove (view);
+ view._superView = null;
+ view._tabIndex = -1;
+ SetNeedsLayout ();
+ SetNeedsDisplay ();
+
+ foreach (var v in _subviews) {
+ if (v.Frame.IntersectsWith (touched)) {
+ view.SetNeedsDisplay ();
+ }
+ }
+ OnRemoved (new SuperViewChangedEventArgs (this, view));
+ if (Focused == view) {
+ Focused = null;
+ }
+ }
+
+ ///
+ /// Method invoked when a subview is being removed from this view.
+ ///
+ /// Event args describing the subview being removed.
+ public virtual void OnRemoved (SuperViewChangedEventArgs e)
+ {
+ var view = e.Child;
+ view.IsAdded = false;
+ view.Removed?.Invoke (this, e);
+ }
+
+
+ void PerformActionForSubview (View subview, Action action)
+ {
+ if (_subviews.Contains (subview)) {
+ action (subview);
+ }
+
+ SetNeedsDisplay ();
+ subview.SetNeedsDisplay ();
+ }
+
+ ///
+ /// Brings the specified subview to the front so it is drawn on top of any other views.
+ ///
+ /// The subview to send to the front
+ ///
+ /// .
+ ///
+ public void BringSubviewToFront (View subview) => PerformActionForSubview (subview, x => {
+ _subviews.Remove (x);
+ _subviews.Add (x);
+ });
+
+ ///
+ /// Sends the specified subview to the front so it is the first view drawn
+ ///
+ /// The subview to send to the front
+ ///
+ /// .
+ ///
+ public void SendSubviewToBack (View subview) => PerformActionForSubview (subview, x => {
+ _subviews.Remove (x);
+ _subviews.Insert (0, subview);
+ });
+
+ ///
+ /// Moves the subview backwards in the hierarchy, only one step
+ ///
+ /// The subview to send backwards
+ ///
+ /// If you want to send the view all the way to the back use SendSubviewToBack.
+ ///
+ public void SendSubviewBackwards (View subview) => PerformActionForSubview (subview, x => {
+ var idx = _subviews.IndexOf (x);
+ if (idx > 0) {
+ _subviews.Remove (x);
+ _subviews.Insert (idx - 1, x);
+ }
+ });
+
+ ///
+ /// Moves the subview backwards in the hierarchy, only one step
+ ///
+ /// The subview to send backwards
+ ///
+ /// If you want to send the view all the way to the back use SendSubviewToBack.
+ ///
+ public void BringSubviewForward (View subview) => PerformActionForSubview (subview, x => {
+ var idx = _subviews.IndexOf (x);
+ if (idx + 1 < _subviews.Count) {
+ _subviews.Remove (x);
+ _subviews.Insert (idx + 1, x);
+ }
+ });
+
+ ///
+ /// Get the top superview of a given .
+ ///
+ /// The superview view.
+ public View GetTopSuperView (View view = null, View superview = null)
+ {
+ var top = superview ?? Application.Top;
+ for (var v = view?.SuperView ?? this?.SuperView; v != null; v = v.SuperView) {
+ top = v;
+ if (top == superview) {
+ break;
}
}
- List _subviews; // This is null, and allocated on demand.
- ///
- /// This returns a list of the subviews contained by this view.
- ///
- /// The subviews.
- public IList Subviews => _subviews?.AsReadOnly () ?? _empty;
+ return top;
+ }
- // Internally, we use InternalSubviews rather than subviews, as we do not expect us
- // to make the same mistakes our users make when they poke at the Subviews.
- internal IList InternalSubviews => _subviews ?? _empty;
- ///
- /// Returns a value indicating if this View is currently on Top (Active)
- ///
- public bool IsCurrentTop => Application.Current == this;
- ///
- /// Event fired when this view is added to another.
- ///
- public event EventHandler Added;
+ #region Focus
+ internal enum Direction {
+ Forward,
+ Backward
+ }
- internal bool _addingView;
+ ///
+ /// Event fired when the view gets focus.
+ ///
+ public event EventHandler Enter;
- ///
- /// Adds a subview (child) to this view.
- ///
- ///
- /// The Views that have been added to this view can be retrieved via the property.
- /// See also
- ///
- public virtual void Add (View view)
- {
- if (view == null) {
- return;
+ ///
+ /// Event fired when the view looses focus.
+ ///
+ public event EventHandler Leave;
+
+ Direction _focusDirection;
+
+ internal Direction FocusDirection {
+ get => SuperView?.FocusDirection ?? _focusDirection;
+ set {
+ if (SuperView != null) {
+ SuperView.FocusDirection = value;
+ } else {
+ _focusDirection = value;
}
- if (_subviews == null) {
- _subviews = new List ();
+ }
+ }
+
+
+ // BUGBUG: v2 - Seems weird that this is in View and not Responder.
+ bool _hasFocus;
+
+ ///
+ public override bool HasFocus => _hasFocus;
+
+ void SetHasFocus (bool value, View view, bool force = false)
+ {
+ if (_hasFocus != value || force) {
+ _hasFocus = value;
+ if (value) {
+ OnEnter (view);
+ } else {
+ OnLeave (view);
}
- if (_tabIndexes == null) {
- _tabIndexes = new List ();
+ SetNeedsDisplay ();
+ }
+
+ // Remove focus down the chain of subviews if focus is removed
+ if (!value && Focused != null) {
+ var f = Focused;
+ f.OnLeave (view);
+ f.SetHasFocus (false, view);
+ Focused = null;
+ }
+ }
+
+ ///
+ /// Event fired when the value is being changed.
+ ///
+ public event EventHandler CanFocusChanged;
+
+ ///
+ public override void OnCanFocusChanged () => CanFocusChanged?.Invoke (this, EventArgs.Empty);
+
+ bool _oldCanFocus;
+
+ ///
+ public override bool CanFocus {
+ get => base.CanFocus;
+ set {
+ if (!_addingView && IsInitialized && SuperView?.CanFocus == false && value) {
+ throw new InvalidOperationException ("Cannot set CanFocus to true if the SuperView CanFocus is false!");
}
- _subviews.Add (view);
- _tabIndexes.Add (view);
- view._superView = this;
- if (view.CanFocus) {
- _addingView = true;
- if (SuperView?.CanFocus == false) {
- SuperView._addingView = true;
+ if (base.CanFocus != value) {
+ base.CanFocus = value;
+
+ switch (value) {
+ case false when _tabIndex > -1:
+ TabIndex = -1;
+ break;
+ case true when SuperView?.CanFocus == false && _addingView:
SuperView.CanFocus = true;
- SuperView._addingView = false;
- }
- CanFocus = true;
- view._tabIndex = _tabIndexes.IndexOf (view);
- _addingView = false;
- }
- if (view.Enabled && !Enabled) {
- view._oldEnabled = true;
- view.Enabled = false;
- }
-
- OnAdded (new SuperViewChangedEventArgs (this, view));
- if (IsInitialized && !view.IsInitialized) {
- view.BeginInit ();
- view.EndInit ();
- }
-
- SetNeedsLayout ();
- SetNeedsDisplay ();
- }
-
- ///
- /// Adds the specified views (children) to the view.
- ///
- /// Array of one or more views (can be optional parameter).
- ///
- /// The Views that have been added to this view can be retrieved via the property.
- /// See also
- ///
- public void Add (params View [] views)
- {
- if (views == null) {
- return;
- }
- foreach (var view in views) {
- Add (view);
- }
- }
-
- ///
- /// Method invoked when a subview is being added to this view.
- ///
- /// Event where is the subview being added.
- public virtual void OnAdded (SuperViewChangedEventArgs e)
- {
- var view = e.Child;
- view.IsAdded = true;
- view.OnResizeNeeded ();
- view.Added?.Invoke (this, e);
- }
-
- ///
- /// Indicates whether the view was added to .
- ///
- public bool IsAdded { get; private set; }
-
- ///
- /// Event fired when this view is removed from another.
- ///
- public event EventHandler Removed;
-
- ///
- /// Removes all subviews (children) added via or from this View.
- ///
- public virtual void RemoveAll ()
- {
- if (_subviews == null) {
- return;
- }
-
- while (_subviews.Count > 0) {
- Remove (_subviews [0]);
- }
- }
-
- ///
- /// Removes a subview added via or from this View.
- ///
- ///
- ///
- public virtual void Remove (View view)
- {
- if (view == null || _subviews == null) return;
-
- var touched = view.Frame;
- _subviews.Remove (view);
- _tabIndexes.Remove (view);
- view._superView = null;
- view._tabIndex = -1;
- SetNeedsLayout ();
- SetNeedsDisplay ();
-
- foreach (var v in _subviews) {
- if (v.Frame.IntersectsWith (touched))
- view.SetNeedsDisplay ();
- }
- OnRemoved (new SuperViewChangedEventArgs (this, view));
- if (_focused == view) {
- _focused = null;
- }
- }
-
- ///
- /// Method invoked when a subview is being removed from this view.
- ///
- /// Event args describing the subview being removed.
- public virtual void OnRemoved (SuperViewChangedEventArgs e)
- {
- var view = e.Child;
- view.IsAdded = false;
- view.Removed?.Invoke (this, e);
- }
-
-
- void PerformActionForSubview (View subview, Action action)
- {
- if (_subviews.Contains (subview)) {
- action (subview);
- }
-
- SetNeedsDisplay ();
- subview.SetNeedsDisplay ();
- }
-
- ///
- /// Brings the specified subview to the front so it is drawn on top of any other views.
- ///
- /// The subview to send to the front
- ///
- /// .
- ///
- public void BringSubviewToFront (View subview)
- {
- PerformActionForSubview (subview, x => {
- _subviews.Remove (x);
- _subviews.Add (x);
- });
- }
-
- ///
- /// Sends the specified subview to the front so it is the first view drawn
- ///
- /// The subview to send to the front
- ///
- /// .
- ///
- public void SendSubviewToBack (View subview)
- {
- PerformActionForSubview (subview, x => {
- _subviews.Remove (x);
- _subviews.Insert (0, subview);
- });
- }
-
- ///
- /// Moves the subview backwards in the hierarchy, only one step
- ///
- /// The subview to send backwards
- ///
- /// If you want to send the view all the way to the back use SendSubviewToBack.
- ///
- public void SendSubviewBackwards (View subview)
- {
- PerformActionForSubview (subview, x => {
- var idx = _subviews.IndexOf (x);
- if (idx > 0) {
- _subviews.Remove (x);
- _subviews.Insert (idx - 1, x);
- }
- });
- }
-
- ///
- /// Moves the subview backwards in the hierarchy, only one step
- ///
- /// The subview to send backwards
- ///
- /// If you want to send the view all the way to the back use SendSubviewToBack.
- ///
- public void BringSubviewForward (View subview)
- {
- PerformActionForSubview (subview, x => {
- var idx = _subviews.IndexOf (x);
- if (idx + 1 < _subviews.Count) {
- _subviews.Remove (x);
- _subviews.Insert (idx + 1, x);
- }
- });
- }
-
- ///
- /// Get the top superview of a given .
- ///
- /// The superview view.
- public View GetTopSuperView (View view = null, View superview = null)
- {
- View top = superview ?? Application.Top;
- for (var v = view?.SuperView ?? (this?.SuperView); v != null; v = v.SuperView) {
- top = v;
- if (top == superview) {
break;
}
- }
- return top;
- }
-
-
-
- #region Focus
- View _focused = null;
-
- internal enum Direction {
- Forward,
- Backward
- }
-
- ///
- /// Event fired when the view gets focus.
- ///
- public event EventHandler Enter;
-
- ///
- /// Event fired when the view looses focus.
- ///
- public event EventHandler Leave;
-
- Direction _focusDirection;
- internal Direction FocusDirection {
- get => SuperView?.FocusDirection ?? _focusDirection;
- set {
- if (SuperView != null)
- SuperView.FocusDirection = value;
- else
- _focusDirection = value;
- }
- }
-
-
- // BUGBUG: v2 - Seems weird that this is in View and not Responder.
- bool _hasFocus;
-
- ///
- public override bool HasFocus => _hasFocus;
-
- void SetHasFocus (bool value, View view, bool force = false)
- {
- if (_hasFocus != value || force) {
- _hasFocus = value;
- if (value) {
- OnEnter (view);
- } else {
- OnLeave (view);
+ if (value && _tabIndex == -1) {
+ TabIndex = SuperView != null ? SuperView._tabIndexes.IndexOf (this) : -1;
}
+ TabStop = value;
+
+ if (!value && SuperView?.Focused == this) {
+ SuperView.Focused = null;
+ }
+ if (!value && HasFocus) {
+ SetHasFocus (false, this);
+ SuperView?.EnsureFocus ();
+ if (SuperView != null && SuperView.Focused == null) {
+ SuperView.FocusNext ();
+ if (SuperView.Focused == null && Application.Current != null) {
+ Application.Current.FocusNext ();
+ }
+ Application.BringOverlappedTopToFront ();
+ }
+ }
+ if (_subviews != null && IsInitialized) {
+ foreach (var view in _subviews) {
+ if (view.CanFocus != value) {
+ if (!value) {
+ view._oldCanFocus = view.CanFocus;
+ view._oldTabIndex = view._tabIndex;
+ view.CanFocus = false;
+ view._tabIndex = -1;
+ } else {
+ if (_addingView) {
+ view._addingView = true;
+ }
+ view.CanFocus = view._oldCanFocus;
+ view._tabIndex = view._oldTabIndex;
+ view._addingView = false;
+ }
+ }
+ }
+ }
+ OnCanFocusChanged ();
SetNeedsDisplay ();
}
+ }
+ }
- // Remove focus down the chain of subviews if focus is removed
- if (!value && _focused != null) {
- var f = _focused;
- f.OnLeave (view);
- f.SetHasFocus (false, view);
- _focused = null;
- }
+
+ ///
+ public override bool OnEnter (View view)
+ {
+ var args = new FocusEventArgs (view);
+ Enter?.Invoke (this, args);
+ if (args.Handled) {
+ return true;
+ }
+ if (base.OnEnter (view)) {
+ return true;
}
- ///
- /// Event fired when the value is being changed.
- ///
- public event EventHandler CanFocusChanged;
+ return false;
+ }
- ///
- public override void OnCanFocusChanged () => CanFocusChanged?.Invoke (this, EventArgs.Empty);
-
- bool _oldCanFocus;
- ///
- public override bool CanFocus {
- get => base.CanFocus;
- set {
- if (!_addingView && IsInitialized && SuperView?.CanFocus == false && value) {
- throw new InvalidOperationException ("Cannot set CanFocus to true if the SuperView CanFocus is false!");
- }
- if (base.CanFocus != value) {
- base.CanFocus = value;
-
- switch (value) {
- case false when _tabIndex > -1:
- TabIndex = -1;
- break;
- case true when SuperView?.CanFocus == false && _addingView:
- SuperView.CanFocus = true;
- break;
- }
-
- if (value && _tabIndex == -1) {
- TabIndex = SuperView != null ? SuperView._tabIndexes.IndexOf (this) : -1;
- }
- TabStop = value;
-
- if (!value && SuperView?.Focused == this) {
- SuperView._focused = null;
- }
- if (!value && HasFocus) {
- SetHasFocus (false, this);
- SuperView?.EnsureFocus ();
- if (SuperView != null && SuperView.Focused == null) {
- SuperView.FocusNext ();
- if (SuperView.Focused == null && Application.Current != null) {
- Application.Current.FocusNext ();
- }
- Application.BringOverlappedTopToFront ();
- }
- }
- if (_subviews != null && IsInitialized) {
- foreach (var view in _subviews) {
- if (view.CanFocus != value) {
- if (!value) {
- view._oldCanFocus = view.CanFocus;
- view._oldTabIndex = view._tabIndex;
- view.CanFocus = false;
- view._tabIndex = -1;
- } else {
- if (_addingView) {
- view._addingView = true;
- }
- view.CanFocus = view._oldCanFocus;
- view._tabIndex = view._oldTabIndex;
- view._addingView = false;
- }
- }
- }
- }
- OnCanFocusChanged ();
- SetNeedsDisplay ();
- }
- }
+ ///
+ public override bool OnLeave (View view)
+ {
+ var args = new FocusEventArgs (view);
+ Leave?.Invoke (this, args);
+ if (args.Handled) {
+ return true;
+ }
+ if (base.OnLeave (view)) {
+ return true;
}
+ Driver?.SetCursorVisibility (CursorVisibility.Invisible);
+ return false;
+ }
- ///
- public override bool OnEnter (View view)
- {
- var args = new FocusEventArgs (view);
- Enter?.Invoke (this, args);
- if (args.Handled) {
- return true;
- }
- if (base.OnEnter (view)) {
- return true;
- }
+ ///
+ /// Returns the currently focused view inside this view, or null if nothing is focused.
+ ///
+ /// The focused.
+ public View Focused { get; private set; }
- return false;
- }
-
- ///
- public override bool OnLeave (View view)
- {
- var args = new FocusEventArgs (view);
- Leave?.Invoke (this, args);
- if (args.Handled) {
- return true;
- }
- if (base.OnLeave (view)) {
- return true;
- }
-
- Driver?.SetCursorVisibility (CursorVisibility.Invisible);
- return false;
- }
-
- ///
- /// Returns the currently focused view inside this view, or null if nothing is focused.
- ///
- /// The focused.
- public View Focused => _focused;
-
- ///
- /// Returns the most focused view in the chain of subviews (the leaf view that has the focus).
- ///
- /// The most focused View.
- public View MostFocused {
- get {
- if (Focused == null)
- return null;
- var most = Focused.MostFocused;
- if (most != null)
- return most;
- return Focused;
- }
- }
-
- ///
- /// Causes the specified subview to have focus.
- ///
- /// View.
- void SetFocus (View view)
- {
- if (view == null) {
- return;
- }
- //Console.WriteLine ($"Request to focus {view}");
- if (!view.CanFocus || !view.Visible || !view.Enabled) {
- return;
- }
- if (_focused?._hasFocus == true && _focused == view) {
- return;
- }
- if ((_focused?._hasFocus == true && _focused?.SuperView == view) || view == this) {
-
- if (!view._hasFocus) {
- view._hasFocus = true;
- }
- return;
- }
- // Make sure that this view is a subview
- View c;
- for (c = view._superView; c != null; c = c._superView)
- if (c == this)
- break;
- if (c == null)
- throw new ArgumentException ("the specified view is not part of the hierarchy of this view");
-
- if (_focused != null)
- _focused.SetHasFocus (false, view);
-
- var f = _focused;
- _focused = view;
- _focused.SetHasFocus (true, f);
- _focused.EnsureFocus ();
-
- // Send focus upwards
- if (SuperView != null) {
- SuperView.SetFocus (this);
- } else {
- SetFocus (this);
- }
- }
-
- ///
- /// Causes the specified view and the entire parent hierarchy to have the focused order updated.
- ///
- public void SetFocus ()
- {
- if (!CanBeVisible (this) || !Enabled) {
- if (HasFocus) {
- SetHasFocus (false, this);
- }
- return;
- }
-
- if (SuperView != null) {
- SuperView.SetFocus (this);
- } else {
- SetFocus (this);
- }
- }
-
- ///
- /// Finds the first view in the hierarchy that wants to get the focus if nothing is currently focused, otherwise, does nothing.
- ///
- public void EnsureFocus ()
- {
- if (_focused == null && _subviews?.Count > 0) {
- if (FocusDirection == Direction.Forward) {
- FocusFirst ();
- } else {
- FocusLast ();
- }
- }
- }
-
- ///
- /// Focuses the first focusable subview if one exists.
- ///
- public void FocusFirst ()
- {
- if (!CanBeVisible (this)) {
- return;
- }
-
- if (_tabIndexes == null) {
- SuperView?.SetFocus (this);
- return;
- }
-
- foreach (var view in _tabIndexes) {
- if (view.CanFocus && view._tabStop && view.Visible && view.Enabled) {
- SetFocus (view);
- return;
- }
- }
- }
-
- ///
- /// Focuses the last focusable subview if one exists.
- ///
- public void FocusLast ()
- {
- if (!CanBeVisible (this)) {
- return;
- }
-
- if (_tabIndexes == null) {
- SuperView?.SetFocus (this);
- return;
- }
-
- for (var i = _tabIndexes.Count; i > 0;) {
- i--;
-
- var v = _tabIndexes [i];
- if (v.CanFocus && v._tabStop && v.Visible && v.Enabled) {
- SetFocus (v);
- return;
- }
- }
- }
-
- ///
- /// Focuses the previous view.
- ///
- /// if previous was focused, otherwise.
- public bool FocusPrev ()
- {
- if (!CanBeVisible (this)) {
- return false;
- }
-
- FocusDirection = Direction.Backward;
- if (_tabIndexes == null || _tabIndexes.Count == 0)
- return false;
-
- if (_focused == null) {
- FocusLast ();
- return _focused != null;
- }
-
- var focusedIdx = -1;
- for (var i = _tabIndexes.Count; i > 0;) {
- i--;
- var w = _tabIndexes [i];
-
- if (w.HasFocus) {
- if (w.FocusPrev ())
- return true;
- focusedIdx = i;
- continue;
- }
- if (w.CanFocus && focusedIdx != -1 && w._tabStop && w.Visible && w.Enabled) {
- _focused.SetHasFocus (false, w);
-
- if (w.CanFocus && w._tabStop && w.Visible && w.Enabled)
- w.FocusLast ();
-
- SetFocus (w);
- return true;
- }
- }
- if (_focused != null) {
- _focused.SetHasFocus (false, this);
- _focused = null;
- }
- return false;
- }
-
- ///
- /// Focuses the next view.
- ///
- /// if next was focused, otherwise.
- public bool FocusNext ()
- {
- if (!CanBeVisible (this)) {
- return false;
- }
-
- FocusDirection = Direction.Forward;
- if (_tabIndexes == null || _tabIndexes.Count == 0)
- return false;
-
- if (_focused == null) {
- FocusFirst ();
- return _focused != null;
- }
- var focusedIdx = -1;
- for (var i = 0; i < _tabIndexes.Count; i++) {
- var w = _tabIndexes [i];
-
- if (w.HasFocus) {
- if (w.FocusNext ())
- return true;
- focusedIdx = i;
- continue;
- }
- if (w.CanFocus && focusedIdx != -1 && w._tabStop && w.Visible && w.Enabled) {
- _focused.SetHasFocus (false, w);
-
- if (w.CanFocus && w._tabStop && w.Visible && w.Enabled)
- w.FocusFirst ();
-
- SetFocus (w);
- return true;
- }
- }
- if (_focused != null) {
- _focused.SetHasFocus (false, this);
- _focused = null;
- }
- return false;
- }
-
- View GetMostFocused (View view)
- {
- if (view == null) {
+ ///
+ /// Returns the most focused view in the chain of subviews (the leaf view that has the focus).
+ ///
+ /// The most focused View.
+ public View MostFocused {
+ get {
+ if (Focused == null) {
return null;
}
+ var most = Focused.MostFocused;
+ if (most != null) {
+ return most;
+ }
+ return Focused;
+ }
+ }
- return view._focused != null ? GetMostFocused (view._focused) : view;
+ ///
+ /// Causes the specified subview to have focus.
+ ///
+ /// View.
+ void SetFocus (View view)
+ {
+ if (view == null) {
+ return;
+ }
+ //Console.WriteLine ($"Request to focus {view}");
+ if (!view.CanFocus || !view.Visible || !view.Enabled) {
+ return;
+ }
+ if (Focused?._hasFocus == true && Focused == view) {
+ return;
+ }
+ if (Focused?._hasFocus == true && Focused?.SuperView == view || view == this) {
+
+ if (!view._hasFocus) {
+ view._hasFocus = true;
+ }
+ return;
+ }
+ // Make sure that this view is a subview
+ View c;
+ for (c = view._superView; c != null; c = c._superView) {
+ if (c == this) {
+ break;
+ }
+ }
+ if (c == null) {
+ throw new ArgumentException ("the specified view is not part of the hierarchy of this view");
}
- ///
- /// Positions the cursor in the right position based on the currently focused view in the chain.
- ///
- /// Views that are focusable should override to ensure
- /// 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. Views should make sure that they place the cursor
- /// in a visually sensible place.
- public virtual void PositionCursor ()
- {
- if (!CanBeVisible (this) || !Enabled) {
+ if (Focused != null) {
+ Focused.SetHasFocus (false, view);
+ }
+
+ var f = Focused;
+ Focused = view;
+ Focused.SetHasFocus (true, f);
+ Focused.EnsureFocus ();
+
+ // Send focus upwards
+ if (SuperView != null) {
+ SuperView.SetFocus (this);
+ } else {
+ SetFocus (this);
+ }
+ }
+
+ ///
+ /// Causes the specified view and the entire parent hierarchy to have the focused order updated.
+ ///
+ public void SetFocus ()
+ {
+ if (!CanBeVisible (this) || !Enabled) {
+ if (HasFocus) {
+ SetHasFocus (false, this);
+ }
+ return;
+ }
+
+ if (SuperView != null) {
+ SuperView.SetFocus (this);
+ } else {
+ SetFocus (this);
+ }
+ }
+
+ ///
+ /// Finds the first view in the hierarchy that wants to get the focus if nothing is currently focused, otherwise, does
+ /// nothing.
+ ///
+ public void EnsureFocus ()
+ {
+ if (Focused == null && _subviews?.Count > 0) {
+ if (FocusDirection == Direction.Forward) {
+ FocusFirst ();
+ } else {
+ FocusLast ();
+ }
+ }
+ }
+
+ ///
+ /// Focuses the first focusable subview if one exists.
+ ///
+ public void FocusFirst ()
+ {
+ if (!CanBeVisible (this)) {
+ return;
+ }
+
+ if (_tabIndexes == null) {
+ SuperView?.SetFocus (this);
+ return;
+ }
+
+ foreach (var view in _tabIndexes) {
+ if (view.CanFocus && view._tabStop && view.Visible && view.Enabled) {
+ SetFocus (view);
return;
}
+ }
+ }
- // BUGBUG: v2 - This needs to support children of Frames too
+ ///
+ /// Focuses the last focusable subview if one exists.
+ ///
+ public void FocusLast ()
+ {
+ if (!CanBeVisible (this)) {
+ return;
+ }
- if (_focused == null && SuperView != null) {
- SuperView.EnsureFocus ();
- } else if (_focused?.Visible == true && _focused?.Enabled == true && _focused?.Frame.Width > 0 && _focused.Frame.Height > 0) {
- _focused.PositionCursor ();
- } else if (_focused?.Visible == true && _focused?.Enabled == false) {
- _focused = null;
- } else if (CanFocus && HasFocus && Visible && Frame.Width > 0 && Frame.Height > 0) {
- Move (TextFormatter.HotKeyPos == -1 ? 0 : TextFormatter.CursorPosition, 0);
- } else {
- Move (_frame.X, _frame.Y);
+ if (_tabIndexes == null) {
+ SuperView?.SetFocus (this);
+ return;
+ }
+
+ for (var i = _tabIndexes.Count; i > 0;) {
+ i--;
+
+ var v = _tabIndexes [i];
+ if (v.CanFocus && v._tabStop && v.Visible && v.Enabled) {
+ SetFocus (v);
+ return;
}
}
- #endregion Focus
}
-}
+
+ ///
+ /// Focuses the previous view.
+ ///
+ /// if previous was focused, otherwise.
+ public bool FocusPrev ()
+ {
+ if (!CanBeVisible (this)) {
+ return false;
+ }
+
+ FocusDirection = Direction.Backward;
+ if (_tabIndexes == null || _tabIndexes.Count == 0) {
+ return false;
+ }
+
+ if (Focused == null) {
+ FocusLast ();
+ return Focused != null;
+ }
+
+ var focusedIdx = -1;
+ for (var i = _tabIndexes.Count; i > 0;) {
+ i--;
+ var w = _tabIndexes [i];
+
+ if (w.HasFocus) {
+ if (w.FocusPrev ()) {
+ return true;
+ }
+ focusedIdx = i;
+ continue;
+ }
+ if (w.CanFocus && focusedIdx != -1 && w._tabStop && w.Visible && w.Enabled) {
+ Focused.SetHasFocus (false, w);
+
+ if (w.CanFocus && w._tabStop && w.Visible && w.Enabled) {
+ w.FocusLast ();
+ }
+
+ SetFocus (w);
+ return true;
+ }
+ }
+ if (Focused != null) {
+ Focused.SetHasFocus (false, this);
+ Focused = null;
+ }
+ return false;
+ }
+
+ ///
+ /// Focuses the next view.
+ ///
+ /// if next was focused, otherwise.
+ public bool FocusNext ()
+ {
+ if (!CanBeVisible (this)) {
+ return false;
+ }
+
+ FocusDirection = Direction.Forward;
+ if (_tabIndexes == null || _tabIndexes.Count == 0) {
+ return false;
+ }
+
+ if (Focused == null) {
+ FocusFirst ();
+ return Focused != null;
+ }
+ var focusedIdx = -1;
+ for (var i = 0; i < _tabIndexes.Count; i++) {
+ var w = _tabIndexes [i];
+
+ if (w.HasFocus) {
+ if (w.FocusNext ()) {
+ return true;
+ }
+ focusedIdx = i;
+ continue;
+ }
+ if (w.CanFocus && focusedIdx != -1 && w._tabStop && w.Visible && w.Enabled) {
+ Focused.SetHasFocus (false, w);
+
+ if (w.CanFocus && w._tabStop && w.Visible && w.Enabled) {
+ w.FocusFirst ();
+ }
+
+ SetFocus (w);
+ return true;
+ }
+ }
+ if (Focused != null) {
+ Focused.SetHasFocus (false, this);
+ Focused = null;
+ }
+ return false;
+ }
+
+ View GetMostFocused (View view)
+ {
+ if (view == null) {
+ return null;
+ }
+
+ return view.Focused != null ? GetMostFocused (view.Focused) : view;
+ }
+
+ ///
+ /// Positions the cursor in the right position based on the currently focused view in the chain.
+ ///
+ /// Views that are focusable should override
+ ///
+ /// to ensure
+ /// 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. Views should make sure that they place the cursor
+ /// in a visually sensible place.
+ public virtual void PositionCursor ()
+ {
+ if (!CanBeVisible (this) || !Enabled) {
+ return;
+ }
+
+ // BUGBUG: v2 - This needs to support children of Frames too
+
+ if (Focused == null && SuperView != null) {
+ SuperView.EnsureFocus ();
+ } else if (Focused?.Visible == true && Focused?.Enabled == true && Focused?.Frame.Width > 0 && Focused.Frame.Height > 0) {
+ Focused.PositionCursor ();
+ } else if (Focused?.Visible == true && Focused?.Enabled == false) {
+ Focused = null;
+ } else if (CanFocus && HasFocus && Visible && Frame.Width > 0 && Frame.Height > 0) {
+ Move (TextFormatter.HotKeyPos == -1 ? 0 : TextFormatter.CursorPosition, 0);
+ } else {
+ Move (_frame.X, _frame.Y);
+ }
+ }
+ #endregion Focus
+}
\ No newline at end of file
diff --git a/Terminal.Gui/View/ViewText.cs b/Terminal.Gui/View/ViewText.cs
index ce82df182..d8114bdc9 100644
--- a/Terminal.Gui/View/ViewText.cs
+++ b/Terminal.Gui/View/ViewText.cs
@@ -190,37 +190,43 @@ public partial class View {
}
sizeRequired = Bounds.Size;
- if (!AutoSize && !string.IsNullOrEmpty (TextFormatter.Text)) {
- switch (TextFormatter.IsVerticalDirection (TextDirection)) {
- case true:
- var 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 (AutoSize || string.IsNullOrEmpty (TextFormatter.Text)) {
+ return false;
+ }
+
+ switch (TextFormatter.IsVerticalDirection (TextDirection)) {
+ case true:
+ var 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;
}
if (GetMinimumSizeOfText (out var size)) {
+ // TODO: This is a hack.
+ //_width = size.Width;
+ //_height = size.Height;
_frame = new Rect (_frame.Location, size);
+ //throw new InvalidOperationException ("This is a hack.");
return true;
}
return false;
@@ -275,10 +281,12 @@ public partial class View {
{
if (!IsInitialized) {
TextFormatter.Size = Size.Empty;
+ return;
}
if (string.IsNullOrEmpty (TextFormatter.Text)) {
TextFormatter.Size = Bounds.Size;
+ return;
}
TextFormatter.Size = new Size (Bounds.Size.Width + GetHotKeySpecifierLength (),
@@ -299,9 +307,9 @@ public partial class View {
x = Bounds.X;
y = Bounds.Y;
}
- var rect = TextFormatter.CalcRect (x, y, TextFormatter.Text, TextFormatter.Direction);
- var newWidth = rect.Size.Width - GetHotKeySpecifierLength () + Margin.Thickness.Horizontal + Border.Thickness.Horizontal + Padding.Thickness.Horizontal;
- var newHeight = rect.Size.Height - GetHotKeySpecifierLength (false) + Margin.Thickness.Vertical + Border.Thickness.Vertical + Padding.Thickness.Vertical;
+ var rect = TextFormatter.CalcRect (x, y, TextFormatter.Text, TextFormatter.Direction);
+ int newWidth = rect.Size.Width - GetHotKeySpecifierLength () + (Margin == null ? 0 : Margin.Thickness.Horizontal + Border.Thickness.Horizontal + Padding.Thickness.Horizontal);
+ int newHeight = rect.Size.Height - GetHotKeySpecifierLength (false) + (Margin == null ? 0 : Margin.Thickness.Vertical + Border.Thickness.Vertical + Padding.Thickness.Vertical);
return new Size (newWidth, newHeight);
}
diff --git a/Terminal.Gui/Views/Label.cs b/Terminal.Gui/Views/Label.cs
index 40350e9c5..b69418564 100644
--- a/Terminal.Gui/Views/Label.cs
+++ b/Terminal.Gui/Views/Label.cs
@@ -1,141 +1,116 @@
-//
-// Label.cs: Label control
-//
-// Authors:
-// Miguel de Icaza (miguel@gnome.org)
-//
+using System;
-using System;
-using System.Text;
+namespace Terminal.Gui;
+
+///
+/// The Label displays a string at a given position and supports multiple lines separated by newline
+/// characters.
+/// Multi-line Labels support word wrap.
+///
+///
+/// The view is functionality identical to and is included for API backwards
+/// compatibility.
+///
+public class Label : View {
+ ///
+ public Label () => SetInitialProperties ();
+
+ ///
+ public Label (Rect frame, bool autosize = false) : base (frame) => SetInitialProperties (autosize);
+
+ ///
+ public Label (string text, bool autosize = true) : base (text) => SetInitialProperties (autosize);
+
+ ///
+ public Label (Rect rect, string text, bool autosize = false) : base (rect, text) => SetInitialProperties (autosize);
+
+ ///
+ public Label (int x, int y, string text, bool autosize = true) : base (x, y, text) => SetInitialProperties (autosize);
+
+ ///
+ public Label (string text, TextDirection direction, bool autosize = true)
+ : base (text, direction) => SetInitialProperties (autosize);
+
+ void SetInitialProperties (bool autosize = true)
+ {
+ Height = 1;
+ AutoSize = autosize;
+ // Things this view knows how to do
+ AddCommand (Command.Default, () => {
+ // BUGBUG: This is a hack, but it does work.
+ var can = CanFocus;
+ CanFocus = true;
+ SetFocus ();
+ SuperView.FocusNext ();
+ CanFocus = can;
+ return true;
+ });
+ AddCommand (Command.Accept, () => AcceptKey ());
+
+ // Default key bindings for this view
+ KeyBindings.Add (KeyCode.Space, Command.Accept);
+ }
+
+ bool AcceptKey ()
+ {
+ if (!HasFocus) {
+ SetFocus ();
+ }
+ OnClicked ();
+ return true;
+ }
-namespace Terminal.Gui {
///
- /// The Label displays a string at a given position and supports multiple lines separated by newline characters.
- /// Multi-line Labels support word wrap.
+ /// The event fired when the user clicks the primary mouse button within the Bounds of this
+ /// or if the user presses the action key while this view is focused. (TODO: IsDefault)
///
///
- /// The view is functionality identical to and is included for API backwards compatibility.
+ /// Client code can hook up to this event, it is
+ /// raised when the button is activated either with
+ /// the mouse or the keyboard.
///
- public class Label : View {
- ///
- public Label ()
- {
- SetInitialProperties ();
+ public event EventHandler Clicked;
+
+ ///
+ /// Method invoked when a mouse event is generated
+ ///
+ ///
+ /// true, if the event was handled, false otherwise.
+ public override bool OnMouseEvent (MouseEvent mouseEvent)
+ {
+ var args = new MouseEventEventArgs (mouseEvent);
+ if (OnMouseClick (args)) {
+ return true;
+ }
+ if (MouseEvent (mouseEvent)) {
+ return true;
}
- ///
- public Label (Rect frame, bool autosize = false) : base (frame)
- {
- SetInitialProperties (autosize);
- }
-
- ///
- public Label (string text, bool autosize = true) : base (text)
- {
- SetInitialProperties (autosize);
- }
-
- ///
- public Label (Rect rect, string text, bool autosize = false) : base (rect, text)
- {
- SetInitialProperties (autosize);
- }
-
- ///
- public Label (int x, int y, string text, bool autosize = true) : base (x, y, text)
- {
- SetInitialProperties (autosize);
- }
-
- ///
- public Label (string text, TextDirection direction, bool autosize = true)
- : base (text, direction)
- {
- SetInitialProperties (autosize);
- }
-
- void SetInitialProperties (bool autosize = true)
- {
- Height = 1;
- AutoSize = autosize;
- // Things this view knows how to do
- AddCommand (Command.Default, () => {
- // BUGBUG: This is a hack, but it does work.
- var can = CanFocus;
- CanFocus = true;
- SetFocus ();
- SuperView.FocusNext ();
- CanFocus = can;
- return true;
- });
- AddCommand (Command.Accept, () => AcceptKey ());
-
- // Default key bindings for this view
- KeyBindings.Add (KeyCode.Space, Command.Accept);
- }
-
- bool AcceptKey ()
- {
- if (!HasFocus) {
+ if (mouseEvent.Flags == MouseFlags.Button1Clicked) {
+ if (!HasFocus && SuperView != null) {
+ if (!SuperView.HasFocus) {
+ SuperView.SetFocus ();
+ }
SetFocus ();
+ SetNeedsDisplay ();
}
+
OnClicked ();
return true;
}
-
- ///
- /// The event fired when the user clicks the primary mouse button within the Bounds of this
- /// or if the user presses the action key while this view is focused. (TODO: IsDefault)
- ///
- ///
- /// Client code can hook up to this event, it is
- /// raised when the button is activated either with
- /// the mouse or the keyboard.
- ///
- public event EventHandler Clicked;
-
- ///
- /// Method invoked when a mouse event is generated
- ///
- ///
- /// true, if the event was handled, false otherwise.
- public override bool OnMouseEvent (MouseEvent mouseEvent)
- {
- MouseEventEventArgs args = new MouseEventEventArgs (mouseEvent);
- if (OnMouseClick (args))
- return true;
- if (MouseEvent (mouseEvent))
- return true;
-
- if (mouseEvent.Flags == MouseFlags.Button1Clicked) {
- if (!HasFocus && SuperView != null) {
- if (!SuperView.HasFocus) {
- SuperView.SetFocus ();
- }
- SetFocus ();
- SetNeedsDisplay ();
- }
-
- OnClicked ();
- return true;
- }
- return false;
- }
-
- ///
- public override bool OnEnter (View view)
- {
- Application.Driver.SetCursorVisibility (CursorVisibility.Invisible);
-
- return base.OnEnter (view);
- }
-
- ///
- /// Virtual method to invoke the event.
- ///
- public virtual void OnClicked ()
- {
- Clicked?.Invoke (this, EventArgs.Empty);
- }
+ return false;
}
-}
+
+ ///
+ public override bool OnEnter (View view)
+ {
+ Application.Driver.SetCursorVisibility (CursorVisibility.Invisible);
+
+ return base.OnEnter (view);
+ }
+
+ ///
+ /// Virtual method to invoke the event.
+ ///
+ public virtual void OnClicked () => Clicked?.Invoke (this, EventArgs.Empty);
+}
\ No newline at end of file
diff --git a/UICatalog/Scenarios/ListColumns.cs b/UICatalog/Scenarios/ListColumns.cs
index 96b27d7f3..42f0fad3d 100644
--- a/UICatalog/Scenarios/ListColumns.cs
+++ b/UICatalog/Scenarios/ListColumns.cs
@@ -3,331 +3,358 @@ using System.Collections;
using System.Collections.Generic;
using System.Data;
using Terminal.Gui;
-using static Terminal.Gui.TableView;
-namespace UICatalog.Scenarios {
+namespace UICatalog.Scenarios;
- [ScenarioMetadata (Name: "ListColumns", Description: "Implements a columned list via a data table.")]
- [ScenarioCategory ("TableView")]
- [ScenarioCategory ("Controls")]
- [ScenarioCategory ("Dialogs")]
- [ScenarioCategory ("Text and Formatting")]
- [ScenarioCategory ("Top Level Windows")]
- public class ListColumns : Scenario {
- TableView listColView;
- DataTable currentTable;
- private MenuItem _miCellLines;
- private MenuItem _miExpandLastColumn;
- private MenuItem _miAlwaysUseNormalColorForVerticalCellLines;
- private MenuItem _miSmoothScrolling;
- private MenuItem _miAlternatingColors;
- private MenuItem _miCursor;
- private MenuItem _miTopline;
- private MenuItem _miBottomline;
- private MenuItem _miOrientVertical;
- private MenuItem _miScrollParallel;
+[ScenarioMetadata ("ListColumns", "Implements a columned list via a data table.")]
+[ScenarioCategory ("TableView")]
+[ScenarioCategory ("Controls")]
+[ScenarioCategory ("Dialogs")]
+[ScenarioCategory ("Text and Formatting")]
+[ScenarioCategory ("Top Level Windows")]
+public class ListColumns : Scenario {
+ MenuItem _miAlternatingColors;
+ MenuItem _miAlwaysUseNormalColorForVerticalCellLines;
+ MenuItem _miBottomline;
+ MenuItem _miCellLines;
+ MenuItem _miCursor;
+ MenuItem _miExpandLastColumn;
+ MenuItem _miOrientVertical;
+ MenuItem _miScrollParallel;
+ MenuItem _miSmoothScrolling;
+ MenuItem _miTopline;
- ColorScheme alternatingColorScheme;
+ ColorScheme alternatingColorScheme;
+ DataTable currentTable;
+ TableView listColView;
- public override void Setup ()
- {
- Win.Title = this.GetName ();
- Win.Y = 1; // menu
- Win.Height = Dim.Fill (1); // status bar
+ public override void Setup ()
+ {
+ Win.Title = GetName ();
+ Win.Y = 1; // menu
+ Win.Height = Dim.Fill (1); // status bar
- this.listColView = new TableView () {
- X = 0,
- Y = 0,
- Width = Dim.Fill (),
- Height = Dim.Fill (1),
- Style = new TableStyle {
- ShowHeaders = false,
- ShowHorizontalHeaderOverline = false,
- ShowHorizontalHeaderUnderline = false,
- ShowHorizontalBottomline = false,
- ExpandLastColumn = false,
+ listColView = new TableView {
+ X = 0,
+ Y = 0,
+ Width = Dim.Fill (),
+ Height = Dim.Fill (1),
+ Style = new TableStyle {
+ ShowHeaders = false,
+ ShowHorizontalHeaderOverline = false,
+ ShowHorizontalHeaderUnderline = false,
+ ShowHorizontalBottomline = false,
+ ExpandLastColumn = false
+ }
+ };
+ var listColStyle = new ListColumnStyle ();
+
+ var menu = new MenuBar (new MenuBarItem [] {
+ new ("_File", new MenuItem [] {
+ new ("Open_BigListExample", "", () => OpenSimpleList (true)),
+ new ("Open_SmListExample", "", () => OpenSimpleList (false)),
+ new ("_CloseExample", "", () => CloseExample ()),
+ new ("_Quit", "", () => Quit ())
+ }),
+ new ("_View", new [] {
+ _miTopline = new MenuItem ("_TopLine", "", () => ToggleTopline ()) {
+ Checked = listColView.Style.ShowHorizontalHeaderOverline,
+ CheckType = MenuItemCheckStyle.Checked
+ },
+ _miBottomline = new MenuItem ("_BottomLine", "", () => ToggleBottomline ()) {
+ Checked = listColView.Style.ShowHorizontalBottomline,
+ CheckType = MenuItemCheckStyle.Checked
+ },
+ _miCellLines = new MenuItem ("_CellLines", "", () => ToggleCellLines ()) {
+ Checked = listColView.Style.ShowVerticalCellLines,
+ CheckType = MenuItemCheckStyle.Checked
+ },
+ _miExpandLastColumn = new MenuItem ("_ExpandLastColumn", "", () => ToggleExpandLastColumn ()) {
+ Checked = listColView.Style.ExpandLastColumn,
+ CheckType = MenuItemCheckStyle.Checked
+ },
+ _miAlwaysUseNormalColorForVerticalCellLines =
+ new MenuItem ("_AlwaysUseNormalColorForVerticalCellLines", "",
+ () => ToggleAlwaysUseNormalColorForVerticalCellLines ()) {
+ Checked = listColView.Style.AlwaysUseNormalColorForVerticalCellLines,
+ CheckType = MenuItemCheckStyle.Checked
+ },
+ _miSmoothScrolling = new MenuItem ("_SmoothHorizontalScrolling", "", () => ToggleSmoothScrolling ()) {
+ Checked = listColView.Style.SmoothHorizontalScrolling,
+ CheckType = MenuItemCheckStyle.Checked
+ },
+ _miAlternatingColors = new MenuItem ("Alternating Colors", "", () => ToggleAlternatingColors ())
+ { CheckType = MenuItemCheckStyle.Checked },
+ _miCursor = new MenuItem ("Invert Selected Cell First Character", "",
+ () => ToggleInvertSelectedCellFirstCharacter ()) {
+ Checked = listColView.Style.InvertSelectedCellFirstCharacter,
+ CheckType = MenuItemCheckStyle.Checked
}
- };
- var listColStyle = new ListColumnStyle ();
+ }),
+ new ("_List", new [] {
+ //new MenuItem ("_Hide Headers", "", HideHeaders),
+ _miOrientVertical = new MenuItem ("_OrientVertical", "", () => ToggleVerticalOrientation ()) {
+ Checked = listColStyle.Orientation == Orientation.Vertical,
+ CheckType = MenuItemCheckStyle.Checked
+ },
+ _miScrollParallel = new MenuItem ("_ScrollParallel", "", () => ToggleScrollParallel ())
+ { Checked = listColStyle.ScrollParallel, CheckType = MenuItemCheckStyle.Checked },
+ new ("Set _Max Cell Width", "", SetListMaxWidth),
+ new ("Set Mi_n Cell Width", "", SetListMinWidth)
+ })
+ });
- var menu = new MenuBar (new MenuBarItem [] {
- new MenuBarItem ("_File", new MenuItem [] {
- new MenuItem ("Open_BigListExample", "", () => OpenSimpleList (true)),
- new MenuItem ("Open_SmListExample", "", () => OpenSimpleList (false)),
- new MenuItem ("_CloseExample", "", () => CloseExample ()),
- new MenuItem ("_Quit", "", () => Quit()),
- }),
- new MenuBarItem ("_View", new MenuItem [] {
- _miTopline = new MenuItem ("_TopLine", "", () => ToggleTopline ()) { Checked = listColView.Style.ShowHorizontalHeaderOverline, CheckType = MenuItemCheckStyle.Checked },
- _miBottomline = new MenuItem ("_BottomLine", "", () => ToggleBottomline ()) { Checked = listColView.Style.ShowHorizontalBottomline, CheckType = MenuItemCheckStyle.Checked },
- _miCellLines = new MenuItem ("_CellLines", "", () => ToggleCellLines ()) { Checked = listColView.Style.ShowVerticalCellLines, CheckType = MenuItemCheckStyle.Checked },
- _miExpandLastColumn = new MenuItem ("_ExpandLastColumn", "", () => ToggleExpandLastColumn ()) { Checked = listColView.Style.ExpandLastColumn, CheckType = MenuItemCheckStyle.Checked },
- _miAlwaysUseNormalColorForVerticalCellLines = new MenuItem ("_AlwaysUseNormalColorForVerticalCellLines", "", () => ToggleAlwaysUseNormalColorForVerticalCellLines ()) { Checked = listColView.Style.AlwaysUseNormalColorForVerticalCellLines, CheckType = MenuItemCheckStyle.Checked },
- _miSmoothScrolling = new MenuItem ("_SmoothHorizontalScrolling", "", () => ToggleSmoothScrolling ()) { Checked = listColView.Style.SmoothHorizontalScrolling, CheckType = MenuItemCheckStyle.Checked },
- _miAlternatingColors = new MenuItem ("Alternating Colors", "", () => ToggleAlternatingColors ()) { CheckType = MenuItemCheckStyle.Checked},
- _miCursor = new MenuItem ("Invert Selected Cell First Character", "", () => ToggleInvertSelectedCellFirstCharacter ()) { Checked = listColView.Style.InvertSelectedCellFirstCharacter,CheckType = MenuItemCheckStyle.Checked},
- }),
- new MenuBarItem ("_List", new MenuItem [] {
- //new MenuItem ("_Hide Headers", "", HideHeaders),
- _miOrientVertical = new MenuItem ("_OrientVertical", "", () => ToggleVerticalOrientation ()) { Checked = listColStyle.Orientation == Orientation.Vertical, CheckType = MenuItemCheckStyle.Checked },
- _miScrollParallel = new MenuItem ("_ScrollParallel", "", () => ToggleScrollParallel ()) { Checked = listColStyle.ScrollParallel, CheckType = MenuItemCheckStyle.Checked },
- new MenuItem ("Set _Max Cell Width", "", SetListMaxWidth),
- new MenuItem ("Set Mi_n Cell Width", "", SetListMinWidth),
- }),
- });
+ Application.Top.Add (menu);
- Application.Top.Add (menu);
+ var statusBar = new StatusBar (new StatusItem [] {
+ new (KeyCode.F2, "~F2~ OpenBigListEx", () => OpenSimpleList (true)),
+ new (KeyCode.F3, "~F3~ CloseExample", () => CloseExample ()),
+ new (KeyCode.F4, "~F4~ OpenSmListEx", () => OpenSimpleList (false)),
+ new (Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit ())
+ });
+ Application.Top.Add (statusBar);
- var statusBar = new StatusBar (new StatusItem [] {
- new StatusItem(KeyCode.F2, "~F2~ OpenBigListEx", () => OpenSimpleList (true)),
- new StatusItem(KeyCode.F3, "~F3~ CloseExample", () => CloseExample ()),
- new StatusItem(KeyCode.F4, "~F4~ OpenSmListEx", () => OpenSimpleList (false)),
- new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit()),
- });
- Application.Top.Add (statusBar);
+ Win.Add (listColView);
- Win.Add (listColView);
+ var selectedCellLabel = new Label {
+ X = 0,
+ Y = Pos.Bottom (listColView),
+ Text = "0,0",
+ Width = Dim.Fill (),
+ TextAlignment = TextAlignment.Right
- var selectedCellLabel = new Label () {
- X = 0,
- Y = Pos.Bottom (listColView),
- Text = "0,0",
- Width = Dim.Fill (),
- TextAlignment = TextAlignment.Right
+ };
- };
+ Win.Add (selectedCellLabel);
- Win.Add (selectedCellLabel);
+ listColView.SelectedCellChanged += (s, e) => { selectedCellLabel.Text = $"{listColView.SelectedRow},{listColView.SelectedColumn}"; };
+ listColView.KeyDown += TableViewKeyPress;
- listColView.SelectedCellChanged += (s, e) => { selectedCellLabel.Text = $"{listColView.SelectedRow},{listColView.SelectedColumn}"; };
- listColView.KeyDown += TableViewKeyPress;
+ SetupScrollBar ();
- SetupScrollBar ();
+ alternatingColorScheme = new ColorScheme {
- alternatingColorScheme = new ColorScheme () {
+ Disabled = Win.ColorScheme.Disabled,
+ HotFocus = Win.ColorScheme.HotFocus,
+ Focus = Win.ColorScheme.Focus,
+ Normal = new Attribute (Color.White, Color.BrightBlue)
+ };
- Disabled = Win.ColorScheme.Disabled,
- HotFocus = Win.ColorScheme.HotFocus,
- Focus = Win.ColorScheme.Focus,
- Normal = new Attribute (Color.White, Color.BrightBlue)
- };
+ // if user clicks the mouse in TableView
+ listColView.MouseClick += (s, e) => {
- // if user clicks the mouse in TableView
- listColView.MouseClick += (s, e) => {
+ listColView.ScreenToCell (e.MouseEvent.X, e.MouseEvent.Y, out var clickedCol);
+ };
- listColView.ScreenToCell (e.MouseEvent.X, e.MouseEvent.Y, out int? clickedCol);
- };
+ listColView.KeyBindings.Add (KeyCode.Space, Command.ToggleChecked);
+ }
- listColView.KeyBindings.Add (KeyCode.Space, Command.ToggleChecked);
- }
+ void SetupScrollBar ()
+ {
+ var scrollBar = new ScrollBarView (listColView, true); // (listColView, true, true);
- private void SetupScrollBar ()
- {
- var scrollBar = new ScrollBarView (listColView, true); // (listColView, true, true);
-
- scrollBar.ChangedPosition += (s, e) => {
- listColView.RowOffset = scrollBar.Position;
- if (listColView.RowOffset != scrollBar.Position) {
- scrollBar.Position = listColView.RowOffset;
- }
- listColView.SetNeedsDisplay ();
- };
- /*
- scrollBar.OtherScrollBarView.ChangedPosition += (s,e) => {
- listColView.ColumnOffset = scrollBar.OtherScrollBarView.Position;
- if (listColView.ColumnOffset != scrollBar.OtherScrollBarView.Position) {
- scrollBar.OtherScrollBarView.Position = listColView.ColumnOffset;
- }
- listColView.SetNeedsDisplay ();
- };
- */
-
- listColView.DrawContent += (s, e) => {
- scrollBar.Size = listColView.Table?.Rows ?? 0;
+ scrollBar.ChangedPosition += (s, e) => {
+ listColView.RowOffset = scrollBar.Position;
+ if (listColView.RowOffset != scrollBar.Position) {
scrollBar.Position = listColView.RowOffset;
- //scrollBar.OtherScrollBarView.Size = listColView.Table?.Columns - 1 ?? 0;
- //scrollBar.OtherScrollBarView.Position = listColView.ColumnOffset;
- scrollBar.Refresh ();
- };
-
- }
-
- private void TableViewKeyPress (object sender, Key e)
- {
- if (e.KeyCode == KeyCode.Delete) {
-
- // set all selected cells to null
- foreach (var pt in listColView.GetAllSelectedCells ()) {
- currentTable.Rows [pt.Y] [pt.X] = DBNull.Value;
- }
-
- listColView.Update ();
- e.Handled = true;
- }
-
- }
-
- private void ToggleTopline ()
- {
- _miTopline.Checked = !_miTopline.Checked;
- listColView.Style.ShowHorizontalHeaderOverline = (bool)_miTopline.Checked;
- listColView.Update ();
- }
- private void ToggleBottomline ()
- {
- _miBottomline.Checked = !_miBottomline.Checked;
- listColView.Style.ShowHorizontalBottomline = (bool)_miBottomline.Checked;
- listColView.Update ();
- }
- private void ToggleExpandLastColumn ()
- {
- _miExpandLastColumn.Checked = !_miExpandLastColumn.Checked;
- listColView.Style.ExpandLastColumn = (bool)_miExpandLastColumn.Checked;
-
- listColView.Update ();
-
- }
-
- private void ToggleAlwaysUseNormalColorForVerticalCellLines ()
- {
- _miAlwaysUseNormalColorForVerticalCellLines.Checked = !_miAlwaysUseNormalColorForVerticalCellLines.Checked;
- listColView.Style.AlwaysUseNormalColorForVerticalCellLines = (bool)_miAlwaysUseNormalColorForVerticalCellLines.Checked;
-
- listColView.Update ();
- }
- private void ToggleSmoothScrolling ()
- {
- _miSmoothScrolling.Checked = !_miSmoothScrolling.Checked;
- listColView.Style.SmoothHorizontalScrolling = (bool)_miSmoothScrolling.Checked;
-
- listColView.Update ();
-
- }
- private void ToggleCellLines ()
- {
- _miCellLines.Checked = !_miCellLines.Checked;
- listColView.Style.ShowVerticalCellLines = (bool)_miCellLines.Checked;
- listColView.Update ();
- }
- private void ToggleAlternatingColors ()
- {
- //toggle menu item
- _miAlternatingColors.Checked = !_miAlternatingColors.Checked;
-
- if (_miAlternatingColors.Checked == true) {
- listColView.Style.RowColorGetter = (a) => { return a.RowIndex % 2 == 0 ? alternatingColorScheme : null; };
- } else {
- listColView.Style.RowColorGetter = null;
}
listColView.SetNeedsDisplay ();
- }
-
- private void ToggleInvertSelectedCellFirstCharacter ()
- {
- //toggle menu item
- _miCursor.Checked = !_miCursor.Checked;
- listColView.Style.InvertSelectedCellFirstCharacter = (bool)_miCursor.Checked;
+ };
+ /*
+ scrollBar.OtherScrollBarView.ChangedPosition += (s,e) => {
+ listColView.ColumnOffset = scrollBar.OtherScrollBarView.Position;
+ if (listColView.ColumnOffset != scrollBar.OtherScrollBarView.Position) {
+ scrollBar.OtherScrollBarView.Position = listColView.ColumnOffset;
+ }
listColView.SetNeedsDisplay ();
- }
+ };
+ */
- private void ToggleVerticalOrientation ()
- {
- _miOrientVertical.Checked = !_miOrientVertical.Checked;
- if ((ListTableSource)listColView.Table != null) {
- ((ListTableSource)listColView.Table).Style.Orientation = (bool)_miOrientVertical.Checked ? Orientation.Vertical : Orientation.Horizontal;
- listColView.SetNeedsDisplay ();
+ listColView.DrawContent += (s, e) => {
+ scrollBar.Size = listColView.Table?.Rows ?? 0;
+ scrollBar.Position = listColView.RowOffset;
+ //scrollBar.OtherScrollBarView.Size = listColView.Table?.Columns - 1 ?? 0;
+ //scrollBar.OtherScrollBarView.Position = listColView.ColumnOffset;
+ scrollBar.Refresh ();
+ };
+
+ }
+
+ void TableViewKeyPress (object sender, Key e)
+ {
+ if (e.KeyCode == KeyCode.Delete) {
+
+ // set all selected cells to null
+ foreach (var pt in listColView.GetAllSelectedCells ()) {
+ currentTable.Rows [pt.Y] [pt.X] = DBNull.Value;
}
+
+ listColView.Update ();
+ e.Handled = true;
}
- private void ToggleScrollParallel ()
- {
- _miScrollParallel.Checked = !_miScrollParallel.Checked;
- if ((ListTableSource)listColView.Table != null) {
- ((ListTableSource)listColView.Table).Style.ScrollParallel = (bool)_miScrollParallel.Checked;
- listColView.SetNeedsDisplay ();
- }
- }
+ }
- private void SetListMinWidth ()
- {
- RunListWidthDialog ("MinCellWidth", (s, v) => s.MinCellWidth = v, (s) => s.MinCellWidth);
+ void ToggleTopline ()
+ {
+ _miTopline.Checked = !_miTopline.Checked;
+ listColView.Style.ShowHorizontalHeaderOverline = (bool)_miTopline.Checked;
+ listColView.Update ();
+ }
+
+ void ToggleBottomline ()
+ {
+ _miBottomline.Checked = !_miBottomline.Checked;
+ listColView.Style.ShowHorizontalBottomline = (bool)_miBottomline.Checked;
+ listColView.Update ();
+ }
+
+ void ToggleExpandLastColumn ()
+ {
+ _miExpandLastColumn.Checked = !_miExpandLastColumn.Checked;
+ listColView.Style.ExpandLastColumn = (bool)_miExpandLastColumn.Checked;
+
+ listColView.Update ();
+
+ }
+
+ void ToggleAlwaysUseNormalColorForVerticalCellLines ()
+ {
+ _miAlwaysUseNormalColorForVerticalCellLines.Checked = !_miAlwaysUseNormalColorForVerticalCellLines.Checked;
+ listColView.Style.AlwaysUseNormalColorForVerticalCellLines = (bool)_miAlwaysUseNormalColorForVerticalCellLines.Checked;
+
+ listColView.Update ();
+ }
+
+ void ToggleSmoothScrolling ()
+ {
+ _miSmoothScrolling.Checked = !_miSmoothScrolling.Checked;
+ listColView.Style.SmoothHorizontalScrolling = (bool)_miSmoothScrolling.Checked;
+
+ listColView.Update ();
+
+ }
+
+ void ToggleCellLines ()
+ {
+ _miCellLines.Checked = !_miCellLines.Checked;
+ listColView.Style.ShowVerticalCellLines = (bool)_miCellLines.Checked;
+ listColView.Update ();
+ }
+
+ void ToggleAlternatingColors ()
+ {
+ //toggle menu item
+ _miAlternatingColors.Checked = !_miAlternatingColors.Checked;
+
+ if (_miAlternatingColors.Checked == true) {
+ listColView.Style.RowColorGetter = a => { return a.RowIndex % 2 == 0 ? alternatingColorScheme : null; };
+ } else {
+ listColView.Style.RowColorGetter = null;
+ }
+ listColView.SetNeedsDisplay ();
+ }
+
+ void ToggleInvertSelectedCellFirstCharacter ()
+ {
+ //toggle menu item
+ _miCursor.Checked = !_miCursor.Checked;
+ listColView.Style.InvertSelectedCellFirstCharacter = (bool)_miCursor.Checked;
+ listColView.SetNeedsDisplay ();
+ }
+
+ void ToggleVerticalOrientation ()
+ {
+ _miOrientVertical.Checked = !_miOrientVertical.Checked;
+ if ((ListTableSource)listColView.Table != null) {
+ ((ListTableSource)listColView.Table).Style.Orientation = (bool)_miOrientVertical.Checked ? Orientation.Vertical : Orientation.Horizontal;
listColView.SetNeedsDisplay ();
}
-
- private void SetListMaxWidth ()
- {
- RunListWidthDialog ("MaxCellWidth", (s, v) => s.MaxCellWidth = v, (s) => s.MaxCellWidth);
- listColView.SetNeedsDisplay ();
- }
-
- private void RunListWidthDialog (string prompt, Action setter, Func getter)
- {
- var accepted = false;
- var ok = new Button ("Ok", is_default: true);
- ok.Clicked += (s, e) => { accepted = true; Application.RequestStop (); };
- var cancel = new Button ("Cancel");
- cancel.Clicked += (s, e) => { Application.RequestStop (); };
- var d = new Dialog (ok, cancel) { Title = prompt };
-
- var tf = new TextField () {
- Text = getter (listColView).ToString (),
- X = 0,
- Y = 1,
- Width = Dim.Fill ()
- };
-
- d.Add (tf);
- tf.SetFocus ();
-
- Application.Run (d);
-
- if (accepted) {
-
- try {
- setter (listColView, int.Parse (tf.Text));
- } catch (Exception ex) {
- MessageBox.ErrorQuery (60, 20, "Failed to set", ex.Message, "Ok");
- }
- }
- }
-
- private void CloseExample ()
- {
- listColView.Table = null;
- }
-
- private void Quit ()
- {
- Application.RequestStop ();
- }
-
- private void OpenSimpleList (bool big)
- {
- SetTable (BuildSimpleList (big ? 1023 : 31));
- }
-
- private void SetTable (IList list)
- {
- listColView.Table = new ListTableSource (list, listColView);
- if ((ListTableSource)listColView.Table != null) {
- currentTable = ((ListTableSource)listColView.Table).DataTable;
- }
- }
-
- ///
- /// Builds a simple list in which values are the index. This helps testing that scrolling etc is working correctly and not skipping out values when paging
- ///
- ///
- ///
- public static IList BuildSimpleList (int items)
- {
- var list = new List