Skip to content

Commit be2ac08

Browse files
committed
8373967: [macos] User interactions with List do not trigger ItemEvent after programmatic change
Reviewed-by: azvegint
1 parent f1c5041 commit be2ac08

File tree

2 files changed

+130
-14
lines changed

2 files changed

+130
-14
lines changed

src/java.desktop/macosx/classes/sun/lwawt/LWListPeer.java

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@
4949
import javax.swing.event.ListSelectionEvent;
5050
import javax.swing.event.ListSelectionListener;
5151

52+
import static java.awt.event.ItemEvent.DESELECTED;
53+
import static java.awt.event.ItemEvent.ITEM_STATE_CHANGED;
54+
import static java.awt.event.ItemEvent.SELECTED;
55+
5256
/**
5357
* Lightweight implementation of {@link ListPeer}. Delegates most of the work to
5458
* the {@link JList}, which is placed inside {@link JScrollPane}.
@@ -280,21 +284,18 @@ public void setSkipStateChangedEvent(boolean skipStateChangedEvent) {
280284

281285
@Override
282286
public void valueChanged(final ListSelectionEvent e) {
283-
if (!e.getValueIsAdjusting() && !isSkipStateChangedEvent()) {
284-
final JList<?> source = (JList<?>) e.getSource();
285-
for(int i = 0 ; i < source.getModel().getSize(); i++) {
286-
287-
final boolean wasSelected = Arrays.binarySearch(oldSelectedIndices, i) >= 0;
288-
final boolean isSelected = source.isSelectedIndex(i);
289-
290-
if (wasSelected == isSelected) {
291-
continue;
287+
if (!e.getValueIsAdjusting()) {
288+
JList<?> source = (JList<?>) e.getSource();
289+
if (!isSkipStateChangedEvent()) {
290+
for (int i = 0; i < source.getModel().getSize(); i++) {
291+
boolean wasSelected =
292+
Arrays.binarySearch(oldSelectedIndices, i) >= 0;
293+
if (wasSelected != source.isSelectedIndex(i)) {
294+
int state = wasSelected ? DESELECTED : SELECTED;
295+
LWListPeer.this.postEvent(new ItemEvent(getTarget(),
296+
ITEM_STATE_CHANGED, i, state));
297+
}
292298
}
293-
294-
final int state = !wasSelected && isSelected ? ItemEvent.SELECTED: ItemEvent.DESELECTED;
295-
296-
LWListPeer.this.postEvent(new ItemEvent(getTarget(), ItemEvent.ITEM_STATE_CHANGED,
297-
i, state));
298299
}
299300
oldSelectedIndices = source.getSelectedIndices();
300301
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/*
2+
* Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
import java.awt.Frame;
25+
import java.awt.List;
26+
import java.awt.Point;
27+
import java.awt.Robot;
28+
import java.awt.event.ItemEvent;
29+
import java.awt.event.KeyEvent;
30+
import java.util.concurrent.ArrayBlockingQueue;
31+
import java.util.concurrent.BlockingQueue;
32+
import java.util.concurrent.TimeUnit;
33+
34+
import jdk.test.lib.Platform;
35+
36+
/**
37+
* @test
38+
* @bug 8373967
39+
* @key headful
40+
* @summary Checks that programmatic changes to a List do not fire events,
41+
* only user interactions do
42+
* @library /test/lib
43+
* @build jdk.test.lib.Platform
44+
* @run main/othervm MixProgrammaticUserChange
45+
*/
46+
public final class MixProgrammaticUserChange {
47+
48+
private static Robot robot;
49+
private static final BlockingQueue<ItemEvent> events =
50+
new ArrayBlockingQueue<>(10);
51+
52+
public static void main(String[] args) throws Exception {
53+
Frame frame = new Frame();
54+
try {
55+
robot = new Robot();
56+
robot.setAutoWaitForIdle(true);
57+
robot.setAutoDelay(100);
58+
59+
List list = new List(1, true);
60+
list.add("Item");
61+
list.addItemListener(e -> {
62+
if (e.getID() == ItemEvent.ITEM_STATE_CHANGED) {
63+
events.offer(e);
64+
}
65+
});
66+
67+
frame.add(list);
68+
frame.setUndecorated(true);
69+
frame.pack();
70+
frame.setLocationRelativeTo(null);
71+
frame.setVisible(true);
72+
robot.waitForIdle(100);
73+
74+
Point loc = list.getLocationOnScreen();
75+
loc.translate(list.getWidth() / 2, list.getHeight() / 2);
76+
77+
test(() -> click(loc), ItemEvent.SELECTED);
78+
test(() -> list.deselect(0), -1);
79+
test(() -> click(loc), ItemEvent.SELECTED);
80+
test(() -> click(loc), ItemEvent.DESELECTED);
81+
test(() -> list.select(0), -1);
82+
test(() -> click(loc), ItemEvent.DESELECTED);
83+
} finally {
84+
frame.dispose();
85+
}
86+
if (!events.isEmpty()) {
87+
throw new RuntimeException("Unexpected events: " + events);
88+
}
89+
}
90+
91+
private static void test(Runnable action, int state) throws Exception {
92+
action.run();
93+
// Large delay, we are waiting for unexpected events
94+
ItemEvent e = events.poll(1, TimeUnit.SECONDS);
95+
if (e == null && state == -1) {
96+
return; // no events as expected
97+
} else if (e != null && e.getStateChange() == state) {
98+
return; // expected event received
99+
}
100+
String text = (state == -1) ? "null" : state == ItemEvent.SELECTED
101+
? "SELECTED" : "DESELECTED";
102+
throw new RuntimeException("Expected: %s, got: %s".formatted(text, e));
103+
}
104+
105+
private static void click(Point p) {
106+
robot.mouseMove(p.x, p.y);
107+
int keyCode = Platform.isOSX() ? KeyEvent.VK_META : KeyEvent.VK_CONTROL;
108+
robot.keyPress(keyCode);
109+
try {
110+
robot.click();
111+
} finally {
112+
robot.keyRelease(keyCode);
113+
}
114+
}
115+
}

0 commit comments

Comments
 (0)