diff --git a/src/main/java/schule/ngb/zm/Zeichenfenster.java b/src/main/java/schule/ngb/zm/Zeichenfenster.java
index 378e598..61c897c 100644
--- a/src/main/java/schule/ngb/zm/Zeichenfenster.java
+++ b/src/main/java/schule/ngb/zm/Zeichenfenster.java
@@ -14,6 +14,14 @@ import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
+/**
+ * Ein Zeichenfenster ist das Programmfenster für die Zeichenmaschine.
+ *
+ * Das Zeichenfenster implementiert hilfreiche Funktionen, um ein
+ * Programmfenster mit einer Zeichenleinwand als zentrales Element zu erstellen.
+ * Ein Zeichenfenster kann auch ohne eine Zeichenmaschine verwendet werden, um
+ * eigene Programmabläufe zu implementieren.
+ */
public class Zeichenfenster extends JFrame {
public static final void setLookAndFeel() {
@@ -46,24 +54,22 @@ public class Zeichenfenster extends JFrame {
}
/**
- * Das Anzeigefenster, auf dem die ZM gestartet wurde (muss nicht gleich
- * dem Aktuellen sein, wenn das Fenster verschoben wurde).
+ * Das Anzeigefenster, auf dem die ZM gestartet wurde (muss nicht gleich dem
+ * Aktuellen sein, wenn das Fenster verschoben wurde).
*/
private GraphicsDevice displayDevice;
- private int canvasWidth, canvasHeight;
-
/**
- * Höhe und Breite der Zeichenmaschine, bevor sie mit
- * {@link #setFullscreen(boolean)} in den Vollbild-Modus versetzt wurde.
- * Wird verwendet, um die Fenstergröße wiederherzustellen, sobald der
- * Vollbild-Modus verlassen wird.
+ * Bevorzugte Abmessungen der Zeichenleinwand. Für das Zeichenfenster hat
+ * es Priorität die Leinwand auf dieser Größe zu halten. Gegebenenfalls unter
+ * Missachtung anderer Größenvorgaben. Allerdings kann das Fenster keine
+ * Garantie für die Größe der Leinwand übernehmen.
*/
- private int initialWidth, initialHeight;
+ private int canvasPreferredWidth, canvasPreferredHeight;
/**
- * Speichert, ob die Zeichenmaschine mit {@link #setFullscreen(boolean)}
- * in den Vollbildmodus versetzt wurde.
+ * Speichert, ob die Zeichenmaschine mit {@link #setFullscreen(boolean)} in
+ * den Vollbildmodus versetzt wurde.
*/
private boolean fullscreen = false;
@@ -85,15 +91,32 @@ public class Zeichenfenster extends JFrame {
private Zeichenleinwand canvas;
public Zeichenfenster( int width, int height, String title ) {
- this(width, height, title, getGraphicsDevice());
+ this(new Zeichenleinwand(width, height), title, getGraphicsDevice());
}
public Zeichenfenster( int width, int height, String title, GraphicsDevice displayDevice ) {
+ this(new Zeichenleinwand(width, height), title, displayDevice);
+ }
+
+ public Zeichenfenster( Zeichenleinwand canvas, String title ) {
+ this(canvas, title, getGraphicsDevice());
+ }
+
+ public Zeichenfenster( Zeichenleinwand canvas, String title, GraphicsDevice displayDevice ) {
super(Validator.requireNotNull(displayDevice).getDefaultConfiguration());
this.displayDevice = displayDevice;
+ Validator.requireNotNull(canvas, "Every Zeichenfenster needs a Zeichenleinwand, but got .");
+
+ this.canvasPreferredWidth = canvas.getWidth();
+ this.canvasPreferredHeight = canvas.getHeight();
+ //this.add(canvas, BorderLayout.CENTER);
+ this.add(canvas);
+ this.canvas = canvas;
+
// Konfiguration des Frames
- this.setTitle(title);
+ this.setTitle(title == null ? "Zeichenfenster " + Constants.APP_VERSION: title);
+ // Kann vom Aufrufenden überschrieben werden
this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
// Das Icon des Fensters ändern
@@ -118,68 +141,51 @@ public class Zeichenfenster extends JFrame {
// Dock Icon in macOS konnte nicht gesetzt werden :(
LOG.warn("Could not set dock icon: %s", macex.getMessage());
}
-
- this.canvasWidth = width;
- this.canvasHeight = height;
-
- // Fenster zusammenbauen, auf dem Bildschirm zentrieren ...
+ // Fenster zusammenbauen, auf dem Bildschirm positionieren ...
this.pack();
this.setResizable(false);
- this.centerFrame();
+ this.setLocationByPlatform(true);
+ // this.centerFrame();
}
- public void addCanvas( Zeichenleinwand canvas ) {
- this.canvas = canvas;
- this.add(canvas, 0);
-
- this.canvasWidth = canvas.getWidth();
- this.canvasHeight = canvas.getHeight();
- this.pack();
+ public GraphicsDevice getDisplayDevice() {
+ return displayDevice;
}
- public java.awt.Rectangle getScreenBounds() {
+ public Rectangle getScreenBounds() {
return displayDevice.getDefaultConfiguration().getBounds();
}
- public java.awt.Rectangle getCanvasBounds() {
- java.awt.Insets insets = getInsets();
- return new java.awt.Rectangle(insets.top, insets.left, canvasWidth, canvasHeight);
+ public Zeichenleinwand getCanvas() {
+ return canvas;
}
+ public Rectangle getCanvasBounds() {
+ return canvas.getBounds();
+ }
/**
* Zentriert das Zeichenfenster auf dem aktuellen Bildschirm.
*/
public final void centerFrame() {
- java.awt.Rectangle bounds = getScreenBounds();
- java.awt.Insets insets = getInsets();
+ java.awt.Rectangle screenBounds = getScreenBounds();
+ java.awt.Rectangle frameBounds = getBounds();
this.setLocation(
- (int) (bounds.x + (bounds.width - canvasWidth) / 2.0 - insets.left),
- (int) (bounds.y + (bounds.height - canvasHeight) / 2.0- insets.top)
+ (int) (screenBounds.x + (screenBounds.width - frameBounds.width) / 2.0),
+ (int) (screenBounds.y + (screenBounds.height - frameBounds.height) / 2.0)
);
}
- @Override
- public void setSize( int newWidth, int newHeight ) {
- java.awt.Rectangle bounds = getScreenBounds();
- java.awt.Insets insets = this.getInsets();
-
+ public void setCanvasSize( int newWidth, int newHeight ) {
+ // TODO: (ngb) Put constains on max/min frame/canvas size
if( fullscreen ) {
- initialWidth = Math.min(newWidth, bounds.width - insets.left - insets.right);
- initialHeight = Math.min(newHeight, bounds.height - insets.top - insets.bottom);
+ canvasPreferredWidth = newWidth;
+ canvasPreferredHeight = newHeight;
setFullscreen(false);
} else {
- canvasWidth = Math.min(newWidth, bounds.width - insets.left - insets.right);
- canvasHeight = Math.min(newHeight, bounds.height - insets.top - insets.bottom);
-
- if( canvas != null ) {
- canvas.setSize(canvasWidth, canvasHeight);
- }
-
- /*super.setSize(
- width + insets.left + insets.right,
- height + insets.top + insets.bottom
- );*/
- super.pack();
+ canvas.setSize(newWidth, newHeight);
+ canvasPreferredWidth = canvas.getWidth();
+ canvasPreferredHeight = canvas.getHeight();
+ this.pack();
}
}
@@ -200,16 +206,13 @@ public class Zeichenfenster extends JFrame {
// See https://docs.oracle.com/javase/tutorial/extra/fullscreen/index.html
if( displayDevice.isFullScreenSupported() ) {
if( pEnable && !fullscreen ) {
- // Store width / height
- initialWidth = getWidth();
- initialHeight = getHeight();
-
// frame.setUndecorated(true);
this.setResizable(false); // Should be set anyway
displayDevice.setFullScreenWindow(this);
java.awt.Rectangle bounds = getScreenBounds();
- this.setSize(bounds.width, bounds.height);
+ // TODO: (ngb) We need to switch layouts to allow the LayoutManger to maximize the canvas
+ canvas.setSize(bounds.width, bounds.height);
// Register ESC to exit fullscreen
canvas.addKeyListener(fullscreenExitListener);
@@ -219,7 +222,7 @@ public class Zeichenfenster extends JFrame {
canvas.removeKeyListener(fullscreenExitListener);
displayDevice.setFullScreenWindow(null);
- this.setSize(initialWidth, initialHeight);
+ canvas.setSize(canvasPreferredWidth, canvasPreferredHeight);
this.pack();
// frame.setUndecorated(false);
}
diff --git a/src/main/java/schule/ngb/zm/Zeichenleinwand.java b/src/main/java/schule/ngb/zm/Zeichenleinwand.java
index 7855536..63c4c96 100644
--- a/src/main/java/schule/ngb/zm/Zeichenleinwand.java
+++ b/src/main/java/schule/ngb/zm/Zeichenleinwand.java
@@ -1,16 +1,16 @@
package schule.ngb.zm;
import schule.ngb.zm.layers.ColorLayer;
+import schule.ngb.zm.layers.ShapesLayer;
import schule.ngb.zm.util.Log;
-import java.awt.Canvas;
-import java.awt.Graphics;
-import java.awt.Graphics2D;
-import java.awt.Toolkit;
+import java.awt.*;
import java.awt.image.BufferStrategy;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
/**
* Eine Leinwand ist die Hauptkomponente einer Zeichenmaschine. Sie besteht aus
@@ -37,8 +37,8 @@ public class Zeichenleinwand extends Canvas {
*/
public Zeichenleinwand( int width, int height ) {
super.setSize(width, height);
- this.setPreferredSize(this.getSize());
- this.setMinimumSize(this.getSize());
+ this.setPreferredSize(getSize());
+ this.setMinimumSize(getSize());
this.setBackground(Constants.DEFAULT_BACKGROUND.getJavaColor());
// Liste der Ebenen initialisieren und die Standardebenen einfügen
@@ -60,8 +60,8 @@ public class Zeichenleinwand extends Canvas {
@Override
public void setSize( int width, int height ) {
super.setSize(width, height);
- this.setPreferredSize(this.getSize());
- this.setMinimumSize(this.getSize());
+ this.setPreferredSize(getSize());
+ this.setMinimumSize(getSize());
synchronized( layers ) {
for( Layer layer : layers ) {
diff --git a/src/main/java/schule/ngb/zm/Zeichenmaschine.java b/src/main/java/schule/ngb/zm/Zeichenmaschine.java
index b16ca5d..7f99063 100644
--- a/src/main/java/schule/ngb/zm/Zeichenmaschine.java
+++ b/src/main/java/schule/ngb/zm/Zeichenmaschine.java
@@ -275,21 +275,21 @@ public class Zeichenmaschine extends Constants {
public Zeichenmaschine( int width, int height, String title, boolean run_once ) {
LOG.info("Starting " + APP_NAME + " " + APP_VERSION);
- // Erstellen des Zeichenfensters
- frame = createFrame(width, height, title);
-
- // Wir kennen nun den Bildschirm und können die Breite / Höhe abrufen.
- this.canvasWidth = width;
- this.canvasHeight = height;
- java.awt.Rectangle displayBounds = frame.getScreenBounds();
- this.screenWidth = (int) displayBounds.getWidth();
- this.screenHeight = (int) displayBounds.getHeight();
-
// Erstellen der Leinwand
canvas = new Zeichenleinwand(width, height);
- frame.addCanvas(canvas);
- // Die drei Standardebenen merken, für den einfachen Zugriff aus unterklassen.
+ // Erstellen des Zeichenfensters
+ frame = createFrame(canvas, title);
+
+ // Wir kennen nun den Bildschirm und können die Breite / Höhe abrufen.
+ java.awt.Rectangle canvasBounds = frame.getCanvasBounds();
+ this.canvasWidth = canvasBounds.width;
+ this.canvasHeight = canvasBounds.height;
+ java.awt.Rectangle displayBounds = frame.getScreenBounds();
+ this.screenWidth = displayBounds.width;
+ this.screenHeight = displayBounds.height;
+
+ // Die drei Standardebenen merken, für den einfachen Zugriff aus Unterklassen.
background = getBackgroundLayer();
drawing = getDrawingLayer();
shapes = getShapesLayer();
@@ -333,7 +333,7 @@ public class Zeichenmaschine extends Constants {
}
});
- // Fesnter anzeigen
+ // Fenster anzeigen
frame.setVisible(true);
// Nach dem Anzeigen kann die Pufferstrategie erstellt werden.
@@ -357,14 +357,22 @@ public class Zeichenmaschine extends Constants {
*
* @param title
*/
- // TODO: Implement in conjunction with Zeichenfenster
- private final Zeichenfenster createFrame( int width, int height, String title ) {
- // Setzen des Look&Feel
- // TODO: (ngb) Ist das überhaupt notwendig oder eh schon der Default?
- Zeichenfenster.setLookAndFeel();
-
- Zeichenfenster frame = new Zeichenfenster(width, height, title);
-
+ private final Zeichenfenster createFrame( Zeichenleinwand c, String title ) {
+ while( frame == null ) {
+ try {
+ TaskRunner.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ Zeichenfenster.setLookAndFeel();
+ frame = new Zeichenfenster(canvas, title);
+ }
+ }).get();
+ } catch( InterruptedException e ) {
+ } catch( ExecutionException e ) {
+ LOG.error(e, "Error initializing application frame: %s", e.getMessage());
+ throw new RuntimeException(e);
+ }
+ }
return frame;
}