hjohn at xs4all.nl
Sun Sep 16 04:11:30 PDT 2012
This weekend I spent trying to make TabPane work properly when
controlled by remote control. This means that there are two important
1) A remote control usually does not have keys for "Tab" and "Shift Tab"
to 'escape' traversal contexts
2) The Application itself usually does not have input focus (as in
keyboard focus) -- this is because it receives events by remote control
no matter which application is focused. This doesn't mean it cannot
receive keyboard events while not focused -- the RC will send these to
the App even when it does not have focus (this is the easiest way to
make an application remote controlled usually).
I encountered the following difficulties:
1) When a TabPane has focus, it consumes all of the "Down", "Up", "Left"
and "Right" keys, regardless of actual Tab orientation or whether or not
the first or last tab was already selected. For a remote control app,
that means there is no way to exit the TabPane once it gets focus.
2) When the Stage does not have focus, but some other app does, TabPane
does not respond to keys received by remote at all (most controls work
fine, but TabPane does not). I tracked this down to an
"getControl().isFocused()" check in the TabPaneBehavior class -- I donot
really understand why that check is there as I assume if you get those
traversal events that you must be the focus owner already...
3) Once the content inside a Tab has focus, it again is impossible to
leave this area with just the Up/Down/Left/Right navigation keys.
So, I spent a day hacking the TabPaneBehavior class, and in the process
learning a bit about the internal traversal mechanism -- it looks pretty
interesting already -- I'm hoping this will become public soon. (Yes, I
know this is not public API yet, and I know it might break at any time,
but that's cool as my application is just a personal hobby project).
Anyway, I've done the following:
1) I created a subclass of TabPaneSkin which does some reflection
trickery in its constructor to remove the "standard" TabPaneBehaviour
(which was just installed in the super constructor) and replace it with
my own. I did not see any mechanism to subclass a Skin and only provide
a new Behavior.
2) I subclassed TabPaneBehavior (I had to subclass because there is an
explicit cast somewhere to TabPaneBehavior in the TabPane code).
3) I only overrode the callAction method and changed it in the following
- Removed the isFocused checks
- Navigation keys consumed depend on the tab orientation (ie, Side.TOP
or Side.BOTTOM only use "Left" and "Right")
- If first or last tab is already selected, we escape the TabPane
traversal by calling BehaviorBase traversal methods
- If "Down" is pressed when tabs are Side.TOP then we navigate to the
tab's content (same for "Right" when side == Side.LEFT, and so on).
- I never called super.callAction (as that would result in the wrong
behavior) and instead called methods of BehaviorBase directly when I
needed the focus to escape the TabPane.
4) This left me with one more problem, how to escape from the Tab itself
when its content is focused... for that I wrapped the content in its own
Pane. This Pane keeps an eye on traversal occuring inside itself. If
any Traversal is unsuccesful, it will try and escape the Tab. The
solution I created here is a bit unsatisfactory because I donot know how
to get the 'next higher' traversal engine to tell it to navigate
up/down/left/right... instead when no focus change occured and the user
pressed left or up, I translated this to "Previous", and down/right to
Anyway, I'm posting this in the hope it might be useful if this API is
made public. I did not see any JIRA issues I could comment on for this
yet. I can share the code to show exactly what I did to make the
TabPane remote control friendly, and I'm hoping that in the future I can
get this behavior using only public API.
More information about the openjfx-dev