Terminal.Gui icon indicating copy to clipboard operation
Terminal.Gui copied to clipboard

Adornments - Views in `Margin`, `Border`, and `Padding`

Open tig opened this issue 2 years ago • 7 comments

  • Adornments - Adornments is the term used to describe Views that are added to Margin, Border, and Padding.
    • Adornments are not part of the View's content and are not clipped by the View's ClipArea.
    • Why?
      • Changing the API such that myview.Add is ONLY for subviews and adding a View.<Frame>.Add API for menu, StatusBar, scroll bar... will enable us to simplify the codebase.
      • Simplfiies the programming model for mainstream use-cases: Developers know that Views.Subviews ONLY contains views they explicitly added with View.Add.
    • Examples of Adornments:
      • A TitleBar renders the View's Title and a horizontal line defining the top of the View. Added to Border.
      • One or more LineViews that render the View's 'Border`
      • A Vertical Scrollbar adds thickness to Padding.Right (or .Left when right-to-left language support is added).
      • A Horizontal Scrollbar adds thickness to Padding.Bottom when enabled.
      • A MenuBar adds thickness to Padding.Top (NOTE: This is a change from v1 where subview.Y = 1 is required).
      • A StatusBar adds thickness ot Padding.Bottom and is rendered at the bottom of Padding.
      • In Dialog (and derived classes), the buttons can be added to Padding (Y = Pos.AnchorEnd(1)).

I've just built an experiment in modifying Dialog such that the buttons are placed in Padding. This raised some questions:

  • Should expanding of, e.g. Padding.Thickness be automatic when Padding.Add is called? If Dim.Fit is implemented, it might be possible for this to happen. For now: I suggest devs adding adornments manually expand thickness as appropriate. In my Dialog prototype, I did this:
		public void AddButton (Button button)
		{
			if (button == null) {
				return;
			}
			
			_buttons.Add (button);
			Padding.Thickness = new Thickness (Padding.Thickness.Top, Padding.Thickness.Left, Padding.Thickness.Right, 1);
			Padding.Add (button);
			LayoutButtons ();
		}
  • How should Focus work? Because Padding is in a different view hierarchy from the dialog, Tab etc... doesn't tab into the buttons. I knew this would be an issue, but hadn't thought it all the way through.

    • Proposal:
      • Padding - Any views added to Padding are included in Padding.Parent's focus chain. If View.Subviews[^1].HasFocus and the user presses Tab Padding.SubViews[0] will get focus. Etc...
      • Border - Ctrl-F10 is used to set focus on Border.Subviews[0]. Subsequent Tab presses cycle through Border.Subviews. Ctrl-F10 again returns focus to Border.Parent.
      • Margin - No default behavior. If someone adds views to Margin they are on their own to figure out a focus model (I can't think of any use case where it makes sense to add a subview to Margin.
  • How should MouseEvents work? In the current v2_develop Margin, Border, & Padding are not getting mouse events (or there's a bug where they are but the coords are wrong. I think this just needs to be fixed. I don't think there's anything special that needs to happen other than ensuring these views and their subviews get the right mouse events.

Am I missing anything else here?

What do @bidisp & @tznind (and others) think of this proposal?

tig avatar Apr 17 '23 13:04 tig

How you think on handling the StatusBar and a ScrollBar on Padding? If a StatusBar exists then the ScrollBar should be above, right? The MouseEvent should be handled in the same way as the KeyEvent by using the the Parent property. The FindDeepestView must be changed to look in the parent too if a null is returned.

BDisp avatar Apr 17 '23 14:04 BDisp

How you think on handling the StatusBar and a ScrollBar on Padding? If a StatusBar exists then the ScrollBar should be above, right?

Yes, I think that's right.

This reminds me of something else I was thinking about:

Thickness needs rules around which edges have precedence, by default.

E.g. which is more true, given view.Padding.Thickness = new Thickness(1); view.Width = 10; view.Height = 10;:

  • Padding.Top.Width == 10 and Padding.Left.Height == 10 OR
  • Padding.Top.Width == 10 and Padding.Left.Height == 8

Note, I do NOT mean Padding.Left.Height should be different if Padding.Top/Bottom > 0 literally; just to illustrate something that could be very confusing.

Regardless, here's how I think we'd lay out a View with all four adornments:

Padding.Thickness = new Thickness (
  _menuBar.Visible ? 1 : 0,    // Top
  0,  // Left
  _verticalScrollBar.Visible ? 1: 0,  // Right
  (_statusBar.Visible ? 1 : 0) + (_horizontalScrollBar.Visible ? 1 :0));  // Bottom

// These have all been `Padding.Add()`ed
_menuBar.X = 0;
_menuBar.Y = 0;
_menuBar.Width = Dim.Fill();
_menuBar.Height = 1;

_statusBar.X = 0;
_statusBar.Y = Pos.AnchorEnd (1);
_statusBar.Width = Dim.Fill();
_statusBar.Height = 1;

_verticalScrollbar.X = Pos.AnchorEnd(1);
_verticalScrollbar.Y = _menuBar.Visible ? Pos.Bottom (_menuBar) : 0;
_verticalScrollbar.Width = 1;
_verticalScrollbar.Height = Dim.Fill ((_menuBar.Visible ? 1 : 0) + (_statusBar.Visible ? 1 : 0) + (_horizontalScrollBar.Visible ? 1 : 0));

_horizontalScrollbar.X = 0;
_horizontalcrollbar.Y = Pos.AnchorEnd (_statusBar.Visible ? 2 : 1);
_horizontalScrollbar.Width = Dim.Fill ();
_horizontalScrollbar.Height = 1;

tig avatar Apr 17 '23 15:04 tig

  • In Dialog (and derived classes), the buttons can be added to Padding (Y = Pos.AnchorEnd(1)).

And in the cases where we want buttons arbitrary on others locations, that possible, right? Added to Padding you only mean the ones that are passed by the params, right?

BDisp avatar Apr 17 '23 15:04 BDisp

  • In Dialog (and derived classes), the buttons can be added to Padding (Y = Pos.AnchorEnd(1)).

And in the cases where we want buttons arbitrary on others locations, that possible, right? Added to Padding you only mean the ones that are passed by the params, right?

Correct.

On the same note, for Wizard I plan on putting the HelpText view (a TextView that is set to readonly) in Padding:

Padding.Thickness = new Thickness (Padding.Thickness.Top, Padding.Thickness.Left, Padding.Thickness.Right, 1);
_helpText.X = Pos.AnchorEnd (1);
_helpText.Y = 0;
_helpText.Width = 15; 
_helpText.Height = Dim.Fill (2); // +1 for buttons on Bottom
Padding.Add (_helpText);

tig avatar Apr 17 '23 15:04 tig

A key part of addressing this issue will be:

  • enable Margin, Border, Padding to be overridden/replaced by a View derivative

Less important (and highly debatlve), but interesting. Including this just to make sure we are intentional:

  • enable Margin, Border, Padding to be overriden/replaced by a user of a View.

tig avatar Nov 15 '23 15:11 tig

A key part of addressing this issue will be:

  • enable Margin, Border, Padding to be overridden/replaced by a View derivative

Less important (and highly debatlve), but interesting. Including this just to make sure we are intentional:

  • enable Margin, Border, Padding to be overriden/replaced by a user of a View.

Just to remember that you may want to join LineCanvas through a View (Margin, Border, Padding) but not with others views.

BDisp avatar Nov 15 '23 16:11 BDisp

A key part of addressing this issue will be:

  • enable Margin, Border, Padding to be overridden/replaced by a View derivative

Less important (and highly debatlve), but interesting. Including this just to make sure we are intentional:

  • enable Margin, Border, Padding to be overriden/replaced by a user of a View.

Just to remember that you may want to join LineCanvas through a View (Margin, Border, Padding) but not with others views.

If I understand you correctly, this is what SuperViewRendersLineCanvas is for.

tig avatar Nov 15 '23 19:11 tig