Skip to content

Conversation

@slook
Copy link
Contributor

@slook slook commented Nov 12, 2025

This PR enables keyboard accessibility for the TTkTabButton widgets of a TTkTabBar widget because otherwise currently its nearly impossible to see while navigating across the buttons while everything has the same white borders...

  • Added: Keyboard access highlight to enable seeing which TabButton is navigated with the left and right keyboard cursors.
  • Added: Blink side arrow ends on tab key focus and disable to grey when highlighted selection reached either end.
  • Changed: Brighter blue background and bold foreground text to more clearly indicate which TabButton is currently selected.
  • Changed: Mouse hover border color light blue instead of white so it can be seen, matches other widgets such as list selection.

Start of navigation (key tab onto it) all the tabs are highlighted at first so there is no doubt which widget has got the focus:
image

During keyboard navigation (left and right keys) it can be clearly seen in yellow which button is being highlighted:
image

At the end the offset indicator arrow stops blinking and turns grey to let you know the end is reached:
image

The NERD tabs now also support keyboard navigation too:
image

Implementation with slightly darker borders shows higher contrast of the selection to clearly see which mode the program is in as well as the keyboard navigation currently highlighted in bold yellow makes it easy to use even without mouse:
image

@ceccopierangiolieugenio
Copy link
Owner

this is nice,
in the the left right navigation keys, I would highlight only the arrow and not the border to avoid the ZX Spectrum effect on the partially highlighted bottom part.
Same for the rest, the overall idea is good.

@slook
Copy link
Contributor Author

slook commented Nov 15, 2025

Thank you for taking a look. I agree with you about there being room for future improvement of the border coloring (frankly, in other widgets of the library also the lack of proper coloring/theming is like being in the early days!).

However I don't see an obvious way to implement what you have suggested without making larger changes that would be beyond the scope of this PR. Ideally, the whole class style needs to be refactored or consolidated and the bottom row of all the buttons (including side ends) should be assigned the Selected style as those characters also form part of the active tab border. The wrong color assignment is more obvious when using a non-default border color:

image

the bottom row of all the buttons (including side ends) should carry the Selected style and the widget frame border should be white instead of blue in the above example screenshot.

If you have a code suggestion then I could help you implement it at a later time, but otherwise I do not have any better idea for now. I think this PR is a small step in the right direction to help us with development of ideas even if my code is unused/overwritten/reimplemented in a better way at a later time then I don't mind.

@ceccopierangiolieugenio
Copy link
Owner

Unfortunately I need to refactor the way the theme is propagated to the child components,
I hit this problem the first time I made the tab widgets and on my second iteration, when I introduced the classStyles, I added the mergeStyle method to workaround a little bit this problem, but I think it is the time to define a proper proxy for the style, the focus and the event handling when there are widgets composed of multiple ones.
I.e., define al the basic styles in the main tab widget and let the proxy propagate the border to the children, and the focus as well, technically the focus is assigned to the tab widgetand in the tab widget context is the button to have the focus.

@slook
Copy link
Contributor Author

slook commented Nov 16, 2025

It is not unfortunate that the theme handling needs to be refactored because, frankly, anything shall be better than the way things have had to be implemented at this stage, so I am glad you are already willing to upgrade it for something completely different.

You should not really view the current classStyle workarounds a problem, but rather a natural progression in development of this widget which is turning out to be really good I think one of the best implementations of proper tabs in a TUI that I have ever seen. So you should be pleased with your work on it so far, albeit there is much potential for refinement to improve it even more.

technically the focus is assigned to the tab widgetand in the tab widget context is the button to have the focus.

This is where some confusion may have arisen since "focus" in this context has an abstract meaning here due to multiple button sub-widgets on the overall tab bar itself. For this reason I have elected to abolish the "focus" classStyle in favour of the "selected" terminology instead in this PR to help us differentiate between the current tab and whichever auxiliary the keyboard focus is on (because this is also not the same as those widgets which need to be "highlighted" to help with the keyboard focus navigation/accessibility).

In other words, I think the meaning of "focus" should be used only to represent the main outer widget that has keyboard control (i.e. the entire TabBar widget in this case) and I consider that the tab buttons are mearly "highlighted" while navigating left/right.

With some careful thought and planning of your intended focus proxy system I am confident we can make it work and look the way you would like. If you find my feedback helps you figure this out then please feel free to ask me about your ideas or if you get stuck on something while deciding on a module design that works the best because workable theming is very important not only for visual appearance but also, perhaps even more importantly, for the logical functionality of accessible keyboard navigation too.

I am looking forward to testing your future development on this. I don't personally mind if you need to make breaking changes in the main branch, then I'd say just go for it, or let us know if you make development branch for testing.

@ceccopierangiolieugenio
Copy link
Owner

The core routine need to be rewritten and it require some time of proper planning, I am expecting the implementation to be straightforward after this and I think there will not be breaking changes.

Right now the focussed widget is only one and in the current design it receive all the keypresses, unless the keypress event return False, so the control goes back to root that decide to handle it (i.e. focus the next widget) or do nothing.

At the moment In order to let the parent to handle special keys (i.e. tab),
I needed to introduce some hacks for example in the dateform widget.

The theme trickery I mentioned was not the classStyle itself but the mergeStyle method that is quite abused and force the use of shortcuts and workarounds.

I am thinking to introduce the concept of focus proxy or focus orchestrators where, in the case of the tab widget, the tab widget get the focus from the root, and inside domain of the tab focus proxy, the tab button receive the focus, any unhandled keypresses (i.e. direction keys), instead of going back to root, will be handled by the tab widget (i.e. to switch between tabs)
this will allow a widget/container to have better control over it and, for example, to have positional widgets with customised controls (i.e. a virtual remote controller or num pad where you can switch focus contextually to the 4 directions)

@slook
Copy link
Contributor Author

slook commented Nov 18, 2025

What advantages would be offered by the implementing a library-wide focus proxy to orchestrate which widget or sub-widget the keyboard controls, over and above implementations of custom written methods specific to each widget?

What are the reasons why the existing implementation can't be evolved and refined to provide the desired functionality?

@ceccopierangiolieugenio
Copy link
Owner

ceccopierangiolieugenio commented Nov 19, 2025

the advantage is to have a proxy that can be customised to change the focus logic,
at the moment it just move to the next widget added to the layout, and this could be the default behavior, but it can be customised to decide the next focus based on the position of the widget and to react to different inputs for example up and down.
Anyway I am already working on it and it is working quite well just delegating the focus logic to the container itself without the need of an external proxy, if you create a widget subclassing a container (i.e. TabWidget or DateTime) you can just add your focus logic inside the keypress if the default logic does not apply.
This was not possible before the rework because the keypress is handled directly to the focussed widget without being processed by the parent container and I had to hack my way to allow the tab to react to the mouse and keypresses.

@slook
Copy link
Contributor Author

slook commented Nov 21, 2025

I see thank you for clariying. Yes I agree it would be better in many cases if the focus handler was more easily available in the parent container widget instead of being trapped inside a sub-widget.

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