Proposal: Focus/Navigation Changes - e.g. Remove `Tabindex` and `TabIndexes` in v2
Remove Tabindex and TabIndexes in v2
Why?
- I can't find any real uses of it outside of the
DynamicMenubarscenario and a simple usage inFileDialog. - I've done a code search of terminal.gui/gui-cs projects and can't find any examples of where it's used.
- TGD doesn't support it https://github.com/gui-cs/TerminalGuiDesigner/issues/79
- It has a ton of complexity and is a source of bugs. The current implementation is full of bugs. I've added a bunch of unit tests in #3627 and fixed some of the bugs, but ...
- The same concept has been deprecated from Maui: https://github.com/dotnet/maui/issues/1646
If there are no objections, I am going to remove View.TabIndex and View.TabIndexes from Terminal.Gui v2 in #3627.
Questions
Q: What if I want to set the tab order?
A: Simply re-order Subviews.
- Side note:
Dialogcurrently has a bug where in most typical use cases where Views are added withdlg.AddafterButtonshas been set, the tab order is wrong. The buttons should always be the last items in the order, but are first. I plan on fixing this independently of whetherTabIndexexists or not by overridingAddto always ensure theButtons` are at the end of the subview List.
Q: Web frameworks have the concept. E.g. React. Are you sure you want to remove it? A: Yes, the same issues that plagued the original concept in Win32, then Winforms, then Xamarin, that are outlined in the Maui issue above apply to jQuery, React, etc...
Q: What about TabStop?
A: The need indicates how a View behaves in keyboard navigation is real. I've already changed TabStop to be of type TabBehavior and I think that is still needed.
No objections. Normally when we create an app using var sometimes we need to initialize a view to being referenced by another view, but we want the later view added to be tabbed first. But if we use private fields for views at class scope this isn't an issue.
Works for me!
Rename Leave to Blur
Why?
I wrote this (in the new navigation.md doc) and after doing so was convinced we can do better:
Lexicon & Taxonomy
- Navigation refers to the user-experience for moving Focus between views in the application view-hierarchy.
- Focus - Refers to the state where a particular UI element (
View), such as a button, input field, or any interactive component, is actively selected and ready to receive user input. When an element has focus, it typically responds to keyboard events and other interactions. - Focus Chain - The ordered sequence of UI elements that can receive focus, starting from the currently focused element and extending to its parent (SuperView) elements up to the root of the focus tree (
Application.Top). This chain determines the path that focus traversal follows within the application. Only one focus chain in an application can have focus (top.HasFocus == true), and there is one, and only one, View in a focus chain that is the most-focused; the one receiving keyboard input. - Navigation - Refers to the user-experience for moving Focus between views in focus chain.
- Cursor - A visual indicator to the user where keyboard input will have an impact. There is one Cursor per terminal session. See Cursor for a deep-dive.
- Focus Ordering - The order focusable Views are navigated. Focus Ordering is typically used in UI frameworks to enable screen readers and improve the Accessibility of an application. In v1,
TabIndex/TabIndexesenabled Focus Ordering. - Tab - Describes the
Tabkey found on all keyboards, a break in text that is wider than a space, or a UI element that is a stop-point for keyboard navigation. The use of the word "Tab" for this comes from the typewriter, and is re-enforced by the existence of aTabkey on all keyboards. - TabStop - A
Viewthat is an ultimate stop-point for keyboard navigation. In this usage, ultimate means theViewhas no focusable subviews. TheApplication.NextTabStopKeyandApplication.PrevTabStopKeyareKey.TabandKey.Tab.WithShiftrespectively. These keys navigate only between peer-views. - TabGroup - A
Viewthat is a container for other focusable views. TheApplication.NextTabGroupKeyandApplication.PrevTabGroupKeyareKey.PageDown.WithCtrlandKey.PageUp.WithCtrlrespectively. These keys enable the user to use the keyboard to navigate up and down the view-hierarchy. - Enter / Gain - Means a View that previously was not focused is now becoming focused. "The View is entering focus" is the same as "The View is gaining focus".
- Leave / Lose - Means a View that previously was focused is now becoming un-focused. "The View is leaving focus" is the same as "The View is losing focus".
I did a bunch of research and other frameworks either:
- Avoid verbs for "leave". E.g. SwiftUI just uses a
focused(_:)modifieer. Simliarly for Flutter. - Use
Blur. E.g.jQuery,React, etc...
We don't have a programming model that easily enables the former. So I want to swtich the the later.
Thoughts?
I guess an alternative that would just avoid "leave"/"lost"/"blur" is to
- Merge
OnEnter/Enter/OnLeave/LeaveintoOnHasFocusChanging/HJasFocusChangingandOnHasFocusChanged/HasFocusChanged
hmmm....
Not a big fan of Blur as a word. To me that just sounds like someone heard Focus and thought of cameras lenses. To me a View gains and looses a thing. Or users input enters and leaves a view.
I don't mind the idea of a single event for focus change. And it's got the word focus in it so it won't be hard for api user to find
Not a big fan of Blur as a word. To me that just sounds like someone heard Focus and thought of cameras lenses. To me a View gains and looses a thing. Or users input enters and leaves a view.
I don't mind the idea of a single event for focus change. And it's got the word focus in it so it won't be hard for api user to find
Ok, here's what I'd write in the migration guide:
- In v1, the
View.OnEnter/EnterandView.OnLeave/Leavevirtual methods/events could be used to notify that a view had gained or lost focus, but had confusing semantics around what it mean to override (requiring callingbase) and bug-ridden behavior on what the return values signified. The "Enter" and "Leave" terminology was confusing. In v2,View.OnHasFocusChanging/HasFocusChangingandView.OnHasFocusChanged/HasFocusChangedreplaceView.OnEnter/EnterandView.OnLeave/Leave. These virtual methods/events follow standard Terminal.Gui event patterns. TheView.OnHasFocusChanging/HasFocusChangingevent supports being cancelled.
Just to alert that the Application.PrevTabStopKey which is the Key.Tab.WithShift won't work on Linux and that why it has an alternate key. Don't work on the TextView .
Just to alert that the
Application.PrevTabStopKeywhich is theKey.Tab.WithShiftwon't work onLinuxand that why it has an alternate key. Don't work on theTextView.
Yep. Current migratingfrom v1.md in #3627 says the following. And this is all implemented in that PR:
In v1, the keys used for navigation were both hard-coded and configurable, but in an inconsistent way. Tab and Shift+Tab worked consistently for navigating between Subviews, but were not configurable. Ctrl+Tab and Ctrl+Shift+Tab navigated across Overlapped views and had configurable "alternate" versions (Ctrl+PageDown and Ctrl+PageUp).
In v2, this is made consistent and configurable:
Application.NextTabStopKey(Key.Tab) - Navigates to the next subview that is aTabStop(see below). If there is no next, the first subview that is aTabStopwill gain focus.Application.PrevTabStopKey(Key.Tab.WithShift) - Opposite ofApplication.NextTabStopKey.Key.CursorRight- Operates identically toApplication.NextTabStopKey.Key.CursorDown- Operates identically toApplication.NextTabStopKey.Key.CursorLeft- Operates identically toApplication.PrevTabStopKey.Key.CursorUp- Operates identically toApplication.PrevTabStopKey.Application.NextTabGroupKey(Key.F6) - Navigates to the next view in the view-hierarchy that is aTabGroup(see below). If there is no next, the first view that is aTabGroupwill gain focus.Application.PrevTabGroupKey(Key.F6.WithShift) - Opposite ofApplication.NextTabGroupKey.
F6 was chosen to match Windows
These keys are all registered as KeyBindingScope.Application key bindings by Application. Because application-scoped key bindings have the lowest priority, Views can override the behaviors of these keys (e.g. TextView overrides Key.Tab by default, enabling the user to enter \t into text). The AllViews_AtLeastOneNavKey_Leaves unit test ensures all built-in Views have at least one of the above keys that can advance.