Remember window size/position between sessions

This commit is contained in:
drdev
2013-08-07 15:37:48 +00:00
parent 0dbd8155ae
commit d8319fd89e
8 changed files with 240 additions and 11 deletions

1
.gitattributes vendored
View File

@@ -13094,6 +13094,7 @@ res/defaults/gauntlet/LOCKED_DotP[!!-~]Preconstructed.dat -text
res/defaults/gauntlet/LOCKED_Swimming[!!-~]With[!!-~]Sharks.dat -text
res/defaults/home.xml svneol=native#text/xml
res/defaults/match.xml svneol=native#text/xml
res/defaults/window.xml -text
res/draft/cube_juzamjedi.draft -text
res/draft/cube_skiera.draft -text
res/draft/rankings.txt -text

View File

@@ -8,6 +8,12 @@ Forge Beta: 0#-##-2013 ver 1.4.6
Release Notes
-------------
- Window size/position now remembered between sessions -
Works with multiple monitors.
Remembers whether the window was maximized or un-maximized.
Remembers last un-maximized size even if the window is currently maximized such that, if you un-maximize the window, it will restore to that size at the center of the current monitor.
Window will be made accessible even if monitor setup or screen resolution is different between sessions.
- Zoomer Updates -
Split cards (name contains "//") are now rotated 90 degrees for easier viewing.

2
res/defaults/window.xml Normal file
View File

@@ -0,0 +1,2 @@
<?xml version="1.0"?>
<layout x="0" y="0" w="800" h="600" state="6"></layout>

View File

@@ -185,12 +185,18 @@ public enum FControl {
Singletons.getModel().getQuest().load(QuestDataIO.loadData(data));
}
// Handles resizing in null layouts of layers in JLayeredPane.
// Handles resizing in null layouts of layers in JLayeredPane as well as saving window layout
Singletons.getView().getFrame().addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(final ComponentEvent e) {
sizeChildren();
}
@Override
public void componentResized(final ComponentEvent e) {
sizeChildren();
SLayoutIO.saveWindowLayout();
}
@Override
public void componentMoved(final ComponentEvent e) {
SLayoutIO.saveWindowLayout();
}
});
FView.SINGLETON_INSTANCE.getLpnDocument().addMouseListener(SOverflowUtil.getHideOverflowListener());

View File

@@ -2,7 +2,12 @@ package forge.gui.framework;
import java.awt.Color;
import java.awt.Component;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.KeyboardFocusManager;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.Timer;
import java.util.TimerTask;
@@ -105,4 +110,18 @@ public class SDisplayUtil {
};
FThreads.invokeInEdtLater(showTabRoutine);
}
public static Rectangle getScreenBoundsForPoint(Point point) {
Rectangle bounds;
for (GraphicsDevice device : GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()) {
for (GraphicsConfiguration config : device.getConfigurations()) {
bounds = config.getBounds();
if (bounds.contains(point)) {
return bounds;
}
}
}
//return bounds of default monitor if point not on any screen
return GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds();
}
}

View File

@@ -1,6 +1,10 @@
package forge.gui.framework;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Point;
import java.awt.Rectangle;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -12,6 +16,7 @@ import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.JFrame;
import javax.swing.border.EmptyBorder;
import javax.xml.stream.XMLEventFactory;
import javax.xml.stream.XMLEventReader;
@@ -41,7 +46,6 @@ import forge.view.FView;
* <br><br><i>(S at beginning of class name denotes a static factory.)</i>
*/
public final class SLayoutIO {
/** Each cell must save these elements of its display. */
private static class Property {
public final static String x = "x";
public final static String y = "y";
@@ -49,11 +53,203 @@ public final class SLayoutIO {
public final static String h = "h";
public final static String sel = "sel";
public final static String doc = "doc";
public final static String state = "state";
}
private static final XMLEventFactory EF = XMLEventFactory.newInstance();
private static final XMLEvent NEWLINE = EF.createDTD("\n");
private static final XMLEvent TAB = EF.createDTD("\t");
private static int normalWindowWidth, normalWindowHeight;
private final static AtomicBoolean saveWindowRequested = new AtomicBoolean(false);
public static void saveWindowLayout() {
if (saveWindowRequested.getAndSet(true)) { return; }
FThreads.delay(500, new Runnable() {
@Override
public void run() {
finishSaveWindowLayout();
saveWindowRequested.set(false);
}
});
}
private synchronized static void finishSaveWindowLayout() {
final JFrame window = FView.SINGLETON_INSTANCE.getFrame();
final int state = window.getExtendedState();
if (state == Frame.ICONIFIED) { return; } //don't update saved layout if minimized
final Rectangle bounds = window.getBounds();
final int x = bounds.x;
final int y = bounds.y;
if ((state & Frame.MAXIMIZED_HORIZ) != Frame.MAXIMIZED_HORIZ) { //only modify saved width if not maximized horizontally
normalWindowWidth = bounds.width;
}
else if (normalWindowWidth == 0) {
normalWindowWidth = window.getMinimumSize().width;
}
if ((state & Frame.MAXIMIZED_VERT) != Frame.MAXIMIZED_VERT) { //only modify saved width if not maximized vertically
normalWindowHeight = bounds.height;
}
else if (normalWindowHeight == 0) {
normalWindowHeight = window.getMinimumSize().height;
}
final FileLocation file = NewConstants.WINDOW_LAYOUT_FILE;
final String fWriteTo = file.userPrefLoc;
final XMLOutputFactory out = XMLOutputFactory.newInstance();
FileOutputStream fos = null;
XMLEventWriter writer = null;
try {
fos = new FileOutputStream(fWriteTo);
writer = out.createXMLEventWriter(fos);
writer.add(EF.createStartDocument());
writer.add(NEWLINE);
writer.add(EF.createStartElement("", "", "layout"));
writer.add(EF.createAttribute(Property.x, String.valueOf(x)));
writer.add(EF.createAttribute(Property.y, String.valueOf(y)));
writer.add(EF.createAttribute(Property.w, String.valueOf(normalWindowWidth)));
writer.add(EF.createAttribute(Property.h, String.valueOf(normalWindowHeight)));
writer.add(EF.createAttribute(Property.state, String.valueOf(state)));
writer.add(EF.createEndElement("", "", "layout"));
writer.flush();
writer.add(EF.createEndDocument());
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block ignores the exception, but sends it to System.err and probably forge.log.
e.printStackTrace();
} catch (XMLStreamException e) {
// TODO Auto-generated catch block ignores the exception, but sends it to System.err and probably forge.log.
e.printStackTrace();
} finally {
if (writer != null ) {
try { writer.close(); } catch (XMLStreamException e) {}
}
if ( fos != null ) {
try { fos.close(); } catch (IOException e) {}
}
}
}
public static void loadWindowLayout() {
final JFrame window = FView.SINGLETON_INSTANCE.getFrame();
final XMLInputFactory inputFactory = XMLInputFactory.newInstance();
final FileLocation file = NewConstants.WINDOW_LAYOUT_FILE;
boolean usedCustomPrefsFile = false;
FileInputStream fis = null;
try {
File userSetting = new File(file.userPrefLoc);
if (userSetting.exists()) {
usedCustomPrefsFile = true;
fis = new FileInputStream(userSetting);
}
else {
fis = new FileInputStream(file.defaultLoc);
}
XMLEventReader reader = null;
try {
XMLEvent event;
StartElement element;
Iterator<?> attributes;
Attribute attribute;
reader = inputFactory.createXMLEventReader(fis);
while (reader != null && reader.hasNext()) {
event = reader.nextEvent();
if (event.isStartElement()) {
element = event.asStartElement();
if (element.getName().getLocalPart().equals("layout")) {
attributes = element.getAttributes();
Dimension minSize = window.getMinimumSize();
int x = 0, y = 0, w = minSize.width, h = minSize.height, state = Frame.MAXIMIZED_BOTH;
while (attributes.hasNext()) {
attribute = (Attribute) attributes.next();
switch (attribute.getName().toString()) {
case Property.x: x = Integer.parseInt(attribute.getValue()); break;
case Property.y: y = Integer.parseInt(attribute.getValue()); break;
case Property.w: w = Integer.parseInt(attribute.getValue()); break;
case Property.h: h = Integer.parseInt(attribute.getValue()); break;
case Property.state: state = Integer.parseInt(attribute.getValue()); break;
}
}
//set normal size to loaded size
normalWindowWidth = w;
normalWindowHeight = h;
//update x and y if needed such that window is centered on that axis
//when un-maximized if starting out maximized on that axis
int centerX = x + w / 2;
int centerY = y + h / 2;
Rectangle screenBounds = SDisplayUtil.getScreenBoundsForPoint(new Point(centerX, centerY));
if ((state & Frame.MAXIMIZED_HORIZ) == Frame.MAXIMIZED_HORIZ) {
x = screenBounds.x + (screenBounds.width - w) / 2;
}
else { //ensure the window is accessible
if (centerX < screenBounds.x) {
x = screenBounds.x;
}
else if (centerX > screenBounds.x + screenBounds.width) {
x = screenBounds.x + screenBounds.width - w;
if (x < screenBounds.x) {
x = screenBounds.x;
}
}
}
if ((state & Frame.MAXIMIZED_VERT) == Frame.MAXIMIZED_VERT) {
y = screenBounds.y + (screenBounds.height - h) / 2;
}
else { //ensure the window is accessible
if (centerY < screenBounds.y) {
y = screenBounds.y;
}
else if (centerY > screenBounds.y + screenBounds.height) {
y = screenBounds.y + screenBounds.height - h;
if (y < screenBounds.y) {
y = screenBounds.y;
}
}
}
window.setBounds(x, y, w, h);
window.setExtendedState(state);
}
}
}
}
catch (final Exception e) {
try {
if (reader != null) { reader.close(); };
}
catch (final XMLStreamException x) {
e.printStackTrace();
}
e.printStackTrace();
if (usedCustomPrefsFile) {
throw new InvalidLayoutFileException();
}
else {
throw new RuntimeException(e);
}
}
}
catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
finally {
if (fis != null ) {
try {
fis.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* Gets preferred layout file corresponding to current state of UI.

View File

@@ -86,6 +86,7 @@ public final class NewConstants {
// data that has defaults in the program dir but overrides/additions in the user dir
private static final String _DEFAULTS_DIR = _RES_ROOT + "defaults/";
public static final FileLocation EDITOR_PREFERENCES_FILE = new FileLocation(_DEFAULTS_DIR, USER_PREFS_DIR, "editor.preferences");
public static final FileLocation WINDOW_LAYOUT_FILE = new FileLocation(_DEFAULTS_DIR, USER_PREFS_DIR, "window.xml");
public static final FileLocation HOME_LAYOUT_FILE = new FileLocation(_DEFAULTS_DIR, USER_PREFS_DIR, "home.xml");
public static final FileLocation MATCH_LAYOUT_FILE = new FileLocation(_DEFAULTS_DIR, USER_PREFS_DIR, "match.xml");
public static final FileLocation EDITOR_LAYOUT_FILE = new FileLocation(_DEFAULTS_DIR, USER_PREFS_DIR, "editor.xml");

View File

@@ -4,7 +4,6 @@ import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
@@ -42,6 +41,7 @@ import forge.gui.deckeditor.VDeckEditorUI;
import forge.gui.framework.DragCell;
import forge.gui.framework.EDocID;
import forge.gui.framework.SLayoutConstants;
import forge.gui.framework.SLayoutIO;
import forge.gui.home.VHomeUI;
import forge.gui.match.TargetingOverlay;
import forge.gui.match.VMatchUI;
@@ -95,7 +95,6 @@ public enum FView {
// Frame styling
frmDocument.setMinimumSize(new Dimension(800, 600));
frmDocument.setLocationRelativeTo(null);
frmDocument.setExtendedState(frmDocument.getExtendedState() | Frame.MAXIMIZED_BOTH);
frmDocument.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
frmDocument.setIconImage(FSkin.getIcon(FSkin.InterfaceIcons.ICO_FAVICON).getImage());
frmDocument.setTitle("Forge: " + BuildInfo.getVersionString());
@@ -141,9 +140,8 @@ public enum FView {
FView.this.frmSplash.dispose();
FView.this.frmSplash = null;
// Allow OS to set location. Hopefully this doesn't cause issues
frmDocument.setLocationByPlatform(true);
SLayoutIO.loadWindowLayout();
frmDocument.setVisible(true);
// remove this once our userbase has been migrated to the profile layout