javaswingtraversaljcomponent

Java Focus Traversal Policy


I need help implementing my own traversal policy. My goal was to be able to traverse with tab some of the components in a panel, not all of them. I was able to research the FocusTraversalPolicy class and figure out a little how this works. The code below shows how far I got. But I seem to have hit a wall.

This code will allow me to traverse from one component to another, but there are in my Container smaller jpanels with jtextfields in them. For some reason, I am not able to traverse to these using tab, even though I added the jpanels into my policy.

I am aware that I can just access the textfields inside these jpanels and add them to my policy. However, since these smaller jpanels are sort of dynamic and can add extra components inside of them, I am looking more for something that will allow me to pass from the Parent container traversal cycle to the smaller one.

I could be wrong, but I believe I should add something to the getComponentAfter(...) and the getComponentBefore(...) methods. As you can see, I commented out some of the things I was trying. I would appreciate any help you can give me. I attached a picture of my panel so you can see what I mean.

enter image description here

public class TabFocusAdapter extends FocusTraversalPolicy implements FocusListener, MouseListener {
    
    private ArrayList<Component> comps = new ArrayList<Component>();
    private int focus, def;
    private boolean mouse_focus = false;
    
    
    public TabFocusAdapter(int f) {
        focus = def = f;
    }
    
    
    @Override
    /***Returns the component after the current one 
     * if current one is the last in the cycle, it will return the first
     * it will skip all disabled components */
    public Component getComponentAfter(Container aContainer, Component aComponent) {
        //if(((Container) aComponent).getFocusTraversalPolicy()!=null)
        //if(aComponent) 
            //return ((Container)aComponent).getFocusTraversalPolicy().getFirstComponent((Container) aComponent);
        focus++;
        if(focus == comps.size()) focus = 0;
        Component comp = comps.get(focus);
        while(!comp.isEnabled() || !comp.isShowing() || !comp.isVisible()) {
            focus++;
            if(focus == comps.size()) focus = 0;
            comp = comps.get(focus);
        }
        return comps.get(focus);
    }

    @Override
    /**Returns the component before the current one 
     * if current one is the first in the cycle, it will return the last
     * it will skip all disabled components */
    public Component getComponentBefore(Container aContainer, Component aComponent) {
        focus--;
        if(focus == -1) focus = comps.size()-1;
        Component comp = comps.get(focus);
        while(!comp.isEnabled() || !comp.isShowing() || !comp.isVisible()) {
            focus--;
            if(focus == -1) focus = comps.size()-1;
            comp = comps.get(focus);
        }
        return comps.get(focus);
    }
    
    @Override
    public Component getFirstComponent(Container aContainer) {
        return comps.get(0);
    }
    
    @Override
    public Component getLastComponent(Container aContainer) {
        return comps.get(comps.size() - 1);
    }
    
    @Override
    /**Returns the starting component */
    public Component getDefaultComponent(Container aContainer) {
        return comps.get(def);
    }
    
    public void addComp(Component comp) {
        comps.add(comp);
        comp.addMouseListener(this);
        comp.addFocusListener(this);
    }
    
    @Override
    public void focusGained(FocusEvent e) {
        Component comp = e.getComponent();
        if(!mouse_focus && comp instanceof JTextComponent) 
            ((JTextComponent)comp).selectAll();
    }
    
    @Override
    public void mousePressed(MouseEvent e) {
        mouse_focus = true;
        focus = comps.indexOf(e.getComponent());
    }
    
    @Override
    public void mouseReleased(MouseEvent e) {
        mouse_focus = false;
    }
    
    
    
    @Override
    public void mouseClicked(MouseEvent e) {}

    @Override
    public void focusLost(FocusEvent e) {}

    @Override
    public void mouseEntered(MouseEvent e) {}

    @Override
    public void mouseExited(MouseEvent e) {}
}

Solution

  • I solved my problem by including a series of checks into the getComponentAfter(...) and getComponentBefore(...) methods. I am including my code below.

    Basically, to determine whether to move to a lower cycle, it asks whether the next component is a Focus Cycle Root. When concluding a cycle, to determine whether to move to an upper cycle, it asks whether the parent of the container is a Focus Traversal Policy Provider.

    I got the general idea for this solution by looking at how these methods where implemented by the default traversal policies of my JFrame.

    '''

    public Component getComponentAfter(Container aContainer, Component aComponent) {
            Component comp;
            do{
                focus++;
                if(focus == comps.size()) {
                    focus = 0;
                    ancestor = aContainer;
                    do ancestor = ancestor.getParent();
                    while(ancestor!=null && !ancestor.isFocusTraversalPolicyProvider());
                    if(ancestor != null && ancestor.isFocusTraversalPolicyProvider())
                        return (ancestor
                                .getFocusTraversalPolicy()
                                .getComponentAfter(ancestor, aContainer));
                }
                comp = comps.get(focus);
            } while(!comp.isEnabled() || !comp.isShowing() || !comp.isVisible());
            if(comp instanceof Container && ((Container)comp).isFocusCycleRoot()) {
                return ((Container)comp)
                        .getFocusTraversalPolicy()
                        .getDefaultComponent((Container) comp);
            }
            return comp;
        }
    

    '''