diff --git a/Terminal.Gui/Core/View.cs b/Terminal.Gui/Core/View.cs index a6cdb4fc8..8548be267 100644 --- a/Terminal.Gui/Core/View.cs +++ b/Terminal.Gui/Core/View.cs @@ -1,28 +1,15 @@ -// -// Authors: -// Miguel de Icaza (miguel@gnome.org) -// -// Pending: -// - Check for NeedDisplay on the hierarchy and repaint -// - Layout support -// - "Colors" type or "Attributes" type? -// - What to surface as "BackgroundCOlor" when clearing a window, an attribute or colors? -// -// Optimizations -// - Add rendering limitation to the exposed area -using System; -using System.Collections; +using System; using System.Collections.Generic; using System.ComponentModel; -using System.Diagnostics; using System.Linq; +using System.Reflection; using NStack; namespace Terminal.Gui { /// - /// Determines the LayoutStyle for a view, if Absolute, during LayoutSubviews, the - /// value from the Frame will be used, if the value is Computed, then the Frame - /// will be updated from the X, Y Pos objects and the Width and Height Dim objects. + /// Determines the LayoutStyle for a , if Absolute, during , the + /// value from the will be used, if the value is Computed, then + /// will be updated from the X, Y objects and the Width and Height objects. /// public enum LayoutStyle { /// @@ -38,17 +25,19 @@ namespace Terminal.Gui { } /// - /// 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. + /// 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. /// /// /// - /// The View defines the base functionality for user interface elements in Terminal.Gui. Views + /// 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: Absolute or Computed. The choice as to which layout style is used by the View + /// 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 + /// 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. /// @@ -61,9 +50,9 @@ namespace Terminal.Gui { /// 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 + /// points. The Width and Height properties are of type /// and can use absolute position, - /// percentages and anchors. These are useful as they will take + /// 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. /// @@ -73,17 +62,17 @@ namespace Terminal.Gui { /// property. /// /// - /// Subviews (child views) can be added to a View by calling the method. + /// 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 . + /// To flag a region of the View's to be redrawn call . + /// To flag the entire view for redraw call . /// /// /// 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 + /// 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. /// @@ -99,14 +88,14 @@ namespace Terminal.Gui { /// /// /// 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 + /// 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 + /// the last focused view. So views should make sure that they place the cursor /// in a visually sensible place. /// /// /// The method is invoked when the size or layout of a view has - /// changed. The default processing system will keep the size and dimensions + /// 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 . /// @@ -248,9 +237,9 @@ namespace Terminal.Gui { /// 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 { get { return Application.Driver; } } + public static ConsoleDriver Driver => Application.Driver; - static IList empty = new List (0).AsReadOnly (); + static readonly IList empty = new List (0).AsReadOnly (); // This is null, and allocated on demand. List subviews; @@ -259,7 +248,7 @@ namespace Terminal.Gui { /// This returns a list of the subviews contained by this view. /// /// The subviews. - public IList Subviews => subviews == null ? empty : subviews.AsReadOnly (); + 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. @@ -278,7 +267,7 @@ namespace Terminal.Gui { /// This returns a tab index list of the subviews contained by this view. /// /// The tabIndexes. - public IList TabIndexes => tabIndexes == null ? empty : tabIndexes.AsReadOnly (); + public IList TabIndexes => tabIndexes?.AsReadOnly () ?? empty; int tabIndex = -1; @@ -309,7 +298,7 @@ namespace Terminal.Gui { int GetTabIndex (int idx) { - int i = 0; + var i = 0; foreach (var v in SuperView.tabIndexes) { if (v.tabIndex == -1 || v == this) { continue; @@ -321,7 +310,7 @@ namespace Terminal.Gui { void SetTabIndex () { - int i = 0; + var i = 0; foreach (var v in SuperView.tabIndexes) { if (v.tabIndex == -1) { continue; @@ -334,10 +323,11 @@ namespace Terminal.Gui { bool tabStop = true; /// - /// This only be true if the is also true and the focus can be avoided by setting this to false + /// This only be if the is also + /// and the focus can be avoided by setting this to /// public bool TabStop { - get { return tabStop; } + get => tabStop; set { if (tabStop == value) { return; @@ -358,12 +348,16 @@ namespace Terminal.Gui { } if (base.CanFocus != value) { base.CanFocus = value; - if (!value && tabIndex > -1) { + + switch (value) { + case false when tabIndex > -1: TabIndex = -1; + break; + case true when SuperView?.CanFocus == false && addingView: + SuperView.CanFocus = true; + break; } - if (value && SuperView?.CanFocus == false && addingView) { - SuperView.CanFocus = value; - } + if (value && tabIndex == -1) { TabIndex = SuperView != null ? SuperView.tabIndexes.IndexOf (this) : -1; } @@ -375,7 +369,7 @@ namespace Terminal.Gui { if (!value && HasFocus) { SetHasFocus (false, this); SuperView?.EnsureFocus (); - if (SuperView != null && SuperView?.Focused == null) { + if (SuperView != null && SuperView.Focused == null) { SuperView.FocusNext (); if (SuperView.Focused == null) { Application.Current.FocusNext (); @@ -389,7 +383,7 @@ namespace Terminal.Gui { if (!value) { view.oldCanFocus = view.CanFocus; view.oldTabIndex = view.tabIndex; - view.CanFocus = value; + view.CanFocus = false; view.tabIndex = -1; } else { if (addingView) { @@ -423,22 +417,18 @@ namespace Terminal.Gui { /// /// Returns a value indicating if this View is currently on Top (Active) /// - public bool IsCurrentTop { - get { - return Application.Current == this; - } - } + public bool IsCurrentTop => Application.Current == this; /// /// Gets or sets a value indicating whether this wants mouse position reports. /// - /// true if want mouse position reports; otherwise, false. - public virtual bool WantMousePositionReports { get; set; } = false; + /// if want mouse position reports; otherwise, . + public virtual bool WantMousePositionReports { get; set; } /// /// Gets or sets a value indicating whether this want continuous button pressed event. /// - public virtual bool WantContinuousButtonPressed { get; set; } = false; + public virtual bool WantContinuousButtonPressed { get; set; } /// /// Gets or sets the frame for the view. The frame is relative to the view's container (). @@ -446,7 +436,7 @@ namespace Terminal.Gui { /// The frame. /// /// - /// 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 @@ -480,8 +470,10 @@ namespace Terminal.Gui { LayoutStyle layoutStyle; /// - /// Controls how the View's is computed during the LayoutSubviews method, if the style is set to , - /// LayoutSubviews does not change the . If the style is the is updated using + /// Controls how the View's is computed during the LayoutSubviews method, if the style is set to + /// , + /// LayoutSubviews does not change the . If the style is + /// the is updated using /// the , , , and properties. /// /// The layout style. @@ -511,19 +503,17 @@ namespace Terminal.Gui { /// public Rect Bounds { get => new Rect (Point.Empty, Frame.Size); - set { - Frame = new Rect (frame.Location, value.Size); - } + set => Frame = new Rect (frame.Location, value.Size); } Pos x, y; /// - /// Gets or sets the X position for the view (the column). Only used the is . + /// Gets or sets the X position for the view (the column). Only used if the is . /// /// The X Position. /// - /// If is changing this property has no effect and its value is indeterminate. + /// If is changing this property has no effect and its value is indeterminate. /// public Pos X { get => x; @@ -539,11 +529,11 @@ namespace Terminal.Gui { } /// - /// Gets or sets the Y position for the view (the row). Only used the is . + /// Gets or sets the Y position for the view (the row). Only used if the is . /// /// The y position (line). /// - /// If is changing this property has no effect and its value is indeterminate. + /// If is changing this property has no effect and its value is indeterminate. /// public Pos Y { get => y; @@ -560,11 +550,11 @@ namespace Terminal.Gui { Dim width, height; /// - /// Gets or sets the width of the view. Only used the is . + /// Gets or sets the width of the view. Only used the is . /// /// The width. /// - /// If is changing this property has no effect and its value is indeterminate. + /// If is changing this property has no effect and its value is indeterminate. /// public Dim Width { get => width; @@ -587,10 +577,10 @@ namespace Terminal.Gui { } /// - /// Gets or sets the height of the view. Only used the is . + /// Gets or sets the height of the view. Only used the is . /// /// The height. - /// If is changing this property has no effect and its value is indeterminate. + /// If is changing this property has no effect and its value is indeterminate. public Dim Height { get => height; set { @@ -612,18 +602,18 @@ namespace Terminal.Gui { } /// - /// Forces validation with layout + /// Forces validation with layout /// to avoid breaking the and settings. /// public bool ForceValidatePosDim { get; set; } - bool ValidatePosDim (object oldvalue, object newValue) + bool ValidatePosDim (object oldValue, object newValue) { - if (!IsInitialized || layoutStyle == LayoutStyle.Absolute || oldvalue == null || oldvalue.GetType () == newValue.GetType () || this is Toplevel) { + if (!IsInitialized || layoutStyle == LayoutStyle.Absolute || oldValue == null || oldValue.GetType () == newValue.GetType () || this is Toplevel) { return true; } if (layoutStyle == LayoutStyle.Computed) { - if (oldvalue.GetType () != newValue.GetType () && !(newValue is Pos.PosAbsolute || newValue is Dim.DimAbsolute)) { + if (oldValue.GetType () != newValue.GetType () && !(newValue is Pos.PosAbsolute || newValue is Dim.DimAbsolute)) { return true; } } @@ -634,7 +624,7 @@ namespace Terminal.Gui { /// Verifies if the minimum width or height can be sets in the view. /// /// The size. - /// if the size can be set, otherwise. + /// if the size can be set, otherwise. public bool GetMinWidthHeight (out Size size) { size = Size.Empty; @@ -663,7 +653,7 @@ namespace Terminal.Gui { /// /// Sets the minimum width or height if the view can be resized. /// - /// if the size can be set, otherwise. + /// if the size can be set, otherwise. public bool SetMinWidthHeight () { if (GetMinWidthHeight (out Size size)) { @@ -686,13 +676,13 @@ namespace Terminal.Gui { public View SuperView => container; /// - /// Initializes a new instance of a class with the absolute - /// dimensions specified in the frame parameter. + /// 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 + /// This constructor initialize a View with a of . + /// Use to initialize a View with of /// public View (Rect frame) { @@ -700,50 +690,50 @@ namespace Terminal.Gui { } /// - /// Initializes a new instance of using layout. + /// 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 + /// The will be created using + /// coordinates. The initial size () will be /// adjusted to fit the contents of , including newlines ('\n') for multiple lines. /// /// - /// If Height is greater than one, word wrapping is provided. + /// If is greater than one, word wrapping is provided. /// /// - /// This constructor initialize a View with a of . + /// 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. + /// Initializes a new instance of using layout. /// /// /// /// The will be created at the given - /// coordinates with the given string. The size ( will be + /// 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 Label. - /// row to locate the Label. + /// column to locate the View. + /// row to locate the View. /// text to initialize the property with. public View (int x, int y, ustring text) : this (TextFormatter.CalcRect (x, y, text), text) { } /// - /// Initializes a new instance of using layout. + /// Initializes a new instance of using layout. /// /// /// /// The will be created at the given - /// coordinates with the given string. The initial size ( will be + /// coordinates with the given string. The initial size () will be /// adjusted to fit the contents of , including newlines ('\n') for multiple lines. /// /// @@ -759,16 +749,16 @@ namespace Terminal.Gui { } /// - /// Initializes a new instance of using layout. + /// Initializes a new instance of using layout. /// /// /// - /// The will be created using - /// coordinates with the given string. The initial size ( will be + /// 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 Height is greater than one, word wrapping is provided. + /// If is greater than one, word wrapping is provided. /// /// /// text to initialize the property with. @@ -795,12 +785,8 @@ namespace Terminal.Gui { TabStop = false; LayoutStyle = layoutStyle; // BUGBUG: CalcRect doesn't account for line wrapping - Rect r; - if (rect.IsEmpty) { - r = TextFormatter.CalcRect (0, 0, text, direction); - } else { - r = rect; - } + + var r = rect.IsEmpty ? TextFormatter.CalcRect (0, 0, text, direction) : rect; Frame = r; Text = text; @@ -809,7 +795,7 @@ namespace Terminal.Gui { } /// - /// Can be overridden if the has + /// Can be overridden if the has /// different format than the default. /// protected virtual void UpdateTextFormatterText () @@ -823,18 +809,18 @@ namespace Terminal.Gui { /// protected virtual void ProcessResizeView () { - var _x = x is Pos.PosAbsolute ? x.Anchor (0) : frame.X; - var _y = y is Pos.PosAbsolute ? y.Anchor (0) : frame.Y; + var actX = x is Pos.PosAbsolute ? x.Anchor (0) : frame.X; + var actY = y is Pos.PosAbsolute ? y.Anchor (0) : frame.Y; if (AutoSize) { var s = GetAutoSize (); var w = width is Dim.DimAbsolute && width.Anchor (0) > s.Width ? width.Anchor (0) : s.Width; var h = height is Dim.DimAbsolute && height.Anchor (0) > s.Height ? height.Anchor (0) : s.Height; - frame = new Rect (new Point (_x, _y), new Size (w, h)); + frame = new Rect (new Point (actX, actY), new Size (w, h)); } else { var w = width is Dim.DimAbsolute ? width.Anchor (0) : frame.Width; var h = height is Dim.DimAbsolute ? height.Anchor (0) : frame.Height; - frame = new Rect (new Point (_x, _y), new Size (w, h)); + frame = new Rect (new Point (actX, actY), new Size (w, h)); SetMinWidthHeight (); } TextFormatter.Size = GetBoundsTextFormatterSize (); @@ -842,7 +828,7 @@ namespace Terminal.Gui { SetNeedsDisplay (); } - private void TextFormatter_HotKeyChanged (Key obj) + void TextFormatter_HotKeyChanged (Key obj) { HotKeyChanged?.Invoke (obj); } @@ -894,10 +880,11 @@ namespace Terminal.Gui { var h = Math.Max (NeedDisplay.Height, region.Height); NeedDisplay = new Rect (x, y, w, h); } - if (container != null) - container.SetChildNeedsDisplay (); + container?.SetChildNeedsDisplay (); + if (subviews == null) return; + foreach (var view in subviews) if (view.Frame.IntersectsWith (region)) { var childRegion = Rect.Intersect (view.Frame, region); @@ -919,13 +906,14 @@ namespace Terminal.Gui { container.SetChildNeedsDisplay (); } - internal bool addingView = false; + internal bool addingView; /// /// Adds a subview (child) to this view. /// /// - /// The Views that have been added to this view can be retrieved via the property. See also + /// The Views that have been added to this view can be retrieved via the property. + /// See also /// public virtual void Add (View view) { @@ -965,7 +953,8 @@ namespace Terminal.Gui { /// /// 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 + /// The Views that have been added to this view can be retrieved via the property. + /// See also /// public void Add (params View [] views) { @@ -1106,9 +1095,9 @@ namespace Terminal.Gui { { var h = Frame.Height; var w = Frame.Width; - for (int line = 0; line < h; line++) { + for (var line = 0; line < h; line++) { Move (0, line); - for (int col = 0; col < w; col++) + for (var col = 0; col < w; col++) Driver.AddRune (' '); } } @@ -1123,9 +1112,9 @@ namespace Terminal.Gui { { var h = regionScreen.Height; var w = regionScreen.Width; - for (int line = regionScreen.Y; line < regionScreen.Y + h; line++) { + for (var line = regionScreen.Y; line < regionScreen.Y + h; line++) { Driver.Move (regionScreen.X, line); - for (int col = 0; col < w; col++) + for (var col = 0; col < w; col++) Driver.AddRune (' '); } } @@ -1137,17 +1126,18 @@ namespace Terminal.Gui { /// View-relative row. /// Absolute column; screen-relative. /// Absolute row; screen-relative. - /// Whether to clip the result of the ViewToScreen method, if set to true, the rcol, rrow values are clamped to the screen (terminal) dimensions (0..TerminalDim-1). + /// Whether to clip the result of the ViewToScreen method, if set to , the rcol, rrow values are clamped to the screen (terminal) dimensions (0..TerminalDim-1). internal void ViewToScreen (int col, int row, out int rcol, out int rrow, bool clipped = true) { // Computes the real row, col relative to the screen. rrow = row + frame.Y; rcol = col + frame.X; - var ccontainer = container; - while (ccontainer != null) { - rrow += ccontainer.frame.Y; - rcol += ccontainer.frame.X; - ccontainer = ccontainer.container; + + var curContainer = container; + while (curContainer != null) { + rrow += curContainer.frame.Y; + rcol += curContainer.frame.X; + curContainer = curContainer.container; } // The following ensures that the cursor is always in the screen boundaries. @@ -1222,7 +1212,7 @@ namespace Terminal.Gui { /// /// View-relative region for the frame to be drawn. /// The padding to add around the outside of the drawn frame. - /// If set to true it fill will the contents. + /// If set to it fill will the contents. public void DrawFrame (Rect region, int padding = 0, bool fill = false) { var scrRect = ViewToScreen (region); @@ -1259,7 +1249,7 @@ namespace Terminal.Gui { /// 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 true 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 (ustring text, bool focused, ColorScheme scheme) { @@ -1276,15 +1266,15 @@ namespace Terminal.Gui { /// Col. /// Row. /// Whether to clip the result of the ViewToScreen method, - /// if set to true, the col, row values are clamped to the screen (terminal) dimensions (0..TerminalDim-1). + /// if set to , the col, row values are clamped to the screen (terminal) dimensions (0..TerminalDim-1). public void Move (int col, int row, bool clipped = true) { if (Driver.Rows == 0) { return; } - ViewToScreen (col, row, out var rcol, out var rrow, clipped); - Driver.Move (rcol, rrow); + ViewToScreen (col, row, out var rCol, out var rRow, clipped); + Driver.Move (rCol, rRow); } /// @@ -1313,12 +1303,9 @@ namespace Terminal.Gui { } bool hasFocus; + /// - public override bool HasFocus { - get { - return hasFocus; - } - } + public override bool HasFocus => hasFocus; void SetHasFocus (bool value, View view, bool force = false) { @@ -1361,7 +1348,7 @@ namespace Terminal.Gui { } /// - /// Method invoked when a subview is being added to this view. + /// Method invoked when a subview is being added to this view. /// /// The subview being added. public virtual void OnAdded (View view) @@ -1388,7 +1375,7 @@ namespace Terminal.Gui { /// public override bool OnEnter (View view) { - FocusEventArgs args = new FocusEventArgs (view); + var args = new FocusEventArgs (view); Enter?.Invoke (args); if (args.Handled) return true; @@ -1401,7 +1388,7 @@ namespace Terminal.Gui { /// public override bool OnLeave (View view) { - FocusEventArgs args = new FocusEventArgs (view); + var args = new FocusEventArgs (view); Leave?.Invoke (args); if (args.Handled) return true; @@ -1420,7 +1407,7 @@ namespace Terminal.Gui { /// /// Returns the most focused view in the chain of subviews (the leaf view that has the focus). /// - /// The most focused. + /// The most focused View. public View MostFocused { get { if (Focused == null) @@ -1491,7 +1478,7 @@ namespace Terminal.Gui { /// /// /// Overrides of must ensure they do not set Driver.Clip to a clip region - /// larger than the region parameter. + /// larger than the parameter, as this will cause the driver to clip the entire region. /// /// public virtual void Redraw (Rect bounds) @@ -1502,16 +1489,19 @@ namespace Terminal.Gui { var clipRect = new Rect (Point.Empty, frame.Size); - //if (ColorScheme != null && !(this is Toplevel)) { if (ColorScheme != null) { Driver.SetAttribute (HasFocus ? ColorScheme.Focus : ColorScheme.Normal); } if (Border != null) { Border.DrawContent (this); + } else if ((GetType ().IsPublic || GetType ().IsNestedPublic) && !IsOverridden (this, "Redraw") && + (!NeedDisplay.IsEmpty || ChildNeedsDisplay || LayoutNeeded)) { + + Clear (ViewToScreen (bounds)); } - if (!ustring.IsNullOrEmpty (TextFormatter.Text) || (this is Label && !AutoSize)) { + if (!ustring.IsNullOrEmpty (TextFormatter.Text)) { Clear (); // Draw any Text if (TextFormatter != null) { @@ -1695,7 +1685,7 @@ namespace Terminal.Gui { return false; } - KeyEventEventArgs args = new KeyEventEventArgs (keyEvent); + var args = new KeyEventEventArgs (keyEvent); KeyPress?.Invoke (args); if (args.Handled) return true; @@ -1704,10 +1694,8 @@ namespace Terminal.Gui { if (args.Handled) return true; } - if (Focused?.Enabled == true && Focused?.ProcessKey (keyEvent) == true) - return true; - - return false; + + return Focused?.Enabled == true && Focused?.ProcessKey (keyEvent) == true; } /// @@ -1737,7 +1725,7 @@ namespace Terminal.Gui { // if ever see a true then that's what we will return if (thisReturn ?? false) { - toReturn = thisReturn.Value; + toReturn = true; } } } @@ -1753,11 +1741,11 @@ namespace Terminal.Gui { /// If the key is already bound to a different it will be /// rebound to this one /// Commands are only ever applied to the current (i.e. this feature - /// cannot be used to switch focus to another view and perform multiple commands there) + /// cannot be used to switch focus to another view and perform multiple commands there) /// /// /// The command(s) to run on the when is pressed. - /// When specifying multiple, all commands will be applied in sequence. The bound strike + /// When specifying multiple commands, all commands will be applied in sequence. The bound strike /// will be consumed if any took effect. public void AddKeyBinding (Key key, params Command [] command) { @@ -1787,18 +1775,17 @@ namespace Terminal.Gui { } /// - /// Checks if key combination already exist. + /// Checks if the key binding already exists. /// /// The key to check. - /// true If the key already exist, falseotherwise. + /// If the key already exist, otherwise. public bool ContainsKeyBinding (Key key) { return KeyBindings.ContainsKey (key); } /// - /// Removes all bound keys from the View making including the default - /// key combinations such as cursor navigation, scrolling etc + /// Removes all bound keys from the View and resets the default bindings. /// public void ClearKeybindings () { @@ -1806,7 +1793,7 @@ namespace Terminal.Gui { } /// - /// Clears the existing keybinding (if any) for the given + /// Clears the existing keybinding (if any) for the given . /// /// public void ClearKeybinding (Key key) @@ -1815,7 +1802,7 @@ namespace Terminal.Gui { } /// - /// Removes all key bindings that trigger the given command. Views can have multiple different + /// Removes all key bindings that trigger the given command. Views can have multiple different /// keys bound to the same command and this method will clear all of them. /// /// @@ -1848,7 +1835,7 @@ namespace Terminal.Gui { } /// - /// Returns all commands that are supported by this + /// Returns all commands that are supported by this . /// /// public IEnumerable GetSupportedCommands () @@ -1863,7 +1850,7 @@ namespace Terminal.Gui { /// The used by a public Key GetKeyFromCommand (params Command [] command) { - return KeyBindings.First (x => x.Value.SequenceEqual (command)).Key; + return KeyBindings.First (kb => kb.Value.SequenceEqual (command)).Key; } /// @@ -1873,7 +1860,7 @@ namespace Terminal.Gui { return false; } - KeyEventEventArgs args = new KeyEventEventArgs (keyEvent); + var args = new KeyEventEventArgs (keyEvent); if (MostFocused?.Enabled == true) { MostFocused?.KeyPress?.Invoke (args); if (args.Handled) @@ -1883,6 +1870,7 @@ namespace Terminal.Gui { return true; if (subviews == null || subviews.Count == 0) return false; + foreach (var view in subviews) if (view.Enabled && view.ProcessHotKey (keyEvent)) return true; @@ -1896,7 +1884,7 @@ namespace Terminal.Gui { return false; } - KeyEventEventArgs args = new KeyEventEventArgs (keyEvent); + var args = new KeyEventEventArgs (keyEvent); KeyPress?.Invoke (args); if (args.Handled) return true; @@ -1909,6 +1897,7 @@ namespace Terminal.Gui { return true; if (subviews == null || subviews.Count == 0) return false; + foreach (var view in subviews) if (view.Enabled && view.ProcessColdKey (keyEvent)) return true; @@ -1916,7 +1905,7 @@ namespace Terminal.Gui { } /// - /// Invoked when a key is pressed + /// Invoked when a key is pressed. /// public event Action KeyDown; @@ -1927,7 +1916,7 @@ namespace Terminal.Gui { return false; } - KeyEventEventArgs args = new KeyEventEventArgs (keyEvent); + var args = new KeyEventEventArgs (keyEvent); KeyDown?.Invoke (args); if (args.Handled) { return true; @@ -1946,7 +1935,7 @@ namespace Terminal.Gui { } /// - /// Invoked when a key is released + /// Invoked when a key is released. /// public event Action KeyUp; @@ -1957,7 +1946,7 @@ namespace Terminal.Gui { return false; } - KeyEventEventArgs args = new KeyEventEventArgs (keyEvent); + var args = new KeyEventEventArgs (keyEvent); KeyUp?.Invoke (args); if (args.Handled) { return true; @@ -1976,7 +1965,7 @@ namespace Terminal.Gui { } /// - /// Finds the first view in the hierarchy that wants to get the focus if nothing is currently focused, otherwise, it does nothing. + /// Finds the first view in the hierarchy that wants to get the focus if nothing is currently focused, otherwise, does nothing. /// public void EnsureFocus () { @@ -2025,10 +2014,10 @@ namespace Terminal.Gui { return; } - for (int i = tabIndexes.Count; i > 0;) { + for (var i = tabIndexes.Count; i > 0;) { i--; - View v = tabIndexes [i]; + var v = tabIndexes [i]; if (v.CanFocus && v.tabStop && v.Visible && v.Enabled) { SetFocus (v); return; @@ -2039,7 +2028,7 @@ namespace Terminal.Gui { /// /// Focuses the previous view. /// - /// true, if previous was focused, false otherwise. + /// if previous was focused, otherwise. public bool FocusPrev () { if (!CanBeVisible (this)) { @@ -2054,21 +2043,22 @@ namespace Terminal.Gui { FocusLast (); return focused != null; } - int focused_idx = -1; - for (int i = tabIndexes.Count; i > 0;) { + + var focusedIdx = -1; + for (var i = tabIndexes.Count; i > 0;) { i--; - View w = tabIndexes [i]; + var w = tabIndexes [i]; if (w.HasFocus) { if (w.FocusPrev ()) return true; - focused_idx = i; + focusedIdx = i; continue; } - if (w.CanFocus && focused_idx != -1 && w.tabStop && w.Visible && w.Enabled) { + if (w.CanFocus && focusedIdx != -1 && w.tabStop && w.Visible && w.Enabled) { focused.SetHasFocus (false, w); - if (w != null && w.CanFocus && w.tabStop && w.Visible && w.Enabled) + if (w.CanFocus && w.tabStop && w.Visible && w.Enabled) w.FocusLast (); SetFocus (w); @@ -2085,7 +2075,7 @@ namespace Terminal.Gui { /// /// Focuses the next view. /// - /// true, if next was focused, false otherwise. + /// if next was focused, otherwise. public bool FocusNext () { if (!CanBeVisible (this)) { @@ -2100,21 +2090,21 @@ namespace Terminal.Gui { FocusFirst (); return focused != null; } - int n = tabIndexes.Count; - int focused_idx = -1; - for (int i = 0; i < n; i++) { - View w = tabIndexes [i]; + var n = tabIndexes.Count; + var focusedIdx = -1; + for (var i = 0; i < n; i++) { + var w = tabIndexes [i]; if (w.HasFocus) { if (w.FocusNext ()) return true; - focused_idx = i; + focusedIdx = i; continue; } - if (w.CanFocus && focused_idx != -1 && w.tabStop && w.Visible && w.Enabled) { + if (w.CanFocus && focusedIdx != -1 && w.tabStop && w.Visible && w.Enabled) { focused.SetHasFocus (false, w); - if (w != null && w.CanFocus && w.tabStop && w.Visible && w.Enabled) + if (w.CanFocus && w.tabStop && w.Visible && w.Enabled) w.FocusFirst (); SetFocus (w); @@ -2131,14 +2121,10 @@ namespace Terminal.Gui { View GetMostFocused (View view) { if (view == null) { - return view; + return null; } - if (view.focused != null) { - return GetMostFocused (view.focused); - } else { - return view; - } + return view.focused != null ? GetMostFocused (view.focused) : view; } /// @@ -2150,7 +2136,7 @@ namespace Terminal.Gui { /// internal void SetRelativeLayout (Rect hostFrame) { - int w, h, _x, _y; + int actW, actH, actX, actY; var s = Size.Empty; if (AutoSize) { @@ -2159,71 +2145,76 @@ namespace Terminal.Gui { if (x is Pos.PosCenter) { if (width == null) { - w = AutoSize ? s.Width : hostFrame.Width; + actW = AutoSize ? s.Width : hostFrame.Width; } else { - w = width.Anchor (hostFrame.Width); - w = AutoSize && s.Width > w ? s.Width : w; + actW = width.Anchor (hostFrame.Width); + actW = AutoSize && s.Width > actW ? s.Width : actW; } - _x = x.Anchor (hostFrame.Width - w); + actX = x.Anchor (hostFrame.Width - actW); } else { - if (x == null) - _x = 0; - else - _x = x.Anchor (hostFrame.Width); - if (width == null) { - w = AutoSize ? s.Width : hostFrame.Width; - } else if (width is Dim.DimFactor && !((Dim.DimFactor)width).IsFromRemaining ()) { - w = width.Anchor (hostFrame.Width); - w = AutoSize && s.Width > w ? s.Width : w; - } else { - w = Math.Max (width.Anchor (hostFrame.Width - _x), 0); - w = AutoSize && s.Width > w ? s.Width : w; + actX = x?.Anchor (hostFrame.Width) ?? 0; + + switch (width) { + case null: + actW = AutoSize ? s.Width : hostFrame.Width; + break; + case Dim.DimFactor factor when !factor.IsFromRemaining (): + actW = width.Anchor (hostFrame.Width); + actW = AutoSize && s.Width > actW ? s.Width : actW; + break; + default: + actW = Math.Max (width.Anchor (hostFrame.Width - actX), 0); + actW = AutoSize && s.Width > actW ? s.Width : actW; + break; } } if (y is Pos.PosCenter) { if (height == null) { - h = AutoSize ? s.Height : hostFrame.Height; + actH = AutoSize ? s.Height : hostFrame.Height; } else { - h = height.Anchor (hostFrame.Height); - h = AutoSize && s.Height > h ? s.Height : h; + actH = height.Anchor (hostFrame.Height); + actH = AutoSize && s.Height > actH ? s.Height : actH; } - _y = y.Anchor (hostFrame.Height - h); + actY = y.Anchor (hostFrame.Height - actH); } else { - if (y == null) - _y = 0; - else - _y = y.Anchor (hostFrame.Height); - if (height == null) { - h = AutoSize ? s.Height : hostFrame.Height; - } else if (height is Dim.DimFactor && !((Dim.DimFactor)height).IsFromRemaining ()) { - h = height.Anchor (hostFrame.Height); - h = AutoSize && s.Height > h ? s.Height : h; - } else { - h = Math.Max (height.Anchor (hostFrame.Height - _y), 0); - h = AutoSize && s.Height > h ? s.Height : h; + actY = y?.Anchor (hostFrame.Height) ?? 0; + + switch (height) { + case null: + actH = AutoSize ? s.Height : hostFrame.Height; + break; + case Dim.DimFactor factor when !factor.IsFromRemaining (): + actH = height.Anchor (hostFrame.Height); + actH = AutoSize && s.Height > actH ? s.Height : actH; + break; + default: + actH = Math.Max (height.Anchor (hostFrame.Height - actY), 0); + actH = AutoSize && s.Height > actH ? s.Height : actH; + break; } } - var r = new Rect (_x, _y, w, h); + + var r = new Rect (actX, actY, actW, actH); if (Frame != r) { - Frame = new Rect (_x, _y, w, h); + Frame = new Rect (actX, actY, actW, actH); if (!SetMinWidthHeight ()) TextFormatter.Size = GetBoundsTextFormatterSize (); } } // https://en.wikipedia.org/wiki/Topological_sorting - List TopologicalSort (HashSet nodes, HashSet<(View From, View To)> edges) + List TopologicalSort (IEnumerable nodes, ICollection<(View From, View To)> edges) { var result = new List (); // Set of all nodes with no incoming edges - var S = new HashSet (nodes.Where (n => edges.All (e => !e.To.Equals (n)))); + var noEdgeNodes = new HashSet (nodes.Where (n => edges.All (e => !e.To.Equals (n)))); - while (S.Any ()) { + while (noEdgeNodes.Any ()) { // remove a node n from S - var n = S.First (); - S.Remove (n); + var n = noEdgeNodes.First (); + noEdgeNodes.Remove (n); // add n to tail of L if (n != this?.SuperView) @@ -2239,13 +2230,13 @@ namespace Terminal.Gui { // if m has no other incoming edges then if (edges.All (me => !me.To.Equals (m)) && m != this?.SuperView) { // insert m into S - S.Add (m); + noEdgeNodes.Add (m); } } } if (edges.Any ()) { - var (from, to) = edges.First (); + (var from, var to) = edges.First (); if (from != Application.Top) { if (!ReferenceEquals (from, to)) { throw new InvalidOperationException ($"TopologicalSort (for Pos/Dim) cannot find {from} linked with {to}. Did you forget to add it to {this}?"); @@ -2270,7 +2261,7 @@ namespace Terminal.Gui { } /// - /// Fired after the Views's method has completed. + /// Fired after the View's method has completed. /// /// /// Subscribe to this event to perform tasks when the has been resized or the layout has otherwise changed. @@ -2286,7 +2277,7 @@ namespace Terminal.Gui { } /// - /// Fired after the Views's method has completed. + /// Fired after the View's method has completed. /// /// /// Subscribe to this event to perform tasks when the has been resized or the layout has otherwise changed. @@ -2321,7 +2312,7 @@ namespace Terminal.Gui { return; } - Rect oldBounds = Bounds; + var oldBounds = Bounds; OnLayoutStarted (new LayoutEventArgs () { OldBounds = oldBounds }); TextFormatter.Size = GetBoundsTextFormatterSize (); @@ -2333,7 +2324,9 @@ namespace Terminal.Gui { void CollectPos (Pos pos, View from, ref HashSet nNodes, ref HashSet<(View, View)> nEdges) { - if (pos is Pos.PosView pv) { + switch (pos) { + case Pos.PosView pv: + { if (pv.Target != this) { nEdges.Add ((pv.Target, from)); } @@ -2342,17 +2335,22 @@ namespace Terminal.Gui { } return; } - if (pos is Pos.PosCombine pc) { + case Pos.PosCombine pc: + { foreach (var v in from.InternalSubviews) { CollectPos (pc.left, from, ref nNodes, ref nEdges); CollectPos (pc.right, from, ref nNodes, ref nEdges); } + break; + } } } void CollectDim (Dim dim, View from, ref HashSet nNodes, ref HashSet<(View, View)> nEdges) { - if (dim is Dim.DimView dv) { + switch (dim) { + case Dim.DimView dv: + { if (dv.Target != this) { nEdges.Add ((dv.Target, from)); } @@ -2361,11 +2359,14 @@ namespace Terminal.Gui { } return; } - if (dim is Dim.DimCombine dc) { + case Dim.DimCombine dc: + { foreach (var v in from.InternalSubviews) { CollectDim (dc.left, from, ref nNodes, ref nEdges); CollectDim (dc.right, from, ref nNodes, ref nEdges); } + break; + } } } @@ -2438,10 +2439,10 @@ namespace Terminal.Gui { /// /// Gets or sets a flag that determines whether the View will be automatically resized to fit the . - /// The default is `false`. Set to `true` to turn on AutoSize. If is `true` the + /// The default is . Set to to turn on AutoSize. If is the /// and will always be used if the text size is lower. If the text size is higher the bounds will /// be resized to fit it. - /// In addition, if is `true` the new values of and + /// In addition, if is the new values of and /// must be of the same types of the existing one to avoid breaking the settings. /// public virtual bool AutoSize { @@ -2459,10 +2460,11 @@ namespace Terminal.Gui { } /// - /// Gets or sets a flag that determines whether will have trailing spaces preserved - /// or not when is enabled. If `true` any trailing spaces will be trimmed when - /// either the property is changed or when is set to `true`. - /// The default is `false`. + /// Gets or sets a flag that determines whether will have trailing spaces preserved + /// or not when is enabled. If + /// any trailing spaces will be trimmed when either the property is changed or + /// when is set to . + /// The default is . /// public virtual bool PreserveTrailingSpaces { get => TextFormatter.PreserveTrailingSpaces; @@ -2488,7 +2490,7 @@ namespace Terminal.Gui { } /// - /// Gets or sets how the View's is aligned verticaly when drawn. Changing this property will redisplay the . + /// 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 { @@ -2507,7 +2509,7 @@ namespace Terminal.Gui { get => TextFormatter.Direction; set { if (TextFormatter.Direction != value) { - var isValidOldAutSize = autoSize && IsValidAutoSize (out Size autSize); + var isValidOldAutSize = autoSize && IsValidAutoSize (out var _); var directionChanged = TextFormatter.IsHorizontalDirection (TextFormatter.Direction) != TextFormatter.IsHorizontalDirection (value); @@ -2558,7 +2560,7 @@ namespace Terminal.Gui { foreach (var view in subviews) { if (!value) { view.oldEnabled = view.Enabled; - view.Enabled = value; + view.Enabled = false; } else { view.Enabled = view.oldEnabled; view.addingView = false; @@ -2618,7 +2620,7 @@ namespace Terminal.Gui { void SetHotKey () { - TextFormatter.FindHotKey (text, HotKeySpecifier, true, out _, out Key hk); + TextFormatter.FindHotKey (text, HotKeySpecifier, true, out _, out var hk); if (hotKey != hk) { HotKey = hk; } @@ -2645,9 +2647,9 @@ namespace Terminal.Gui { bool SetWidthHeight (Size nBounds) { - bool aSize = false; - var canSizeW = SetWidth (nBounds.Width - GetHotKeySpecifierLength (), out int rW); - var canSizeH = SetHeight (nBounds.Height - GetHotKeySpecifierLength (false), out int rH); + var aSize = false; + var canSizeW = SetWidth (nBounds.Width - GetHotKeySpecifierLength (), out var rW); + var canSizeH = SetHeight (nBounds.Height - GetHotKeySpecifierLength (false), out var rH); if (canSizeW) { aSize = true; width = rW; @@ -2702,10 +2704,10 @@ namespace Terminal.Gui { } /// - /// Get the width or height of the length. + /// Get the width or height of the length. /// - /// trueif is the width (default)falseif is the height. - /// The length of the . + /// if is the width (default) if is the height. + /// The length of the . public int GetHotKeySpecifierLength (bool isWidth = true) { if (isWidth) { @@ -2720,9 +2722,9 @@ namespace Terminal.Gui { } /// - /// Gets the bounds size from a . + /// Gets the bounds size from a . /// - /// The bounds size minus the length. + /// The bounds size minus the length. public Size GetTextFormatterBoundsSize () { return new Size (TextFormatter.Size.Width - GetHotKeySpecifierLength (), @@ -2732,7 +2734,7 @@ namespace Terminal.Gui { /// /// Gets the text formatter size from a size. /// - /// The text formatter size more the length. + /// The text formatter size more the length. public Size GetBoundsTextFormatterSize () { if (ustring.IsNullOrEmpty (TextFormatter.Text)) @@ -2743,7 +2745,7 @@ namespace Terminal.Gui { } /// - /// Specifies the event arguments for . This is a higher-level construct + /// Specifies the event arguments for . This is a higher-level construct /// than the wrapped class and is used for the events defined on /// and subclasses of View (e.g. and ). /// @@ -2781,14 +2783,10 @@ namespace Terminal.Gui { return false; } - MouseEventArgs args = new MouseEventArgs (mouseEvent); + var args = new MouseEventArgs (mouseEvent); MouseEnter?.Invoke (args); - if (args.Handled) - return true; - if (base.OnMouseEnter (mouseEvent)) - return true; - - return false; + + return args.Handled || base.OnMouseEnter (mouseEvent); } /// @@ -2802,21 +2800,17 @@ namespace Terminal.Gui { return false; } - MouseEventArgs args = new MouseEventArgs (mouseEvent); + var args = new MouseEventArgs (mouseEvent); MouseLeave?.Invoke (args); - if (args.Handled) - return true; - if (base.OnMouseLeave (mouseEvent)) - return true; - - return false; + + return args.Handled || base.OnMouseLeave (mouseEvent); } /// /// Method invoked when a mouse event is generated /// /// - /// true, if the event was handled, false otherwise. + /// , if the event was handled, otherwise. public virtual bool OnMouseEvent (MouseEvent mouseEvent) { if (!Enabled) { @@ -2827,7 +2821,7 @@ namespace Terminal.Gui { return false; } - MouseEventArgs args = new MouseEventArgs (mouseEvent); + var args = new MouseEventArgs (mouseEvent); if (OnMouseClick (args)) return true; if (MouseEvent (mouseEvent)) @@ -2869,8 +2863,8 @@ namespace Terminal.Gui { /// protected override void Dispose (bool disposing) { - for (int i = InternalSubviews.Count - 1; i >= 0; i--) { - View subview = InternalSubviews [i]; + for (var i = InternalSubviews.Count - 1; i >= 0; i--) { + var subview = InternalSubviews [i]; Remove (subview); subview.Dispose (); } @@ -2927,7 +2921,7 @@ namespace Terminal.Gui { bool CanSetWidth (int desiredWidth, out int resultWidth) { - int w = desiredWidth; + var w = desiredWidth; bool canSetWidth; if (Width is Dim.DimCombine || Width is Dim.DimView || Width is Dim.DimFill) { // It's a Dim.DimCombine and so can't be assigned. Let it have it's width anchored. @@ -2951,13 +2945,18 @@ namespace Terminal.Gui { bool CanSetHeight (int desiredHeight, out int resultHeight) { - int h = desiredHeight; + var h = desiredHeight; bool canSetHeight; - if (Height is Dim.DimCombine || Height is Dim.DimView || Height is Dim.DimFill) { + switch (Height) { + case Dim.DimCombine _: + case Dim.DimView _: + case Dim.DimFill _: // It's a Dim.DimCombine and so can't be assigned. Let it have it's height anchored. h = Height.Anchor (h); canSetHeight = !ForceValidatePosDim; - } else if (Height is Dim.DimFactor factor) { + break; + case Dim.DimFactor factor: + { // Tries to get the SuperView height otherwise the view height. var sh = SuperView != null ? SuperView.Frame.Height : h; if (factor.IsFromRemaining ()) { @@ -2965,8 +2964,11 @@ namespace Terminal.Gui { } h = Height.Anchor (sh); canSetHeight = !ForceValidatePosDim; - } else { + break; + } + default: canSetHeight = true; + break; } resultHeight = h; @@ -2978,7 +2980,7 @@ namespace Terminal.Gui { /// /// The desired width. /// The real result width. - /// true if the width can be directly assigned, false otherwise. + /// if the width can be directly assigned, otherwise. public bool SetWidth (int desiredWidth, out int resultWidth) { return CanSetWidth (desiredWidth, out resultWidth); @@ -2989,7 +2991,7 @@ namespace Terminal.Gui { /// /// The desired height. /// The real result height. - /// true if the height can be directly assigned, false otherwise. + /// if the height can be directly assigned, otherwise. public bool SetHeight (int desiredHeight, out int resultHeight) { return CanSetHeight (desiredHeight, out resultHeight); @@ -2999,10 +3001,10 @@ namespace Terminal.Gui { /// Gets the current width based on the settings. /// /// The real current width. - /// true if the width can be directly assigned, false otherwise. + /// if the width can be directly assigned, otherwise. public bool GetCurrentWidth (out int currentWidth) { - SetRelativeLayout (SuperView == null ? frame : SuperView.frame); + SetRelativeLayout (SuperView?.frame ?? frame); currentWidth = frame.Width; return CanSetWidth (0, out _); @@ -3012,10 +3014,10 @@ namespace Terminal.Gui { /// Calculate the height based on the settings. /// /// The real current height. - /// true if the height can be directly assigned, false otherwise. + /// if the height can be directly assigned, otherwise. public bool GetCurrentHeight (out int currentHeight) { - SetRelativeLayout (SuperView == null ? frame : SuperView.frame); + SetRelativeLayout (SuperView?.frame ?? frame); currentHeight = frame.Height; return CanSetHeight (0, out _); @@ -3024,8 +3026,8 @@ namespace Terminal.Gui { /// /// Determines the current based on the value. /// - /// if is - /// or if is . + /// if is + /// or if is . /// If it's overridden can return other values. public virtual Attribute GetNormalColor () { @@ -3040,12 +3042,24 @@ namespace Terminal.Gui { { View top = Application.Top; for (var v = this?.SuperView; v != null; v = v.SuperView) { - if (v != null) { - top = v; - } + top = v; } return top; } + + /// + /// Check if the is overridden in the . + /// + /// The view. + /// The method name. + /// if it's overridden, otherwise. + public bool IsOverridden (View view, string method) + { + Type t = view.GetType (); + MethodInfo m = t.GetMethod (method); + + return (m.DeclaringType == t || m.ReflectedType == t) && m.GetBaseDefinition ().DeclaringType == typeof (Responder); + } } } diff --git a/Terminal.Gui/Views/ScrollView.cs b/Terminal.Gui/Views/ScrollView.cs index 820275178..fc186bc0e 100644 --- a/Terminal.Gui/Views/ScrollView.cs +++ b/Terminal.Gui/Views/ScrollView.cs @@ -13,7 +13,6 @@ using System; using System.Linq; -using System.Reflection; namespace Terminal.Gui { /// @@ -217,7 +216,7 @@ namespace Terminal.Gui { /// The view to add to the scrollview. public override void Add (View view) { - if (!IsOverridden (view)) { + if (!IsOverridden (view, "MouseEvent")) { view.MouseEnter += View_MouseEnter; view.MouseLeave += View_MouseLeave; } @@ -237,14 +236,6 @@ namespace Terminal.Gui { Application.GrabMouse (this); } - bool IsOverridden (View view) - { - Type t = view.GetType (); - MethodInfo m = t.GetMethod ("MouseEvent"); - - return (m.DeclaringType == t || m.ReflectedType == t) && m.GetBaseDefinition ().DeclaringType == typeof (Responder); - } - /// /// Gets or sets the visibility for the horizontal scroll indicator. /// @@ -515,7 +506,7 @@ namespace Terminal.Gui { vertical.MouseEvent (me); } else if (me.Y == horizontal.Frame.Y && ShowHorizontalScrollIndicator) { horizontal.MouseEvent (me); - } else if (IsOverridden (me.View)) { + } else if (IsOverridden (me.View, "MouseEvent")) { Application.UngrabMouse (); } return true; diff --git a/UnitTests/ViewTests.cs b/UnitTests/ViewTests.cs index 5218060ab..eeb606d65 100644 --- a/UnitTests/ViewTests.cs +++ b/UnitTests/ViewTests.cs @@ -2123,6 +2123,28 @@ Y Assert.Equal (new Rect (0, 0, 8, 4), pos); } + [Fact, AutoInitShutdown] + public void DrawFrame_With_Minimum_Size () + { + var view = new View (new Rect (0, 0, 2, 2)); + + view.DrawContent += (_) => view.DrawFrame (view.Bounds, 0, true); + + Assert.Equal (Point.Empty, new Point (view.Frame.X, view.Frame.Y)); + Assert.Equal (new Size (2, 2), new Size (view.Frame.Width, view.Frame.Height)); + + Application.Top.Add (view); + Application.Begin (Application.Top); + + var expected = @" +┌┐ +└┘ +"; + + var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output); + Assert.Equal (new Rect (0, 0, 2, 2), pos); + } + [Fact, AutoInitShutdown] public void DrawFrame_With_Negative_Positions () { @@ -2452,7 +2474,7 @@ Y Width = Dim.Fill (), Height = Dim.Fill () }; - view.LayoutComplete += e => { + view.DrawContent += e => { view.DrawFrame (view.Bounds); var savedClip = Application.Driver.Clip; Application.Driver.Clip = new Rect (1, 1, view.Bounds.Width - 2, view.Bounds.Height - 2); @@ -2500,7 +2522,7 @@ Y Width = Dim.Fill (), Height = Dim.Fill () }; - view.LayoutComplete += e => { + view.DrawContent += e => { view.DrawFrame (view.Bounds); var savedClip = Application.Driver.Clip; Application.Driver.Clip = new Rect (1, 1, view.Bounds.Width - 2, view.Bounds.Height - 2);