Vertex
Vertex

Reputation: 2712

Focus Traversal Policy in TitledPane

On JavaFX: How to change the focus traversal policy? Alexander Kirov show how to customize the focus traversal policy for a JavaFX application. It works fine, but not for TitledPanes. If a node in a TitledPane has focus, the setted TraversalEngine is not called.

Here is a full example to show this phenomenon:

package org.example;

import com.sun.javafx.scene.traversal.Direction;
import com.sun.javafx.scene.traversal.TraversalEngine;

import javafx.application.Application;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TitledPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class FocusTest extends Application {
    @Override
    public void start(Stage primaryStage) throws Exception {
        // Create UI
        final VBox root = new VBox();
        final Button foo = new Button("foo");
        foo.setId("foo");
        root.getChildren().add(foo);
        final Button bar = new Button("bar");
        bar.setId("bar");
        final Pane content = new Pane();
        content.getChildren().add(bar);
        final TitledPane tp = new TitledPane("tp", content);
        root.getChildren().add(tp);

        // Set TraversalEngine
        final TraversalEngine te = new TraversalEngine(root, false) {
            @Override
            public void trav(Node owner, Direction direction) {
                System.out.printf("trav owner: %s, direction: %s%n",
                        owner.getId(), direction);
                switch (direction) {
                case DOWN:
                case RIGHT:
                case NEXT:
                    if (owner == foo) {
                        bar.requestFocus();
                    } else if (owner == bar) {
                        foo.requestFocus();
                    }
                    break;
                case LEFT:
                case PREVIOUS:
                case UP:
                    if (owner == foo) {
                        bar.requestFocus();
                    } else if (owner == bar) {
                        foo.requestFocus();
                    }
                    break;
                }
            }
        };
        root.setImpl_traversalEngine(te);

        // Show Scene
        final Scene scene = new Scene(root);
        primaryStage.setHeight(200);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

The root of the scene is a VBox and the custom TraversalEngine is set to it. If the button foo has the focus and I press [Tab] then te.trav is called and the focus is set to bar. That's how I expected. But when bar has the focus, te.trav is not called. bar is a child of the TitledPane. This behaviour is shown in 1.

Has anyone a solution for this?

Focus Traversal in TitledPane

Upvotes: 2

Views: 1592

Answers (3)

richetyann
richetyann

Reputation: 46

In case the component you want to focus on is a Swing one, you just need to overload its requestFocus() to callback its SwingNode parent. Nothing more :

    SwingNode n = new SwingNode() {

        @Override
        public void requestFocus() {
            System.err.println("SwingNode.requestFocus "); // just to check
            super.requestFocus();
        }
    };
    JTextArea p = new JTextArea() {

        @Override
        public void requestFocus() {
            System.err.println("JTextArea.requestFocus");
            n.requestFocus(); // <- HERE IS THE TRICK: use SwingNode focus instead of Swing component one
        }
    };
    n.setContent(p);

Upvotes: 0

FxGodino
FxGodino

Reputation: 38

I save the fx:id in the field:

nombreDelPropietario.setUserData("#rbTodos");

I set On Action event this function:

    

public void procesaEnter(ActionEvent event) {

        /* Obtiene el Objeto que recibió el Enter */

        Node source = (Node) event.getSource();

        /* Obtiene el ID del objeto al que hay que ir */

        String idDestino = (String) source.getUserData();

        /* Si está informado */

        if (idDestino != null) {

            /* Recupera el Nodo */

            Node destino = (Node) ((Scene) source.getScene()).lookup(idDestino);

            /* Si está el nodo activo, accede al nodo seleccionado */

            if (destino != null) {
                destino.requestFocus();
            }

        }

    }

Upvotes: 0

Vertex
Vertex

Reputation: 2712

This is a tricky solution that deals with TitledPanes:

@SuppressWarnings("deprecation")
private static void registerTraversalEngine(final Parent parent,
        final TraversalEngine te) {
    parent.setImpl_traversalEngine(te);
    for (Node child : parent.getChildrenUnmodifiable()) {
        if (child instanceof Parent) {
            registerTraversalEngine((Parent) child, te);
        }
    }
    if (parent instanceof TitledPane) {
        final TitledPane tp = (TitledPane) parent;
        if (tp.getContent() instanceof Parent) {
            registerTraversalEngine((Parent) tp.getContent(), te);
        }
    }
}

I think the problem with TitltedPanes is, that the content is not in the children set:

TitledPane.getChildrenUnmodifiable().contains(TitledPane.getContent()) is always false

Upvotes: 1

Related Questions