Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #2558. MenuBar positions wrong in some situations. #2567

Merged
merged 21 commits into from
May 5, 2023

Conversation

BDisp
Copy link
Collaborator

@BDisp BDisp commented Apr 20, 2023

Fixes #2558 - Allows managing driver frame offset for smaller tops. @tig I think you'll prefer this PR instead of #2561.

Pull Request checklist:

  • I've named my PR in the form of "Fixes #issue. Terse description."
  • My code follows the style guidelines of Terminal.Gui - if you use Visual Studio, hit CTRL-K-D to automatically reformat your files before committing.
  • My code follows the Terminal.Gui library design guidelines
  • I ran dotnet test before commit
  • I have made corresponding changes to the API documentation (using /// style comments)
  • My changes generate no new warnings
  • I have checked my code and corrected any poor grammar or misspellings
  • I conducted basic QA to assure all features are working

menu-on-top-dialog

@tig
Copy link
Collaborator

tig commented Apr 23, 2023

@BDisp I've started reviewing this, and as I've read the code I've just gotten myself confused.

I need to step back from all of the details and make sure I fully understand how we want menus to work.

Let's riff on it here in the comments, please... Please correct anything I state below if I'm incorrect!

What is "MenuBar"?

  • A "MenuBar" is a View that shows a set of menu items horizontally aligned next to each other in a single row.
  • A MenuBar contains MenuBarItems (menuBar.Menus).
  • In v1, it is possible to have MenuBar.SuperView == null.
    • I believe this is ONLY used in the case of ContextMenu. I also believe this is poor design and should be fixed in v2: "MenuBar is just a view like any other view. We don't allow any normal views to not be Added to a superview EXCEPT Application.Top.
    • In these MenuBar.SuperView == null cases:
      • Application.Top is assumed to be the SuperView
      • Application.Current.Focused is used to return focus to the previously focused View after the menu closes.
  • A MenuBar has a default position of (0, 0) with a dimension of (Dim.Fill(), 1). Visible is true by default.
  • Calling MenuBar.Activate or MenuBar.OpenMenu causes the first contained MenuBarItem to be shown.

Where should MenuBar render?

  • Never outside of SuperView.Bounds - Other view's can't do this, so why should menubar be special?

Can a MenuBar be drawn vertically?

  • No (at least not yet).

What's a MenuBarItem?

  • A MenuBarItem is a subclass of MenuItem.
  • A MenuBarItem is NOT a View. It is a class that contains a list of MenuItem instances (menuBarItem.Children).
  • A MenuBar's Children are displayed Vertically - Three scenarios:
      1. When the MenuBarItem is contained by a MenuBar and is activated and drops down below the MenuBar
      1. When the MenuBarItem is contained by a MenuBar created by ContextMenu. When ContextMenu.Show is called, the ContextMenu.MenuBar is instantiated and menuBar.OpenMenu is called dropping the menu down at a specific location, see below. Note, in this case, the MenuBar itself is never rendered!
      1. When a MenuBarItem, contained by a drop-down MenuBar (from There is a problem with the high-intensity colors, they are not showing up #1 or Add resizing support to the core #2 above) is activated, it will drop down alongside the MenuBarItem
    • Note: the UseSubMenusSingleFrame property tweaks the above behavior to constrain all of the contained MenuBarItems are drawn within a rectangle defined by either the largest MenuItem or the parent of the MenuBar (I'm not sure this is right; this feature appears broken in current builds).

Where should MenuBarItem render?

  • Never outside of Parent.SuperView.Bounds

What's ContextMenu and how does it work?

  • This class is a View that is a helper for showing MenuBarItems in a "context menu" scenario. It uses MenuBar internally in a fairly hacky way.
  • One way to view ContextMenu is it is a "Hidden MenuBar that supports only a single MenuBarItem".
  • ContextMenu.MenuBar is public and besides keep the MenuBar in sync with open/close events it is ONLY used in Unit Tests.
  • ContextMenu.Host is documented as "The host View which position will be used, Otherwise if it's null the container will be used."
    • It is ONLY used to determine the POSITION of the the embedded MenuBar
  • MenuBar.isContextMenuClosing is poor OO design; it is an internal linkage between MenuBar and ContextMenu.
  • UseSubMenusSingleFrame duplicates the same property in MenuBar.
  • IsShow ("Gets whether the ContextMenu is showing or not.") is a static meaning only a single context menu can be shown at a time (which makes sense).
  • Key is documented as "specifies the keyboard key that will activate the context menu with the keyboard." but this is actually not accurate. All it does is specifies the key that will DEACTIVATE the context menu. Callers need to set up a Command or something else to know when to call contextMenu.Show().

What is private class Menu : View?

  • PRIVATE class that does the work of showing a MenuBarItem as a drop-down or pop-up. Remember, MenuBarItem is NOT a View. So this class basically wraps the data in a MenuBarItem in a View.
  • When MenuBar.OpenMenu is called, instances of Menu are created for each MenuBarItem in the menu hierarchy, dynamically. When MenuBar.CloseMenu is called all of these instances are cleaned up.
  • Contains a copy of the list of MenuBarItems of it's 'host'.
  • Whenever a Menu is opened (Menu.OpenMenu) it is added to Application.Top as a subview.
  • IOW, Menu is a View whose SuperView is Application.Top (Application.Top.Add (openMenu);)
    • THIS IS A DESIGN FLAW that I think is the root cause of this issue (MenuBar positions wrong in some situations #2558).
    • Instead, the SuperView of a Menu should be the hosting MenuBar's SuperView
    • Menu should be a MODAL (menu.Modal = true) View.

PROPOSAL:

  • Change private class Menu : View as follows:

    • Make it a modal (Modal = true); create it with Applicaiton.Run (openMenu)
    • Get rid of Menu.Run per above
    • Use computed layout for positioning and sizing instead of MakeFrame (have caller set X, Y, Height, Width)
  • Introduce a new base class for menus named MenuBase, derived from View. Have it contain

    • MenuBar
      • .Menus
      • .<keyboard handling members> (including default Commands).
      • .<style members> (e.g. UseSubMenusSingleFrame)
      • .OnEnter/OnLeave
      • .MenuOpening/Opened/Closing etc... events
      • .OpenMenu()/CloseMenu()
      • Enforce that SuperView != null
      • Move IsShow to MenuBase and change it to be of public static MenuBase ActiveMenu. If 'nullthen it's the same asIsShow == true. It should get set to thisin.OpenMenu()and tonullin.CloseMenu(); the logic should ensure that one MenuBarBase` is closed before another one is open.
  • Refactor MenuBar to derive from MenuBase and :

    • .OnDraw (horizontal rendering)
    • When activated set ActiveMenu = this.
  • Refactor ContextMenu to derive from MenuBase and :

    • .Visible = false at construction
    • Get rid of .Host. All views MUST have a SuperView. Use SuperView for positioning and sizing.
    • Get rid of Position - .X and .Y already provide the location!
    • .Show() should be an override of MenuBase.OpenMenu(). It should:
      • Set .Visible = true
      • Set .X and .Y to coordinates that
    • Fix ContextMenu.Key documentation to be more clear.

In short, I think we need to refactor the entire Menu class hierarchy to work well in a world where

  • We have the concept of Modal views that don't have to be of type Toplevel
  • We have Bounds != Frame

By doing this, I believe the complexity of all of this will be reduced.

I could be wrong. But I think it's worth taking a serious look at.

Are you willing to do so, @BDisp?

@BDisp
Copy link
Collaborator Author

BDisp commented Apr 24, 2023

@BDisp I've started reviewing this, and as I've read the code I've just gotten myself confused.

I need to step back from all of the details and make sure I fully understand how we want menus to work.

Let's riff on it here in the comments, please... Please correct anything I state below if I'm incorrect!

What is "MenuBar"?

* A "MenuBar" is a `View` that shows a set of menu items horizontally aligned next to each other in a single row.

* A MenuBar _contains_ `MenuBarItems` (`menuBar.Menus`).

* In v1, it is possible to have `MenuBar.SuperView == null`.
  
  * I believe this is ONLY used in the case of `ContextMenu`. I also believe this is poor design and should be fixed in v2: "MenuBar is just a view like any other view. We don't allow any normal views to not be `Add`ed to a superview EXCEPT `Application.Top`.
  * In these `MenuBar.SuperView == null` cases:

Actually we only can create Menus with a MenuBar because the Menu class is private. In the case of the ContextMenu we only needed to use the Menus and not the MenuBar itself and that the reason why it isn't added to the Application.SuperView.

    * `Application.Top` is assumed to be the SuperView

I already fixed that in this PR. Is the Application.Current that must be assumed as SuperView, but could happen to be the same as Application.Top. If the Application.Current is smaller than the console driver size (Cols and Rows) then we need to use the offset size to draw correctly in the right position and this PR is a suggestion fix for this.

    * `Application.Current.Focused` is used to return focus to the previously focused View after the menu closes.

* A `MenuBar` has a default position of `(0, 0)` with a dimension of `(Dim.Fill(), 1)`. `Visible` is `true` by default.

* Calling `MenuBar.Activate` or `MenuBar.OpenMenu` causes the first contained `MenuBarItem` to be shown.

Where should MenuBar render?

* Never outside of `SuperView.Bounds` - Other view's can't do this, so why should menubar be special?

I think you are confusing the MenuBar with the Menu. A visible MenuBar is never rendered outside a SuperView.Bounds but a Menu in my opinion can, because if you are opening a Menu on a smaller superview it must be showed, no matter if it fit or not in the SuperView.Bounds, like a drop-down list of a ComboBox must also could do.

Can a MenuBar be drawn vertically?

* No (at least not yet).

Of course it can, but not actually yet. Is a design and layout matter.

What's a MenuBarItem?

* A MenuBarItem is a subclass of `MenuItem`.

* A MenuBarItem is NOT a `View`. It is a class that contains a list of `MenuItem` instances (`menuBarItem.Children`).

* A MenuBar's `Children` are displayed Vertically - Three scenarios:
  
  * 1. When the MenuBarItem is contained by a MenuBar and is activated and drops down below the MenuBar
  * 2. When the MenuBarItem is contained by a MenuBar created by `ContextMenu`. When `ContextMenu.Show` is called, the `ContextMenu.MenuBar` is instantiated and `menuBar.OpenMenu` is called dropping the menu down at a specific location, see below. Note, in this case, the `MenuBar` itself is never rendered!

ContextMenu.MenuBar is instantiated with the parent/host of the menu, providing the specific location. The MenuBar doesn't need to be rendered. It only is used to show the Menu.

  * 3. When a MenuBarItem, contained by a drop-down MenuBar (from [There is a problem with the high-intensity colors, they are not showing up #1](https://github.com/gui-cs/Terminal.Gui/issues/1) or [Add resizing support to the core #2](https://github.com/gui-cs/Terminal.Gui/issues/2) above) is activated, it will drop down alongside the MenuBarItem
  * Note: the `UseSubMenusSingleFrame` property tweaks the above behavior to constrain all of the contained `MenuBarItem`s are drawn within a rectangle defined by either the largest MenuItem or the parent of the MenuBar (I'm not sure this is right; this feature appears broken in current builds).

It was broken in the v2, but I already fixed. See the unit tests

Numbers
┌─────────────┐
│◄ Two │
├─────────────┤
│ Sub-Menu 1 │
│ Sub-Menu 2 │
└─────────────┘
, which is working well.

Where should MenuBarItem render?

* Never outside of `Parent.SuperView.Bounds`

I don't agree because of the reasons I already explained above. Only the MenuBar shouldn't never be render outside the Parent.SuperView.Bounds, but the Menu can be render outside the Parent.SuperView.Bounds to allow be showed on smaller toplevel.

What's ContextMenu and how does it work?

* This class is a `View` that is a helper for showing `MenuBarItems` in a "context menu" scenario. It uses `MenuBar` internally in a fairly hacky way.

* One way to view `ContextMenu` is it is a "Hidden MenuBar that supports only a single MenuBarItem".

But also supports a MenuItem that has sub-menus.

* `ContextMenu.MenuBar` is `public` and besides keep the `MenuBar` in sync with open/close events it is ONLY used in Unit Tests.

But also can be used on a view, if needed.

* `ContextMenu.Host` is documented as "The host View which position will be used, Otherwise if it's null the container will be used."

It can be null if you right click on any location of a toplevel to open the ContextMenu in the current mouse position.

  * It is ONLY used to determine the POSITION of the the embedded `MenuBar`

* `MenuBar.isContextMenuClosing` is poor OO design; it is an internal linkage between `MenuBar` and `ContextMenu`.

Where is that code? I can't find it. I think you are mean about MenuBar.isContextMenuLoading and it's really a linkage.

* UseSubMenusSingleFrame duplicates the same property in `MenuBar`.

The reason for that is a user can prefer use this property as true on a MenuBar and as false on a ContextMenu or vice-versa.

* `IsShow` ("Gets whether the ContextMenu is showing or not.") is a `static` meaning only a single context menu can be shown at a time (which makes sense).

That right :-)

* `Key` is documented as "specifies the keyboard key that will activate the context menu with the keyboard." but this is actually not accurate. All it does is specifies the key that will DEACTIVATE the context menu. Callers need to set up a `Command` or something else to know when to call `contextMenu.Show()`.

The reason for this property is to avoid each view having different key to activate the ContextMenu. Otherwise would be very difficult to decorate all the keys for all views. This normally is initialized on the constructor of the view who need it by using AddKeyBinding (ContextMenu.Key, Command.Accept);

What is private class Menu : View?

* PRIVATE class that does the work of showing a `MenuBarItem` as a drop-down or pop-up. Remember, `MenuBarItem` is NOT a `View`. So this class basically wraps the data in a `MenuBarItem` in a `View`.

Like each item of a ListView isn't a View. It's only a design matter. We can manipulate and show the way as we like.

* When `MenuBar.OpenMenu` is called, instances of `Menu` are created for each `MenuBarItem` in the menu hierarchy, dynamically. When `MenuBar.CloseMenu` is called all of these instances are cleaned up.

* Contains a copy of the list of `MenuBarItems` of it's 'host'.

* Whenever a `Menu` is opened (`Menu.OpenMenu`) it is added to `Application.Top` as a subview.

* IOW, `Menu` is a `View` whose `SuperView` is `Application.Top` (`Application.Top.Add (openMenu);`)

I already fixed this in this PR. A Menu should always be added to the Application.Current, because it may be not the Application.Top.

  * THIS IS A DESIGN FLAW that I think is the root cause of this issue ([MenuBar positions wrong in some situations #2558](https://github.com/gui-cs/Terminal.Gui/issues/2558)).
  * Instead, the `SuperView` of a `Menu` should be the hosting `MenuBar`'s `SuperView`

That is only true if the MenuBar superview and the Menu are in the same SuperView. But there is other cases where you can have a MenuBar on a superview that isn't the same as the Menu superview. Is the case where you can have a MenuBar on a SubView and the Menu should be added to the Application.Current. The reason for this is for the cases where the superview of the MenuBar haven't enough space to accommodate the Menu. But the root top is the same of course.

  * `Menu` should be a MODAL (`menu.Modal = true`) View.

I think I also agree.

PROPOSAL:

* Change `private class Menu : View` as follows:
  
  * Make it a modal (`Modal = true`); create it with `Applicaiton.Run (openMenu)`
  * Get rid of `Menu.Run` per above
  * Use computed layout for positioning and sizing instead of `MakeFrame` (have caller set X, Y, Height, Width)

It's fine If they are both on the same superview, otherwise I'm not sure if the layout will work.

* Introduce a new base class for menus named `MenuBase`, derived from `View`. Have it contain
  
  * `MenuBar`
    
    * `.Menus`
    * `.<keyboard handling members>` (including default `Command`s).
    * `.<style members>` (e.g. `UseSubMenusSingleFrame`)
    * `.OnEnter/OnLeave`
    * `.MenuOpening/Opened/Closing` etc... events
    * `.OpenMenu()/CloseMenu()`
    * Enforce that `SuperView != null`

But we need the SuperView == null for ContextMenu. How to distinguish them?

    * Move `IsShow` to `MenuBase` and change it to be of `public static MenuBase ActiveMenu`. If 'null`then it's the same as`IsShow == true`. It should get set to `this`in`.OpenMenu()`and to`null`in`.CloseMenu()`; the logic should ensure that one `MenuBarBase` is closed before another one is open.

* Refactor `MenuBar` to derive from `MenuBase` and :
  
  * `.OnDraw` (horizontal rendering)
  * When activated set `ActiveMenu = this`.

* Refactor `ContextMenu` to derive from `MenuBase` and :
  
  * `.Visible = false` at construction

I think the Visible will not working here because we need to recreate it to show it. Imagine that on a resize or on a layout change the ContextMenu position should be adjusted. Another disadvantage is if there are a lot of view with ContextMenu the app will be supercharged with many ContextMenu already created without needed.

  * Get rid of `.Host`. All views MUST have a `SuperView`. Use `SuperView` for positioning and sizing.

If you are meaning that the Menu should be a subview of the MenuBar that will not work because it doesn't fit.

  * Get rid of `Position` - `.X` and `.Y` already provide the location!

The Position property here is used by the containers to set the start position of the ContextMenu and can be ignored if the it needed to fit on the available space.

  * `.Show()` should be an override of `MenuBase.OpenMenu()`. It should:
    
    * Set `.Visible = true`
    * Set `.X` and `.Y` to coordinates that
  * Fix `ContextMenu.Key` documentation to be more clear.

It's not a good idea to create them and hiding. They must be created dynamically as needed.

In short, I think we need to refactor the entire Menu class hierarchy to work well in a world where

* We have the concept of Modal views that don't have to be of type Toplevel

* We have Bounds != Frame

By doing this, I believe the complexity of all of this will be reduced.

I could be wrong. But I think it's worth taking a serious look at.

Are you willing to do so, @BDisp?

It's not very easy but I can try it.

@BDisp
Copy link
Collaborator Author

BDisp commented Apr 24, 2023

Menu is always aligned on resizing now.

menu-align-on-resize

@tig
Copy link
Collaborator

tig commented Apr 25, 2023

BTW, all the refactoring stuff above doesn't seem very high priority relative to other v2 stuff.

If you want to dive into something big, I'd suggest these:

@BDisp
Copy link
Collaborator Author

BDisp commented Apr 25, 2023

BTW, all the refactoring stuff above doesn't seem very high priority relative to other v2 stuff.

The refactoring for the Menu changes you mentioned, I agree isn't a very high priority, but this PR has changes that will help manipulate views in the future.

If you want to dive into something big, I'd suggest these:

* [Remove dependency on ustring #92](https://github.com/gui-cs/Terminal.Gui/issues/92)

* [Refactor `Redraw` - Non-virtual with the right set of virtual `OnXXX` methods #2482](https://github.com/gui-cs/Terminal.Gui/issues/2482)

* [Colors: Update Color System (TrueColor, Blink, Underline, etc...) for v2 (Master Issue) #457](https://github.com/gui-cs/Terminal.Gui/issues/457)

Thanks for remember me this.

By the way, do you want I close the #2561? I think I prefer the approach of this PR.

@tig
Copy link
Collaborator

tig commented Apr 25, 2023

I'll be travelling internationally next few days, so I may or may not get a chance to dive deep into this PR again for a bit (depends on how travel goes and how wifi is ;-)).

Copy link
Collaborator

@tig tig left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@BDisp When I wrote the comments above, I didn't understand this PR. I think I now understand it better, but I'm still confused.

I'd like you to revisit DriverFrame and DriverFrameOffset. I think they are confusing and complex.

  • DriverFrame
    • does not need to be a Rect. It is a Size.
    • should not be on View. Why not just use Driver.Cols and Driver.Rows directly? If you think a helper is needed, then put it on Application as Application.Size.
  • DriverFrameOffset
    • I am not sure why this is needed. But assuming it is:
    • This is a Rect, but you instead return two Points. "Dimension" is, literally, a Size, not a point.
    • Location is Application.Top.Frame.Location.
    • Dimension is Application.Top.Frame minus Application.Size, which makes no sense to me.
    • The name "DriverFrameOffset" is confusing.

Can you help me understand all of this please?

Terminal.Gui/View/View.cs Outdated Show resolved Hide resolved
Terminal.Gui/View/View.cs Outdated Show resolved Hide resolved
Terminal.Gui/View/ViewLayout.cs Outdated Show resolved Hide resolved
@BDisp
Copy link
Collaborator Author

BDisp commented Apr 29, 2023

Can you help me understand all of this please?

They are really irrelevant. The correct solution of this are in the Menu and ContextMenu.

Copy link
Collaborator

@tig tig left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still confused. See my new comment at line 511 of View.cs

@@ -508,5 +508,30 @@ protected override void Dispose (bool disposing)
}
base.Dispose (disposing);
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't understand what these are for, nor do I think they are named correctly. My comments before still apply. I do not think these belong on View.

You've said several times "Remember that there are views that needed to draw outside the Application.Top".

I'm either not understanding what you mean, or I disagree.

I know v1 didn't work this way, but I think v2 should:

  • No View should draw outside of it's Frame. Menus, popups, etc... that are created by a View should be bound by that view's Frame.
  • The system (Application) can draw anywhere and will do so in some cases (e.g. drawing drop-shadows).

If the view that is Application.Top is too small for content to fit within it's frame then the developer can make it bigger.

I may be missing some very obvious scenario that you are seeing clearly though. Please help me see it!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't understand what these are for, nor do I think they are named correctly. My comments before still apply. I do not think these belong on View.

Ok, I'll move them to the Menu as private methods.

You've said several times "Remember that there are views that needed to draw outside the Application.Top".

I'm either not understanding what you mean, or I disagree.

I know v1 didn't work this way, but I think v2 should:

* No View should draw outside of it's Frame. Menus, popups, etc... that are created by a View should be bound by that view's Frame.

* The system (`Application`) can draw anywhere and will do so in some cases (e.g. drawing drop-shadows).

If the view that is Application.Top is too small for content to fit within it's frame then the developer can make it bigger.

Well, sorry I disagree with you in this specific matter. The user may not know the size of a drop-down, a context menu or other popup. If you use WPF or WindowsForms it allows to draw outside the container of the control.

I may be missing some very obvious scenario that you are seeing clearly though. Please help me see it!

@tig this unit test in the ContextMenuTests explain perfectly what I' saying. It's a TextField on a smaller Dialog that is also the Application.Top and, of course, the Application.Current. As you can see the ContextMenu is also showing outside the Dialog bounds, over the non-full console.

		[Fact, AutoInitShutdown]
		public void Draw_A_ContextMenu_Over_A_Top_Dialog ()
		{
			((FakeDriver)Application.Driver).SetBufferSize (20, 15);

			Assert.Equal (new Rect (0, 0, 20, 15), Application.Driver.Clip);
			TestHelpers.AssertDriverContentsWithFrameAre (@"", output);

			var dialog = new Dialog () { X = 2, Y = 2, Width = 15, Height = 4 };
			dialog.Add (new TextField ("Test") { X = Pos.Center (), Width = 10 });
			var rs = Application.Begin (dialog);

			Assert.Equal (new Rect (2, 2, 15, 4), dialog.Frame);
			Assert.Equal (dialog, Application.Top);
			TestHelpers.AssertDriverContentsWithFrameAre (@"
  ┌─────────────┐
  │ Test        │
  │             │
  └─────────────┘", output);

			ReflectionTools.InvokePrivate (
				typeof (Application),
				"ProcessMouseEvent",
				new MouseEvent () {
					X = 9,
					Y = 3,
					Flags = MouseFlags.Button3Clicked
				});

			var firstIteration = false;
			Application.RunMainLoopIteration (ref rs, true, ref firstIteration);
			TestHelpers.AssertDriverContentsWithFrameAre (@"
  ┌─────────────┐   
  │ Test        │   
┌───────────────────
│ Select All   Ctrl+
│ Delete All   Ctrl+
│ Copy         Ctrl+
│ Cut          Ctrl+
│ Paste        Ctrl+
│ Undo         Ctrl+
│ Redo         Ctrl+
└───────────────────", output);

			Application.End (rs);
		}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But, a ContextMenu is just a Menu which is a View. The Dialog is not drawing out side of it's Frame. The Menu is. And that's ok because it is not a subview of the dialog.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case it's a sub view of the Dialog because is added into the Dialog. See the ContextMenu code. In the case of the MenuBar it really is a Dialog subview and it only is draw inside the Dialog frame. Only the Menu can be draw outside there aren't enough space to accommodate it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remember, in v2, 'MenuBar' will be a subview of 'view.Padding' (or maybe 'Border', I haven't decided yet).

'ContextMenu' should not be a View subclass at all. It is just a helper for causing 'Menu' objects to be shown without a 'MenuBar'.

Proposal to resolve this:

  • Rule: a subview can never draw outside of its superview's Bounds
  • If 'view.Modal == true'
    • 'view.SuperView' must be 'null'.
    • 'view.Parent' must not be null
    • The view will be constrained within 'Parent.Frame'
  • 'Menu' (the private class in Menu.cs) will always be created with 'Modal = true' and 'Parent = Application.Top'.

This solves both my desire to have a model where Views can't draw outside their superview.Bounds and the need for menus to sometimes escape.

Does this proposal sound reasonable to you?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this proposal sound reasonable to you?

I think that fit the logic of handling float views. I only have a doubt when you referred Parent = Application.Top may be is better Parent = Application.Current. Imagine you have two toplevels opened and if the float view is managed by some view in the Current, which is modal, then must be this one the container of the float view, right?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this proposal sound reasonable to you?

I think that fit the logic of handling float views. I only have a doubt when you referred Parent = Application.Top may be is better Parent = Application.Current. Imagine you have two toplevels opened and if the float view is managed by some view in the Current, which is modal, then must be this one the container of the float view, right?

Sorta. In v2 I think Application.Current goes away with the concept of fully supporting Overlapped. Any view can host Overlapped views (formerly called Mdi). There is only ONE Application.Top. It cannot be changed between Run/End.

However, ANY view can have overlapped subviews. ONE of those overalapped subviews can have the topmost "z-order", which is equivalent to "Current". It's also the same as "Most Focused" in Overlapped scenarios.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @tig. Understood. Tell me what you want I change in this PR.

Copy link
Collaborator

@tig tig left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make the "GetDriverOffset" functions more clear. See my comments.

Terminal.Gui/Views/Menu.cs Outdated Show resolved Hide resolved
Terminal.Gui/Views/Menu.cs Outdated Show resolved Hide resolved
Terminal.Gui/Views/Menu.cs Outdated Show resolved Hide resolved
Terminal.Gui/Views/Menu.cs Outdated Show resolved Hide resolved
Terminal.Gui/Views/Menu.cs Outdated Show resolved Hide resolved
Terminal.Gui/Views/Menu.cs Outdated Show resolved Hide resolved
@tig tig merged commit 915af9b into gui-cs:v2_develop May 5, 2023
@BDisp BDisp deleted the v2_menu-on-smaller-top-fix_2558 branch May 5, 2023 20:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants