Tab key navigation in JavaFX TextArea

后端 未结 6 1437
暖寄归人
暖寄归人 2021-02-02 16:47

How do I make hitting the Tab Key in TextArea navigates to the next control ?

I could add a listener to cath de key pressed event, but how do I make te TextArea control

相关标签:
6条回答
  • 2021-02-02 17:07

    As of Java 9 (2017), most answers in this page don't work, since you can't do skin.getBehavior() anymore.

    This works:

    @Override
    public void handle(KeyEvent event) {
        KeyCode code = event.getCode();
    
        if (code == KeyCode.TAB && !event.isShiftDown() && !event.isControlDown()) {
            event.consume();
            Node node = (Node) event.getSource();
            try {
                Robot robot = new Robot();
                robot.keyPress(KeyCode.CONTROL.getCode());
                robot.keyPress(KeyCode.TAB.getCode());
                robot.delay(10);
                robot.keyRelease(KeyCode.TAB.getCode());
                robot.keyRelease(KeyCode.CONTROL.getCode());
                }
            catch (AWTException e) { }
            }
        }
    

    This also works:

    @Override
    public void handle(KeyEvent event) {
        KeyCode code = event.getCode();
    
        if (code == KeyCode.TAB && !event.isShiftDown() && !event.isControlDown()) {
            event.consume();
            Node node = (Node) event.getSource();            
            KeyEvent newEvent 
              = new KeyEvent(event.getSource(),
                         event.getTarget(), event.getEventType(),
                         event.getCharacter(), event.getText(),
                         event.getCode(), event.isShiftDown(),
                         true, event.isAltDown(),
                         event.isMetaDown());
    
            node.fireEvent(newEvent);            
            }
        }
    

    Both simulate pressing CTRL+TAB when the user presses TAB. The default behaviour of the TextArea for CTRL+TAB is moving the focus to the next control. Please note the second code is based on Johan De Schutter's answer.

    0 讨论(0)
  • 2021-02-02 17:07

    Inspired by the previous answers and for a very similar case, I built the following class:

    /**
     * Handles tab/shift-tab keystrokes to navigate to other fields,
     * ctrl-tab to insert a tab character in the text area.
     */
    public class TabTraversalEventHandler implements EventHandler<KeyEvent> {
        @Override
        public void handle(KeyEvent event) {
            if (event.getCode().equals(KeyCode.TAB)) {
                Node node = (Node) event.getSource();
                if (node instanceof TextArea) {
                    TextAreaSkin skin = (TextAreaSkin) ((TextArea)node).getSkin();
                    if (!event.isControlDown()) {
                        // Tab or shift-tab => navigational action
                        if (event.isShiftDown()) {
                            skin.getBehavior().traversePrevious();
                        } else {
                            skin.getBehavior().traverseNext();
                        }
                    } else {
                        // Ctrl-Tab => insert a tab character in the text area
                        TextArea textArea = (TextArea) node;
                        textArea.replaceSelection("\t");
                    }
                    event.consume();
                }
            }
        }
    }
    

    I just have not seen the necessity of handling tab in the context of a TextField so I removed this part.

    Then this class can be very easily used as described by User:

    TextArea myTextArea = new TextArea();
    mytTextArea.addEventFilter(KeyEvent.KEY_PRESSED, new TabTraversalEventHandler());
    

    And the whole thing works like a charm :)

    0 讨论(0)
  • 2021-02-02 17:11

    I had the same issue and I like the traverse-methods that Tom uses. But I also want to insert a tab when ctrl+tab is pressed.

    The call

    behavior.callAction("InsertTab");
    

    doesn´t work with JavaFX8. A look in the TextAreaBehaviour class showed me that there now is a "TraverseOrInsertTab" action.

    But however, I think this kind of action calling is quite unstable across several java versions because it relies on a string that is passed.

    So instead of the callAction() method, I used

    textArea.replaceSelection("\t");
    
    0 讨论(0)
  • 2021-02-02 17:27

    If a different solution for the Tab - Focus problem. The default behaviour of the TextArea for the CTRL+TAB key is a move of focus to the next control. So I replaced the TAB key event with a CTRL+TAB key event, and when the user hits CTRL+TAB a tab character is inserted in the TextArea.

    My question: is it OK to fire an event in the event filter? And is it OK to replace the text of the KeyEvent with the FOCUS_EVENT_TEXT, in order to have an indication if it is the an event generated by the user, or from the event created in the event filter.

    The event filter:

    javafx.scene.control.TextArea textArea1 = new javafx.scene.control.TextArea();
    textArea1.addEventFilter(KeyEvent.KEY_PRESSED, new TextAreaTabToFocusEventHandler());
    

    The event handler:

    public class TextAreaTabToFocusEventHandler implements EventHandler<KeyEvent>
    {
        private static final String FOCUS_EVENT_TEXT = "TAB_TO_FOCUS_EVENT";
    
        @Override
        public void handle(final KeyEvent event)
        {
            if (!KeyCode.TAB.equals(event.getCode()))
            {
                return;
            }
    
            // handle events where the TAB key or TAB + CTRL key is pressed
            // so don't handle the event if the ALT, SHIFT or any other modifier key is pressed
            if (event.isAltDown() || event.isMetaDown() || event.isShiftDown())
            {
                return;
            }
    
            if (!(event.getSource() instanceof TextArea))
            {
                return;
            }
    
            final TextArea textArea = (TextArea) event.getSource();
            if (event.isControlDown())
            {
                // if the event text contains the special focus event text
                // => do not consume the event, and let the default behaviour (= move focus to the next control) happen.
                //
                // if the focus event text is not present, then the user has pressed CTRL + TAB key,
                // then consume the event and insert or replace selection with tab character
                if (!FOCUS_EVENT_TEXT.equalsIgnoreCase(event.getText()))
                {
                    event.consume();
                    textArea.replaceSelection("\t");
                }
            }
            else
            {
                // The default behaviour of the TextArea for the CTRL+TAB key is a move of focus to the next control.
                // So we consume the TAB key event, and fire a new event with the CTRL + TAB key.
    
                event.consume();
    
                final KeyEvent tabControlEvent = new KeyEvent(event.getSource(), event.getTarget(), event.getEventType(), event.getCharacter(),
                                                              FOCUS_EVENT_TEXT, event.getCode(), event.isShiftDown(), true, event.isAltDown(), event.isMetaDown());
                textArea.fireEvent(tabControlEvent);
            }
        }
    }
    
    0 讨论(0)
  • 2021-02-02 17:30

    This code traverse focus if pressing TAB and insert tab if pressing CONTROL+TAB

    textArea.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
            @Override
            public void handle(KeyEvent event) {
                if (event.getCode() == KeyCode.TAB) {
                    SkinBase skin = (SkinBase) textArea.getSkin();
                    if (skin.getBehavior() instanceof TextAreaBehavior) {
                        TextAreaBehavior behavior = (TextAreaBehavior) skin.getBehavior();
                        if (event.isControlDown()) {
                            behavior.callAction("InsertTab");
                        } else {
                            behavior.callAction("TraverseNext");
                        }
                        event.consume();
                    }
    
                }
            }
        });
    
    0 讨论(0)
  • 2021-02-02 17:31

    I use the traverse-methods

    @Override
    public void handle(KeyEvent event) {
        if (event.getCode().equals(KeyCode.TAB)) {
            Node node = (Node) event.getSource();
            if (node instanceof TextField) {
                TextFieldSkin skin = (TextFieldSkin) ((TextField)node).getSkin();
                if (event.isShiftDown()) {
                    skin.getBehavior().traversePrevious();
                }
                else {
                    skin.getBehavior().traverseNext();
                }               
            }
            else if (node instanceof TextArea) {
                TextAreaSkin skin = (TextAreaSkin) ((TextArea)node).getSkin();
                if (event.isShiftDown()) {
                    skin.getBehavior().traversePrevious();
                }
                else {
                    skin.getBehavior().traverseNext();
                }
            }
    
            event.consume();
        }
    }
    
    0 讨论(0)
提交回复
热议问题