diff --git a/modules/javafx.graphics/src/main/java/javafx/scene/layout/HeaderBar.java b/modules/javafx.graphics/src/main/java/javafx/scene/layout/HeaderBar.java index bfb55f95be7..e4233f9ba8a 100644 --- a/modules/javafx.graphics/src/main/java/javafx/scene/layout/HeaderBar.java +++ b/modules/javafx.graphics/src/main/java/javafx/scene/layout/HeaderBar.java @@ -30,6 +30,9 @@ import com.sun.javafx.scene.layout.HeaderButtonBehavior; import com.sun.javafx.stage.HeaderButtonMetrics; import com.sun.javafx.stage.StageHelper; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; import javafx.beans.property.BooleanProperty; import javafx.beans.property.BooleanPropertyBase; import javafx.beans.property.ObjectProperty; @@ -38,7 +41,6 @@ import javafx.beans.property.ReadOnlyDoubleWrapper; import javafx.beans.property.ReadOnlyObjectProperty; import javafx.beans.property.ReadOnlyObjectWrapper; -import javafx.beans.value.ObservableValue; import javafx.css.StyleableDoubleProperty; import javafx.event.Event; import javafx.geometry.Dimension2D; @@ -65,14 +67,14 @@ * method. *
* {@code HeaderBar} is a layout container that allows applications to place scene graph nodes in three areas: - * {@link #leadingProperty() leading}, {@link #centerProperty() center}, and {@link #trailingProperty() trailing}. + * {@link #leftProperty() left}, {@link #centerProperty() center}, and {@link #rightProperty() right}. * All areas can be {@code null}. The default {@link #minHeightProperty() minHeight} of the {@code HeaderBar} is * set to match the height of the platform-specific default header buttons. * *
+     * By default, {@code HeaderBar}.{@link #minHeightProperty() minHeight} is set to the value of
+     * {@code minSystemHeight}, unless {@code minHeight} is explicitly set by a stylesheet or application code.
+     *
+     * @param stage the {@code Stage}
+     * @return the {@code minSystemHeight} property
+     */
+    public static ReadOnlyDoubleProperty minSystemHeightProperty(Stage stage) {
+        return AttachedProperties.of(stage).minSystemHeight.getReadOnlyProperty();
+    }
+
+    /**
+     * Gets the value of the {@link #minSystemHeightProperty(Stage) minSystemHeight} property.
+     *
+     * @param stage the {@code Stage}
+     * @return the system-provided minimum recommended height for the {@code HeaderBar}
+     */
+    public static double getMinSystemHeight(Stage stage) {
+        return AttachedProperties.of(stage).minSystemHeight.get();
+    }
+
     /**
      * Sets the alignment for the child when contained in a {@code HeaderBar}.
      * If set, will override the header bar's default alignment for the child's position.
@@ -314,9 +391,7 @@ public static Insets getMargin(Node child) {
         return (Insets)Pane.getConstraint(child, MARGIN);
     }
 
-    private Subscription subscription = Subscription.EMPTY;
-    private HeaderButtonMetrics currentMetrics;
-    private boolean currentFullScreen;
+    private Subscription subscriptions = Subscription.EMPTY;
 
     /**
      * Creates a new {@code HeaderBar}.
@@ -328,157 +403,43 @@ public HeaderBar() {
         // user code changes the property value before we set it to the height of the native title bar.
         minHeightProperty();
 
-        ObservableValue 
-     * Note that the left system inset refers to the left side of the window, independent of layout orientation.
-     */
-    private final ReadOnlyObjectWrapper 
-     * Note that the right system inset refers to the right side of the window, independent of layout orientation.
-     */
-    private final ReadOnlyObjectWrapper 
-     * By default, {@link #minHeightProperty() minHeight} is set to the value of {@code minSystemHeight},
-     * unless {@code minHeight} is explicitly set by a stylesheet or application code.
-     */
-    private final ReadOnlyDoubleWrapper minSystemHeight =
-        new ReadOnlyDoubleWrapper(this, "minSystemHeight") {
-            @Override
-            protected void invalidated() {
-                double height = get();
-                var minHeight = (StyleableDoubleProperty)minHeightProperty();
-
-                // Only change minHeight if it was not set by a stylesheet or application code.
-                if (minHeight.getStyleOrigin() == null) {
-                    minHeight.applyStyle(null, height);
-                }
-            }
-        };
-
-    public final ReadOnlyDoubleProperty minSystemHeightProperty() {
-        return minSystemHeight.getReadOnlyProperty();
-    }
-
-    public final double getMinSystemHeight() {
-        return minSystemHeight.get();
-    }
-
-    /**
-     * The leading area of the {@code HeaderBar}.
-     *  
-     * The leading area corresponds to the left area in a left-to-right layout, and to the right area
-     * in a right-to-left layout.
+     * The left area of the {@code HeaderBar}.
      *
      * @defaultValue {@code null}
      */
-    private final ObjectProperty 
-     * The trailing area corresponds to the right area in a left-to-right layout, and to the left area
-     * in a right-to-left layout.
+     * The right area of the {@code HeaderBar}.
      *
      * @defaultValue {@code null}
      */
-    private final ObjectProperty 
      * Applications that use a single {@code HeaderBar} extending the entire width of the window should
      * set this property to {@code true} to prevent the header buttons from overlapping the content of the
      * {@code HeaderBar}.
      *
      * @defaultValue {@code true}
-     * @see #trailingSystemPaddingProperty() trailingSystemPadding
+     * @see #rightSystemPaddingProperty() rightSystemPadding
      */
-    private final BooleanProperty leadingSystemPadding = new BooleanPropertyBase(true) {
+    private final BooleanProperty leftSystemPadding = new BooleanPropertyBase(true) {
         @Override
         public Object getBean() {
             return HeaderBar.this;
@@ -543,7 +501,7 @@ public Object getBean() {
 
         @Override
         public String getName() {
-            return "leadingSystemPadding";
+            return "leftSystemPadding";
         }
 
         @Override
@@ -552,32 +510,32 @@ protected void invalidated() {
         }
     };
 
-    public final BooleanProperty leadingSystemPaddingProperty() {
-        return leadingSystemPadding;
+    public final BooleanProperty leftSystemPaddingProperty() {
+        return leftSystemPadding;
     }
 
-    public final boolean isLeadingSystemPadding() {
-        return leadingSystemPadding.get();
+    public final boolean isLeftSystemPadding() {
+        return leftSystemPadding.get();
     }
 
-    public final void setLeadingSystemPadding(boolean value) {
-        leadingSystemPadding.set(value);
+    public final void setLeftSystemPadding(boolean value) {
+        leftSystemPadding.set(value);
     }
 
     /**
-     * Specifies whether additional padding should be added to the trailing side of the {@code HeaderBar}.
+     * Specifies whether additional padding should be added to the right side of the {@code HeaderBar}.
      * The size of the additional padding corresponds to the size of the system-reserved area that contains
      * the default header buttons (iconify, maximize, and close). If the system-reserved area contains no
-     * header buttons, no additional padding is added to the trailing side of the {@code HeaderBar}.
+     * header buttons, no additional padding is added to the right side of the {@code HeaderBar}.
      *  
      * Applications that use a single {@code HeaderBar} extending the entire width of the window should
      * set this property to {@code true} to prevent the header buttons from overlapping the content of the
      * {@code HeaderBar}.
      *
      * @defaultValue {@code true}
-     * @see #leadingSystemPaddingProperty() leadingSystemPadding
+     * @see #leftSystemPaddingProperty() leftSystemPadding
      */
-    private final BooleanProperty trailingSystemPadding = new BooleanPropertyBase(true) {
+    private final BooleanProperty rightSystemPadding = new BooleanPropertyBase(true) {
         @Override
         public Object getBean() {
             return HeaderBar.this;
@@ -585,7 +543,7 @@ public Object getBean() {
 
         @Override
         public String getName() {
-            return "trailingSystemPadding";
+            return "rightSystemPadding";
         }
 
         @Override
@@ -594,61 +552,59 @@ protected void invalidated() {
         }
     };
 
-    public final BooleanProperty trailingSystemPaddingProperty() {
-        return trailingSystemPadding;
-    }
-
-    public final boolean isTrailingSystemPadding() {
-        return trailingSystemPadding.get();
-    }
-
-    public final void setTrailingSystemPadding(boolean value) {
-        trailingSystemPadding.set(value);
+    public final BooleanProperty rightSystemPaddingProperty() {
+        return rightSystemPadding;
     }
 
-    private boolean isLeftSystemPadding(NodeOrientation nodeOrientation) {
-        return nodeOrientation == NodeOrientation.LEFT_TO_RIGHT && isLeadingSystemPadding()
-            || nodeOrientation == NodeOrientation.RIGHT_TO_LEFT && isTrailingSystemPadding();
+    public final boolean isRightSystemPadding() {
+        return rightSystemPadding.get();
     }
 
-    private boolean isRightSystemPadding(NodeOrientation nodeOrientation) {
-        return nodeOrientation == NodeOrientation.LEFT_TO_RIGHT && isTrailingSystemPadding()
-            || nodeOrientation == NodeOrientation.RIGHT_TO_LEFT && isLeadingSystemPadding();
+    public final void setRightSystemPadding(boolean value) {
+        rightSystemPadding.set(value);
     }
 
     @Override
     protected double computeMinWidth(double height) {
-        Node leading = getLeading();
+        Node left = getLeft();
         Node center = getCenter();
-        Node trailing = getTrailing();
+        Node right = getRight();
         Insets insets = getInsets();
         double leftPrefWidth;
         double rightPrefWidth;
         double centerMinWidth;
-        double systemPaddingWidth = 0;
+        double leftSystemPaddingWidth = 0;
+        double rightSystemPaddingWidth = 0;
 
         if (height != -1
-                && (childHasContentBias(leading, Orientation.VERTICAL) ||
-                    childHasContentBias(trailing, Orientation.VERTICAL) ||
+                && (childHasContentBias(left, Orientation.VERTICAL) ||
+                    childHasContentBias(right, Orientation.VERTICAL) ||
                     childHasContentBias(center, Orientation.VERTICAL))) {
             double areaHeight = Math.max(0, height);
-            leftPrefWidth = getAreaWidth(leading, areaHeight, false);
-            rightPrefWidth = getAreaWidth(trailing, areaHeight, false);
+            leftPrefWidth = getAreaWidth(left, areaHeight, false);
+            rightPrefWidth = getAreaWidth(right, areaHeight, false);
             centerMinWidth = getAreaWidth(center, areaHeight, true);
         } else {
-            leftPrefWidth = getAreaWidth(leading, -1, false);
-            rightPrefWidth = getAreaWidth(trailing, -1, false);
+            leftPrefWidth = getAreaWidth(left, -1, false);
+            rightPrefWidth = getAreaWidth(right, -1, false);
             centerMinWidth = getAreaWidth(center, -1, true);
         }
 
-        NodeOrientation nodeOrientation = getEffectiveNodeOrientation();
+        Scene scene = getScene();
+        Stage stage = scene != null
+            ? scene.getWindow() instanceof Stage s ? s : null
+            : null;
 
-        if (isLeftSystemPadding(nodeOrientation)) {
-            systemPaddingWidth += getLeftSystemInset().getWidth();
-        }
+        if (stage != null) {
+            var attachedProperties = AttachedProperties.of(stage);
 
-        if (isRightSystemPadding(nodeOrientation)) {
-            systemPaddingWidth += getRightSystemInset().getWidth();
+            if (scene.getEffectiveNodeOrientation() != getEffectiveNodeOrientation()) {
+                leftSystemPaddingWidth = isLeftSystemPadding() ? attachedProperties.rightSystemInset.get().getWidth() : 0;
+                rightSystemPaddingWidth = isRightSystemPadding() ? attachedProperties.leftSystemInset.get().getWidth() : 0;
+            } else {
+                leftSystemPaddingWidth = isLeftSystemPadding() ? attachedProperties.leftSystemInset.get().getWidth() : 0;
+                rightSystemPaddingWidth = isRightSystemPadding() ? attachedProperties.rightSystemInset.get().getWidth() : 0;
+            }
         }
 
         return insets.getLeft()
@@ -656,91 +612,93 @@ protected double computeMinWidth(double height) {
              + centerMinWidth
              + rightPrefWidth
              + insets.getRight()
-             + systemPaddingWidth;
+             + leftSystemPaddingWidth
+             + rightSystemPaddingWidth;
     }
 
     @Override
     protected double computeMinHeight(double width) {
-        Node leading = getLeading();
+        Node left = getLeft();
         Node center = getCenter();
-        Node trailing = getTrailing();
+        Node right = getRight();
         Insets insets = getInsets();
-        double leadingMinHeight = getAreaHeight(leading, -1, true);
-        double trailingMinHeight = getAreaHeight(trailing, -1, true);
+        double leftMinHeight = getAreaHeight(left, -1, true);
+        double rightMinHeight = getAreaHeight(right, -1, true);
         double centerMinHeight;
 
         if (width != -1 && childHasContentBias(center, Orientation.HORIZONTAL)) {
-            double leadingPrefWidth = getAreaWidth(leading, -1, false);
-            double trailingPrefWidth = getAreaWidth(trailing, -1, false);
-            centerMinHeight = getAreaHeight(center, Math.max(0, width - leadingPrefWidth - trailingPrefWidth), true);
+            double leftPrefWidth = getAreaWidth(left, -1, false);
+            double rightPrefWidth = getAreaWidth(right, -1, false);
+            centerMinHeight = getAreaHeight(center, Math.max(0, width - leftPrefWidth - rightPrefWidth), true);
         } else {
             centerMinHeight = getAreaHeight(center, -1, true);
         }
 
         return insets.getTop()
              + insets.getBottom()
-             + Math.max(centerMinHeight, Math.max(trailingMinHeight, leadingMinHeight));
+             + Math.max(centerMinHeight, Math.max(rightMinHeight, leftMinHeight));
     }
 
     @Override
     protected double computePrefHeight(double width) {
-        Node leading = getLeading();
+        Node left = getLeft();
         Node center = getCenter();
-        Node trailing = getTrailing();
+        Node right = getRight();
         Insets insets = getInsets();
-        double leadingPrefHeight = getAreaHeight(leading, -1, false);
-        double trailingPrefHeight = getAreaHeight(trailing, -1, false);
+        double leftPrefHeight = getAreaHeight(left, -1, false);
+        double rightPrefHeight = getAreaHeight(right, -1, false);
         double centerPrefHeight;
 
         if (width != -1 && childHasContentBias(center, Orientation.HORIZONTAL)) {
-            double leadingPrefWidth = getAreaWidth(leading, -1, false);
-            double trailingPrefWidth = getAreaWidth(trailing, -1, false);
-            centerPrefHeight = getAreaHeight(center, Math.max(0, width - leadingPrefWidth - trailingPrefWidth), false);
+            double leftPrefWidth = getAreaWidth(left, -1, false);
+            double rightPrefWidth = getAreaWidth(right, -1, false);
+            centerPrefHeight = getAreaHeight(center, Math.max(0, width - leftPrefWidth - rightPrefWidth), false);
         } else {
             centerPrefHeight = getAreaHeight(center, -1, false);
         }
 
         return insets.getTop()
              + insets.getBottom()
-             + Math.max(centerPrefHeight, Math.max(trailingPrefHeight, leadingPrefHeight));
-    }
-
-    @Override
-    public boolean usesMirroring() {
-        return false;
+             + Math.max(centerPrefHeight, Math.max(rightPrefHeight, leftPrefHeight));
     }
 
     @Override
     protected void layoutChildren() {
+        Node left = getLeft();
         Node center = getCenter();
-        Node left, right;
+        Node right = getRight();
         Insets insets = getInsets();
-        NodeOrientation nodeOrientation = getEffectiveNodeOrientation();
-        boolean rtl = nodeOrientation == NodeOrientation.RIGHT_TO_LEFT;
         double width = Math.max(getWidth(), minWidth(-1));
         double height = Math.max(getHeight(), minHeight(-1));
         double leftWidth = 0;
         double rightWidth = 0;
         double insideY = insets.getTop();
         double insideHeight = height - insideY - insets.getBottom();
-        double insideX, insideWidth;
-        double leftSystemPaddingWidth = isLeftSystemPadding(nodeOrientation) ? getLeftSystemInset().getWidth() : 0;
-        double rightSystemPaddingWidth = isRightSystemPadding(nodeOrientation) ? getRightSystemInset().getWidth() : 0;
-
-        if (rtl) {
-            left = getTrailing();
-            right = getLeading();
-            insideX = insets.getRight() + leftSystemPaddingWidth;
-            insideWidth = width - insideX - insets.getLeft() - rightSystemPaddingWidth;
-        } else {
-            left = getLeading();
-            right = getTrailing();
-            insideX = insets.getLeft() + leftSystemPaddingWidth;
-            insideWidth = width - insideX - insets.getRight() - rightSystemPaddingWidth;
+        double rightSystemPaddingWidth = 0;
+        double leftSystemPaddingWidth = 0;
+
+        Scene scene = getScene();
+        Stage stage = scene != null
+            ? scene.getWindow() instanceof Stage s ? s : null
+            : null;
+
+        if (stage != null) {
+            AttachedProperties attachedProperties = AttachedProperties.of(stage);
+
+            if (scene.getEffectiveNodeOrientation() != getEffectiveNodeOrientation()) {
+                leftSystemPaddingWidth = isLeftSystemPadding() ? attachedProperties.rightSystemInset.get().getWidth() : 0;
+                rightSystemPaddingWidth = isRightSystemPadding() ? attachedProperties.leftSystemInset.get().getWidth() : 0;
+            } else {
+                leftSystemPaddingWidth = isLeftSystemPadding() ? attachedProperties.leftSystemInset.get().getWidth() : 0;
+                rightSystemPaddingWidth = isRightSystemPadding() ? attachedProperties.rightSystemInset.get().getWidth() : 0;
+            }
         }
 
+        double insideX = insets.getLeft() + leftSystemPaddingWidth;
+        double insideWidth = width - insideX - insets.getRight() - rightSystemPaddingWidth;
+
         if (left != null && left.isManaged()) {
-            Insets leftMargin = adjustMarginForRTL(getNodeMargin(left), rtl);
+            Insets leftMargin = getNodeMargin(left);
             double adjustedWidth = adjustWidthByMargin(insideWidth, leftMargin);
             double childWidth = resizeChild(left, adjustedWidth, false, insideHeight, leftMargin);
             leftWidth = snapSpaceX(leftMargin.getLeft()) + childWidth + snapSpaceX(leftMargin.getRight());
@@ -756,7 +714,7 @@ protected void layoutChildren() {
         }
 
         if (right != null && right.isManaged()) {
-            Insets rightMargin = adjustMarginForRTL(getNodeMargin(right), rtl);
+            Insets rightMargin = getNodeMargin(right);
             double adjustedWidth = adjustWidthByMargin(insideWidth - leftWidth, rightMargin);
             double childWidth = resizeChild(right, adjustedWidth, false, insideHeight, rightMargin);
             rightWidth = snapSpaceX(rightMargin.getLeft()) + childWidth + snapSpaceX(rightMargin.getRight());
@@ -772,7 +730,7 @@ protected void layoutChildren() {
         }
 
         if (center != null && center.isManaged()) {
-            Insets centerMargin = adjustMarginForRTL(getNodeMargin(center), rtl);
+            Insets centerMargin = getNodeMargin(center);
             Pos alignment = getAlignment(center);
 
             if (alignment == null || alignment.getHpos() == HPos.CENTER) {
@@ -809,16 +767,6 @@ protected void layoutChildren() {
         }
     }
 
-    private Insets adjustMarginForRTL(Insets margin, boolean rtl) {
-        if (margin == null) {
-            return null;
-        }
-
-        return rtl
-            ? new Insets(margin.getTop(), margin.getLeft(), margin.getBottom(), margin.getRight())
-            : margin;
-    }
-
     private boolean childHasContentBias(Node child, Orientation orientation) {
         if (child != null && child.isManaged()) {
             return child.getContentBias() == orientation;
@@ -864,6 +812,26 @@ private Insets getNodeMargin(Node child) {
         return margin != null ? margin : Insets.EMPTY;
     }
 
+    private void onStageChanged(Stage stage) {
+        subscriptions.unsubscribe();
+
+        if (stage != null) {
+            var attachedProperties = AttachedProperties.of(stage);
+
+            subscriptions = Subscription.combine(
+                attachedProperties.minSystemHeight.subscribe(height -> {
+                    var minHeight = (StyleableDoubleProperty)minHeightProperty();
+
+                    // Only change minHeight if it was not set by a stylesheet or application code.
+                    if (minHeight.getStyleOrigin() == null) {
+                        minHeight.applyStyle(null, height);
+                    }
+                }),
+                attachedProperties.subscribeLayoutInvalidated(this::requestLayout)
+            );
+        }
+    }
+
     private final class NodeProperty extends ObjectPropertyBase