From c00de4a0927fe6015d3f7da8c74ef19e9bb8022a Mon Sep 17 00:00:00 2001 From: Tig Date: Thu, 27 Feb 2025 12:24:31 -0700 Subject: [PATCH] Makes `Window` default `Arrangement` not be movable/resizable (#3931) * Window -> not sizeable/movable by default. Window: Code cleanup. Frameview: Code cleanup. * View API docs updates * View API docs updates --- Terminal.Gui/View/View.cs | 291 +++++++++++++++----- Terminal.Gui/Views/FrameView.cs | 30 +- Terminal.Gui/Views/Window.cs | 35 +-- UICatalog/Scenarios/Adornments.cs | 2 +- UICatalog/Scenarios/ComputedLayout.cs | 139 +++++----- UICatalog/Scenarios/WindowsAndFrameViews.cs | 16 +- UnitTests/Views/WindowTests.cs | 2 + docfx/docs/scrolling.md | 4 +- 8 files changed, 345 insertions(+), 174 deletions(-) diff --git a/Terminal.Gui/View/View.cs b/Terminal.Gui/View/View.cs index 35de80f2b..095ca31ad 100644 --- a/Terminal.Gui/View/View.cs +++ b/Terminal.Gui/View/View.cs @@ -8,8 +8,13 @@ namespace Terminal.Gui; /// /// View is the base class all visible elements. View can render itself and -/// contains zero or more nested views, called SubViews. View provides basic functionality for layout, positioning, and -/// drawing. In addition, View provides keyboard and mouse event handling. +/// contains zero or more nested views, called SubViews. View provides basic functionality for layout, arrangement, and +/// drawing. In addition, View provides keyboard and mouse event handling. See the +/// +/// View +/// Deep Dive +/// +/// for more. /// /// /// @@ -27,73 +32,222 @@ namespace Terminal.Gui; /// /// SuperViewThe View that is a container for SubViews. /// +/// +/// Input +/// +/// +/// Key Bindings is the preferred way of handling keyboard input in View implementations. +/// The View calls +/// to declare +/// it supports a particular command and then uses +/// to indicate which key presses will invoke the command. +/// +/// +/// Mouse Bindings is the preferred way of handling mouse input in View implementations. The View calls +/// to declare +/// it supports a +/// particular command and then uses to indicate which mouse events will +/// invoke the command. +/// +/// +/// See the +/// +/// Mouse +/// Deep Dive +/// +/// and +/// +/// Keyboard +/// Deep Dive +/// +/// for more information. +/// +/// +/// +/// +/// Layout +/// +/// +/// Terminal.Gui provides a rich system for how View objects are laid out relative to each other. The +/// layout system also defines how coordinates are specified. +/// +/// +/// The , , , and properties are +/// and objects that dynamically update the position of a view. The +/// X and Y properties are of type and you can use either absolute positions, +/// percentages, or anchor points. The Width and Height properties are of type and +/// can use absolute position, percentages, and anchors. These are useful as they will take care of +/// repositioning views when view's adornments are resized or if the terminal size changes. +/// +/// +/// See the +/// +/// Layout +/// Deep Dive +/// +/// for more information. +/// +/// +/// +/// +/// Arrangement +/// +/// +/// Complimenting the Layout system, controls how the user can use the mouse +/// and keyboard to arrange views and enables either Tiled or Overlapped layouts. +/// +/// +/// See the +/// +/// Arrangement +/// Deep Dive +/// +/// for more information. +/// +/// +/// +/// +/// Drawing +/// +/// +/// Apps draw using the and APIs. Move selects the +/// column and row of the Cell and AddRune places +/// the specified glyph in that cell using the that was most recently set via +/// . +/// The ConsoleDriver caches all changed Cells and efficiently outputs them to the terminal each +/// iteration of the Application. In other words, Terminal.Gui uses deferred rendering. +/// +/// +/// The View draw APIs all take coordinates specified in Viewport-Relative coordinates. That is, +/// (0,0) is the top-left cell visible to the user. +/// +/// +/// If a View need to redraw because something changed within it's Content Area it can call +/// . +/// +/// +/// Terminal.Gui supports the full range of Unicode/wide characters. +/// This includes emoji, CJK characters, and other wide characters. For Unicode characters that require +/// more than one cell, +/// AddRune and the ConsoleDriver automatically manage the cells. Extension methods to Rune are +/// provided to determine if a Rune is a wide character and to get the width of a Rune. +/// +/// +/// The provides consistent colors across all views. The +/// is inherited from the . The +/// is used to set the for drawing. +/// +/// The class represents a color. It provides automatic mapping between the legacy +/// 4-bit (16-color) system and 24-bit colors. It contains properties for the red, green, and blue +/// components of the color. +/// The Color class also contains a static property for each of the 16 ANSI colors. Use +/// to change the colors used when drawing. +/// +/// +/// +/// Clipping enables better performance by ensuring on regions of the terminal that need to be drawn +/// actually get drawn by the ConsoleDriver. Terminal.Gui supports non-rectangular clip regions with +/// . +/// There is an -managed clip region. Developers cannot change this directly, +/// but can use , , and to +/// modify the clip region. +/// +/// +/// provides auto join, a smart TUI drawing system that automatically selects +/// the correct line/box drawing glyphs for intersections making drawing complex shapes easy. +/// +/// +/// A set of static properties are provided for the common glyphs used in TUI apps. See +/// . +/// +/// +/// See the +/// +/// Drawing +/// Deep Dive +/// +/// for more information. +/// +/// +/// +/// +/// Text +/// +/// +/// A rich text formatting engine is provided in . TextFormatter provides +/// methods for formatting text with horizontal and vertical alignment, word wrapping, and hotkeys. +/// +/// +/// See the +/// +/// Navigation +/// Deep Dive +/// +/// for more information. +/// +/// +/// +/// +/// Navigation +/// +/// +/// Navigation refers to the user experience for moving focus between views in the application +/// view-hierarchy. Focus is a concept that is used to describe which View is currently receiving user +/// input. Only +/// Views that are +/// , , and will receive focus. NOTE: +/// is by default. +/// +/// +/// Views that are focusable should override to make sure that the cursor +/// is +/// placed in a location that makes sense. Some terminals do not have a way of hiding the cursor, so it +/// can be +/// distracting to have the cursor left at the last focused view. So views should make sure that they +/// place the +/// cursor in a visually sensible place. The default implementation of +/// will place the +/// cursor at either the hotkey (if defined) or 0,0. +/// +/// +/// See the +/// +/// Navigation +/// Deep Dive +/// +/// for more information. +/// +/// +/// +/// +/// Scrolling +/// +/// +/// The ability to scroll content is built into View. The represents the +/// scrollable "viewport" into the View's Content Area (which is defined by the return value of +/// ). +/// +/// +/// Terminal.Gui also provides the ability show a visual scroll bar that responds to mouse input. This +/// ability is not enabled by default given how precious TUI screen real estate is. +/// Use and to enable this feature. +/// +/// +/// Use to adjust the behavior of scrolling. +/// +/// +/// See the +/// +/// Scrolling +/// Deep Dive +/// +/// for more information. +/// +/// +/// /// /// -/// Focus is a concept that is used to describe which View is currently receiving user input. Only Views that are -/// , , and will receive focus. -/// -/// -/// Views that are focusable should override to make sure that the cursor is -/// placed in a location that makes sense. Some terminals do not have a way of hiding the cursor, so it can be -/// distracting to have the cursor left at the last focused view. So views should make sure that they place the -/// cursor in a visually sensible place. The default implementation of will place the -/// cursor at either the hotkey (if defined) or 0,0. -/// -/// -/// 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. -/// -/// -/// To create a View using Absolute layout, call a constructor that takes a Rect parameter to specify the -/// absolute position and size or simply set ). To create a View using Computed layout use -/// a constructor that does not take a Rect parameter and set the X, Y, Width and Height properties on the view to -/// non-absolute values. Both approaches use coordinates that are relative to the of the -/// the View is added to. -/// -/// -/// Computed layout is more flexible and supports dynamic console apps where controls adjust layout as the -/// terminal resizes or other Views change size or position. The , , -/// , and properties are and objects -/// that dynamically update the position of a view. The X and Y properties are of type and you -/// can use either absolute positions, percentages, or anchor points. The Width and Height properties are of type -/// and can use absolute position, percentages, and anchors. These are useful as they will take -/// care of repositioning views when view's adornments are resized or if the terminal size changes. -/// -/// -/// Absolute layout requires specifying coordinates and sizes of Views explicitly, and the View will typically -/// stay in a fixed position and size. To change the position and size use the property. -/// -/// -/// Subviews (child views) can be added to a View by calling the method. The container of -/// a View can be accessed with the property. -/// -/// -/// To flag a region of the View's to be redrawn call -/// -/// . -/// To flag the entire view for redraw call . -/// -/// -/// The method is called when the size or layout of a view has changed. The -/// will -/// cause to be called on the next so there is normally -/// no reason to direclty call -/// see . -/// -/// -/// Views have a property that defines the default colors that subviews should use for -/// rendering. This ensures that the views fit in the context where they are being used, and allows for themes to -/// be plugged in. For example, the default colors for windows and Toplevels uses a blue background, while it uses -/// a white background for dialog boxes and a red background for errors. -/// -/// -/// Subclasses should not rely on being set at construction time. If a -/// is not set on a view, the view will inherit the value from its -/// and the value might only be valid once a view has been added to a SuperView. -/// -/// By using applications will work both in color as well as black and white displays. -/// -/// Views can also opt-in to more sophisticated initialization by implementing overrides to +/// Views can opt in to more sophisticated initialization by implementing overrides to /// and which will be called /// when the view is added to a . /// @@ -369,6 +523,7 @@ public partial class View : IDisposable, ISupportInitializeNotification SetNeedsLayout (); SuperView?.SetNeedsLayout (); SetNeedsDraw (); + if (SuperView is { }) { SuperView?.SetNeedsDraw (); @@ -576,7 +731,7 @@ public partial class View : IDisposable, ISupportInitializeNotification } /// - /// Riased when the is being disposed. + /// Riased when the is being disposed. /// public event EventHandler? Disposing; diff --git a/Terminal.Gui/Views/FrameView.cs b/Terminal.Gui/Views/FrameView.cs index 20bbbec46..886c9ca1f 100644 --- a/Terminal.Gui/Views/FrameView.cs +++ b/Terminal.Gui/Views/FrameView.cs @@ -1,9 +1,21 @@ -namespace Terminal.Gui; +#nullable enable +namespace Terminal.Gui; // TODO: FrameView is mis-named, really. It's far more about it being a TabGroup than a frame. + /// -/// The FrameView is a container View with a border around it. +/// A non-overlapped container for other views with a border and optional title. /// +/// +/// +/// FrameView has set to and +/// inherits it's color scheme from the . +/// +/// +/// +/// +/// +/// public class FrameView : View { /// @@ -14,21 +26,9 @@ public class FrameView : View { CanFocus = true; TabStop = TabBehavior.TabGroup; - Border.Thickness = new Thickness (1); - Border.LineStyle = DefaultBorderStyle; - - //Border.ColorScheme = ColorScheme; - Border.Data = "Border"; - MouseClick += FrameView_MouseClick; + BorderStyle = DefaultBorderStyle; } - private void FrameView_MouseClick (object sender, MouseEventArgs e) - { - // base sets focus on HotKey - e.Handled = InvokeCommand (Command.HotKey, new ([Command.HotKey], this, this)) == true; - } - - /// /// The default for 's border. The default is /// . diff --git a/Terminal.Gui/Views/Window.cs b/Terminal.Gui/Views/Window.cs index 51cebd300..2bbfc701c 100644 --- a/Terminal.Gui/Views/Window.cs +++ b/Terminal.Gui/Views/Window.cs @@ -1,25 +1,22 @@ -namespace Terminal.Gui; +#nullable enable +namespace Terminal.Gui; /// -/// A with set to -/// . Provides a container for other views. +/// An overlapped container for other views with a border and optional title. /// /// /// -/// If any subview is a button and the property is set to true, the Enter key will -/// invoke the command on that subview. +/// Window has set to , +/// set to , and +/// uses the Base color scheme by default. +/// +/// +/// To enable Window to be sized and moved by the user, adjust . /// /// +/// public class Window : Toplevel { - - /// - /// Gets or sets whether all s are shown with a shadow effect by default. - /// - [SerializableConfigurationProperty (Scope = typeof (ThemeScope))] - public static ShadowStyle DefaultShadow { get; set; } = ShadowStyle.None; - - /// /// Initializes a new instance of the class. /// @@ -27,12 +24,18 @@ public class Window : Toplevel { CanFocus = true; TabStop = TabBehavior.TabGroup; - Arrangement = ViewArrangement.Movable | ViewArrangement.Overlapped | ViewArrangement.Resizable; - ColorScheme = Colors.ColorSchemes ["Base"]; // TODO: make this a theme property + Arrangement = ViewArrangement.Overlapped; + base.ColorScheme = Colors.ColorSchemes ["Base"]; // TODO: make this a theme property BorderStyle = DefaultBorderStyle; - ShadowStyle = DefaultShadow; + base.ShadowStyle = DefaultShadow; } + /// + /// Gets or sets whether all s are shown with a shadow effect by default. + /// + [SerializableConfigurationProperty (Scope = typeof (ThemeScope))] + public static ShadowStyle DefaultShadow { get; set; } = ShadowStyle.None; + // TODO: enable this ///// ///// The default for 's border. The default is . diff --git a/UICatalog/Scenarios/Adornments.cs b/UICatalog/Scenarios/Adornments.cs index 55feb28ec..f2e664f9c 100644 --- a/UICatalog/Scenarios/Adornments.cs +++ b/UICatalog/Scenarios/Adornments.cs @@ -34,7 +34,7 @@ public class Adornments : Scenario var window = new Window { Title = "The _Window", - Arrangement = ViewArrangement.Movable, + Arrangement = ViewArrangement.Overlapped | ViewArrangement.Movable, Width = Dim.Fill (Dim.Func (() => editor.Frame.Width )), Height = Dim.Fill () diff --git a/UICatalog/Scenarios/ComputedLayout.cs b/UICatalog/Scenarios/ComputedLayout.cs index c27eabdb3..4eb15e4af 100644 --- a/UICatalog/Scenarios/ComputedLayout.cs +++ b/UICatalog/Scenarios/ComputedLayout.cs @@ -2,12 +2,11 @@ using System.Collections.Generic; using System.Linq; using Terminal.Gui; -using static Terminal.Gui.Dialog; namespace UICatalog.Scenarios; /// -/// This Scenario demonstrates how to use Terminal.Gui's Dim and Pos Layout System. +/// This Scenario demonstrates how to use Terminal.Gui's Dim and Pos Layout System. /// [ScenarioMetadata ("Computed Layout", "Demonstrates the Computed (Dim and Pos) Layout System.")] [ScenarioCategory ("Layout")] @@ -19,7 +18,7 @@ public class ComputedLayout : Scenario Window app = new () { - Title = GetQuitKeyAndName (), + Title = GetQuitKeyAndName () }; // Demonstrate using Dim to create a horizontal ruler that always measures the parent window's width @@ -51,19 +50,20 @@ public class ComputedLayout : Scenario }; app.SubviewsLaidOut += (s, a) => - { - if (horizontalRuler.Viewport.Width == 0 || horizontalRuler.Viewport.Height == 0) - { - return; - } - horizontalRuler.Text = - rule.Repeat ((int)Math.Ceiling (horizontalRuler.Viewport.Width / (double)rule.Length)) [ - ..horizontalRuler.Viewport.Width]; + { + if (horizontalRuler.Viewport.Width == 0 || horizontalRuler.Viewport.Height == 0) + { + return; + } - verticalRuler.Text = - vrule.Repeat ((int)Math.Ceiling (verticalRuler.Viewport.Height * 2 / (double)rule.Length)) - [..(verticalRuler.Viewport.Height * 2)]; - }; + horizontalRuler.Text = + rule.Repeat ((int)Math.Ceiling (horizontalRuler.Viewport.Width / (double)rule.Length)) [ + ..horizontalRuler.Viewport.Width]; + + verticalRuler.Text = + vrule.Repeat ((int)Math.Ceiling (verticalRuler.Viewport.Height * 2 / (double)rule.Length)) + [..(verticalRuler.Viewport.Height * 2)]; + }; app.Add (verticalRuler); @@ -77,7 +77,12 @@ public class ComputedLayout : Scenario // Demonstrate using Dim to create a window that fills the parent with a margin var margin = 10; - var subWin = new Window { X = Pos.Center (), Y = 2, Width = Dim.Fill (margin), Height = 7 }; + + var subWin = new Window + { + X = Pos.Center (), Y = 2, Width = Dim.Fill (margin), Height = 7, + Arrangement = ViewArrangement.Movable | ViewArrangement.Resizable | ViewArrangement.Overlapped + }; subWin.Initialized += (s, a) => { @@ -89,10 +94,10 @@ public class ComputedLayout : Scenario var i = 1; var txt = "Resize the terminal to see computed layout in action."; List