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

Allow to focus individual tabs in TabBar/TabContainer #79104

Merged
merged 1 commit into from
Sep 25, 2023

Conversation

DrRevert
Copy link
Contributor

@DrRevert DrRevert commented Jul 6, 2023

This is a refactor of #49928 compatible with stable releases of Godot 4, previous version was based on pre-alpha codebase. Since then few things changed: Tabs class was renamed to TabBar and TabContainer now internally uses TabBar.

@Calinou
Copy link
Member

Calinou commented Jul 6, 2023

Do you have a testing project available to test this pull request? If you do, please upload it here 🙂

@DrRevert
Copy link
Contributor Author

DrRevert commented Jul 6, 2023

Yeah I have, but it's very bare bones
project.zip

@KoBeWi
Copy link
Member

KoBeWi commented Jul 10, 2023

It does not work well with a controller:

godot.windows.editor.dev.x86_64_6INRTJghI8.mp4

Something like #63168 should be implemented probably.

Also there doesn't seem to be any way to disable TabContainer focus (without using script), as they internal TabBar is focusable. I wonder if we should make it easier 🤔 It'd be useful when someone implements custom tab switching with keys different than left/right.

@DrRevert
Copy link
Contributor Author

DrRevert commented Jul 11, 2023

It does not work well with a controller:
godot.windows.editor.dev.x86_64_6INRTJghI8.mp4

Something like #63168 should be implemented probably.

Thanks, I will look into that.

Also there doesn't seem to be any way to disable TabContainer focus (without using script), as they internal TabBar is focusable. I wonder if we should make it easier thinking It'd be useful when someone implements custom tab switching with keys different than left/right.

I think it should be possible to use Tab key to switch focus away and I vaguely remember containers not letting focus outside was a thing at least in 3.x.
As for custom switching - I could move the functionality into separate methods (bool switch_to_next_tab, bool switch_to_prev_tab - it will return true if switching was successful to know when to consume event) so implementing custom switching won't require writing the same logic to omit disabled tabs.

@DrRevert
Copy link
Contributor Author

Added the fix for the gamepads based on similar implementation in #63168
Logic for changing tabs moved to separate methods and exposed.

As for the changing focus from tab_container: in my case it is possible to move the focus away using Tab key and if the active Tab contains any focusable element I can easily navigate to it using up and down. It's not possible to move focus away from container but I'm pretty sure that logic is the same for all containers.

@KoBeWi
Copy link
Member

KoBeWi commented Jul 11, 2023

Ok I wasn't clear. I meant making the container not focusable at all, not taking away focus. TabContainer is special, because it has an internal TabBar which itself can take focus and you don't have control over it.
The problem with that is when you want to make a keyboard-only and/or controller-only UI, because all controls respond to mouse by default. This can be disabled by changing mouse filter, but it's not available for the internal TabBar of TabContainer. When someone clicks the TabBar, you will end up with a focus state that shouldn't be possible.

@DrRevert
Copy link
Contributor Author

Ok I wasn't clear. I meant making the container not focusable at all, not taking away focus. TabContainer is special, because it has an internal TabBar which itself can take focus and you don't have control over it. The problem with that is when you want to make a keyboard-only and/or controller-only UI, because all controls respond to mouse by default. This can be disabled by changing mouse filter, but it's not available for the internal TabBar of TabContainer. When someone clicks the TabBar, you will end up with a focus state that shouldn't be possible.

Alright. I think exposing the focus mode of the internal TabBar to the TabContainer as parameter should do a trick, then based on the needs everyone can setup Tabs and containers the way they want.

@DrRevert
Copy link
Contributor Author

Exposed methods for changing tabs in TabContainer (forgot about those earlier).
Exposed the FocusMode of the internal TabBar in the TabContainer - this will allow to opt-out from focusable tabs in container so the behavior will be the same as it is now without this change.

@KoBeWi
Copy link
Member

KoBeWi commented Jul 11, 2023

All tabs in the editor are focusable and use the style from the default theme:
image
I think they shouldn't be focusable or at least editor theme needs a tab focus style.

scene/gui/tab_container.cpp Outdated Show resolved Hide resolved
scene/gui/tab_bar.h Outdated Show resolved Hide resolved
@DrRevert
Copy link
Contributor Author

DrRevert commented Jul 12, 2023

All tabs in the editor are focusable and use the style from the default theme: image I think they shouldn't be focusable or at least editor theme needs a tab focus style.

I don't really see this as a problem TBH. I think that focusable tabs in editor is just another way to interact with editor without a mouse, granted many people will still use mouse but being able to just press Tab few times and navigate through entire editor UI as an alternative sounds like a good idea, unless something is broken in editor because left/right is supposed to do something else but the event is intercepted by the TabBar logic causing the tabs to be switched instead or it's too easy to switch tabs when you don't want to.

As for style I just copied the style of the button when in focus. It looks okay to me here, but I'm open to change if there is a concrete proposition how it should look like. For example: this is how tab with focus looked like in Windows 7 tabs Win7
or maybe make it smaller similar how it's on this site?
obraz

EDIT: I took another look at the editor and just now noticed that all the focus styles are tinted blue in default editor style. I think I will need some help how to properly set those up for editor styles, not sure where this is stored in the codebase. Nevermind I think I got it:
editortab

As for possibly shrinking the rectangle I thought I could use negative margins but that's not possible, the change would have to be done when element is drawn, having to add some arbitrary value to the mix.

@DrRevert
Copy link
Contributor Author

Focused tab in editor now uses accent color

Copy link
Member

@Calinou Calinou left a comment

Choose a reason for hiding this comment

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

Tested locally (rebased on top of master 287f3aa), it works as expected.

@DrRevert
Copy link
Contributor Author

If exposing the option needs more discussion, it should be removed and made default for now (since this would be consistent with other classes). We can't easily remove it later as it would break compatibility.

Ok I'll be removing the rollover parameter, however I don't think it should stay off for the time being. I'm looking at the other Control nodes to compare the behaviors and the closest I found is the ItemList which doesn't rollover/loop the input. (Side note: ItemList appears to block the input if the next element is not selectable and/or disabled, which for me is a bug.) Maybe I'm missing something, @YuriSizov could you provide me an example of the class which has a rollover behavior active (and which can't be turned off)?

@YuriSizov
Copy link
Contributor

could you provide me an example of the class which has a rollover behavior active (and which can't be turned off)?

Honestly, I just followed @YeldhamDev's feedback on this one, sorry 🙃 I'm fine with either option that makes sense.

@YuriSizov YuriSizov changed the title Focusable UI tabs Allow to focus individual tabs in TabBar/TabContainer Aug 30, 2023
@DrRevert
Copy link
Contributor Author

Alright. I'm finishing up the merge from master as there were some changes in editor_themes files. I should be done in few minutes.

@YuriSizov
Copy link
Contributor

As you do, please amend the commit message to follow project's preferred style. The current title of the PR should be a good option.

@DrRevert DrRevert force-pushed the focusable_tabs_refactor branch 2 times, most recently from 7eb2614 to c5a6498 Compare August 30, 2023 13:44
@DrRevert
Copy link
Contributor Author

Now it should be fine. Let me know if I miss something... again.

@YuriSizov
Copy link
Contributor

Your commit message is slightly wrong, seems like the old name is appended to the new one :)

Copy link
Contributor

@YuriSizov YuriSizov left a comment

Choose a reason for hiding this comment

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

Other than the commit message, I think it's good now.

@DrRevert
Copy link
Contributor Author

Commit message fixed - hopefully didn't break anything else.

@YeldhamDev
Copy link
Member

@DrRevert could you provide me an example of the class which has a rollover behavior active (and which can't be turned off)?

From the top of my head, PopupMenu. However, if you could provide examples from other GUI toolkits that allow toggling the rollover behavior in tabs, we could give in for the sake of familiarity with those tools.

@DrRevert
Copy link
Contributor Author

@DrRevert could you provide me an example of the class which has a rollover behavior active (and which can't be turned off)?

From the top of my head, PopupMenu. However, if you could provide examples from other GUI toolkits that allow toggling the rollover behavior in tabs, we could give in for the sake of familiarity with those tools.

I'm not aware of any GUI toolkit allowing for such a customization.
As for PopupMenu it does make sense to have a rollover as user is expecting for this menu to stay on top other elements and should disappear entirely when user is done interacting with it. Tabs on the other hand are always present.

@YeldhamDev
Copy link
Member

BTW, a reminder that one can press Shift in order to get out of a focused Control, so even having the rollover behavior always on (which seems to not be the case currently) you won't get stuck on it.

<method name="select_next_available">
<return type="bool" />
<description>
Selects first available tab on the right from the currently selected. Returns [code]true[/code] if tab selection changed.
Copy link
Member

Choose a reason for hiding this comment

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

I think UI mirroring will contradict this, as with a RTL layout the "next" tab will be to the left. So this may need to be rephrased to avoid this ambiguity (same in TabContainer). CC @bruvzg

Copy link
Member

Choose a reason for hiding this comment

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

Indeed, in RTL layout, "next" is "left".

Copy link
Contributor Author

@DrRevert DrRevert Sep 4, 2023

Choose a reason for hiding this comment

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

Oh, in that case we have a bigger issue, as pressing left on keyboard or controller will go in opposite direction in RTL. I wasn't aware tabs are reordered in this way.

Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Selects first available tab on the right from the currently selected. Returns [code]true[/code] if tab selection changed.
Selects the first available tab on the right from the currently selected. Returns [code]true[/code] if tab selection changed.

Same below

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, in that case we have a bigger issue, as pressing left on keyboard or controller will go in opposite direction in RTL. I wasn't aware tabs are reordered in this way.

The fix should be pretty simple, you would need to check is_layout_rtl() and use the opposite direction for the target tab. It would be fine to have a bit of code repetition between the next and the previous tab method.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm having a little problem with rephrasing that sentence to be grammatically correct. How does
"Selects the first available tab with greater/lower index than the currently selected." sounds like? In case of lower index it sounds like the function will always select first tab ever so I'm not sure about this one.

@DrRevert
Copy link
Contributor Author

I though I already submitted the change in doc description like a week ago. Sorry about the delay.
Right to left layout is now accounted for, I made the check outside the select_xxx_available for the possible top/down layout in the future.

@AThousandShips
Copy link
Member

Needs a rebase

@DrRevert
Copy link
Contributor Author

Rebased and modified to be compatible with 2924bfd

Copy link
Member

@akien-mga akien-mga left a comment

Choose a reason for hiding this comment

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

Looks good to me.

<method name="select_next_available">
<return type="bool" />
<description>
Selects the first available tab with greater index than the currently selected. Returns [code]true[/code] if tab selection changed.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Selects the first available tab with greater index than the currently selected. Returns [code]true[/code] if tab selection changed.
Selects the first available tab with higher index than the currently selected. Returns [code]true[/code] if tab selection changed.

Just potential nitpick, higher/lower are more complementary

Copy link
Member

Choose a reason for hiding this comment

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

Sorry I missed your review comment while merging my last batch.

<method name="select_next_available">
<return type="bool" />
<description>
Selects the first available tab with greater index than the currently selected. Returns [code]true[/code] if tab selection changed.
Copy link
Member

Choose a reason for hiding this comment

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

Same here in that case

@akien-mga akien-mga merged commit c57d9f3 into godotengine:master Sep 25, 2023
15 checks passed
@akien-mga
Copy link
Member

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants