23
23
24
24
import java .awt .BorderLayout ;
25
25
import java .awt .Dimension ;
26
+ import java .awt .GraphicsConfiguration ;
27
+ import java .awt .GraphicsEnvironment ;
28
+ import java .awt .Insets ;
29
+ import java .awt .Rectangle ;
26
30
import java .awt .Toolkit ;
27
31
import java .awt .Window ;
28
32
import java .awt .event .WindowAdapter ;
41
45
import javax .swing .JTextArea ;
42
46
import javax .swing .Timer ;
43
47
48
+
44
49
import static javax .swing .SwingUtilities .invokeAndWait ;
45
50
import static javax .swing .SwingUtilities .isEventDispatchThread ;
46
51
@@ -65,7 +70,7 @@ public class PassFailJFrame {
65
70
private static volatile String testFailedReason ;
66
71
private static JFrame frame ;
67
72
68
- public enum Position {HORIZONTAL , VERTICAL }
73
+ public enum Position {HORIZONTAL , VERTICAL , TOP_LEFT_CORNER }
69
74
70
75
public PassFailJFrame (String instructions ) throws InterruptedException ,
71
76
InvocationTargetException {
@@ -176,7 +181,6 @@ public void windowClosing(WindowEvent e) {
176
181
frame .add (buttonsPanel , BorderLayout .SOUTH );
177
182
frame .pack ();
178
183
frame .setLocationRelativeTo (null );
179
- frame .setVisible (true );
180
184
windowList .add (frame );
181
185
}
182
186
@@ -262,31 +266,116 @@ private static void getFailureReason() {
262
266
}
263
267
264
268
/**
265
- * Position the instruction frame with testWindow (testcase created
266
- * window) by the specified position.
267
- * Note: This method should be invoked from the method that creates
268
- * testWindow.
269
+ * Approximately positions the instruction frame relative to the test
270
+ * window as specified by the {@code position} parameter. If {@code testWindow}
271
+ * is {@code null}, only the instruction frame is positioned according to
272
+ * {@code position} parameter.
273
+ * <p>This method should be called before making the test window visible
274
+ * to avoid flickering.</p>
275
+ *
276
+ * @param testWindow test window that the test created.
277
+ * May be {@code null}.
278
+ *
279
+ * @param position position must be one of:
280
+ * <ul>
281
+ * <li>{@code HORIZONTAL} - the test instruction frame is positioned
282
+ * such that its right edge aligns with screen's horizontal center
283
+ * and the test window (if not {@code null}) is placed to the right
284
+ * of the instruction frame.</li>
269
285
*
270
- * @param testWindow test window that the test is created
271
- * @param position position can be either HORIZONTAL (both test
272
- * instruction frame and test window as arranged
273
- * side by side) or VERTICAL (both test instruction
274
- * frame and test window as arranged up and down)
286
+ * <li>{@code VERTICAL} - the test instruction frame is positioned
287
+ * such that its bottom edge aligns with the screen's vertical center
288
+ * and the test window (if not {@code null}) is placed below the
289
+ * instruction frame.</li>
290
+ *
291
+ * <li>{@code TOP_LEFT_CORNER} - the test instruction frame is positioned
292
+ * such that its top left corner is at the top left corner of the screen
293
+ * and the test window (if not {@code null}) is placed to the right of
294
+ * the instruction frame.</li>
295
+ * </ul>
275
296
*/
276
297
public static void positionTestWindow (Window testWindow , Position position ) {
277
298
Dimension screenSize = Toolkit .getDefaultToolkit ().getScreenSize ();
299
+
300
+ // Get the screen insets to position the frame by taking into
301
+ // account the location of taskbar/menubars on screen.
302
+ GraphicsConfiguration gc = GraphicsEnvironment .getLocalGraphicsEnvironment ()
303
+ .getDefaultScreenDevice ().getDefaultConfiguration ();
304
+ Insets screenInsets = Toolkit .getDefaultToolkit ().getScreenInsets (gc );
305
+
278
306
if (position .equals (Position .HORIZONTAL )) {
279
307
int newX = ((screenSize .width / 2 ) - frame .getWidth ());
280
- frame .setLocation (newX , frame .getY ());
281
-
282
- testWindow .setLocation ((frame .getLocation ().x + frame .getWidth () + 5 ), frame .getY ());
308
+ frame .setLocation ((newX + screenInsets .left ),
309
+ (frame .getY () + screenInsets .top ));
310
+ syncLocationToWindowManager ();
311
+ if (testWindow != null ) {
312
+ testWindow .setLocation ((frame .getX () + frame .getWidth () + 5 ),
313
+ frame .getY ());
314
+ }
283
315
} else if (position .equals (Position .VERTICAL )) {
284
316
int newY = ((screenSize .height / 2 ) - frame .getHeight ());
285
- frame .setLocation (frame .getX (), newY );
317
+ frame .setLocation ((frame .getX () + screenInsets .left ),
318
+ (newY + screenInsets .top ));
319
+ syncLocationToWindowManager ();
320
+ if (testWindow != null ) {
321
+ testWindow .setLocation (frame .getX (),
322
+ (frame .getY () + frame .getHeight () + 5 ));
323
+ }
324
+ } else if (position .equals (Position .TOP_LEFT_CORNER )) {
325
+ frame .setLocation (screenInsets .left , screenInsets .top );
326
+ syncLocationToWindowManager ();
327
+ if (testWindow != null ) {
328
+ testWindow .setLocation ((frame .getX () + frame .getWidth () + 5 ),
329
+ frame .getY ());
330
+ }
331
+ }
332
+ // make instruction frame visible after updating
333
+ // frame & window positions
334
+ frame .setVisible (true );
335
+ }
336
+
337
+ /**
338
+ * Ensures the frame location is updated by the window manager
339
+ * if it adjusts the frame location after {@code setLocation}.
340
+ *
341
+ * @see #positionTestWindow
342
+ */
343
+ private static void syncLocationToWindowManager () {
344
+ Toolkit .getDefaultToolkit ().sync ();
345
+ try {
346
+ Thread .sleep (500 );
347
+ } catch (InterruptedException e ) {
348
+ e .printStackTrace ();
349
+ }
350
+ }
286
351
287
- testWindow .setLocation (frame .getX (),
288
- (frame .getLocation ().y + frame .getHeight () + 5 ));
352
+ /**
353
+ * Returns the current position and size of the test instruction frame.
354
+ * This method can be used in scenarios when custom positioning of
355
+ * multiple test windows w.r.t test instruction frame is necessary,
356
+ * at test-case level and the desired configuration is not available
357
+ * as a {@code Position} option.
358
+ *
359
+ * @return Rectangle bounds of test instruction frame
360
+ * @see #positionTestWindow
361
+ *
362
+ * @throws InterruptedException exception thrown when thread is
363
+ * interrupted
364
+ * @throws InvocationTargetException if an exception is thrown while
365
+ * obtaining frame bounds on EDT
366
+ */
367
+ public static Rectangle getInstructionFrameBounds ()
368
+ throws InterruptedException , InvocationTargetException {
369
+ final Rectangle [] bounds = {null };
370
+
371
+ if (isEventDispatchThread ()) {
372
+ bounds [0 ] = frame != null ? frame .getBounds () : null ;
373
+ } else {
374
+ invokeAndWait (() -> {
375
+ bounds [0 ] = frame != null ? frame .getBounds () : null ;
376
+ });
289
377
}
378
+ return bounds [0 ];
290
379
}
291
380
292
381
/**
0 commit comments