From 0dba9a8b6b5f67019f316a74c83f1697a1312c28 Mon Sep 17 00:00:00 2001 From: Florian Kirmaier Date: Wed, 15 Jul 2026 15:06:09 +0200 Subject: [PATCH 1/3] 8388313: Fix redundant evaluation of css when ContextMenu.show(Node,Side,x.y) is called --- .../javafx/scene/control/ContextMenu.java | 10 +++--- .../javafx/scene/control/ContextMenuTest.java | 35 +++++++++++++++++++ .../scene/control/test_css_skin_counter.css | 2 ++ .../sun/javafx/stage/PopupWindowHelper.java | 11 ++++++ .../main/java/javafx/stage/PopupWindow.java | 10 ++++++ 5 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 modules/javafx.controls/src/test/resources/test/javafx/scene/control/test_css_skin_counter.css diff --git a/modules/javafx.controls/src/main/java/javafx/scene/control/ContextMenu.java b/modules/javafx.controls/src/main/java/javafx/scene/control/ContextMenu.java index 1c90448cc72..4598948f33e 100644 --- a/modules/javafx.controls/src/main/java/javafx/scene/control/ContextMenu.java +++ b/modules/javafx.controls/src/main/java/javafx/scene/control/ContextMenu.java @@ -25,6 +25,8 @@ package javafx.scene.control; +import javafx.geometry.Bounds; +import javafx.geometry.NodeOrientation; import javafx.beans.property.ObjectProperty; import javafx.beans.property.ObjectPropertyBase; import javafx.collections.ListChangeListener.Change; @@ -44,6 +46,7 @@ import com.sun.javafx.collections.TrackableObservableList; import com.sun.javafx.tk.Toolkit; import com.sun.javafx.util.Utils; +import com.sun.javafx.stage.PopupWindowHelper; /** *

@@ -249,10 +252,9 @@ public void show(Node anchor, Side side, double dx, double dy) { if (anchor == null) return; if (getItems().size() == 0) return; - getScene().setNodeOrientation(anchor.getEffectiveNodeOrientation()); - if (getScene().getStylesheets().isEmpty()) { - getScene().getStylesheets().setAll(anchor.getScene().getStylesheets()); - } + PopupWindowHelper.ownerWindow(this).set(anchor.getScene().getWindow()); + PopupWindowHelper.ownerNode(this).set(anchor); + PopupWindowHelper.applyStylesheetFromOwner(this, anchor.getScene().getWindow()); HPos hpos = side == Side.LEFT ? HPos.LEFT : side == Side.RIGHT ? HPos.RIGHT : HPos.CENTER; VPos vpos = side == Side.TOP ? VPos.TOP : side == Side.BOTTOM ? VPos.BOTTOM : VPos.CENTER; diff --git a/modules/javafx.controls/src/test/java/test/javafx/scene/control/ContextMenuTest.java b/modules/javafx.controls/src/test/java/test/javafx/scene/control/ContextMenuTest.java index 865850a11f9..0384100bd86 100644 --- a/modules/javafx.controls/src/test/java/test/javafx/scene/control/ContextMenuTest.java +++ b/modules/javafx.controls/src/test/java/test/javafx/scene/control/ContextMenuTest.java @@ -64,6 +64,8 @@ import test.com.sun.javafx.scene.control.infrastructure.KeyEventFirer; import test.com.sun.javafx.scene.control.infrastructure.MouseEventFirer; import test.com.sun.javafx.scene.control.infrastructure.StageLoader; +import javafx.scene.control.skin.ButtonSkin; +import java.util.concurrent.atomic.AtomicInteger; public class ContextMenuTest { @@ -695,6 +697,32 @@ private ContextMenu createContextMenuAndShowSubMenu() { assertEquals(anchorBounds.getMinY(), cmBounds.getMinY(), 0.0); } + @Test public void test_css_skin_counter() { + anchorBtn.getScene().getStylesheets().add( + getClass().getResource("test_css_skin_counter.css").toExternalForm() + ); + anchorBtn.getStyleClass().add("anchor"); + AtomicInteger skinCounter = new AtomicInteger(0); + Button button = new Button(); + button.skinProperty().subscribe(skin -> { + System.out.println("new Skin: " + skin); + new Exception().printStackTrace(); + skinCounter.incrementAndGet(); + }); + menuItem.setGraphic(button); + ContextMenu cm = createContextMenu(false); + cm.show(anchorBtn, Side.TOP, 0, 0); + + Bounds anchorBounds = anchorBtn.localToScreen(anchorBtn.getLayoutBounds()); + Node cmNode = cm.getScene().getRoot(); + Bounds cmBounds = cm.getScene().getRoot().localToScreen(cmNode.getLayoutBounds()); + + assertEquals(anchorBounds.getMinX(), cmBounds.getMinX(), 0.0); + assertEquals(anchorBounds.getMinY(), cmBounds.getMaxY(), 0.0); + + assertEquals(2, skinCounter.get()); + } + @Test public void test_position_withCSS() { anchorBtn.getScene().getStylesheets().add( @@ -775,4 +803,11 @@ private ContextMenu createContextMenuAndShowSubMenu() { assertEquals(0, padding.getLeft(), 0.0); anchorBtn.setGraphic(null); } + + public static class ButtonSkin1 extends ButtonSkin { + public ButtonSkin1(Button button) { super(button); } + } + public static class ButtonSkin2 extends ButtonSkin { + public ButtonSkin2(Button button) { super(button); } + } } diff --git a/modules/javafx.controls/src/test/resources/test/javafx/scene/control/test_css_skin_counter.css b/modules/javafx.controls/src/test/resources/test/javafx/scene/control/test_css_skin_counter.css new file mode 100644 index 00000000000..287a7e6b363 --- /dev/null +++ b/modules/javafx.controls/src/test/resources/test/javafx/scene/control/test_css_skin_counter.css @@ -0,0 +1,2 @@ +.button { -fx-skin: "test.javafx.scene.control.ContextMenuTest$ButtonSkin1"; } +.anchor .button { -fx-skin: "test.javafx.scene.control.ContextMenuTest$ButtonSkin2"; } diff --git a/modules/javafx.graphics/src/main/java/com/sun/javafx/stage/PopupWindowHelper.java b/modules/javafx.graphics/src/main/java/com/sun/javafx/stage/PopupWindowHelper.java index e7d882ff060..df0e9ab16a0 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/javafx/stage/PopupWindowHelper.java +++ b/modules/javafx.graphics/src/main/java/com/sun/javafx/stage/PopupWindowHelper.java @@ -27,6 +27,7 @@ import com.sun.javafx.util.Utils; import javafx.collections.ObservableList; +import javafx.beans.property.ReadOnlyObjectWrapper; import javafx.scene.Node; import javafx.stage.PopupWindow; import javafx.stage.Window; @@ -67,6 +68,14 @@ public static void applyStylesheetFromOwner(PopupWindow popupWindow, Window owne popupWindowAccessor.applyStylesheetFromOwner(popupWindow, owner); } + public static ReadOnlyObjectWrapper ownerWindow(PopupWindow popupWindow) { + return popupWindowAccessor.ownerWindow(popupWindow); + } + + public static ReadOnlyObjectWrapper ownerNode(PopupWindow popupWindow) { + return popupWindowAccessor.ownerNode(popupWindow); + } + public static ObservableList getContent(PopupWindow popupWindow) { return popupWindowAccessor.getContent(popupWindow); } @@ -84,5 +93,7 @@ public interface PopupWindowAccessor { void doVisibleChanging(Window window, boolean visible); void doVisibleChanged(Window window, boolean visible); void applyStylesheetFromOwner(PopupWindow popupWindow, Window owner); + ReadOnlyObjectWrapper ownerWindow(PopupWindow popupWindow); + ReadOnlyObjectWrapper ownerNode(PopupWindow popupWindow); } } diff --git a/modules/javafx.graphics/src/main/java/javafx/stage/PopupWindow.java b/modules/javafx.graphics/src/main/java/javafx/stage/PopupWindow.java index a7b68f0fa54..c595a8b6955 100644 --- a/modules/javafx.graphics/src/main/java/javafx/stage/PopupWindow.java +++ b/modules/javafx.graphics/src/main/java/javafx/stage/PopupWindow.java @@ -121,6 +121,16 @@ public ObservableList getContent(PopupWindow popupWindow) { public void applyStylesheetFromOwner(PopupWindow popupWindow, Window owner) { popupWindow.applyStylesheetFromOwner(owner); } + + @Override + public ReadOnlyObjectWrapper ownerWindow(PopupWindow popupWindow) { + return popupWindow.ownerWindow; + } + + @Override + public ReadOnlyObjectWrapper ownerNode(PopupWindow popupWindow) { + return popupWindow.ownerNode; + } }); } From f5055e4ce7247572c1b9e65c4c00bfe54da94229 Mon Sep 17 00:00:00 2001 From: Florian Kirmaier Date: Wed, 15 Jul 2026 16:55:48 +0200 Subject: [PATCH 2/3] 8388313: readded accidentally removed line. --- .../src/main/java/javafx/scene/control/ContextMenu.java | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/javafx.controls/src/main/java/javafx/scene/control/ContextMenu.java b/modules/javafx.controls/src/main/java/javafx/scene/control/ContextMenu.java index 4598948f33e..db9b29b066b 100644 --- a/modules/javafx.controls/src/main/java/javafx/scene/control/ContextMenu.java +++ b/modules/javafx.controls/src/main/java/javafx/scene/control/ContextMenu.java @@ -255,6 +255,7 @@ public void show(Node anchor, Side side, double dx, double dy) { PopupWindowHelper.ownerWindow(this).set(anchor.getScene().getWindow()); PopupWindowHelper.ownerNode(this).set(anchor); PopupWindowHelper.applyStylesheetFromOwner(this, anchor.getScene().getWindow()); + getScene().setNodeOrientation(anchor.getEffectiveNodeOrientation()); HPos hpos = side == Side.LEFT ? HPos.LEFT : side == Side.RIGHT ? HPos.RIGHT : HPos.CENTER; VPos vpos = side == Side.TOP ? VPos.TOP : side == Side.BOTTOM ? VPos.BOTTOM : VPos.CENTER; From 3302b330b69ef8bca7ee339f71732a481b54d6c4 Mon Sep 17 00:00:00 2001 From: Florian Kirmaier Date: Thu, 23 Jul 2026 10:16:29 +0200 Subject: [PATCH 3/3] 8388313: worked in some comments and mixed improvements. --- .../javafx/scene/control/ContextMenuTest.java | 23 +++++++++---------- .../scene/control/test_css_skin_counter.css | 2 -- .../test/javafx/scene/CssStyleHelperTest.java | 2 +- 3 files changed, 12 insertions(+), 15 deletions(-) delete mode 100644 modules/javafx.controls/src/test/resources/test/javafx/scene/control/test_css_skin_counter.css diff --git a/modules/javafx.controls/src/test/java/test/javafx/scene/control/ContextMenuTest.java b/modules/javafx.controls/src/test/java/test/javafx/scene/control/ContextMenuTest.java index 0384100bd86..dd20218f9d6 100644 --- a/modules/javafx.controls/src/test/java/test/javafx/scene/control/ContextMenuTest.java +++ b/modules/javafx.controls/src/test/java/test/javafx/scene/control/ContextMenuTest.java @@ -36,6 +36,7 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Optional; +import java.util.concurrent.atomic.AtomicInteger; import javafx.css.PseudoClass; import javafx.event.ActionEvent; import javafx.event.EventHandler; @@ -51,6 +52,7 @@ import javafx.scene.control.Label; import javafx.scene.control.Menu; import javafx.scene.control.MenuItem; +import javafx.scene.control.skin.ButtonSkin; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.input.KeyCode; @@ -64,8 +66,7 @@ import test.com.sun.javafx.scene.control.infrastructure.KeyEventFirer; import test.com.sun.javafx.scene.control.infrastructure.MouseEventFirer; import test.com.sun.javafx.scene.control.infrastructure.StageLoader; -import javafx.scene.control.skin.ButtonSkin; -import java.util.concurrent.atomic.AtomicInteger; +import test.javafx.scene.CssStyleHelperTest; public class ContextMenuTest { @@ -697,16 +698,16 @@ private ContextMenu createContextMenuAndShowSubMenu() { assertEquals(anchorBounds.getMinY(), cmBounds.getMinY(), 0.0); } - @Test public void test_css_skin_counter() { - anchorBtn.getScene().getStylesheets().add( - getClass().getResource("test_css_skin_counter.css").toExternalForm() - ); + @Test public void testCssProcessedOnlyOnce() { + String css = """ + .button { -fx-skin: "test.javafx.scene.control.ContextMenuTest$ButtonSkin1"; } + .anchor .button { -fx-skin: "st.javafx.scene.control.ContextMenuTest$ButtonSkin2"; } + """; + anchorBtn.getScene().getStylesheets().add(CssStyleHelperTest.toDataURL(css)); anchorBtn.getStyleClass().add("anchor"); AtomicInteger skinCounter = new AtomicInteger(0); Button button = new Button(); - button.skinProperty().subscribe(skin -> { - System.out.println("new Skin: " + skin); - new Exception().printStackTrace(); + button.skinProperty().subscribe((oldSkin, newSkin) -> { skinCounter.incrementAndGet(); }); menuItem.setGraphic(button); @@ -719,11 +720,9 @@ private ContextMenu createContextMenuAndShowSubMenu() { assertEquals(anchorBounds.getMinX(), cmBounds.getMinX(), 0.0); assertEquals(anchorBounds.getMinY(), cmBounds.getMaxY(), 0.0); - - assertEquals(2, skinCounter.get()); + assertEquals(1, skinCounter.get()); } - @Test public void test_position_withCSS() { anchorBtn.getScene().getStylesheets().add( getClass().getResource("test_position_showOnTopWithCSS.css").toExternalForm() diff --git a/modules/javafx.controls/src/test/resources/test/javafx/scene/control/test_css_skin_counter.css b/modules/javafx.controls/src/test/resources/test/javafx/scene/control/test_css_skin_counter.css deleted file mode 100644 index 287a7e6b363..00000000000 --- a/modules/javafx.controls/src/test/resources/test/javafx/scene/control/test_css_skin_counter.css +++ /dev/null @@ -1,2 +0,0 @@ -.button { -fx-skin: "test.javafx.scene.control.ContextMenuTest$ButtonSkin1"; } -.anchor .button { -fx-skin: "test.javafx.scene.control.ContextMenuTest$ButtonSkin2"; } diff --git a/modules/javafx.graphics/src/test/java/test/javafx/scene/CssStyleHelperTest.java b/modules/javafx.graphics/src/test/java/test/javafx/scene/CssStyleHelperTest.java index a2e4f46440d..fba61a97a3a 100644 --- a/modules/javafx.graphics/src/test/java/test/javafx/scene/CssStyleHelperTest.java +++ b/modules/javafx.graphics/src/test/java/test/javafx/scene/CssStyleHelperTest.java @@ -965,7 +965,7 @@ public void mediaQueryRemovalShouldNotInterruptTransitionsDuringReset() { assertEquals(List.of(1.0, 2.0, 1.5, 1.0), trace); } - private static String toDataURL(String stylesheet) { + public static String toDataURL(String stylesheet) { return "data:text/plain;base64," + Base64.getEncoder().encodeToString(stylesheet.getBytes(StandardCharsets.UTF_8)); } }