Skip to content

Commit bb06b64

Browse files
SaiPradeepDandemAndy Goryachev
authored and
Andy Goryachev
committed
8185831: Pseudo selectors do not appear to work in Node.lookupAll()
Reviewed-by: kcr, angorya, jhendrikx
1 parent f41e3ec commit bb06b64

File tree

2 files changed

+185
-15
lines changed

2 files changed

+185
-15
lines changed

modules/javafx.graphics/src/main/java/javafx/scene/Node.java

+37-7
Original file line numberDiff line numberDiff line change
@@ -1950,9 +1950,15 @@ private void updateDisabled() {
19501950
* into the branch until it finds a match. If more than one sub-node matches the
19511951
* specified selector, this function returns the first of them.
19521952
* <p>
1953-
* For example, if a Node is given the id of "myId", then the lookup method can
1954-
* be used to find this node as follows: <code>scene.lookup("#myId");</code>.
1955-
* </p>
1953+
* If the lookup selector does not specify a pseudo class, the lookup will ignore pseudo class states;
1954+
* it will return the first matching node whether or not it contains pseudo classes.
1955+
* <p>
1956+
* For example, if a Node is given the id of "myId", then the lookup method can
1957+
* be used to find this node as follows: {@code scene.lookup("#myId");}.
1958+
* <p>
1959+
* For example, if two nodes, NodeA and NodeB, have the same style class "myStyle" and NodeA has
1960+
* a pseudo state "myPseudo", then to find NodeA, the lookup method can be used as follows:
1961+
* {@code scene.lookup(".myStyle:myPseudo");} or {@code scene.lookup(":myPseudo");}.
19561962
*
19571963
* @param selector The css selector of the node to find
19581964
* @return The first node, starting from this {@code Node}, which matches
@@ -1961,13 +1967,23 @@ private void updateDisabled() {
19611967
public Node lookup(String selector) {
19621968
if (selector == null) return null;
19631969
Selector s = Selector.createSelector(selector);
1964-
return s != null && s.applies(this) ? this : null;
1970+
return selectorMatches(s) ? this : null;
19651971
}
19661972

19671973
/**
19681974
* Finds all {@code Node}s, including this one and any children, which match
19691975
* the given CSS selector. If no matches are found, an empty unmodifiable set is
19701976
* returned. The set is explicitly unordered.
1977+
* <p>
1978+
* If the lookupAll selector does not specify a pseudo class, the lookupAll will ignore pseudo class states;
1979+
* it will return all matching nodes whether or not the nodes contain pseudo classes.
1980+
* <p>
1981+
* For example, if there are multiple nodes with same style class "myStyle", then the lookupAll method can
1982+
* be used to find all these nodes as follows: {@code scene.lookupAll(".myStyle");}.
1983+
* <p>
1984+
* For example, if multiple nodes have same style class "myStyle" and few nodes have
1985+
* a pseudo state "myPseudo", then to find all nodes with "myPseudo" state, the lookupAll method can be used as follows:
1986+
* {@code scene.lookupAll(".myStyle:myPseudo");} or {@code scene.lookupAll(":myPseudo");}.
19711987
*
19721988
* @param selector The css selector of the nodes to find
19731989
* @return All nodes, starting from and including this {@code Node}, which match
@@ -1986,11 +2002,12 @@ public Set<Node> lookupAll(String selector) {
19862002
* Used by Node and Parent for traversing the tree and adding all nodes which
19872003
* match the given selector.
19882004
*
1989-
* @param selector The Selector. This will never be null.
1990-
* @param results The results. This will never be null.
2005+
* @param selector the css selector of the nodes to find
2006+
* @param results the results
2007+
* @return list of matching nodes
19912008
*/
19922009
List<Node> lookupAll(Selector selector, List<Node> results) {
1993-
if (selector.applies(this)) {
2010+
if (selectorMatches(selector)) {
19942011
// Lazily create the set to reduce some trash.
19952012
if (results == null) {
19962013
results = new LinkedList<>();
@@ -2024,6 +2041,19 @@ public void toFront() {
20242041
}
20252042
}
20262043

2044+
/**
2045+
* Checks whether the provided selector matches the node with both styles and pseudo states.
2046+
* @param s selector to match
2047+
* @return {@code true} if the selector matches
2048+
*/
2049+
private boolean selectorMatches(Selector s) {
2050+
boolean matches = s != null && s.applies(this);
2051+
if (matches && !s.createMatch().getPseudoClasses().isEmpty()) {
2052+
matches = s.stateMatches(this, this.getPseudoClassStates());
2053+
}
2054+
return matches;
2055+
}
2056+
20272057
// TODO: need to verify whether this is OK to do starting from a node in
20282058
// the scene graph other than the root.
20292059
private void doCSSPass() {

modules/javafx.graphics/src/test/java/test/javafx/scene/Node_lookup_Test.java

+148-8
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
import static org.junit.Assert.assertTrue;
3131

3232
import java.util.Set;
33+
34+
import javafx.css.PseudoClass;
3335
import javafx.scene.Group;
3436
import javafx.scene.Node;
3537
import javafx.scene.ParentShim;
@@ -39,11 +41,11 @@
3941

4042
public class Node_lookup_Test {
4143
// Group & #root
42-
// / \
43-
// #a .b.c
44-
// / \ \
45-
// .d #e .d
46-
private Group root, a, bc, d, e, d2;
44+
// / \ \
45+
// #a .b.c .f:testPseudo
46+
// / \ \ / \
47+
// .d #e .d .g:testPseudo1 .h.g:testPseudo1:testPseudo2
48+
private Group root, a, bc, d, e, d2, f, g, hg;
4749

4850
@Before public void setup() {
4951
root = new Group();
@@ -58,12 +60,23 @@ public class Node_lookup_Test {
5860
bc.getStyleClass().addAll("b", "c");
5961
d2 = new Group();
6062
d2.getStyleClass().add("d");
61-
ParentShim.getChildren(root).addAll(a, bc);
63+
f = new Group();
64+
f.getStyleClass().add("f");
65+
f.pseudoClassStateChanged(PseudoClass.getPseudoClass("testPseudo"), true);
66+
g = new Group();
67+
g.getStyleClass().add("g");
68+
g.pseudoClassStateChanged(PseudoClass.getPseudoClass("testPseudo1"), true);
69+
hg = new Group();
70+
hg.getStyleClass().addAll("h", "g");
71+
hg.pseudoClassStateChanged(PseudoClass.getPseudoClass("testPseudo1"), true);
72+
hg.pseudoClassStateChanged(PseudoClass.getPseudoClass("testPseudo2"), true);
73+
ParentShim.getChildren(root).addAll(a, bc, f);
6274
ParentShim.getChildren(a).addAll(d, e);
6375
ParentShim.getChildren(bc).addAll(d2);
76+
ParentShim.getChildren(f).addAll(g, hg);
6477
}
6578

66-
@Test public void quickTest() {
79+
@Test public void test_lookup_on_nodes_without_pseudo_classes() {
6780
Node found = root.lookup("Group");
6881
assertSame(root, found);
6982

@@ -86,7 +99,7 @@ public class Node_lookup_Test {
8699
assertSame(bc, found);
87100
}
88101

89-
@Test public void lookupAllTest() {
102+
@Test public void test_lookupAll_on_nodes_without_pseudo_classes() {
90103
Set<Node> nodes = root.lookupAll("#a");
91104
assertEquals(1, nodes.size());
92105
assertTrue(nodes.contains(a));
@@ -96,4 +109,131 @@ public class Node_lookup_Test {
96109
assertTrue(nodes.contains(d));
97110
assertTrue(nodes.contains(d2));
98111
}
112+
113+
@Test
114+
public void test_lookup_and_lookupAll_on_nodes_with_pseudo_classes() {
115+
Set<Node> nodes = root.lookupAll(".h:testPseudo2");
116+
assertEquals(1, nodes.size());
117+
assertTrue(nodes.contains(hg));
118+
119+
Node found = root.lookup(".h:testPseudo2");
120+
assertSame(hg, found);
121+
122+
found = root.lookup(":testPseudo2");
123+
assertSame(hg, found);
124+
125+
found = root.lookup(".h:testPseudo1:testPseudo2");
126+
assertSame(hg, found);
127+
128+
nodes = root.lookupAll(".g:testPseudo1");
129+
assertEquals(2, nodes.size());
130+
assertTrue(nodes.contains(g));
131+
assertTrue(nodes.contains(hg));
132+
133+
nodes = root.lookupAll(":testPseudo1");
134+
assertEquals(2, nodes.size());
135+
assertTrue(nodes.contains(g));
136+
assertTrue(nodes.contains(hg));
137+
138+
nodes = root.lookupAll(".f > .h:testPseudo1");
139+
assertEquals(1, nodes.size());
140+
assertTrue(nodes.contains(hg));
141+
142+
nodes = root.lookupAll(".f:testPseudo > .h:testPseudo1");
143+
assertEquals(1, nodes.size());
144+
assertTrue(nodes.contains(hg));
145+
146+
nodes = root.lookupAll(".f:randomPseudo");
147+
assertEquals(0, nodes.size());
148+
}
149+
150+
/**
151+
* Verifies that the lookup ignores pseudo classes when selector contains no explicit pseudo class, but all the nodes have pseudo classes set to them.
152+
*/
153+
@Test
154+
public void test_lookupAll_on_nodes_with_pseudo_classes_ignoring_pseudo_classes_in_selector() {
155+
// Except root node all the other nodes (f, g, hg) have pseudo classes set to them
156+
Set<Node> nodes = root.lookupAll(".g");
157+
assertEquals(2, nodes.size());
158+
assertTrue(nodes.contains(g));
159+
assertTrue(nodes.contains(hg));
160+
161+
nodes = root.lookupAll("#root .g");
162+
assertEquals(2, nodes.size());
163+
assertTrue(nodes.contains(g));
164+
assertTrue(nodes.contains(hg));
165+
166+
nodes = root.lookupAll(".f .g");
167+
assertEquals(2, nodes.size());
168+
assertTrue(nodes.contains(g));
169+
assertTrue(nodes.contains(hg));
170+
171+
nodes = root.lookupAll(".f .h");
172+
assertEquals(1, nodes.size());
173+
assertTrue(nodes.contains(hg));
174+
175+
nodes = root.lookupAll(".f > .h");
176+
assertEquals(1, nodes.size());
177+
assertTrue(nodes.contains(hg));
178+
179+
nodes = root.lookupAll(".h");
180+
assertEquals(1, nodes.size());
181+
assertTrue(nodes.contains(hg));
182+
183+
nodes = root.lookupAll("#root > .f");
184+
assertEquals(1, nodes.size());
185+
assertTrue(nodes.contains(f));
186+
187+
nodes = root.lookupAll("#root .f");
188+
assertEquals(1, nodes.size());
189+
assertTrue(nodes.contains(f));
190+
191+
nodes = root.lookupAll(".random");
192+
assertEquals(0, nodes.size());
193+
194+
nodes = root.lookupAll(".random .h");
195+
assertEquals(0, nodes.size());
196+
}
197+
198+
/**
199+
* Verifies that the lookup ignores pseudo classes when selector contains no explicit pseudo class.
200+
*/
201+
@Test
202+
public void test_lookupAll_on_nodes_with_same_style_and_different_pseudo_classes() {
203+
Group root = new Group();
204+
root.setId("root");
205+
206+
Group xy = new Group();
207+
xy.getStyleClass().addAll("x", "y");
208+
209+
Group x = new Group();
210+
x.getStyleClass().addAll("x");
211+
212+
Group x1 = new Group();
213+
x1.getStyleClass().addAll("x");
214+
x1.pseudoClassStateChanged(PseudoClass.getPseudoClass("pseudo"), true);
215+
216+
ParentShim.getChildren(root).addAll(x, x1, xy);
217+
218+
Set<Node> nodes = root.lookupAll(".x");
219+
assertEquals(3, nodes.size());
220+
assertTrue(nodes.contains(x));
221+
assertTrue(nodes.contains(x1));
222+
assertTrue(nodes.contains(xy));
223+
224+
nodes = root.lookupAll(".x:pseudo");
225+
assertTrue(nodes.contains(x1));
226+
227+
nodes = root.lookupAll("#root .x");
228+
assertEquals(3, nodes.size());
229+
assertTrue(nodes.contains(x));
230+
assertTrue(nodes.contains(x1));
231+
assertTrue(nodes.contains(xy));
232+
233+
nodes = root.lookupAll("#root .x:pseudo");
234+
assertTrue(nodes.contains(x1));
235+
236+
nodes = root.lookupAll(".x:random");
237+
assertEquals(0, nodes.size());
238+
}
99239
}

0 commit comments

Comments
 (0)