From fadfa5824d15e532c2c6f18ec37e9685952f8290 Mon Sep 17 00:00:00 2001
From: Goetz Lindenmaier <goetz@openjdk.org>
Date: Mon, 4 Nov 2024 08:04:04 +0000
Subject: [PATCH] 8317116: Provide layouts for multiple test UI in
 PassFailJFrame

Backport-of: 85f0442727201e0d36ed0a71d2199e602501c98d
---
 .../awt/regtesthelpers/PassFailJFrame.java    |  86 ++++++-
 .../awt/regtesthelpers/WindowLayouts.java     | 237 ++++++++++++++++++
 2 files changed, 319 insertions(+), 4 deletions(-)
 create mode 100644 test/jdk/java/awt/regtesthelpers/WindowLayouts.java

diff --git a/test/jdk/java/awt/regtesthelpers/PassFailJFrame.java b/test/jdk/java/awt/regtesthelpers/PassFailJFrame.java
index 45cd191ae39..5a9e7671e39 100644
--- a/test/jdk/java/awt/regtesthelpers/PassFailJFrame.java
+++ b/test/jdk/java/awt/regtesthelpers/PassFailJFrame.java
@@ -170,6 +170,11 @@ public final class PassFailJFrame {
     private static final int ROWS = 10;
     private static final int COLUMNS = 40;
 
+    /**
+     * A gap between windows.
+     */
+    public static final int WINDOW_GAP = 8;
+
     /**
      * Prefix for the user-provided failure reason.
      */
@@ -1007,13 +1012,13 @@ public final class PassFailJFrame {
 
         switch (position) {
             case HORIZONTAL:
-                int newX = ((screenSize.width / 2) - frame.getWidth());
+                int newX = (((screenSize.width + WINDOW_GAP) / 2) - frame.getWidth());
                 frame.setLocation((newX + screenInsets.left),
                                   (frame.getY() + screenInsets.top));
                 break;
 
             case VERTICAL:
-                int newY = ((screenSize.height / 2) - frame.getHeight());
+                int newY = (((screenSize.height + WINDOW_GAP) / 2) - frame.getHeight());
                 frame.setLocation((frame.getX() + screenInsets.left),
                                   (newY + screenInsets.top));
                 break;
@@ -1061,13 +1066,13 @@ public final class PassFailJFrame {
             switch (position) {
                 case HORIZONTAL:
                 case TOP_LEFT_CORNER:
-                    testWindow.setLocation((frame.getX() + frame.getWidth() + 5),
+                    testWindow.setLocation((frame.getX() + frame.getWidth() + WINDOW_GAP),
                                            frame.getY());
                     break;
 
                 case VERTICAL:
                     testWindow.setLocation(frame.getX(),
-                                           (frame.getY() + frame.getHeight() + 5));
+                                           (frame.getY() + frame.getHeight() + WINDOW_GAP));
                     break;
             }
         }
@@ -1370,6 +1375,7 @@ public final class PassFailJFrame {
             return this;
         }
 
+
         /**
          * Adds an implementation of {@link PositionWindows PositionWindows}
          * which the framework will use to position multiple test UI windows.
@@ -1393,6 +1399,77 @@ public final class PassFailJFrame {
             return this;
         }
 
+        /**
+         * Positions the test UI windows in a row to the right of
+         * the instruction frame. The top of the windows is aligned to
+         * that of the instruction frame.
+         *
+         * @return this builder
+         */
+        public Builder positionTestUIRightRow() {
+            return position(Position.HORIZONTAL)
+                   .positionTestUI(WindowLayouts::rightOneRow);
+        }
+
+        /**
+         * Positions the test UI windows in a column to the right of
+         * the instruction frame. The top of the first window is aligned to
+         * that of the instruction frame.
+         *
+         * @return this builder
+         */
+        public Builder positionTestUIRightColumn() {
+            return position(Position.HORIZONTAL)
+                   .positionTestUI(WindowLayouts::rightOneColumn);
+        }
+
+        /**
+         * Positions the test UI windows in a column to the right of
+         * the instruction frame centering the stack of the windows.
+         *
+         * @return this builder
+         */
+        public Builder positionTestUIRightColumnCentered() {
+            return position(Position.HORIZONTAL)
+                   .positionTestUI(WindowLayouts::rightOneColumnCentered);
+        }
+
+        /**
+         * Positions the test UI windows in a row to the bottom of
+         * the instruction frame. The left of the first window is aligned to
+         * that of the instruction frame.
+         *
+         * @return this builder
+         */
+        public Builder positionTestUIBottomRow() {
+            return position(Position.VERTICAL)
+                   .positionTestUI(WindowLayouts::bottomOneRow);
+        }
+
+        /**
+         * Positions the test UI windows in a row to the bottom of
+         * the instruction frame centering the row of the windows.
+         *
+         * @return this builder
+         */
+        public Builder positionTestUIBottomRowCentered() {
+            return position(Position.VERTICAL)
+                   .positionTestUI(WindowLayouts::bottomOneRowCentered);
+        }
+
+        /**
+         * Positions the test UI windows in a column to the bottom of
+         * the instruction frame. The left of the first window is aligned to
+         * that of the instruction frame.
+         *
+         * @return this builder
+         */
+        public Builder positionTestUIBottomColumn() {
+            return position(Position.VERTICAL)
+                   .positionTestUI(WindowLayouts::bottomOneColumn);
+        }
+
+
         /**
          * Adds a {@code WindowListCreator} which the framework will use
          * to create a list of test UI windows.
@@ -1495,6 +1572,7 @@ public final class PassFailJFrame {
             return this;
         }
 
+
         /**
          * Adds a {@code PanelCreator} which the framework will use
          * to create a component with test UI and display it in a split pane.
diff --git a/test/jdk/java/awt/regtesthelpers/WindowLayouts.java b/test/jdk/java/awt/regtesthelpers/WindowLayouts.java
new file mode 100644
index 00000000000..4368e3a5943
--- /dev/null
+++ b/test/jdk/java/awt/regtesthelpers/WindowLayouts.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.GraphicsConfiguration;
+import java.awt.Insets;
+import java.awt.Point;
+import java.awt.Window;
+import java.util.List;
+
+import static java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment;
+import static java.awt.Toolkit.getDefaultToolkit;
+
+/**
+ * A utility class which provides standard window layouts for multi-window
+ * manual tests using the {@link PassFailJFrame} framework.
+ * The layout methods {@code right-} and {@code bottom-} implement the
+ * {@link PassFailJFrame.PositionWindows PositionWindows} interface and
+ * can be used directly or via builder methods.
+ * <p>
+ * There are several helper methods, such as
+ * {@link #getScreenCenter() getScreenCenter}, which could help you
+ * implement customized windows layouts.
+ */
+public final class WindowLayouts {
+
+    /** Private constructor to prevent instantiating the utility class. */
+    private WindowLayouts() {
+    }
+
+    /** A gap between windows. (Local copy makes expressions shorter.) */
+    private static final int WINDOW_GAP = PassFailJFrame.WINDOW_GAP;
+
+    /**
+     * Lays out the window list in one row to the right of
+     * the instruction frame. The top of the windows is aligned to
+     * that of the instruction frame.
+     *
+     * @param windows the list of windows to lay out
+     * @param instructionUI information about the instruction frame
+     */
+    public static void rightOneRow(final List<Window> windows,
+                                   final PassFailJFrame.InstructionUI instructionUI) {
+        layoutRow(instructionUI.getLocation().x
+                  + instructionUI.getSize().width
+                  + WINDOW_GAP,
+                  instructionUI.getLocation().y,
+                  windows);
+    }
+
+    /**
+     * Lays out the window list in one column to the right of
+     * the instruction frame. The top of the first window is aligned to
+     * that of the instruction frame.
+     *
+     * @param windows the list of windows to lay out
+     * @param instructionUI information about the instruction frame
+     */
+    public static void rightOneColumn(final List<Window> windows,
+                                      final PassFailJFrame.InstructionUI instructionUI) {
+        layoutColumn(instructionUI.getLocation().x
+                     + instructionUI.getSize().width
+                     + WINDOW_GAP,
+                     instructionUI.getLocation().y,
+                     windows);
+    }
+
+    /**
+     * Lays out the window list in one column to the right of
+     * the instruction frame centering the stack of the windows.
+     *
+     * @param windows the list of windows to lay out
+     * @param instructionUI information about the instruction frame
+     */
+    public static void rightOneColumnCentered(final List<Window> windows,
+                                              final PassFailJFrame.InstructionUI instructionUI) {
+        layoutColumn(instructionUI.getLocation().x
+                     + instructionUI.getSize().width
+                     + WINDOW_GAP,
+                     getScreenCenter().y
+                     - getWindowListHeight(windows) / 2,
+                     windows);
+    }
+
+
+    /**
+     * Lays out the window list in one row to the bottom of
+     * the instruction frame. The left of the first window is aligned to
+     * that of the instruction frame.
+     *
+     * @param windows the list of windows to lay out
+     * @param instructionUI information about the instruction frame
+     */
+    public static void bottomOneRow(final List<Window> windows,
+                                    final PassFailJFrame.InstructionUI instructionUI) {
+        layoutRow(instructionUI.getLocation().x,
+                  instructionUI.getLocation().y
+                  + instructionUI.getSize().height
+                  + WINDOW_GAP,
+                  windows);
+    }
+
+    /**
+     * Lays out the window list in one row to the bottom of
+     * the instruction frame centering the row of the windows.
+     *
+     * @param windows the list of windows to lay out
+     * @param instructionUI information about the instruction frame
+     */
+    public static void bottomOneRowCentered(final List<Window> windows,
+                                            final PassFailJFrame.InstructionUI instructionUI) {
+        layoutRow(getScreenCenter().x
+                  - getWindowListWidth(windows) / 2,
+                  instructionUI.getLocation().y
+                  + instructionUI.getSize().height
+                  + WINDOW_GAP,
+                  windows);
+    }
+
+    /**
+     * Lays out the window list in one column to the bottom of
+     * the instruction frame. The left of the first window is aligned to
+     * that of the instruction frame.
+     *
+     * @param windows the list of windows to lay out
+     * @param instructionUI information about the instruction frame
+     */
+    public static void bottomOneColumn(final List<Window> windows,
+                                       final PassFailJFrame.InstructionUI instructionUI) {
+        layoutColumn(instructionUI.getLocation().x,
+                     instructionUI.getLocation().y
+                     + instructionUI.getSize().height
+                     + WINDOW_GAP,
+                     windows);
+    }
+
+
+    /**
+     * Lays out the window list in one row starting at
+     * ({@code x0}, {@code y}).
+     *
+     * @param x0 the starting <var>x</var> coordinate of the windows
+     * @param y the <var>y</var> coordinate of the windows
+     * @param windows the list of windows to lay out
+     */
+    public static void layoutRow(final int x0,
+                                 final int y,
+                                 final List<Window> windows) {
+        int x = x0;
+        for (Window w : windows) {
+            w.setLocation(x, y);
+            x += w.getWidth() + WINDOW_GAP;
+        }
+    }
+
+    /**
+     * Lays out the window list in one column starting at
+     * ({@code x}, {@code y0}).
+     *
+     * @param x the <var>x</var> coordinate of the windows
+     * @param y0 the starting <var>y</var> coordinate of the windows
+     * @param windows the list of windows to lay out
+     */
+    public static void layoutColumn(final int x,
+                                    final int y0,
+                                    final List<Window> windows) {
+        int y = y0;
+        for (Window w : windows) {
+            w.setLocation(x, y);
+            y += w.getHeight() + WINDOW_GAP;
+        }
+    }
+
+
+    /**
+     * {@return the center point of the main screen}
+     */
+    public static Point getScreenCenter() {
+        GraphicsConfiguration gc = getLocalGraphicsEnvironment()
+                                   .getDefaultScreenDevice()
+                                   .getDefaultConfiguration();
+        Dimension size = gc.getBounds()
+                           .getSize();
+        Insets insets = getDefaultToolkit()
+                        .getScreenInsets(gc);
+
+        return new Point((size.width - insets.left - insets.right) / 2,
+                         (size.height - insets.top - insets.bottom) / 2);
+    }
+
+    /**
+     * {@return width of the windows in the list, taking into account
+     * the gap between windows}
+     *
+     * @param windows the list of windows to get the width of
+     */
+    public static int getWindowListWidth(final List<Window> windows) {
+        return windows.stream()
+                      .mapToInt(Component::getWidth)
+                      .sum()
+               + WINDOW_GAP * (windows.size() - 1);
+    }
+
+    /**
+     * {@return height of the windows in the list, taking into account
+     * the gap between windows}
+     *
+     * @param windows the list of windows to get the height of
+     */
+    public static int getWindowListHeight(final List<Window> windows) {
+        return windows.stream()
+                      .mapToInt(Component::getHeight)
+                      .sum()
+               + WINDOW_GAP * (windows.size() - 1);
+    }
+}
-- 
GitLab