Skip to content

Commit

Permalink
8291502: Mouse or touch presses on a non-focusable region don't clear…
Browse files Browse the repository at this point in the history
… the focusVisible flag of the current focus owner

Reviewed-by: kcr, aghaisas
  • Loading branch information
Michael Strauß committed Aug 2, 2022
1 parent 8fa8919 commit 5febaca
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 5 deletions.
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -60,6 +60,9 @@ public final class MouseEventFirer {
public MouseEventFirer(EventTarget target) {
this.target = target;

// Use the alternative creation path for MouseEvent by default, see JDK-8253769
this.alternative = true;

// Force the target node onto a stage so that it is accessible
if (target instanceof Node) {
Node n = (Node)target;
Expand Down
22 changes: 18 additions & 4 deletions modules/javafx.graphics/src/main/java/javafx/scene/Scene.java
Expand Up @@ -360,6 +360,18 @@ private Scene(Parent root, double width, double height, Paint fill,
setRoot(root);
init(width, height);
setFill(fill);

// Any mouse or touch press on the scene will clear the focusVisible flag of
// the current focus owner, if there is one.
EventHandler<InputEvent> pressedHandler = event -> {
Node focusOwner = getFocusOwner();
if (focusOwner != null) {
getKeyHandler().setFocusVisible(focusOwner, false);
}
};

addEventFilter(MouseEvent.MOUSE_PRESSED, pressedHandler);
addEventFilter(TouchEvent.TOUCH_PRESSED, pressedHandler);
}

static {
Expand Down Expand Up @@ -4036,7 +4048,9 @@ private PickResult pickNode(PickRay pickRay) {
class KeyHandler {
boolean focusVisible;

private void setFocusOwner(final Node value) {
private void setFocusOwner(Node value, boolean focusVisible) {
this.focusVisible = focusVisible;

// Cancel IM composition if there is one in progress.
// This needs to be done before the focus owner is switched as it
// generates event that needs to be delivered to the old focus owner.
Expand All @@ -4053,6 +4067,7 @@ private void setFocusOwner(final Node value) {
}

private void setFocusVisible(Node node, boolean focusVisible) {
this.focusVisible = focusVisible;
node.focusVisible.set(focusVisible);
node.focusVisible.notifyListeners();
}
Expand Down Expand Up @@ -4100,11 +4115,10 @@ private void process(KeyEvent e) {

private void requestFocus(Node node, boolean focusVisible) {
if (node == null) {
setFocusOwner(null);
setFocusOwner(null, false);
} else if (node.isCanReceiveFocus()) {
if (node != getFocusOwner()) {
this.focusVisible = focusVisible;
setFocusOwner(node);
setFocusOwner(node, focusVisible);
} else {
setFocusVisible(node, focusVisible);
}
Expand Down
Expand Up @@ -28,6 +28,7 @@

import com.sun.javafx.scene.SceneHelper;
import javafx.event.Event;
import javafx.event.EventTarget;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import test.com.sun.javafx.pgstub.StubScene;
Expand All @@ -41,6 +42,7 @@
import static org.junit.Assert.fail;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javafx.scene.Group;
Expand All @@ -49,6 +51,11 @@
import javafx.scene.Scene;
import javafx.scene.SceneShim;

import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.PickResult;
import javafx.scene.input.TouchEvent;
import javafx.scene.input.TouchPoint;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

Expand Down Expand Up @@ -775,6 +782,25 @@ private void fireTabKeyEvent(Node node) {
Event.fireEvent(node, new KeyEvent(KeyEvent.KEY_RELEASED, null, null, KeyCode.TAB, false, false, false, false));
}

private void fireMousePressedEvent(EventTarget target) {
double x = 10, y = 10;
PickResult pickResult = new PickResult(target, x, y);
Event.fireEvent(target, new MouseEvent(
MouseEvent.MOUSE_PRESSED, x, y, x, y, MouseButton.PRIMARY, 1,
false, false, false, false,
true, false, false,
false, false, false, pickResult));
}

private void fireTouchPressedEvent(EventTarget target) {
double x = 10, y = 10;
PickResult pickResult = new PickResult(scene, x, y);
Event.fireEvent(target, new TouchEvent(
TouchEvent.TOUCH_PRESSED,
new TouchPoint(0, TouchPoint.State.PRESSED, x, y, x, y, target, pickResult),
Collections.emptyList(), 0, false, false, false, false));
}

/**
* If a node acquires focus by calling {@link Node#requestFocus()}, it does not acquire visible focus.
*/
Expand Down Expand Up @@ -848,6 +874,44 @@ private void fireTabKeyEvent(Node node) {
assertNotFocusVisible(node2);
}

/**
* When any region of the window is clicked, the focus owner loses visible focus
* even when the focus owner doesn't change.
*/
@Test public void testMousePressedClearsFocusVisible() {
Node node1 = n(), node2 = n();
Group g = new Group(node1, node2);
scene.setRoot(g);
fireTabKeyEvent(g);

assertIsFocused(scene, node1);
assertIsFocusVisible(node1);

fireMousePressedEvent(scene);

assertIsFocused(scene, node1);
assertNotFocusVisible(node1);
}

/**
* When any region of the window is touched, the focus owner loses visible focus
* even when the focus owner doesn't change.
*/
@Test public void testTouchPressedClearsFocusVisible() {
Node node1 = n(), node2 = n();
Group g = new Group(node1, node2);
scene.setRoot(g);
fireTabKeyEvent(g);

assertIsFocused(scene, node1);
assertIsFocusVisible(node1);

fireTouchPressedEvent(scene);

assertIsFocused(scene, node1);
assertNotFocusVisible(node1);
}

/**
* When a node acquires focus, the focusWithin property is set on the node
* and all of its parents.
Expand Down

1 comment on commit 5febaca

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.