- Undocumented Matlab - https://undocumentedmatlab.com -

Fixing a Java focus problem

Posted By Yair Altman On October 20, 2010 | 25 Comments

An aspect of Matlab GUIs that include Java components [1] that is often encountered (most recently reported yesterday [2]) and is easily fixed, is Matlab’s default exclusion of all javacomponents from its focus traversal cycle.
This means that if we place several uicontrols and javacomponents together onscreen, clicking TAB or Shift-TAB will only move the focus between the regular uicontrols, but none of the javacomponents. The javacomponents can still get the focus, but only programmatically or by mouse click – not by keyboard-clicking TAB or Shift-TAB.
For example:

h1 = uicontrol('style','edit','position',  [10 10 150 20]);
h2 = uicontrol('style','edit','position',  [10 40 150 20]);
h3 = javacomponent(javax.swing.JTextField, [10 70 150 20]);

Problem: javacomponent is not TAB-focusable
Problem: javacomponent is not TAB-focusable
(TAB only switches focus between bottom uicontrols)

Java Swing components have several focus-related properties (and accessor methods) that relate to the component’s focus cycle, i.e. selecting (=setting the focus on) the component using the keyboard (overview [3]; technical description [4]).
Matlab documentation calls the focus cycle “tab-order” but only allows selecting the focus cycle order, using the uistack function – for all the extra functionality we need to use these Java properties.
The astute reader of this blog will of course have noticed that the Java component’s Focusable property is set to true and would therefore be justifiably puzzled as to why the cycle-focus doesn’t work.
Luckily, and counter-intuitively, the fix to the focus-cycle problem in Matlab GUI is extremely simple and requires no deep understanding of Java’s focus policy. Simply override the Java component’s Focusable property to true (explanation below):

h3.setFocusable(true);
h3.putClientProperty('TabCycleParticipant', true);  % this may be unnecessary in some cases, but doesn't hurt

Solution: javacomponent is now TAB-focusable, as expected
Solution: javacomponent is now TAB-focusable, as expected
(TAB now cycles between all three controls)

This sounds crazy – After all, if the Focusable property is already true, what’s the use of updating its value to the same value???
The answer is that by setting Focusable to true, we indirectly invoke a back-end Java method that resets the component’s focus policy to its standard behavior, effectively overriding Matlab’s default (non-standard) focus policy.
Such a tricky problem… Such a long detective hunt for an answer… Such a simple and effective fix… – Just the way I like it 🙂

Categories: GUI, Java, Medium risk of breaking in future versions, Undocumented feature


25 Comments (Open | Close)

25 Comments To "Fixing a Java focus problem"

#1 Comment By damien On October 20, 2010 @ 12:30

Hi,

I have a problem I think might be related (or not). I run matlab with a tiling window manager, and when I move (with the keyboard) from another window (a text editor, say) to matlab, I need to click with the mouse on the matlab window to get focus. Perhaps setting the matlab command window to ‘focusable’ might solve it? Do you know if there is there a way to do that, if so?

Thanks!

#2 Comment By Yair Altman On October 20, 2010 @ 13:10

@Damien – I do not believe that this is a ‘focusable’ issue. It works on my Windows XP machine, so I would guess it is related to your OS. I suggest that you send isupport at mathworks.com a query email about this.

#3 Comment By NS On May 11, 2013 @ 18:49

I have the exact same problem that you have and I couldn’t find a solution.
On two of my Windows 7 64 bit PCs, the same thing happens.

I would be glad if someone would find a solution for this issue.
I already emailed matlab but they didn’t respond.

Thanks

#4 Comment By Kesh On October 20, 2010 @ 12:33

Thanks for officially posting this info here! Now, I just need to remember to come to this blog 🙂

#5 Comment By Yair Altman On October 20, 2010 @ 13:07

@Kesh – the easy solution for this is to simply subscribe to new article posts using the links at the top-right of this webpage. New articles will then be delivered to you automatically.

#6 Comment By Mike On November 3, 2010 @ 14:58

Thanks for the fix.

Now, I’ve got a difficult one for you. If I want to change the focus from an axes to a specific component, how do I do it?

My GUI has an axes and a JTree, how can I set a keyboard shortcut for the axes so that the JTree becomes focused?

PS – There are other components, so it’ll have to be a shortcut and not a tab-focus

#7 Comment By Yair Altman On November 7, 2010 @ 15:20

@Mike – you can trap the requested key-stroke in your axes and in its callback do something like jtree.requestFocus()

– Yair

#8 Comment By Henrik Toft On February 15, 2011 @ 01:03

Hi,
Great with a simple solution that works 🙂 Now I only have a small additional question:

I have a simple GUI with two push-buttons (created using GUIDE) and during the GUI-opening-function, I add two JTextField for handing some input text (Java because I want to check the text as it is being typed). Now I want the two JTextFields to be FIRST in my Tab-order, and furthermore it would be really nice if the cursor is blinking in “the first” JTextField ready for input… (similar to “uicontrol(….)”)

I have played with “uistack” and “uicontro(…)” without success – any good suggestions? (I am sure it is possible 🙂

Cheers,
Henrik

#9 Comment By Henrik Toft On February 17, 2011 @ 02:10

Just as an added comment:

Neither of the following 3 lines work (called just before “uiwait”)

jtext.requestFocus()
jtext.requestDefaultFocus()
jtext.requestFocusInWindow()

/Henrik

#10 Comment By Ernst Kloppenburg On March 28, 2011 @ 04:24

When a JPanel is involved to group other java objects, the fix of setting the focusable property to true again has a strange result: tabbing through the controls is possible, but the order is reversed from the normal order (order of adding).

[pl,hcontainer]=javacomponent('javax.swing.JPanel',[0 0 100 100]);
tf1=javaObjectEDT('javax.swing.JTextField','one');
tf2=javaObjectEDT('javax.swing.JTextField','two');
tf3=javaObjectEDT('javax.swing.JTextField','three');
pl.setLayout(javaObjectEDT('javax.swing.BoxLayout',pl,BoxLayout.PAGE_AXIS))
pl.add(tf1)
pl.add(tf2)
pl.add(tf3)
tf1.setFocusable(true)
tf2.setFocusable(true)
tf3.setFocusable(true)
 

#11 Comment By Dirk Engel On September 1, 2011 @ 13:39

It seems the reason for this behavior is a customized focus traversal policy used by TMW. One has to replace this policy:

% Execute pending java callbacks because otherwise pl.getFocusCycleRootAncestor might be empty
drawnow();
% get focus cycle root component, typically this is a com.mathworks.hg.peer.FigureComponentContainer
focusCycleRoot = pl.getFocusCycleRootAncestor();
% replace default policy (com.mathworks.hg.peer.UicontrolFocusTraversalPolicy) by some "regular" policy.
% This works for both, uicontrols and java components, in the expected manner
focusCycleRoot.setFocusTraversalPolicy(javaObjectEDT(javax.swing.LayoutFocusTraversalPolicy));

Note: One cannot simply make the panel a focus traversal policy provider and just setFocusTraversalPolicy for the panel. Perhaps UicontrolFocusTraversalPolicy does not respect policy providers…

#12 Comment By Yair Altman On September 1, 2011 @ 14:36

@Dirk – thanks. This is the second time that I have seen you save the day with some very insightful Matlab-Java fix (the first was some years ago, when you [11] a bug and a workaround to Matlab’s internal DND issue with the DropTarget). You should be a regular here. 🙂

#13 Comment By Al Dunstan On January 25, 2012 @ 15:43

Close! So close! But I’m not quite there yet.

I have a moderately complex GUI developed in Java – JTables, JCheckBoxes, JSpinners, JComboBoxes, JButtons, etc. scattered across five JPanels in a JTabbedPane. If I run the Java outside Matlab (from Eclipse or from the command line – Linux or Windows) it works as expected. From within Matlab, however, tabbing is “odd” to say the least.

For example, the simplest panel has four JSpinners. If I click in the 1st field and hit TAB the text cursor disappears but focus stays in the 1st field (pressing the up & down arrow keys changes the value). From this point on the text cursor never appears until I mouse-click in a field. Pressing Shift-TAB will move focus to the next field (up & down arrows, again), until I get to the last field. Pressing Shift-TAB again causes the four fields to lose focus (up/down arrow keys do nothing, space bar does nothing, Return does nothing). Pressing TAB will return focus to the last window; repeatedly pressing TAB moves focus backwards through the fields.

I added a ‘fixFocusable()’ method to my outermost JPanel & called from Matlab. In there I called setFocusable(true) on all the components the user can interact with. I also tried the fix suggested by Dirk Engel, but focusCycleRoot came back null. I also tried

setFocusTraversalPolicy(new javax.swing.LayoutFocusTraversalPolicy));

on the main JPanel itself, but with no effect. I wasn’t sure what javaObjectEDT was supposed to be, but it didn’t compile.

In another panel (similar to the above, but with some JCheckBoxes thrown in) pressing Shift-TAB makes the text cursor vanish but doesn’t advance the focus to the next field. Keep pressing it and eventually the last field gets focus, but from then on repeatedly pressing TAB or Shift-TAB only makes the last field lose focus, or eventually bring focus back to the last field – it never makes it to any of the other JSpinners or JCheckBoxes. I haven’t tried all the other panels, but the ones I have tried behave more like this.

Nice focus handling isn’t the _most_ important feature of the GUI, but with more than 30 different fields (not counting checkboxes & tables, some of which can enable yet more options & input fields) it does make the UI easier to use.

Any suggestions? Is there any way to restore Java’s default focus handling, or maybe make Matlab not change the focus handling in the first place?

Thanks a bunch for your blog! I’ve learned quite a lot from it already.

#14 Comment By Darchinyan On November 24, 2013 @ 20:03

I don’t know how to put this, but here’s the scenario: I have a GUI that has javacomponents in it, and what I want to happen is that whenever I start the program, I want one of these javacomponents (it’s a button actually, a jButton) to gain the initial focus. I tried googling how to do it, came upon requestFocus, but can’t seem to get it to work as well.

#15 Comment By Yair Altman On November 25, 2013 @ 05:15

@Darchinyan,

jButton = javax.swing.JButton('Click me!');
[hjButton,hContainer] = javacomponent(jButton, [10,10,80,20], gcf);
jButton.requestFocus();

#16 Comment By Chris Rodgers On October 7, 2014 @ 19:04

Dear Yair,

This seems to have broken in R2014b. Calling setFocusable doesn’t seem to have any effect now.

For example:

figure(101)
clf

% Add a text uicontrol to label the slider.
txt = uicontrol('Style','edit', 'Position',[400 45 120 20], 'String','Vertical Exaggeration');

jButton = javax.swing.JButton('Click me!');
jbh = handle(jButton,'CallbackProperties');
set(jbh, 'ActionPerformedCallback', @(varargin) disp('CLICKED!'))
[hjButton,hContainer] = javacomponent(jButton, [10,10,80,20], gcf);

jButton.setFocusable(true);
jButton.requestFocus();

uicontrol('Style','text', 'Position',[100 245 240 60],...
    'String','Press TAB - java button should get focus...','FontSize',16);

If you run this in R2014a, then by pressing TAB and ENTER one can make the “CLICKED” message appear in the command window.

But in R2014b, pressing TAB will never give focus to the java button.

Do you have any ideas how to work around this?

Thanks,

Chris.

#17 Comment By Oleg On January 27, 2015 @ 10:26

Support confirmed that in R2014b we are out of luck and it is not high up in the priority list.

#18 Comment By Richard On March 25, 2015 @ 12:41

Running R2015a, I have what may be a related issue, but I’m not sure. Simply put java callbacks appear to be suppressed when changing the stacking order of components in a uipanel.
I have an edit box, created with standard Matlab and adding a java callback for loss of focus:

function ths = UIedit(oParent,oSkin,sDefault)
    ths.hCtrl = uicontrol('Style','edit',...
                          'Parent',handle(oParent),...
                          'String',ths.sDefault,...
                          'Enable','off',...
                          'ButtonDownFcn',@(hCtrl,oEventData)buttonDown(ths),...
                          'Callback',@(hCtrl,oEventData)callback(ths,oEventData),...
                          'KeyPressFcn',@(hCtrl,oEventData)keyPress(ths,oEventData),...
                          'KeyReleaseFcn',@(hCtrl,oEventData)keyRelease(ths,oEventData));
    ths.jCtrl = findjobj(ths.hCtrl);
    set(ths.jCtrl,'FocusLostCallback',@(src,evnt)focusLost(ths,src,evnt));
end % class constructor

If the edit box loses focus, the focusLost method is triggered and stuff happens. And it works just fine. Except…
I also have a listbox that is a regular Matlab uicontrol:

function ths = UIlistbox(oParent,oSkin)
    ths.hCtrl = uicontrol('Style','listbox',...
                          'Parent',handle(oParent),...
                          'Max',2,...
                          'Visible','off',...
                          'Callback',@(hCtrl,oEventData)callback(ths,oEventData));
end % class constructor

The listbox is initially hidden because it serves as an auto-completion list for the edit box. You know the concept – you start typing in the edit box and the auto-completion listbox appears with suggestions. And when I press enter, or click on a listbox item, or press escape, stuff happens and the editbox is cleared and the listbox is emptied and re-hidden.
Now, the reason for the java callback is if I click away from the editbox while typing – I don’t want the regular callback to fire and capture what I entered, I just want to clear and reset. As it happens this is not a very good solution at all, but it is illustrative of the problem.

Now, here’s the problem. There are other components that belong to the uipanel parent- 5 children in total, with the order of creation such that the listbox is item #3 in the hPanel.Children array. Visually, there is another uicontrol with position coordinates that are on top of the listbox, such that I have to restack the children, like so:

function moveToFront(ths)
    hCtrl = handle(ths);
    uistack(hCtrl,'top');
end

Before restacking the java callback triggers just fine. After restacking, the java callback no longer fires. And I have no idea why. Any idea how to fix this?

#19 Comment By Dirk Engel On August 31, 2015 @ 02:27

I just found an easy way to make it work again in R2015b (probably also works since R2014b). In addition to set components focusable one needs to add a specific client property:

h3.setFocusable(true);
h3.putClientProperty('TabCycleParticipant', true); % only components with this property will be accepted in the focus cycle

#20 Comment By Yair Altman On August 31, 2015 @ 11:29

@Dirk – thanks. I assume this originated from Matlab’s change of its internal Java version, from 6 to 7.

#21 Comment By Sam Roberts On January 11, 2016 @ 18:25

Thanks Dirk, I was pulling my hair out trying to figure out why setFocusable wasn’t doing it. FYI, I can confirm that this works in 14b, 15a and 15b.

#22 Comment By Dave Bryers On July 5, 2016 @ 18:48

This all works for me except when I make a JComboBox editable. At this point the tab traversal link seems to be broken, whilst you can tab to the combo box, tabbing out takes you back to the start.

% Open figure window
hFig = figure;
% Create two stadard text boxes
h1 = uicontrol(hFig,'style','edit','position',  [10 150 150 20],'String','One');
h2 = uicontrol(hFig,'style','edit','position',  [10 120 150 20],'String','Two');
% Create an editable combo box
model = javax.swing.DefaultComboBoxModel({'a','b','c'});
h3 = javacomponent({'javax.swing.JComboBox',model}, [10 90 150 20],hFig);
% Get it to tab here
h3.setFocusable(true)
h3.putClientProperty('TabCycleParticipant', true);
% Turn it into an editable box
h3.setEditable(true);
% Add two more standard text boxes
h4 = uicontrol(hFig,'style','edit','position',  [10 60 150 20],'String','Four');
h5 = uicontrol(hFig,'style','edit','position',  [10 30 150 20],'String','Five');

It is only with the h3.setEditable(true) that the tab order is disturbed.
Anyone have any thoughts?

#23 Comment By Yair Altman On July 17, 2016 @ 12:25

For an editable combobox, you need to set the focusing customizations on the internal textfield sub-component rather than on the parent JComboBox. i.e., instead of:

h3.putClientProperty('TabCycleParticipant', true);
h3.setFocusable(true)

do this:

jEditor = h3.getEditor.getEditorComponent;  % or: h3.getComponent(2)
jEditor.putClientProperty('TabCycleParticipant', true);
jEditor.setFocusable(true)

#24 Comment By Temu On November 8, 2017 @ 15:40

Hi Yair,

Thanks for sharing all these insights! These are really helpful.

I am currently trying to implement a focus-follows-mouse mechanism in matlab, since versions from (at least) 2015a onwards no longer support this behaviour. I have sommething that sometimes works, but not always, and I am at a loss here. I am hoping you have some ideas. I have tried the following:

function focus()
    mde = com.mathworks.mde.desk.MLDesktop.getInstance;
    cw = mde.getClient('Command Window');
    xCmdWndView = cw.getComponent(0).getViewport.getComponent(0);
    h_cw = handle(xCmdWndView,'CallbackProperties');

    function callMe(varargin)
        desktop = com.mathworks.mde.desk.MLDesktop.getInstance;
        desktop.getMainFrame.setFocusable(true);       % This one doesn't always work
        desktop.getMainFrame.requestFocusInWindow();   % This one doesn't always work
    end

    set(h_cw, 'MouseEnteredCallback', @callMe);
end

This often works, but when I, e.g., click the windows desktop first (at an empty space), only the icon in my taskbar becomes blue, but the matlab desktop does not focus. When I come from a different window, it seems to work most of the times.

I hope you can help me with this,

Temu

#25 Comment By Joe Burgel On August 10, 2018 @ 17:34

Hi Yair, Once again, saving my life! Thanks so much for what you do for all these years.


Article printed from Undocumented Matlab: https://undocumentedmatlab.com

URL to article: https://undocumentedmatlab.com/articles/fixing-a-java-focus-problem

URLs in this post:

[1] Java components: http://undocumentedmatlab.com/blog/javacomponent/

[2] yesterday: https://www.mathworks.com/matlabcentral/newsreader/view_thread/294261

[3] overview: http://java.sun.com/docs/books/tutorial/uiswing/misc/focus.html

[4] technical description: http://java.sun.com/javase/6/docs/api/java/awt/doc-files/FocusSpec.html

[5] Detecting window focus events : https://undocumentedmatlab.com/articles/detecting-window-focus-events

[6] Fixing Matlab's actxserver : https://undocumentedmatlab.com/articles/fixing-matlabs-actxserver

[7] Using pure Java GUI in deployed Matlab apps : https://undocumentedmatlab.com/articles/using-pure-java-gui-in-deployed-matlab-apps

[8] Matlab callbacks for Java events in R2014a : https://undocumentedmatlab.com/articles/matlab-callbacks-for-java-events-in-r2014a

[9] FindJObj – find a Matlab component's underlying Java object : https://undocumentedmatlab.com/articles/findjobj-find-underlying-java-object

[10] Extending a Java class with UDD : https://undocumentedmatlab.com/articles/extending-a-java-class-with-udd

[11] : https://www.mathworks.com/matlabcentral/newsreader/view_thread/154949#640039

Copyright © Yair Altman - Undocumented Matlab. All rights reserved.