dermoritz
dermoritz

Reputation: 13001

JavaFX key press not captured

I am trying to capture key press events (page up and down) but there are no key events received at all. Here is the relevant code: Constructor:

private MainLayout() {
    imageView = new ImageView();
    root = new StackPane();

    root.getChildren().add(imageView);
    root.setFocusTraversable(true); //no effect
    //root.requestFocus(); //also no effect
    registerEvents();

}

Both lines regarding the focus don't have an effect. The stack pane is directly added to scene. There are no other nodes than Scene->StackPane->ImageView. I am able to capture key events on the scene, but i need them captured in the stack pane

Here is registerEvents(), all other events are captured fine!:

private void registerEvents() {
    OnScroll onScroll = new OnScroll();
    root.setOnScroll(onScroll);
    OnResize onResize = new OnResize();
    root.heightProperty().addListener(onResize);
    root.widthProperty().addListener(onResize);
    OnMouseDown onMouseDown = new OnMouseDown();
    root.setOnMousePressed(onMouseDown);
    root.setOnMouseReleased((event) -> fitImage());
    root.setOnDragOver((event) -> dragOver(event));
    root.setOnDragDropped((event) -> dropFile(event));
    root.setOnKeyPressed((event) -> {
        LOG.debug("Key captured.");
        if(event.getCode() == KeyCode.PAGE_UP){
            imageView.setImage(ip.prev());
            event.consume();
        } else if(event.getCode() == KeyCode.PAGE_DOWN){
            imageView.setImage(ip.next());
            event.consume();
        }
        if(event.isConsumed()){
            fitImage();
        }
    });

I don't see the log out put and a break point is also not caught. So how to catch and handle key events correctly?

Upvotes: 5

Views: 2968

Answers (2)

dermoritz
dermoritz

Reputation: 13001

Meanwhile i found the solution thanks to this answer. The trick is to setFocusTraversable(true) on ImageView (child of stack pane). Here is the working code:

@Inject
private MainLayout(ImageProvider ip) {
    this.ip = ip;

    imageView = new ImageView();
    imageView.setFocusTraversable(true);
    imageView.requestFocus();

    root = new StackPane();

    root.getChildren().add(imageView);
    registerEvents();
}

Upvotes: 4

ttarczynski
ttarczynski

Reputation: 1014

I don't know if this answer will satisfy you, but I would move handling events from this class to the class where you initialize your scene, and attach the events to the scene itself (since StackPane is, in a way, the scene). I'm guessing that, since the constructor in your code is private, you are instantiating the class via public static method from another class.

public class MainClass extends Application {
   private Scene scene = new Scene(MainLayout.getMainLayout());

   @Override
   public void start(Stage primaryStage) throws Exception {
        registerEvents();        
        primaryStage.setScene(scene);
        primaryStage.show();
    }

   private void registerEvents() {
        OnScroll onScroll = new OnScroll();
        scene.setOnScroll(onScroll);
        OnResize onResize = new OnResize();
        scene.heightProperty().addListener(onResize);
        scene.widthProperty().addListener(onResize);
        OnMouseDown onMouseDown = new OnMouseDown();
        scene.setOnMousePressed(onMouseDown);
        scene.setOnMouseReleased((event) -> fitImage());
        scene.setOnDragOver((event) -> dragOver(event));
        scene.setOnDragDropped((event) -> dropFile(event));
        scene.setOnKeyPressed((event) -> {
            LOG.debug("Key captured.");
            if (event.getCode() == KeyCode.PAGE_UP) {
                imageView.setImage(ip.prev());
                event.consume();
            } else if (event.getCode() == KeyCode.PAGE_DOWN) {
                imageView.setImage(ip.next());
                event.consume();
            }
            if (event.isConsumed()) {
                fitImage();
            }
        });
    }
}

Alternatively, if you want to keep the code for handling events in the MainLayout class, consider making registerEvents a public (or package local, depending on your design) method accepting Scene as a param.

Upvotes: 0

Related Questions