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

Popover - A consistent way of enabling a Subview to popup outside of a View #3691

Closed
tig opened this issue Aug 24, 2024 · 6 comments · Fixed by #3751 · May be fixed by #3749
Closed

Popover - A consistent way of enabling a Subview to popup outside of a View #3691

tig opened this issue Aug 24, 2024 · 6 comments · Fixed by #3751 · May be fixed by #3749
Assignees
Labels
design Issues regarding Terminal.Gui design (bugs, guidelines, debates, etc...)
Milestone

Comments

@tig
Copy link
Collaborator

tig commented Aug 24, 2024

(Very drafty for now... just some thoughts).

This Issue is a proposal for how to build into TG v2 a consistent way for a View to have a UI element show itself outside of the View's Viewport.

Popover - A View that that is displayed outside of the View that owns it's Viewport.
 
There are at least these use-cases of Popovers:

Autocomplete Popup

TextView would like a list of autocomplete items to be displayed below the view, at the cursor position as the user types. The user can use mouse/keyboard to select an autocomplete item and the pop over disappears.

Current Implementation

Drop-down Combobox

Current Implementation

  • Does not support pop-over; The ComboBox needs to be sized to allow the drop-down to show if HideDropdownListOnClick is true.
  • _listview (of type ComboListView) is a subview of Combobox
  • ComboListView overrides OnMouseEvent (which is internal!) with a bunch of hackery to deal with when it should be displayed

Menus (from MenuBar)

Current Implementation

  • MenuBar is a "special" subview of Application.Top (Toplevel has knowledge of it).
  • When MenuBar is activated (via keyboard/mouse/api) it creates instances of Menu and adds them to Top
  • Both MenuBar and Menu have convoluted logic for sensing when the menus should close (via mouse & keyboard).
  • When an open Menu closes, it is removed from Top

Context Menus

Current Implementation

  • Uses a "fake" MenuBar that is created when Show is called.
  • _menuBar is never actually added to any views; BeginInit/EndInit are called manually.

Tooltip

As a user, when I hover the mouse over a View, I'd like a temporary popup to appear providing a "tip" regarding the View's purpose.

Current Implementation

  • Not currently implemented

Proposal

Tenets (Unless you know better ones...)

  • There can only be one - Just like in Highlander, there can only be one Popover visible and active to the user at a time. None of the use-cases above lead to needing to have more than one, thus this is a simplifier. 1
  • Its Rude to Mess With Someone Elses' Subviews - Asking View developers to have to code defensively around the fact that some Subview they add, or some other agent, is adding/removing subviews is just bad juju. It leads to bugs in object lifecycles and requires skill to get right (e.g. adding a Subview during draw can result in Subviews being changed during a foreach iteration). In this design we will ensure there's no need to mess with another View's Subviews.
  • Any View can be a Popover - The design should not require a View that "Pops Over" be coded specially for that purpose.

Design

At the Application level we'll add a peer of Application.Top named Application.Popover:

  • In the run loop, after Top.Draw() is called, if Popover.Visible we'll call Popover.Draw(). This will ensure the Popover is always drawn over everything else.
  • In keyboard handling, if Popover.Visible && Popover.HasFocus we'll give Popover a chance to handle key events.
  • In mouse handling, if Popover.VIsible and Popover == Applicaiton.MouseGrabView we'll let it have mouse events. If a click happens outside of Popover.Frame we set Popover.Visible = false.
  • In focus/navigation handling, if Popover.HasFocus, if any other View gets focus, we'll set Popover.Visible = false.
  • For layout, whenever Top.LayoutSubviews is called, we'll then call Popover.SetRelativeLayout
  • Two APIs:
    • Applicaton.ShowPopover (View popoverView)
public static bool ShowPopover (View popoverView) 
{
   if (Popover is {})
   {
      Popover.Visible = false;
   }

   if (!popoverView.IsInitialized)
   {
      popoverView.BeginInit();
      popoverView.EndInit();
   }

   Popover = popoverView;
   Popover.Visible = true;
   Popover.SetRelativeLayout(screen);
}
  • Application.HidePopover ()
public static void HidePopover () 
{
   if (Popover is {})
   {
      Popover.Visible = false;
   }
}

View will have no knowledge of the popover concept.

Any View subclass that wants a Popover, will:

  • _myPopover = new MyPopoverView();
  • Note, _myPopover will always have this.SuperView is null.
  • Any time the popover should be displayed:
    • Set _myPopover.X/Y/Width/Height to ??? (NEED TO FIGURE THIS OUT)
    • Call Application.ShowPopover(_myPopover)
  • If the view wants to explicitly hide the popover,
    • Call Application.HidePopover()
       

Footnotes

  1. I've considered "As a user, when press a special key, I'd like all visible Views to show temporary popup providing a "tip" regarding the View's purpose." This could be implemented by having the container view showing a Popover who's job it was to show "tips" for each subview.

@tig tig added this to the V2 Beta milestone Aug 24, 2024
@tig tig self-assigned this Aug 24, 2024
@BDisp
Copy link
Collaborator

BDisp commented Aug 24, 2024

  • There can only be one - Just like in Highlander, there can only be one Popover visible and active to the user at a time. None of the use-cases above lead to needing to have more than one, thus this is a simplifier. 1

If I remember, only occur to me that ComboBox has an option to let the dropdown list always visible. How you'll deal about that?

I love the Tooltip implementation and all the others stuff.

@tig
Copy link
Collaborator Author

tig commented Aug 24, 2024

  • There can only be one - Just like in Highlander, there can only be one Popover visible and active to the user at a time. None of the use-cases above lead to needing to have more than one, thus this is a simplifier. 1

If I remember, only occur to me that ComboBox has an option to let the dropdown list always visible. How you'll deal about that?

If the list is always available it is not a "Dropdown Combobox", but just a ComboBox. The current implementation doesn't really provide a "Dropdown Combobox".

@tig
Copy link
Collaborator Author

tig commented Aug 24, 2024

Q: What's the difference between a Modal and a Popover?

  • The showing of a Modal is blocking (synchronous) to the caller (via Run). The showing of Popover is not blocking.

Q: Can't I use a Modal for something like the Autocomplete popup?

  • No. If you did, then while it was shown, the user couldn't continue to type in the TextField

Q: But, can Modal be implemented using the Popover support described above?

  • Maybe. Worth thinking through.

@tig
Copy link
Collaborator Author

tig commented Aug 24, 2024

  • In focus/navigation handling, if Popover.HasFocus, if any other View gets focus, we'll set Popover.Visible = false.

This should say "if any other View than the Popover's owner gets focus".

Otherwise, how would the TextViewAutoComplete popover work?

@BDisp
Copy link
Collaborator

BDisp commented Aug 24, 2024

If the list is always available it is not a "Dropdown Combobox", but just a ComboBox. The current implementation doesn't really provide a "Dropdown Combobox".

I have a suggestion for this. If it's a "Dropdown Combobox" then Popover will be used. With a fixed ComboBox that always has the ListView visible, then ComboBox must have the available dimension to acommodate the TextField and the ListView.

@tig
Copy link
Collaborator Author

tig commented Aug 24, 2024

If the list is always available it is not a "Dropdown Combobox", but just a ComboBox. The current implementation doesn't really provide a "Dropdown Combobox".

I have a suggestion for this. If it's a "Dropdown Combobox" then Popover will be used. With a fixed ComboBox that always has the ListView visible, then ComboBox must have the available dimension to acommodate the TextField and the ListView.

Yes!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
design Issues regarding Terminal.Gui design (bugs, guidelines, debates, etc...)
Projects
Status: ✅ Done
2 participants