mirror of
https://github.com/jneug/zeichenmaschine.git
synced 2026-04-14 06:33:34 +02:00
Verbesserter Vollbildmodus und Trennung GUI / Controller
This commit is contained in:
@@ -11,6 +11,7 @@ import java.awt.event.KeyEvent;
|
||||
import java.awt.event.KeyListener;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -60,8 +61,8 @@ public class Zeichenfenster extends JFrame {
|
||||
private GraphicsDevice displayDevice;
|
||||
|
||||
/**
|
||||
* Bevorzugte Abmessungen der Zeichenleinwand. Für das Zeichenfenster hat
|
||||
* es Priorität die Leinwand auf dieser Größe zu halten. Gegebenenfalls unter
|
||||
* 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.
|
||||
*/
|
||||
@@ -78,12 +79,13 @@ public class Zeichenfenster extends JFrame {
|
||||
* verlassen. Wird von {@link #setFullscreen(boolean)} automatisch
|
||||
* hinzugefügt und entfernt.
|
||||
*/
|
||||
private KeyListener fullscreenExitListener = new KeyAdapter() {
|
||||
private final KeyListener fullscreenExitListener = new KeyAdapter() {
|
||||
@Override
|
||||
public void keyPressed( KeyEvent e ) {
|
||||
if( e.getKeyCode() == KeyEvent.VK_ESCAPE ) {
|
||||
// canvas.removeKeyListener(this);
|
||||
setFullscreen(false);
|
||||
e.consume();
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -110,12 +112,11 @@ public class Zeichenfenster extends JFrame {
|
||||
|
||||
this.canvasPreferredWidth = canvas.getWidth();
|
||||
this.canvasPreferredHeight = canvas.getHeight();
|
||||
//this.add(canvas, BorderLayout.CENTER);
|
||||
this.add(canvas);
|
||||
this.add(canvas, BorderLayout.CENTER);
|
||||
this.canvas = canvas;
|
||||
|
||||
// Konfiguration des Frames
|
||||
this.setTitle(title == null ? "Zeichenfenster " + Constants.APP_VERSION: title);
|
||||
this.setTitle(title == null ? "Zeichenfenster " + Constants.APP_VERSION : title);
|
||||
// Kann vom Aufrufenden überschrieben werden
|
||||
this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
|
||||
|
||||
@@ -144,6 +145,7 @@ public class Zeichenfenster extends JFrame {
|
||||
// Fenster zusammenbauen, auf dem Bildschirm positionieren ...
|
||||
this.pack();
|
||||
this.setResizable(false);
|
||||
this.setFocusable(true);
|
||||
this.setLocationByPlatform(true);
|
||||
// this.centerFrame();
|
||||
}
|
||||
@@ -153,6 +155,7 @@ public class Zeichenfenster extends JFrame {
|
||||
}
|
||||
|
||||
public Rectangle getScreenBounds() {
|
||||
// return GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds();
|
||||
return displayDevice.getDefaultConfiguration().getBounds();
|
||||
}
|
||||
|
||||
@@ -163,6 +166,7 @@ public class Zeichenfenster extends JFrame {
|
||||
public Rectangle getCanvasBounds() {
|
||||
return canvas.getBounds();
|
||||
}
|
||||
|
||||
/**
|
||||
* Zentriert das Zeichenfenster auf dem aktuellen Bildschirm.
|
||||
*/
|
||||
@@ -176,7 +180,7 @@ public class Zeichenfenster extends JFrame {
|
||||
}
|
||||
|
||||
public void setCanvasSize( int newWidth, int newHeight ) {
|
||||
// TODO: (ngb) Put constains on max/min frame/canvas size
|
||||
// TODO: (ngb) Put constrains on max/min frame/canvas size
|
||||
if( fullscreen ) {
|
||||
canvasPreferredWidth = newWidth;
|
||||
canvasPreferredHeight = newHeight;
|
||||
@@ -205,27 +209,53 @@ public class Zeichenfenster extends JFrame {
|
||||
public final void setFullscreen( boolean pEnable ) {
|
||||
// See https://docs.oracle.com/javase/tutorial/extra/fullscreen/index.html
|
||||
if( displayDevice.isFullScreenSupported() ) {
|
||||
// Temporarily stop rendering
|
||||
while( canvas.isRendering() ) {
|
||||
try {
|
||||
canvas.suspendRendering();
|
||||
} catch( InterruptedException ex ) {
|
||||
LOG.info(ex, "setFullsceen(true) was interrupted and canceled.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if( pEnable && !fullscreen ) {
|
||||
// frame.setUndecorated(true);
|
||||
this.setResizable(false); // Should be set anyway
|
||||
// Activate fullscreen
|
||||
dispose();
|
||||
setUndecorated(true);
|
||||
setResizable(false);
|
||||
displayDevice.setFullScreenWindow(this);
|
||||
|
||||
java.awt.Rectangle bounds = getScreenBounds();
|
||||
// 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);
|
||||
this.addKeyListener(fullscreenExitListener);
|
||||
|
||||
// Reset canvas size to its new bounds to recreate buffer and drawing surface
|
||||
java.awt.Rectangle canvasBounds = getCanvasBounds();
|
||||
canvas.setSize(canvasBounds.width, canvasBounds.height);
|
||||
//canvas.requestFocus();
|
||||
this.requestFocus();
|
||||
|
||||
fullscreen = true;
|
||||
} else if( !pEnable && fullscreen ) {
|
||||
fullscreen = false;
|
||||
|
||||
canvas.removeKeyListener(fullscreenExitListener);
|
||||
displayDevice.setFullScreenWindow(null);
|
||||
dispose();
|
||||
setUndecorated(false);
|
||||
setResizable(false);
|
||||
|
||||
this.removeKeyListener(fullscreenExitListener);
|
||||
canvas.setSize(canvasPreferredWidth, canvasPreferredHeight);
|
||||
this.pack();
|
||||
// frame.setUndecorated(false);
|
||||
|
||||
setVisible(true);
|
||||
pack();
|
||||
|
||||
//canvas.requestFocus();
|
||||
this.requestFocus();
|
||||
|
||||
fullscreen = false;
|
||||
}
|
||||
|
||||
// Resume rendering
|
||||
canvas.resumeRendering();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.*;
|
||||
import java.awt.Canvas;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Toolkit;
|
||||
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
|
||||
@@ -24,10 +24,14 @@ import java.util.concurrent.LinkedBlockingQueue;
|
||||
*/
|
||||
public class Zeichenleinwand extends Canvas {
|
||||
|
||||
private final Object[] renderLock = new Object[0];
|
||||
|
||||
/**
|
||||
* Liste der hinzugefügten Ebenen.
|
||||
*/
|
||||
private LinkedList<Layer> layers;
|
||||
private final LinkedList<Layer> layers;
|
||||
|
||||
private boolean rendering = false, suspended = false;
|
||||
|
||||
/**
|
||||
* Erstellt eine neue Zeichenleinwand mit einer festen Größe.
|
||||
@@ -48,6 +52,20 @@ public class Zeichenleinwand extends Canvas {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRendering() {
|
||||
return rendering;
|
||||
}
|
||||
|
||||
public void suspendRendering() throws InterruptedException {
|
||||
synchronized( renderLock ) {
|
||||
suspended = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void resumeRendering() {
|
||||
suspended = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ändert die Größe der Zeichenleinwand auf die angegebene Größe in Pixeln.
|
||||
* <p>
|
||||
@@ -88,11 +106,11 @@ public class Zeichenleinwand extends Canvas {
|
||||
/**
|
||||
* Fügt der Zeichenleinwand eine Ebene an einem bestimmten Index hinzu. Wenn
|
||||
* der Index noch nicht existiert (also größer als die
|
||||
* {@link #getLayerCount() Anzahl der Ebenen} ist, dann wird die neue Ebene
|
||||
* {@link #getLayerCount() Anzahl der Ebenen} ist), dann wird die neue Ebene
|
||||
* als letzte eingefügt. Die aufrufende Methode kann also nicht sicher sein,
|
||||
* dass die neue Ebene am Ende wirklich am Index {@code i} steht.
|
||||
*
|
||||
* @param i Index der Ebene, beginnend mit 0.
|
||||
* @param i Index der Ebene beginnend mit 0.
|
||||
* @param layer Die neue Ebene.
|
||||
*/
|
||||
public void addLayer( int i, Layer layer ) {
|
||||
@@ -249,11 +267,13 @@ public class Zeichenleinwand extends Canvas {
|
||||
* Zeigt den aktuellen Inhalt der Zeichenleinwand an.
|
||||
*/
|
||||
public void render() {
|
||||
if( !suspended && isDisplayable() ) {
|
||||
if( getBufferStrategy() == null ) {
|
||||
allocateBuffer();
|
||||
}
|
||||
|
||||
if( isDisplayable() ) {
|
||||
synchronized( renderLock ) {
|
||||
rendering = true;
|
||||
BufferStrategy strategy = this.getBufferStrategy();
|
||||
if( strategy != null ) {
|
||||
do {
|
||||
@@ -281,6 +301,8 @@ public class Zeichenleinwand extends Canvas {
|
||||
// Repeat the rendering if the drawing buffer was lost
|
||||
} while( strategy.contentsLost() );
|
||||
}
|
||||
rendering = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Hauptklasse der Zeichenmaschine.
|
||||
@@ -37,43 +38,6 @@ public class Zeichenmaschine extends Constants {
|
||||
IN_BLUEJ = System.getProperty("java.class.path").contains("bluej");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt an, ob die Zeichenmaschine unter macOS gestartet wurde.
|
||||
*/
|
||||
public static final boolean MACOS;
|
||||
|
||||
/**
|
||||
* Gibt an, ob die Zeichenmaschine unter Windows gestartet wurde.
|
||||
*/
|
||||
public static final boolean WINDOWS;
|
||||
|
||||
/**
|
||||
* Gibt an, ob die Zeichenmaschine unter Linux gestartet wurde.
|
||||
*/
|
||||
public static final boolean LINUX;
|
||||
|
||||
static {
|
||||
final String name = System.getProperty("os.name");
|
||||
|
||||
if( name.contains("Mac") ) {
|
||||
MACOS = true;
|
||||
WINDOWS = false;
|
||||
LINUX = false;
|
||||
} else if( name.contains("Windows") ) {
|
||||
MACOS = false;
|
||||
WINDOWS = true;
|
||||
LINUX = false;
|
||||
} else if( name.equals("Linux") ) { // true for the ibm vm
|
||||
MACOS = false;
|
||||
WINDOWS = false;
|
||||
LINUX = true;
|
||||
} else {
|
||||
MACOS = false;
|
||||
WINDOWS = false;
|
||||
LINUX = false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Objektvariablen, die von Unterklassen benutzt werden können.
|
||||
*/
|
||||
@@ -111,7 +75,7 @@ public class Zeichenmaschine extends Constants {
|
||||
/**
|
||||
* Das Zeichenfenster der Zeichenmaschine
|
||||
*/
|
||||
private Zeichenfenster frame;
|
||||
protected Zeichenfenster frame;
|
||||
|
||||
// Aktueller Zustand der Zeichenmaschine.
|
||||
|
||||
@@ -312,8 +276,15 @@ public class Zeichenmaschine extends Constants {
|
||||
canvas.addMouseWheelListener(inputListener);
|
||||
canvas.addKeyListener(inputListener);
|
||||
|
||||
/*KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(new KeyEventDispatcher() {
|
||||
@Override
|
||||
public boolean dispatchKeyEvent( KeyEvent e ) {
|
||||
enqueueEvent(e);
|
||||
return false;
|
||||
}
|
||||
});*/
|
||||
|
||||
// Programm beenden, wenn Fenster geschlossen wird
|
||||
// TODO: (ngb) Der Listener hat zu viel FUnktionalität -> nach quit() / exit() auslagern
|
||||
frame.addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosing( WindowEvent e ) {
|
||||
@@ -392,10 +363,18 @@ public class Zeichenmaschine extends Constants {
|
||||
public final void setFullscreen( boolean pEnable ) {
|
||||
if( pEnable && !frame.isFullscreen() ) {
|
||||
frame.setFullscreen(true);
|
||||
|
||||
canvasWidth = canvas.getWidth();
|
||||
canvasHeight = canvas.getHeight();
|
||||
|
||||
if( frame.isFullscreen() )
|
||||
fullscreenChanged();
|
||||
} else if( !pEnable && frame.isFullscreen() ) {
|
||||
frame.setFullscreen(false);
|
||||
|
||||
canvasWidth = canvas.getWidth();
|
||||
canvasHeight = canvas.getHeight();
|
||||
|
||||
if( !frame.isFullscreen() )
|
||||
fullscreenChanged();
|
||||
}
|
||||
@@ -546,6 +525,11 @@ public class Zeichenmaschine extends Constants {
|
||||
}
|
||||
|
||||
public final void exitNow() {
|
||||
// Do nothing, when already quitting
|
||||
if( state == Options.AppState.QUITING ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if( running ) {
|
||||
running = false;
|
||||
terminateImediately = true;
|
||||
@@ -584,6 +568,7 @@ public class Zeichenmaschine extends Constants {
|
||||
* @see System#exit(int)
|
||||
*/
|
||||
public final void quit( boolean exit ) {
|
||||
state = Options.AppState.QUITING;
|
||||
frame.setVisible(false);
|
||||
canvas.dispose();
|
||||
frame.dispose();
|
||||
@@ -1390,7 +1375,7 @@ public class Zeichenmaschine extends Constants {
|
||||
}
|
||||
|
||||
// Display the current buffer content
|
||||
if( canvas != null ) {
|
||||
if( canvas != null && frame.isDisplayable() ) {
|
||||
canvas.render();
|
||||
// canvas.invalidate();
|
||||
// frame.repaint();
|
||||
@@ -1547,6 +1532,7 @@ public class Zeichenmaschine extends Constants {
|
||||
|
||||
}
|
||||
|
||||
// TODO: (ngb) exception handling when update/draw throws ex
|
||||
class UpdateThreadExecutor extends ThreadPoolExecutor {
|
||||
|
||||
private Thread updateThread;
|
||||
@@ -1555,7 +1541,18 @@ public class Zeichenmaschine extends Constants {
|
||||
|
||||
public UpdateThreadExecutor() {
|
||||
super(1, 1, 0L,
|
||||
TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
|
||||
TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(),
|
||||
new ThreadFactory() {
|
||||
private final AtomicInteger threadNumber = new AtomicInteger(1);
|
||||
@Override
|
||||
public Thread newThread( Runnable r ) {
|
||||
Thread t = new Thread(mainThread.getThreadGroup(), r,
|
||||
"updateThread-" + threadNumber.getAndIncrement(),
|
||||
0);
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
}
|
||||
});
|
||||
updateState = Options.AppState.IDLE;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user