Search code examples
javaswingkey-bindingsjsplitpane

How to attach key binding to up and down arrows of JSplitPane divider?


I have JSplitPane that has oneTouchExpandable set to true.

JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
        splitPane.setDividerSize(10);
        splitPane.setOneTouchExpandable(true);

The problem is that I do not know how to attach key bindings to up and down arrows on JSplitPane's divider. For up arrow I want Ctrl+U and for down - Ctrl + D.

Thanks!


Solution

  • The problem is that I do not know how to attach key bindings to up and down arrows on JSplitPane's divider.

    Normally you would try to access the Action of the button. In many cases the component will already define an Action that you can use. See Key Bindings for a list of the default bindings for a JSplitPane. Unfortunately there is no Action to support the one touch clicking options.

    So we need to access the buttons directly from the UI:

    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    import javax.swing.plaf.basic.*;
    
    public class SplitPaneDividerAction extends AbstractAction
    {
        private boolean leading;
    
        public SplitPaneDividerAction(boolean leading)
        {
            this.leading = leading;
        }
    
        @Override
        public void actionPerformed(ActionEvent e)
        {
            JSplitPane splitPane = (JSplitPane)e.getSource();
            BasicSplitPaneUI ui = (BasicSplitPaneUI)splitPane.getUI();
            BasicSplitPaneDivider divider = ui.getDivider();
    
            if (leading)
                ((JButton)divider.getComponent(0)).doClick();
            else
                ((JButton)divider.getComponent(1)).doClick();
        }
    
        private static void createAndShowUI()
        {
            JPanel leading = new JPanel();
            leading.setPreferredSize( new Dimension(200, 100) );
            leading.setBackground( Color.BLUE );
            leading.setFocusable(true);
    
            JPanel trailing = new JPanel();
            trailing.setPreferredSize( new Dimension(200, 100) );
            trailing.setBackground( Color.RED );
            trailing.setFocusable(true);
    
            JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leading, trailing);
            splitPane.setOneTouchExpandable(true);
            splitPane.setDividerLocation(100);
    
            InputMap im = splitPane.getInputMap(JSplitPane.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
            ActionMap am = splitPane.getActionMap();
            im.put(KeyStroke.getKeyStroke("control U"), "leading");
            im.put(KeyStroke.getKeyStroke("control D"), "trailing");
            am.put("leading", new SplitPaneDividerAction(true));
            am.put("trailing", new SplitPaneDividerAction(false));
    
            JFrame frame = new JFrame("SSCCE");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add( splitPane );
            frame.setSize(200, 200);
            frame.setLocationByPlatform( true );
            frame.setVisible( true );
        }
    
        public static void main(String[] args)
        {
            EventQueue.invokeLater(new Runnable()
            {
                public void run()
                {
                    createAndShowUI();
                }
            });
        }
    }
    

    Of course this approach will only work if your LAF extends from the BasicSplitPaneUI.