53
53
import java .util .concurrent .atomic .AtomicInteger ;
54
54
55
55
import javax .imageio .ImageIO ;
56
+ import javax .swing .Box ;
56
57
import javax .swing .JButton ;
57
58
import javax .swing .JComboBox ;
58
59
import javax .swing .JComponent ;
155
156
* <li>the title of the instruction UI,</li>
156
157
* <li>the timeout of the test,</li>
157
158
* <li>the size of the instruction UI via rows and columns, and</li>
159
+ * <li>to add a log area</li>,
158
160
* <li>to enable screenshots.</li>
159
161
* </ul>
160
162
*/
@@ -204,6 +206,8 @@ public final class PassFailJFrame {
204
206
205
207
private static Robot robot ;
206
208
209
+ private static JTextArea logArea ;
210
+
207
211
public enum Position {HORIZONTAL , VERTICAL , TOP_LEFT_CORNER }
208
212
209
213
public PassFailJFrame (String instructions ) throws InterruptedException ,
@@ -373,6 +377,20 @@ private static void invokeOnEDT(Runnable doRun)
373
377
}
374
378
}
375
379
380
+ /**
381
+ * Does the same as {@link #invokeOnEDT(Runnable)}, but does not throw
382
+ * any checked exceptions.
383
+ *
384
+ * @param doRun an operation to run on EDT
385
+ */
386
+ private static void invokeOnEDTUncheckedException (Runnable doRun ) {
387
+ try {
388
+ invokeOnEDT (doRun );
389
+ } catch (InterruptedException | InvocationTargetException e ) {
390
+ throw new RuntimeException (e );
391
+ }
392
+ }
393
+
376
394
private static void createUI (String title , String instructions ,
377
395
long testTimeOut , int rows , int columns ,
378
396
boolean enableScreenCapture ) {
@@ -384,7 +402,8 @@ private static void createUI(String title, String instructions,
384
402
frame .add (createInstructionUIPanel (instructions ,
385
403
testTimeOut ,
386
404
rows , columns ,
387
- enableScreenCapture ),
405
+ enableScreenCapture ,
406
+ false , 0 ),
388
407
BorderLayout .CENTER );
389
408
frame .pack ();
390
409
frame .setLocationRelativeTo (null );
@@ -401,8 +420,9 @@ private static void createUI(Builder builder) {
401
420
createInstructionUIPanel (builder .instructions ,
402
421
builder .testTimeOut ,
403
422
builder .rows , builder .columns ,
404
- builder .screenCapture );
405
-
423
+ builder .screenCapture ,
424
+ builder .addLogArea ,
425
+ builder .logAreaRows );
406
426
if (builder .splitUI ) {
407
427
JSplitPane splitPane = new JSplitPane (
408
428
builder .splitUIOrientation ,
@@ -421,7 +441,9 @@ private static void createUI(Builder builder) {
421
441
private static JComponent createInstructionUIPanel (String instructions ,
422
442
long testTimeOut ,
423
443
int rows , int columns ,
424
- boolean enableScreenCapture ) {
444
+ boolean enableScreenCapture ,
445
+ boolean addLogArea ,
446
+ int logAreaRows ) {
425
447
JPanel main = new JPanel (new BorderLayout ());
426
448
427
449
JLabel testTimeoutLabel = new JLabel ("" , JLabel .CENTER );
@@ -455,7 +477,20 @@ private static JComponent createInstructionUIPanel(String instructions,
455
477
buttonsPanel .add (createCapturePanel ());
456
478
}
457
479
458
- main .add (buttonsPanel , BorderLayout .SOUTH );
480
+ if (addLogArea ) {
481
+ logArea = new JTextArea (logAreaRows , columns );
482
+ logArea .setEditable (false );
483
+
484
+ Box buttonsLogPanel = Box .createVerticalBox ();
485
+
486
+ buttonsLogPanel .add (buttonsPanel );
487
+ buttonsLogPanel .add (new JScrollPane (logArea ));
488
+
489
+ main .add (buttonsLogPanel , BorderLayout .SOUTH );
490
+ } else {
491
+ main .add (buttonsPanel , BorderLayout .SOUTH );
492
+ }
493
+
459
494
main .setMinimumSize (main .getPreferredSize ());
460
495
461
496
return main ;
@@ -1039,13 +1074,45 @@ public static void forceFail(String reason) {
1039
1074
latch .countDown ();
1040
1075
}
1041
1076
1077
+ /**
1078
+ * Adds a {@code message} to the log area, if enabled by
1079
+ * {@link Builder#logArea()} or {@link Builder#logArea(int)}.
1080
+ *
1081
+ * @param message to log
1082
+ */
1083
+ public static void log (String message ) {
1084
+ System .out .println ("PassFailJFrame: " + message );
1085
+ invokeOnEDTUncheckedException (() -> logArea .append (message + "\n " ));
1086
+ }
1087
+
1088
+ /**
1089
+ * Clears the log area, if enabled by
1090
+ * {@link Builder#logArea()} or {@link Builder#logArea(int)}.
1091
+ */
1092
+ public static void logClear () {
1093
+ System .out .println ("\n PassFailJFrame: log cleared\n " );
1094
+ invokeOnEDTUncheckedException (() -> logArea .setText ("" ));
1095
+ }
1096
+
1097
+ /**
1098
+ * Replaces the log area content with provided {@code text}, if enabled by
1099
+ * {@link Builder#logArea()} or {@link Builder#logArea(int)}.
1100
+ * @param text new text for the log area
1101
+ */
1102
+ public static void logSet (String text ) {
1103
+ System .out .println ("\n PassFailJFrame: log set to:\n " + text + "\n " );
1104
+ invokeOnEDTUncheckedException (() -> logArea .setText (text ));
1105
+ }
1106
+
1042
1107
public static final class Builder {
1043
1108
private String title ;
1044
1109
private String instructions ;
1045
1110
private long testTimeOut ;
1046
1111
private int rows ;
1047
1112
private int columns ;
1048
1113
private boolean screenCapture ;
1114
+ private boolean addLogArea ;
1115
+ private int logAreaRows = 10 ;
1049
1116
1050
1117
private List <? extends Window > testWindows ;
1051
1118
private WindowListCreator windowListCreator ;
@@ -1087,6 +1154,37 @@ public Builder screenCapture() {
1087
1154
return this ;
1088
1155
}
1089
1156
1157
+ /**
1158
+ * Adds a log area below the "Pass", "Fail" buttons.
1159
+ * <p>
1160
+ * The log area can be controlled by {@link #log(String)},
1161
+ * {@link #logClear()} and {@link #logSet(String)}.
1162
+ *
1163
+ * @return this builder
1164
+ */
1165
+ public Builder logArea () {
1166
+ this .addLogArea = true ;
1167
+ return this ;
1168
+ }
1169
+
1170
+ /**
1171
+ * Adds a log area below the "Pass", "Fail" buttons.
1172
+ * <p>
1173
+ * The log area can be controlled by {@link #log(String)},
1174
+ * {@link #logClear()} and {@link #logSet(String)}.
1175
+ * <p>
1176
+ * The number of columns is taken from the number of
1177
+ * columns in the instructional JTextArea.
1178
+ *
1179
+ * @param rows of the log area
1180
+ * @return this builder
1181
+ */
1182
+ public Builder logArea (int rows ) {
1183
+ this .addLogArea = true ;
1184
+ this .logAreaRows = rows ;
1185
+ return this ;
1186
+ }
1187
+
1090
1188
/**
1091
1189
* Adds a {@code WindowCreator} which the framework will use
1092
1190
* to create the test UI window.
0 commit comments