TabPane Behaviour

John Hendrikx hjohn at
Sun Sep 16 04:11:30 PDT 2012

Hi List,

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.

John Hendrikx

More information about the openjfx-dev mailing list