Fixing a Java focus problem

An aspect of Matlab GUIs that include Java components that is often encountered (most recently reported yesterday) 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; technical description).

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

Tags: , ,

Bookmark and SharePrint Print

25 Responses to Fixing a Java focus problem

  1. damien says:

    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!

    • @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.

    • NS says:

      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

  2. Kesh says:

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

    • @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.

  3. Mike says:

    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

  4. Henrik Toft says:

    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

    • Henrik Toft says:

      Just as an added comment:

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

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

      /Henrik

  5. Ernst Kloppenburg says:

    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)
    • Dirk Engel says:

      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…

    • @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 found a bug and a workaround to Matlab’s internal DND issue with the DropTarget). You should be a regular here. :-)

  6. Al Dunstan says:

    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.

  7. Darchinyan says:

    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.

  8. Chris Rodgers says:

    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.

  9. Richard says:

    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?

  10. Dirk Engel says:

    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
  11. Dave Bryers says:

    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?

    • 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)
  12. Temu says:

    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

  13. Joe Burgel says:

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

Leave a Reply

Your email address will not be published. Required fields are marked *