46
46
import java .util .Optional ;
47
47
import java .util .concurrent .CompletableFuture ;
48
48
import java .util .function .Consumer ;
49
- import java .util .function .Function ;
50
49
import java .util .function .Predicate ;
51
50
52
51
import static java .lang .String .format ;
61
60
import static pixelitor .utils .Texts .i18n ;
62
61
63
62
/**
64
- * Static methods related to the list of open views.
63
+ * Static methods for managing the collection of open views.
65
64
*/
66
65
public class Views {
67
66
private static final List <View > views = new ArrayList <>();
@@ -108,12 +107,12 @@ public static void viewClosed(View view) {
108
107
109
108
views .remove (view );
110
109
if (views .isEmpty ()) {
111
- onAllViewsClosed ();
110
+ allViewsClosed ();
112
111
}
113
112
ensureActiveViewExists ();
114
113
}
115
114
116
- private static void onAllViewsClosed () {
115
+ private static void allViewsClosed () {
117
116
setActiveView (null , false );
118
117
activationListeners .forEach (ViewActivationListener ::allViewsClosed );
119
118
History .onAllViewsClosed ();
@@ -122,7 +121,7 @@ private static void onAllViewsClosed() {
122
121
FramesUI .resetCascadeCount ();
123
122
}
124
123
125
- // ensures that one of the open views is active
124
+ // ensures that an active view is set if there are any open views remaining
126
125
private static void ensureActiveViewExists () {
127
126
if (!views .isEmpty () && !views .contains (activeView )) {
128
127
activate (views .getFirst ());
@@ -133,6 +132,9 @@ public static void activate(View view) {
133
132
setActiveView (view , true );
134
133
}
135
134
135
+ /**
136
+ * Sets the active view, optionally triggering full UI activation.
137
+ */
136
138
public static void setActiveView (View view , boolean activate ) {
137
139
if (view == activeView ) {
138
140
return ;
@@ -152,7 +154,7 @@ public static void setActiveView(View view, boolean activate) {
152
154
}
153
155
154
156
/**
155
- * Changes the cursor for all views
157
+ * Changes the mouse cursor for all open views.
156
158
*/
157
159
public static void setCursorForAll (Cursor cursor ) {
158
160
for (View view : views ) {
@@ -240,6 +242,9 @@ public static void forEach(Consumer<View> action) {
240
242
}
241
243
242
244
public static View activateRandomView () {
245
+ if (views .isEmpty ()) {
246
+ return null ;
247
+ }
243
248
View view = Rnd .chooseFrom (views );
244
249
if (view != activeView ) {
245
250
activate (view );
@@ -269,6 +274,12 @@ public static void assertNumViewsIsAtLeast(int minimum) {
269
274
minimum , numViews , getOpenCompNamesAsString ()));
270
275
}
271
276
277
+ private static String getOpenCompNamesAsString () {
278
+ return views .stream ()
279
+ .map (View ::getName )
280
+ .collect (joining (", " , "[" , "]" ));
281
+ }
282
+
272
283
public static void assertZoomOfActiveIs (ZoomLevel expected ) {
273
284
if (activeView == null ) {
274
285
throw new AssertionError ("no active view" );
@@ -280,12 +291,9 @@ public static void assertZoomOfActiveIs(ZoomLevel expected) {
280
291
}
281
292
}
282
293
283
- private static String getOpenCompNamesAsString () {
284
- return views .stream ()
285
- .map (View ::getName )
286
- .collect (joining (", " , "[" , "]" ));
287
- }
288
-
294
+ /**
295
+ * Closes the given view, prompting to save unsaved changes if necessary.
296
+ */
289
297
public static void warnAndClose (View view ) {
290
298
if (RandomGUITest .isRunning ()) {
291
299
return ;
@@ -314,6 +322,7 @@ public static void warnAndClose(View view) {
314
322
throw new IllegalStateException ("answer = " + answer );
315
323
}
316
324
} else {
325
+ // no unsaved changes, close directly
317
326
view .close ();
318
327
}
319
328
} catch (Exception ex ) {
@@ -333,10 +342,11 @@ private static void warnAndCloseUnmodified() {
333
342
warnAndCloseAllIf (view -> !view .getComp ().isDirty ());
334
343
}
335
344
345
+ // close all views matching a predicate, prompting for unsaved changes
336
346
private static void warnAndCloseAllIf (Predicate <View > condition ) {
337
347
// make a copy because items will be removed from the original while iterating
338
- Iterable <View > tmpCopy = new ArrayList <>(views );
339
- for (View view : tmpCopy ) {
348
+ List <View > viewsToProcess = new ArrayList <>(views );
349
+ for (View view : viewsToProcess ) {
340
350
if (condition .test (view )) {
341
351
warnAndClose (view );
342
352
}
@@ -379,19 +389,6 @@ public static void onActiveComp(Consumer<Composition> action) {
379
389
}
380
390
}
381
391
382
- public static <T > T fromActiveComp (Function <Composition , T > function ) {
383
- if (activeView != null ) {
384
- return function .apply (activeView .getComp ());
385
- }
386
-
387
- // there is no open view
388
- return null ;
389
- }
390
-
391
- public static BufferedImage getActiveCompositeImage () {
392
- return fromActiveComp (Composition ::getCompositeImage );
393
- }
394
-
395
392
public static Optional <Composition > findCompByName (String name ) {
396
393
return views .stream ()
397
394
.map (View ::getComp )
@@ -406,6 +403,9 @@ public static List<Composition> getUnsavedComps() {
406
403
.collect (toList ());
407
404
}
408
405
406
+ /**
407
+ * Adds a newly loaded composition to the application, sets it as active, and updates related UI.
408
+ */
409
409
public static Composition addJustLoadedComp (Composition comp ) {
410
410
assert comp != null ;
411
411
@@ -423,13 +423,13 @@ public static Composition addJustLoadedComp(Composition comp) {
423
423
}
424
424
425
425
public static void addNewPasted (BufferedImage pastedImage ) {
426
- addNew (pastedImage , null , "Pasted Image " + pastedCount ++);
427
- }
428
-
429
- public static void addNew (BufferedImage image , File file , String name ) {
430
- addNew (Composition .fromImage (image , file , name ));
426
+ String name = "Pasted Image " + pastedCount ++;
427
+ addNew (Composition .fromImage (pastedImage , null , name ));
431
428
}
432
429
430
+ /**
431
+ * Adds the given composition to the UI, creating and configuring a new view for it.
432
+ */
433
433
public static void addNew (Composition comp ) {
434
434
try {
435
435
assert comp .getView () == null : "already has a view" ;
@@ -459,14 +459,6 @@ public static int getNumLayersInActiveHolder() {
459
459
return getActiveLayer ().getHolderForNewLayers ().getNumLayers ();
460
460
}
461
461
462
- public static Layer getActiveTopLevelLayer () {
463
- if (activeView != null ) {
464
- return activeView .getComp ().getActiveTopLevelLayer ();
465
- }
466
-
467
- return null ;
468
- }
469
-
470
462
public static Layer getActiveLayer () {
471
463
if (activeView != null ) {
472
464
return activeView .getComp ().getActiveLayer ();
@@ -477,8 +469,7 @@ public static Layer getActiveLayer() {
477
469
478
470
public static void onActiveLayer (Consumer <Layer > action ) {
479
471
if (activeView != null ) {
480
- Layer activeLayer = activeView .getComp ().getActiveLayer ();
481
- action .accept (activeLayer );
472
+ action .accept (activeView .getComp ().getActiveLayer ());
482
473
}
483
474
}
484
475
@@ -524,24 +515,14 @@ public static Path getActivePath() {
524
515
return null ;
525
516
}
526
517
527
- public static boolean activePathIs (Path path ) {
528
- if (activeView != null ) {
529
- Path activePath = activeView .getComp ().getActivePath ();
530
- return activePath == path ;
531
- }
532
-
533
- // there is no open view
534
- return path == null ;
535
- }
536
-
537
518
public static void setActivePath (Path path ) {
538
519
if (activeView != null ) {
539
520
activeView .getComp ().setActivePath (path );
540
521
}
541
522
}
542
523
543
524
/**
544
- * Checks if a file is already open and prompts the user for confirmation to proceed .
525
+ * Warns the user if a file is already open and prompts for confirmation to open it again .
545
526
*/
546
527
public static boolean warnIfAlreadyOpen (File file ) {
547
528
View view = findViewByFile (file );
@@ -560,6 +541,7 @@ public static boolean warnIfAlreadyOpen(File file) {
560
541
return again ;
561
542
}
562
543
544
+ // finds an open view associated with the given file path
563
545
private static View findViewByFile (File targetFile ) {
564
546
for (View view : views ) {
565
547
File file = view .getComp ().getFile ();
@@ -571,12 +553,12 @@ private static View findViewByFile(File targetFile) {
571
553
}
572
554
573
555
public static void appWindowActivated () {
574
- // Check if any views need to be automatically reloaded
575
- CompletableFuture <Composition > cf = CompletableFuture .completedFuture (null );
556
+ // check if any views need to be automatically reloaded due to external modifications
557
+ CompletableFuture <Composition > chainedChecks = CompletableFuture .completedFuture (null );
576
558
for (View view : views ) {
577
559
// make sure that the next reload is not started
578
560
// before the previous one is finished
579
- cf = cf .thenCompose (comp -> view .checkForExternalModifications ());
561
+ chainedChecks = chainedChecks .thenCompose (comp -> view .checkForExternalModifications ());
580
562
}
581
563
}
582
564
@@ -598,5 +580,6 @@ public static void updateThumbSize(int newThumbSize) {
598
580
public static void clear () {
599
581
views .clear ();
600
582
activeView = null ;
583
+ pastedCount = 1 ;
601
584
}
602
585
}
0 commit comments