4512626: Non-editable JTextArea provides no visual indication of keyboard focus #21
Changes from 4 commits
9463a0b
70da8f6
3b9d9cf
abf5d77
e11d54b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,5 +1,5 @@ | ||||||||||||||||||||||
/* | ||||||||||||||||||||||
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved. | ||||||||||||||||||||||
* Copyright (c) 1997, 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 | ||||||||||||||||||||||
|
@@ -366,6 +366,8 @@ protected void moveCaret(MouseEvent e) { | |||||||||||||||||||||
} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
private int savedBlinkRate = 0; | ||||||||||||||||||||||
private boolean blinkRateSaved = false; | ||||||||||||||||||||||
// --- FocusListener methods -------------------------- | ||||||||||||||||||||||
|
||||||||||||||||||||||
/** | ||||||||||||||||||||||
|
@@ -379,8 +381,21 @@ protected void moveCaret(MouseEvent e) { | |||||||||||||||||||||
public void focusGained(FocusEvent e) { | ||||||||||||||||||||||
if (component.isEnabled()) { | ||||||||||||||||||||||
if (component.isEditable()) { | ||||||||||||||||||||||
setVisible(true); | ||||||||||||||||||||||
if (blinkRateSaved) { | ||||||||||||||||||||||
setBlinkRate(savedBlinkRate); | ||||||||||||||||||||||
savedBlinkRate = 0; | ||||||||||||||||||||||
blinkRateSaved = false; | ||||||||||||||||||||||
} | ||||||||||||||||||||||
} else { | ||||||||||||||||||||||
if (getBlinkRate() != 0) { | ||||||||||||||||||||||
if (!blinkRateSaved) { | ||||||||||||||||||||||
savedBlinkRate = getBlinkRate(); | ||||||||||||||||||||||
blinkRateSaved = true; | ||||||||||||||||||||||
} | ||||||||||||||||||||||
setBlinkRate(0); | ||||||||||||||||||||||
} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
setVisible(true); | ||||||||||||||||||||||
setSelectionVisible(true); | ||||||||||||||||||||||
updateSystemSelection(); | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
@@ -1031,17 +1046,34 @@ public void setVisible(boolean e) { | |||||||||||||||||||||
* @see Caret#setBlinkRate | ||||||||||||||||||||||
*/ | ||||||||||||||||||||||
public void setBlinkRate(int rate) { | ||||||||||||||||||||||
if (rate < 0) { | ||||||||||||||||||||||
throw new IllegalArgumentException("Invalid blink rate: " + rate); | ||||||||||||||||||||||
} | ||||||||||||||||||||||
if (rate != 0) { | ||||||||||||||||||||||
if (flasher == null) { | ||||||||||||||||||||||
flasher = new Timer(rate, handler); | ||||||||||||||||||||||
if (component.isEditable()) { | ||||||||||||||||||||||
if (flasher == null) { | ||||||||||||||||||||||
flasher = new Timer(rate, handler); | ||||||||||||||||||||||
} | ||||||||||||||||||||||
flasher.setDelay(rate); | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Coming back to negative rate, looks like this changes the previous behavior: Before the fix it did throw IAE in all cases: jdk20/src/java.desktop/share/classes/javax/swing/Timer.java Lines 397 to 406 in ca39eb9
After the fix IAE is thrown only if component is editable There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed. |
||||||||||||||||||||||
if (!flasher.isRunning()){ | ||||||||||||||||||||||
flasher.restart(); | ||||||||||||||||||||||
} | ||||||||||||||||||||||
} else { | ||||||||||||||||||||||
savedBlinkRate = rate; | ||||||||||||||||||||||
blinkRateSaved = true; | ||||||||||||||||||||||
} | ||||||||||||||||||||||
flasher.setDelay(rate); | ||||||||||||||||||||||
} else { | ||||||||||||||||||||||
if (flasher != null) { | ||||||||||||||||||||||
flasher.stop(); | ||||||||||||||||||||||
flasher.removeActionListener(handler); | ||||||||||||||||||||||
flasher = null; | ||||||||||||||||||||||
} | ||||||||||||||||||||||
if (component.isEditable()) { | ||||||||||||||||||||||
if (blinkRateSaved) { | ||||||||||||||||||||||
savedBlinkRate = 0; | ||||||||||||||||||||||
blinkRateSaved = false; | ||||||||||||||||||||||
} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
|
@@ -1053,6 +1085,9 @@ public void setBlinkRate(int rate) { | |||||||||||||||||||||
* @see Caret#getBlinkRate | ||||||||||||||||||||||
*/ | ||||||||||||||||||||||
public int getBlinkRate() { | ||||||||||||||||||||||
if (blinkRateSaved) { | ||||||||||||||||||||||
return savedBlinkRate; | ||||||||||||||||||||||
} | ||||||||||||||||||||||
return (flasher == null) ? 0 : flasher.getDelay(); | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
/* | ||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. | ||
* Copyright (c) 2017, 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 | ||
|
@@ -21,10 +21,17 @@ | |
* questions. | ||
*/ | ||
|
||
import javax.swing.*; | ||
import java.awt.*; | ||
import javax.swing.JFrame; | ||
import javax.swing.JMenu; | ||
import javax.swing.JMenuBar; | ||
import javax.swing.JMenuItem; | ||
import javax.swing.JTextField; | ||
import javax.swing.MenuSelectionManager; | ||
import javax.swing.SwingUtilities; | ||
import java.awt.FlowLayout; | ||
import java.awt.Point; | ||
import java.awt.Robot; | ||
import java.awt.event.InputEvent; | ||
import java.awt.image.BufferedImage; | ||
|
||
/** | ||
* @test | ||
|
@@ -39,7 +46,6 @@ public class HidingSelectionTest { | |
private static JTextField field1; | ||
private static JTextField field2; | ||
private static JFrame frame; | ||
private static Rectangle bounds; | ||
private static JMenu menu; | ||
private static JTextField anotherWindow; | ||
private static Point menuLoc; | ||
|
@@ -67,17 +73,9 @@ public static void main(String[] args) throws Exception { | |
Robot robot = new Robot(); | ||
robot.waitForIdle(); | ||
robot.delay(200); | ||
SwingUtilities.invokeAndWait(() -> { | ||
bounds = field2.getBounds(); | ||
bounds.setLocation(field2.getLocationOnScreen()); | ||
}); | ||
BufferedImage nosel = robot.createScreenCapture(bounds); | ||
|
||
SwingUtilities.invokeAndWait(field2::requestFocus); | ||
SwingUtilities.invokeAndWait(field2::selectAll); | ||
robot.waitForIdle(); | ||
robot.delay(200); | ||
BufferedImage sel = robot.createScreenCapture(bounds); | ||
|
||
SwingUtilities.invokeAndWait(() -> { | ||
menuLoc = menu.getLocationOnScreen(); | ||
|
@@ -89,7 +87,7 @@ public static void main(String[] args) throws Exception { | |
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); | ||
robot.waitForIdle(); | ||
robot.delay(200); | ||
if (!biEqual(robot.createScreenCapture(bounds), sel)) { | ||
if (!field2.getCaret().isSelectionVisible()) { | ||
throw new RuntimeException("Test fails: menu hides selection"); | ||
} | ||
|
||
|
@@ -98,7 +96,7 @@ public static void main(String[] args) throws Exception { | |
SwingUtilities.invokeAndWait(field1::requestFocus); | ||
robot.waitForIdle(); | ||
robot.delay(200); | ||
if (!biEqual(robot.createScreenCapture(bounds), nosel)) { | ||
if (field2.getCaret().isSelectionVisible()) { | ||
throw new RuntimeException( | ||
"Test fails: focus lost doesn't hide selection"); | ||
} | ||
|
@@ -119,35 +117,12 @@ public static void main(String[] args) throws Exception { | |
SwingUtilities.invokeAndWait(anotherWindow::requestFocus); | ||
robot.waitForIdle(); | ||
robot.delay(200); | ||
if (biEqual(robot.createScreenCapture(bounds), nosel)) { | ||
if (!field2.getCaret().isSelectionVisible()) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does it need similar change in test/jdk/javax/swing/text/DefaultCaret/HidingSelection/MultiSelectionTest.java There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I haven't tested the MultiSelectionTest but since it is problemlisted on Linux similar change might make it more stable. As of this test - since you are the owner of JDK-8194048 i thought that you can review my changes and deproblemlist the test if it is stable on the rest of the platforms and if changes make sense. I definitely do not want to steal your thunder. As of MultiSelectionTest i can take care of it either here or in the original bug that is noted in ProblemList.txt since my fix does not affect it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess anyway one is supposed to do a CI job for the fix and test the regression test on all platforms, so if the modified test is problemlisted it will not be tested in CI job, so it's better to deproblemlist and test... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have fixed the second test and removed both of them from the problem list. |
||
throw new RuntimeException( | ||
"Test fails: switch window hides selection"); | ||
} | ||
|
||
SwingUtilities.invokeAndWait(anotherWindow::selectAll); | ||
robot.waitForIdle(); | ||
robot.delay(200); | ||
if (biEqual(robot.createScreenCapture(bounds), sel)) { | ||
throw new RuntimeException( | ||
"Test fails: selection ownership is lost selection is shown"); | ||
} | ||
|
||
SwingUtilities.invokeLater(frame2::dispose); | ||
SwingUtilities.invokeLater(frame::dispose); | ||
} | ||
|
||
static boolean biEqual(BufferedImage i1, BufferedImage i2) { | ||
if (i1.getWidth() == i2.getWidth() && | ||
i1.getHeight() == i2.getHeight()) { | ||
for (int x = 0; x < i1.getWidth(); x++) { | ||
for (int y = 0; y < i1.getHeight(); y++) { | ||
if (i1.getRGB(x, y) != i2.getRGB(x, y)) { | ||
return false; | ||
} | ||
} | ||
} | ||
return true; | ||
} | ||
return false; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Naming of
savedBlinkRate
andblinkRateSaved
looks almost the same and confusing for me.Probably
isBlinkRateSaved
would be better to highlight boolean variable.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.