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..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 @@ -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,10 @@ public void show(Node anchor, Side side, double dx, double dy) { if (anchor == null) return; if (getItems().size() == 0) return; + PopupWindowHelper.ownerWindow(this).set(anchor.getScene().getWindow()); + PopupWindowHelper.ownerNode(this).set(anchor); + PopupWindowHelper.applyStylesheetFromOwner(this, anchor.getScene().getWindow()); getScene().setNodeOrientation(anchor.getEffectiveNodeOrientation()); - if (getScene().getStylesheets().isEmpty()) { - getScene().getStylesheets().setAll(anchor.getScene().getStylesheets()); - } 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..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,6 +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 test.javafx.scene.CssStyleHelperTest; public class ContextMenuTest { @@ -695,6 +698,30 @@ private ContextMenu createContextMenuAndShowSubMenu() { assertEquals(anchorBounds.getMinY(), cmBounds.getMinY(), 0.0); } + @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((oldSkin, newSkin) -> { + 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(1, skinCounter.get()); + } @Test public void test_position_withCSS() { anchorBtn.getScene().getStylesheets().add( @@ -775,4 +802,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.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; + } }); } 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)); } }