reworking EditorState so that the Editor window better handles multiple display setups (fixes https://github.com/processing/processing/issues/1566)

This commit is contained in:
Ben Fry
2021-06-15 16:21:38 -04:00
parent 64e59290e7
commit ba9c8fc491
5 changed files with 117 additions and 88 deletions
+6 -2
View File
@@ -15,14 +15,18 @@ We don't have a schedule for a final release. This work is being done by a [tiny
As with all releases, we'll do everything possible to avoid breaking API. However, there will still be tweaks that have to be made. We'll try to keep them minor. Our goal is stability, and keeping everyone's code running.
### alpha 4
* `EditorState(List<Editor> editors)` changed to `EditorState.nextEditor(List<Editor> editors)`, reflecting its nature as closer to a factory method (that makes use of the Editor list) than a constructor that will also be storing information about the list of Editor objects in the created object.
### alpha 2
* See `changes.md` if you're using `surface.setResizable()` with this release on macOS and with P2D or P3D renderers.
* The `static` versions of `selectInput()`, `selectOutput()`, and `selectFolder()` in `PApplet` have been removed. These were not documented, hopefully were not in use anywhere.
* The `frame` object has been removed from `PApplet`. We've been warning folks to use `surface` since 2015, but we still should [warn users](https://github.com/processing/processing4/issues/59)
* The `frame` object has been removed from `PApplet`. We've been warning folks to use `surface` since 2015, but we still should [warn users](https://github.com/processing/processing4/issues/59) or maybe provide an easy way to update code.
* `PImage.checkAlpha()` is now `public` instead of `protected`
* All AWT calls have been moved out of `PImage`, which may be a problem for anything that was relying on those internals
* For instance, `new PImage(java.awt.Image)` is no longer available. It was an undocumented method that was `public` only because it was required by subclasses.
* ~~For instance, `new PImage(java.awt.Image)` is no longer available. It was an undocumented method that was `public` only because it was required by subclasses.~~ As of alpha 4, this is back, because it wasn't deprecated in 3.x, and is likely to break too many things.
* Removed `MouseEvent.getClickCount()` and `MouseEvent.getAmount()`. These had been deprecated, not clear they were used anywhere.
### alpha 1
+1 -1
View File
@@ -1296,7 +1296,7 @@ public class Base {
* can be set by the caller
*/
public Editor handleOpen(String path, boolean untitled) {
return handleOpen(path, untitled, new EditorState(editors));
return handleOpen(path, untitled, EditorState.nextEditor(editors));
}
+1 -1
View File
@@ -163,7 +163,7 @@ public abstract class Editor extends JFrame implements RunnerListener {
protected Editor(final Base base, String path, final EditorState state,
final Mode mode) throws EditorException {
super("Processing", state.checkConfig());
super("Processing", state.getConfig());
this.base = base;
this.state = state;
this.mode = mode;
+103 -81
View File
@@ -3,7 +3,7 @@
/*
Part of the Processing project - http://processing.org
Copyright (c) 2012-19 The Processing Foundation
Copyright (c) 2012-21 The Processing Foundation
Copyright (c) 2011-12 Ben Fry and Casey Reas
This program is free software; you can redistribute it and/or modify
@@ -27,61 +27,79 @@ import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.io.*;
import java.util.List;
import processing.app.Preferences;
import processing.core.PApplet;
//import processing.core.PApplet;
// scenarios:
// 1) new untitled sketch (needs device, needs bounds)
// 2) restoring sketch from recent menu
// - device cannot be found
// - device is found but it's a different size
// - device is found and size is correct
// 3) re-opening sketch in a new mode
/**
* Stores state information (location, size, display device) for an Editor window.
* Originally was used to restore sketch windows on startup, though that was removed
* at some point (3.0?) because it was unreliable and not always appreciated.
*
* This version is primarily just used to get the location (and display) to open
* a new Editor window, though some of the vestigial bits are still in there in case
* we want to restore the ability to restore windows on startup.
*
* Previous scenarios:
* <ul>
* <li>new untitled sketch (needs device, needs bounds)</li>
* <li>restoring sketch from recent menu
* <ul>
* <li>device is found and size matches</li>
* <li>device cannot be found</li>
* <li>device is found but its size has changed</li>
* </ul>
* </li>
* <li>re-opening sketch in a new mode</li>
* </ul>
*/
public class EditorState {
// path to the main .pde file for the sketch
// String path;
// placement of the window
// int windowX, windowY, windowW, windowH;
Rectangle editorBounds;
int dividerLocation;
// width/height of the screen on which this window was placed
// int displayW, displayH;
// String deviceName; // not really useful b/c it's more about bounds anyway
Rectangle deviceBounds;
boolean isMaximized;
GraphicsConfiguration deviceConfig;
// how far to offset a new window from the previous window
/** How far to offset a new window from the previous window */
static final int WINDOW_OFFSET = 28;
/**
* Keep a reference to the last device config so we know which display to
* use when creating a new window after all windows have been closed.
*/
static GraphicsConfiguration lastConfig;
/**
* Create a fresh editor state object from the default screen device and
* set its placement relative to the last opened window.
* @param editors List of active editor objects
*/
public EditorState(List<Editor> editors) {
defaultConfig();
defaultLocation(editors);
static public EditorState nextEditor(List<Editor> editors) {
Editor lastEditor = null;
int editorCount = editors.size();
if (editorCount > 0) {
lastEditor = editors.get(editorCount-1);
}
// update lastConfig so it can be set for this Editor and
// for the next Editor created if the last window is closed.
if (lastEditor != null) {
lastConfig = lastEditor.getGraphicsConfiguration();
}
if (lastConfig == null) {
lastConfig = getDefaultConfig();
}
EditorState outgoing = new EditorState();
outgoing.initLocation(lastConfig, lastEditor);
return outgoing;
}
// EditorState(BufferedReader reader) throws IOException {
// String line = reader.readLine();
// EditorState(String[] pieces) throws IOException {
/*
EditorState(String info) throws IOException {
// String line = reader.readLine();
// String[] pieces = PApplet.split(line, '\t');
String[] pieces = PApplet.split(info, ',');
// path = pieces[0];
String[] pieces = PApplet.split(info, ',');
editorBounds = new Rectangle(Integer.parseInt(pieces[0]),
Integer.parseInt(pieces[1]),
Integer.parseInt(pieces[2]),
@@ -93,15 +111,8 @@ public class EditorState {
Integer.parseInt(pieces[6]),
Integer.parseInt(pieces[7]),
Integer.parseInt(pieces[8]));
// windowX = Integer.parseInt(pieces[1]);
// windowY = Integer.parseInt(pieces[2]);
// windowW = Integer.parseInt(pieces[3]);
// windowH = Integer.parseInt(pieces[4]);
// displayW = Integer.parseInt(pieces[5]);
// displayH = Integer.parseInt(pieces[6]);
}
*/
public String toString() {
@@ -110,19 +121,14 @@ public class EditorState {
editorBounds.width + "," +
editorBounds.height + "," +
dividerLocation + "," +
deviceBounds.x + "," +
deviceBounds.y + "," +
deviceBounds.width + "," +
deviceBounds.height);
deviceConfig);
}
/**
* Returns a GraphicsConfiguration so that a new Editor Frame can be
* constructed. First tries to match the bounds for this state information
* to an existing config (nominally, a display) and if that doesn't work,
* then returns the default configuration/default display.
*/
/*
// * Returns a GraphicsConfiguration so that a new Editor Frame can be
// * constructed. First tries to match the bounds for this state information
// * to an existing config (nominally, a display) and if that doesn't work,
// * then returns the default configuration/default display.
GraphicsConfiguration checkConfig() {
if (deviceBounds != null) {
GraphicsEnvironment graphicsEnvironment =
@@ -133,22 +139,31 @@ public class EditorState {
for (GraphicsConfiguration config : configurations) {
// if (config.getDevice().getIDstring().equals(deviceName)) {
if (config.getBounds().equals(deviceBounds)) {
System.out.println("found config " + config + " " + deviceBounds);
System.out.println("device name is " + config.getDevice().getIDstring());
return config;
}
}
}
}
System.out.println("using default config");
// otherwise go to the default config
return defaultConfig();
}
*/
GraphicsConfiguration defaultConfig() {
public GraphicsConfiguration getConfig() {
return deviceConfig;
}
static GraphicsConfiguration getDefaultConfig() {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice device = ge.getDefaultScreenDevice();
GraphicsConfiguration config = device.getDefaultConfiguration();
// deviceName = device.getIDstring();
deviceBounds = config.getBounds();
// deviceName = device.getIDstring();
// deviceBounds = config.getBounds();
return config;
}
@@ -156,9 +171,12 @@ public class EditorState {
/**
* Figure out the next location by sizing up the last editor in the list.
* If no editors are opened, it'll just open on the main screen.
* @param editors List of editors currently opened
* @param lastOpened The last Editor opened, used to determine display and location
*/
void defaultLocation(List<Editor> editors) {
void initLocation(GraphicsConfiguration lastConfig, Editor lastOpened) {
deviceConfig = lastConfig;
Rectangle deviceBounds = deviceConfig.getBounds();
int defaultWidth =
Toolkit.zoom(Preferences.getInteger("editor.window.width.default"));
int defaultHeight =
@@ -166,9 +184,8 @@ public class EditorState {
defaultWidth = Math.min(defaultWidth, deviceBounds.width);
defaultHeight = Math.min(defaultHeight, deviceBounds.height);
//System.out.println("default w/h = " + defaultWidth + "/" + defaultHeight);
if (editors.size() == 0) {
if (lastOpened == null) {
// If no current active editor, use default placement.
// Center the window on ths screen, taking into account that the
// upper-left corner of the device may have a non (0, 0) origin.
@@ -183,39 +200,40 @@ public class EditorState {
} else {
// With a currently active editor, open the new window using the same
// dimensions and divider location, but offset slightly.
synchronized (editors) {
Editor lastOpened = editors.get(editors.size() - 1);
isMaximized = (lastOpened.getExtendedState() == Frame.MAXIMIZED_BOTH);
editorBounds = lastOpened.getBounds();
editorBounds.x += WINDOW_OFFSET;
editorBounds.y += WINDOW_OFFSET;
dividerLocation = lastOpened.getDividerLocation();
//GraphicsDevice device = lastOpened.getGraphicsConfiguration().getDevice();
//System.out.println("last opened device is " + device);
if (!deviceBounds.contains(editorBounds)) {
// Warp the next window to a randomish location on screen.
editorBounds.x = deviceBounds.x +
(int) (Math.random() * (deviceBounds.width - defaultWidth));
editorBounds.y = deviceBounds.y +
(int) (Math.random() * (deviceBounds.height - defaultHeight));
}
if (isMaximized) {
editorBounds.width = defaultWidth;
editorBounds.height = defaultHeight;
}
isMaximized = (lastOpened.getExtendedState() == Frame.MAXIMIZED_BOTH);
editorBounds = lastOpened.getBounds();
editorBounds.x += WINDOW_OFFSET;
editorBounds.y += WINDOW_OFFSET;
dividerLocation = lastOpened.getDividerLocation();
if (!deviceBounds.contains(editorBounds)) {
// Warp the next window to a randomish location on screen.
editorBounds.x = deviceBounds.x +
(int) (Math.random() * (deviceBounds.width - defaultWidth));
editorBounds.y = deviceBounds.y +
(int) (Math.random() * (deviceBounds.height - defaultHeight));
}
if (isMaximized) {
editorBounds.width = defaultWidth;
editorBounds.height = defaultHeight;
}
}
}
/*
void update(Editor editor) {
// path = editor.getSketch().getMainFilePath();
editorBounds = editor.getBounds();
dividerLocation = editor.getDividerLocation();
GraphicsConfiguration config = editor.getGraphicsConfiguration();
// GraphicsDevice device = config.getDevice();
deviceBounds = config.getBounds();
// deviceName = device.getIDstring();
// GraphicsDevice device = config.getDevice();
deviceName = config.getDevice().getIDstring();
}
*/
void apply(Editor editor) {
@@ -229,6 +247,10 @@ public class EditorState {
if (isMaximized) {
editor.setExtendedState(Frame.MAXIMIZED_BOTH);
}
// note: doesn't do anything with the device, though that could be
// added if it's something that would be necessary (i.e. to store windows and
// re-open them on when re-opening Processing)
}
+6 -3
View File
@@ -31,6 +31,10 @@ X https://github.com/processing/processing/issues/5781
X store -1 as display number when using the default
X ran into weird situation where '1' was renumbered by adding a screen
X so the default was now the external display
X a little modernizing/cleanup in Base, converting things to lambda functions
X editor windows always open on display 1
X https://github.com/processing/processing/issues/1566
X rewrote EditorState to better handle devices and clean it up
earlier
o further streamline the downloader
@@ -48,8 +52,6 @@ X https://github.com/processing/processing4/issues/201
X https://github.com/processing/processing/pull/4097
_ editor windows always open on display 1
_ https://github.com/processing/processing/issues/1566
may be fixed
@@ -112,7 +114,8 @@ _ demo: https://github.com/jcodec/jcodec/blob/master/samples/main/java/org/jco
decisions before final 4.0 release
_ Add ability to move ~/.processing directory
_ use ~/.config as parent, or $XDG_CONFIG_HOME
_ https://github.com/processing/processing/issues/6115
o https://github.com/processing/processing/issues/6115 (moved)
_ https://github.com/processing/processing4/issues/203
X Shutting off VAqua due to interface ugliness and Contribution Manager freezing
_ https://github.com/processing/processing4/issues/129
_ now with a release 9 to cover Big Sur