3

I need to switch tabs on Ctrl-Tab and Ctrl-Shift-Tab. But it works only when TabPane is in focus. I have Tabs with setContent(textArea). When textArea is in focus, and I press Ctrl-Tab, focus goes somewhere in menu:

    TextArea@3f40a15a[styleClass=text-input text-area]
    TextArea@7b7c82a4[styleClass=text-input text-area]
    TabPane[id=null, styleClass=tab-pane]
    TextArea[id=null, styleClass=text-input text-area]
    MenuButtonSkinBase$MenuLabeledImpl[id=null, styleClass=label]
    TextArea[id=null, styleClass=text-input text-area]
    MenuButtonSkinBase$MenuLabeledImpl[id=null, styleClass=label]
    TextArea[id=null, styleClass=text-input text-area]
    TabPane[id=null, styleClass=tab-pane]
    TextArea[id=null, styleClass=text-input text-area]
    MenuButtonSkinBase$MenuLabeledImpl[id=null, styleClass=label]
    TextArea[id=null, styleClass=text-input text-area]

I track focus changes like this:

    scene.focusOwnerProperty().addListener(new ChangeListener<Node>() {

        @Override
        public void changed(ObservableValue<? extends Node> ov, Node t, Node t1) {
            System.out.println(t1);
        }
    });

I have setFocusTraversable to false almost on every component(including menu, toolbar, textArea and panes)

I created Tabs and used setContent(textArea).

Also I want to requestFocus on textArea every time user open/close tab and switch between tabs(using mouse and keyboard hotkeys).

    tabPane.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener<Number>() {

        @Override
        public void changed(ObservableValue<? extends Number> ov, Number t, Number t1) {
            Platform.runLater(new Runnable() {

                @Override
                public void run() {
                    if (tabPane.getTabs().size() > 0) {
                        FileTab tab = (FileTab)tabPane.getTabs().get(tabPane.getSelectionModel().getSelectedIndex());
                        tab.getTextArea().requestFocus();
                    }
                }
            });

        }
    });

This does not work. I tried tab.getContent().requestFocus() - doesn't work too.

About changing tabs when TabPane child is in focus: It seems like TabPane gets KeyEvent, but because it's not in focus(textArea in focus), it just skips this event. Maybe I could duplicate default functionality by firing KeyEvent at TabPane or maybe even re implement it on my own. Or maybe I could call nextTab/previousTab default actions from this listener:

tabPane.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler() {

        @Override
        public void handle(KeyEvent t) {
            if (t.getCode().equals(KeyCode.TAB) && t.isControlDown()) {
                  //Event.fireEvent(tabPane, event); //I'm not sure how to do it like this
                  //Maybe it's better to just call default actions on TabPane.
                  t.consume();
            }
        }
    }); 

http://s23.postimg.org/poeujzwbf/screenshot_185.png

But I still don't know how to set focus on TextArea after tab switch. Maybe I should try to call Platform.runLater() after some delay?

AS3Boyan
  • 149
  • 1
  • 11
  • Can you [filter the event on the scene](http://docs.oracle.com/javafx/2/api/javafx/scene/Scene.html#addEventFilter(javafx.event.EventType,%20javafx.event.EventHandler)) rather than the TabPane? – jewelsea Sep 26 '13 at 10:28
  • Seems like a good idea. I thought that JavaFX has more elegant solution. Anyway I got it working, but still don't know to set focus on TextArea inside Tab after tab switch, sometimes it works, sometimes it doesn't work(requestFocus()). – AS3Boyan Sep 26 '13 at 11:24
  • @AS3Boyan, can you try put only `tab.getTextArea().requestFocus();` in Platform.runLater, namely put the if-statement out of it. Alternatively use timeline to request the focus after 100ms for instance. – Uluk Biy Sep 26 '13 at 13:24
  • @Uluk Biy, I thought about running after delay, I wonder if in Java 8 those things fixed. new Timer() seems like works fine, but Application does not terminate properly. I will try to use Timeline, if you have example on how to use it as timer, please let me know. – AS3Boyan Sep 26 '13 at 14:38
  • @AS3Boyan, see [(http://stackoverflow.com/a/16920898](http://stackoverflow.com/a/16920898) and [http://stackoverflow.com/a/9966213](http://stackoverflow.com/a/9966213). – Uluk Biy Sep 26 '13 at 14:57
  • @UlukBiy, I tried using Timer, but without timer.cancel(); timer.purge(); function call, app just doesn't terminate properly. (With Platform.runLater() inside run() function). – AS3Boyan Sep 26 '13 at 15:12

2 Answers2

4

SOLUTION FOR TABS SWITCH:

    scene.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {

        @Override
        public void handle(KeyEvent t) {
            if (t.getCode().equals(KeyCode.TAB) && t.isControlDown()) {
                int size = tabPane.getTabs().size();

                if (size > 0) {
                    SkinBase skin = (SkinBase) tabPane.getSkin();
                    TabPaneBehavior tabPaneBehavior = (TabPaneBehavior) skin.getBehavior();

                    int selectedIndex = tabPane.getSelectionModel().getSelectedIndex();

                    if (!t.isShiftDown()) {
                        if (selectedIndex < size -1) {
                            tabPaneBehavior.selectNextTab();
                        } else {
                            tabPaneBehavior.selectTab(tabPane.getTabs().get(0));
                        }
                    } else {
                        if (selectedIndex > 0) {
                            tabPaneBehavior.selectPreviousTab();
                        } else {
                            tabPaneBehavior.selectTab(tabPane.getTabs().get(size - 1));
                        }
                    }

                    t.consume();
                }

            }
        }
    });

SOLUTION FOR SETTING FOCUS ON SELECTED TAB:

        tabPane.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener<Number>() {

        @Override
        public void changed(ObservableValue<? extends Number> ov, Number t, final Number t1) {
            Platform.runLater(new Runnable() {

                @Override
                public void run() {
                    if (tabPane.getTabs().size() > 0) {
                        final FileTab tab = (FileTab) tabPane.getTabs().get(t1.intValue());

                        final Timer timer = new Timer();
                        timer.schedule(new TimerTask() {

                            @Override
                            public void run() {
                                Platform.runLater(new Runnable() {

                                    @Override
                                    public void run() {
                                        tab.getContent().requestFocus();
                                        timer.cancel();
                                        timer.purge();
                                    }
                                });
                            }
                        }, 25);
                    }
                }
            });

        }
    });

ALTERNATIVE SOLUTION FOR SETTING FOCUS ON SELECTED TAB(based on https://stackoverflow.com/a/16920898 by @Uluk Biy):

        tabPane.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener<Number>() {

        @Override
        public void changed(ObservableValue<? extends Number> ov, Number t, final Number t1) {
            Platform.runLater(new Runnable() {

                @Override
                public void run() {
                    if (tabPane.getTabs().size() > 0) {
                        final FileTab tab = (FileTab) tabPane.getTabs().get(t1.intValue());

                        final Timeline animation = new Timeline(
                                new KeyFrame(Duration.millis(25),
                                new EventHandler<ActionEvent>() {
                                    @Override
                                    public void handle(ActionEvent actionEvent) {
                                        Platform.runLater(new Runnable() {

                                            @Override
                                            public void run() {
                                                tab.getContent().requestFocus();
                                            }
                                        });
                                    }
                                }));
                        animation.setCycleCount(1);
                        animation.play();
                    }
                }
            });

        }
    });

This should be better solution than just using timer.

Community
  • 1
  • 1
AS3Boyan
  • 149
  • 1
  • 11
  • There is no need to wrap code inside of `KeyFrame` handler in `Platform.runlater(...)` – user11153 Feb 15 '16 at 10:15
  • I am using JDK 8u162, I tested this code and it's working in my project: `getTabs().forEach(e -> e.setOnSelectionChanged(o -> Platform.runLater(() -> e.getContent().requestFocus())));`. Perhaps it will work on yours too. – blackr1234 Apr 28 '18 at 19:59
  • In my above solution, if user clicks again on the `Tab`, its content will lose focus. Use this code on the `TabPane` to make sure subsequent clicks on the `Tab` or clicks on the space in the tab bar will keep focus on its content: `setOnMousePressed(e -> getSelectionModel().getSelectedItem().getContent().requestFocus());`. – blackr1234 Apr 28 '18 at 20:17
0

Just use the following to focus the first tab (i.e. tab 0) :

Platform.runLater(() -> tabPane.getTabs().get(0).getContent().requestFocus());