Support toggling Full Screen with F11 on all platforms

Fix way window state is updated (minimized/normal/maximized/full screen)
This commit is contained in:
drdev
2013-10-06 16:08:29 +00:00
parent 229485494a
commit a2bf02eb82
5 changed files with 193 additions and 132 deletions

View File

@@ -73,6 +73,7 @@ import forge.quest.QuestController;
import forge.quest.data.QuestPreferences.QPref;
import forge.quest.io.QuestDataIO;
import forge.sound.SoundSystem;
import forge.view.FFrame;
import forge.view.FView;
/**
@@ -188,15 +189,18 @@ public enum FControl implements KeyEventDispatcher {
}
// Handles resizing in null layouts of layers in JLayeredPane as well as saving window layout
Singletons.getView().getFrame().addComponentListener(new ComponentAdapter() {
final FFrame window = Singletons.getView().getFrame();
window.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(final ComponentEvent e) {
sizeChildren();
window.updateNormalBounds();
SLayoutIO.saveWindowLayout();
}
@Override
public void componentMoved(final ComponentEvent e) {
window.updateNormalBounds();
SLayoutIO.saveWindowLayout();
}
});

View File

@@ -10,6 +10,7 @@ import java.awt.KeyboardFocusManager;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.Window;
import java.util.Timer;
import java.util.TimerTask;
@@ -113,40 +114,52 @@ 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();
}
public static Rectangle getScreenMaximizedBounds(Point point) {
public static GraphicsDevice getGraphicsDevice(Point point) {
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsConfiguration graphicsConfiguration = null;
for (GraphicsDevice gd : env.getScreenDevices()) {
if (gd.getDefaultConfiguration().getBounds().contains(point)) {
graphicsConfiguration = gd.getDefaultConfiguration();
break;
return gd;
}
}
if (graphicsConfiguration == null) {
return env.getMaximumWindowBounds();
return null;
}
public static GraphicsDevice getGraphicsDevice(Rectangle rect) {
return getGraphicsDevice(new Point(rect.x + (rect.width / 2), rect.y + (rect.height / 2)));
}
public static Rectangle getScreenBoundsForPoint(Point point) {
GraphicsDevice gd = getGraphicsDevice(point);
if (gd == null) {
//return bounds of default monitor if point not on any screen
return GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds();
}
return gd.getDefaultConfiguration().getBounds();
}
public static Rectangle getScreenMaximizedBounds(Rectangle rect) {
GraphicsDevice gd = getGraphicsDevice(rect);
if (gd == null) {
//return bounds of default monitor if center of rect not on any screen
return GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds();
}
Rectangle bounds = graphicsConfiguration.getBounds();
Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets(graphicsConfiguration);
GraphicsConfiguration config = gd.getDefaultConfiguration();
Rectangle bounds = config.getBounds();
Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets(config);
bounds.x += screenInsets.left;
bounds.y += screenInsets.top;
bounds.height -= screenInsets.bottom;
bounds.width -= screenInsets.right;
return bounds;
}
public static boolean setFullScreenWindow(Window window, boolean fullScreen) {
GraphicsDevice gd = getGraphicsDevice(window.getBounds());
if (gd != null && gd.isFullScreenSupported()) {
gd.setFullScreenWindow(fullScreen ? window : null);
return true;
}
return false;
}
}

View File

@@ -2,7 +2,6 @@ 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;
@@ -16,7 +15,6 @@ 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;
@@ -54,14 +52,14 @@ 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";
public final static String max = "max";
public final static String fs = "fs";
}
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() {
@@ -79,22 +77,7 @@ public final class SLayoutIO {
final FFrame window = FView.SINGLETON_INSTANCE.getFrame();
if (window.isMinimized()) { return; } //don't update saved layout if minimized
final int state = window.getExtendedState();
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 Rectangle normalBounds = window.getNormalBounds();
final FileLocation file = NewConstants.WINDOW_LAYOUT_FILE;
final String fWriteTo = file.userPrefLoc;
@@ -108,11 +91,12 @@ public final class SLayoutIO {
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.createAttribute(Property.x, String.valueOf(normalBounds.x)));
writer.add(EF.createAttribute(Property.y, String.valueOf(normalBounds.y)));
writer.add(EF.createAttribute(Property.w, String.valueOf(normalBounds.width)));
writer.add(EF.createAttribute(Property.h, String.valueOf(normalBounds.height)));
writer.add(EF.createAttribute(Property.max, window.isMaximized() ? "1" : "0"));
writer.add(EF.createAttribute(Property.fs, window.isFullScreen() ? "1" : "0"));
writer.add(EF.createEndElement("", "", "layout"));
writer.flush();
writer.add(EF.createEndDocument());
@@ -133,7 +117,7 @@ public final class SLayoutIO {
}
public static void loadWindowLayout() {
final JFrame window = FView.SINGLETON_INSTANCE.getFrame();
final FFrame window = FView.SINGLETON_INSTANCE.getFrame();
final XMLInputFactory inputFactory = XMLInputFactory.newInstance();
final FileLocation file = NewConstants.WINDOW_LAYOUT_FILE;
boolean usedCustomPrefsFile = false;
@@ -165,58 +149,44 @@ public final class SLayoutIO {
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;
int x = 0, y = 0, w = minSize.width, h = minSize.height;
boolean max = false, fs = false;
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;
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.max: max = attribute.getValue().equals("1"); break;
case Property.fs: fs = attribute.getValue().equals("1"); 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
//ensure the window is accessible
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;
if (centerX < screenBounds.x) {
x = screenBounds.x;
}
else { //ensure the window is accessible
if (centerX < screenBounds.x) {
else if (centerX > screenBounds.x + screenBounds.width) {
x = screenBounds.x + screenBounds.width - w;
if (x < 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;
if (centerY < screenBounds.y) {
y = screenBounds.y;
}
else { //ensure the window is accessible
if (centerY < screenBounds.y) {
else if (centerY > screenBounds.y + screenBounds.height) {
y = screenBounds.y + screenBounds.height - h;
if (y < 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);
window.setWindowLayout(x, y, w, h, max, fs);
}
}
}

View File

@@ -49,6 +49,7 @@ public final class LayoutMenu {
}
menu.add(getMenu_ThemeOptions());
menu.addSeparator();
menu.add(getMenuItem_FullScreen());
menu.add(getMenuItem_SetWindowSize());
if (currentScreen != Screens.HOME_SCREEN) {
menu.add(getMenuItem_RevertLayout());
@@ -148,7 +149,6 @@ public final class LayoutMenu {
private static JMenuItem getMenuItem_ShowTitleBar() {
final JCheckBoxMenuItem menuItem = new JCheckBoxMenuItem("Title Bar");
menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F11, 0));
menuItem.setState(!prefs.getPrefBoolean(FPref.UI_HIDE_TITLE_BAR));
menuItem.addActionListener(getShowTitleBarAction(menuItem));
return menuItem;
@@ -262,4 +262,19 @@ public final class LayoutMenu {
};
}
private static JMenuItem getMenuItem_FullScreen() {
final JMenuItem menuItem = new JMenuItem("Full Screen");
menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F11, 0));
menuItem.addActionListener(getFullScreenAction(menuItem));
return menuItem;
}
private static ActionListener getFullScreenAction(final JMenuItem menuItem) {
return new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
final FFrame frame = Singletons.getView().getFrame();
frame.setFullScreen(!frame.isFullScreen());
}
};
}
}

View File

@@ -10,20 +10,20 @@ import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowStateListener;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JRootPane;
import javax.swing.SwingUtilities;
import forge.Singletons;
import forge.gui.framework.SDisplayUtil;
import forge.gui.framework.SResizingUtil;
import forge.gui.toolbox.FSkin;
import forge.gui.toolbox.FSkin.Colors;
import forge.gui.toolbox.FSkin.CompoundSkinBorder;
import forge.gui.toolbox.FSkin.LineSkinBorder;
import forge.properties.ForgePreferences.FPref;
@SuppressWarnings("serial")
public class FFrame extends JFrame {
@@ -33,7 +33,8 @@ public class FFrame extends JFrame {
private Point mouseDownLoc;
private int resizeCursor;
private FTitleBarBase titleBar;
private boolean maximized, showTitleBar, isMainFrame;
private boolean minimized, maximized, fullScreen, hideBorder, showTitleBar, isMainFrame;
private Rectangle normalBounds;
public FFrame() {
setUndecorated(true);
@@ -47,8 +48,14 @@ public class FFrame extends JFrame {
this.isMainFrame = (FView.SINGLETON_INSTANCE.getFrame() == this);
// Frame border
updateBorder();
this.hideBorder = true; //ensure border shown when window layout loaded
addResizeSupport();
this.addWindowStateListener(new WindowStateListener() {
@Override
public void windowStateChanged(WindowEvent arg0) {
setState(arg0.getNewState());
}
});
// Title bar
this.titleBar = titleBar0;
@@ -68,8 +75,8 @@ public class FFrame extends JFrame {
if (this.showTitleBar == showTitleBar0) { return; }
this.showTitleBar = showTitleBar0;
this.titleBar.setVisible(showTitleBar0);
if (!showTitleBar0 && !this.isMaximized()) {
this.setMaximized(true); //only support hidden titlebar if maximized
if (!showTitleBar0 && !this.fullScreen) {
this.setFullScreen(true); //only support hidden titlebar if full screen
}
else if (this.isMainFrame) {
SResizingUtil.resizeWindow(); //ensure window layout updated to account for showing titlebar
@@ -95,36 +102,60 @@ public class FFrame extends JFrame {
//ensure un-maximized if location or size changed
@Override
public void setLocation(Point point) {
this.setMaximized(false);
resetState();
super.setLocation(point);
}
@Override
public void setLocation(int x, int y) {
this.setMaximized(false);
resetState();
super.setLocation(x, y);
}
@Override
public void setSize(Dimension size) {
this.setMaximized(false);
resetState();
super.setSize(size);
}
@Override
public void setSize(int width, int height) {
this.setMaximized(false);
resetState();
super.setSize(width, height);
}
private void resetState() {
if (this.minimized || this.maximized || this.fullScreen) {
this.minimized = false;
this.maximized = false;
this.fullScreen = false;
updateState();
}
}
public void setWindowLayout(int x, int y, int width, int height, boolean maximized0, boolean fullScreen0) {
this.normalBounds = new Rectangle(x, y, width, height);
this.maximized = maximized0;
this.fullScreen = fullScreen0;
updateState();
}
public Rectangle getNormalBounds() {
return this.normalBounds;
}
public void updateNormalBounds() {
if (this.minimized || this.maximized || this.fullScreen) {
return;
}
this.normalBounds = this.getBounds();
}
public boolean isMinimized() {
return getState() == Frame.ICONIFIED;
return this.minimized;
}
public void setMinimized(boolean minimized0) {
if (minimized0) {
setState(Frame.ICONIFIED);
}
else {
setState(Frame.NORMAL);
}
if (this.minimized == minimized0) { return; }
this.minimized = minimized0;
updateState();
}
public boolean isMaximized() {
@@ -133,43 +164,54 @@ public class FFrame extends JFrame {
public void setMaximized(boolean maximized0) {
if (this.maximized == maximized0) { return; }
if (maximized0) {
this.setExtendedState(Frame.MAXIMIZED_BOTH);
}
else {
this.setExtendedState(Frame.NORMAL);
}
this.maximized = maximized0;
updateState();
}
public boolean isFullScreen() {
return this.fullScreen;
}
private void updateMaximizedBounds() {
Rectangle frameBounds = this.getBounds();
Point centerPoint = new Point(frameBounds.x + (frameBounds.width / 2), frameBounds.y + (frameBounds.height / 2));
this.setMaximizedBounds(SDisplayUtil.getScreenMaximizedBounds(centerPoint));
}
@Override
public void setExtendedState(int state) {
updateMaximizedBounds() ; //update maximized bounds whenever extended state changes
super.setExtendedState(state);
if (isMinimized()) { return; } //skip remaining logic while minimized
this.maximized = (state == Frame.MAXIMIZED_BOTH);
if (this.maximized) {
if (this.isMainFrame) { //for main frame, use preference to determine whether to hide title bar when maximizing
this.setShowTitleBar(!Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_HIDE_TITLE_BAR));
}
public void setFullScreen(boolean fullScreen0) {
if (this.fullScreen == fullScreen0) { return; }
this.fullScreen = fullScreen0;
if (!fullScreen0) { //cancel full screen here instead of updateState
SDisplayUtil.setFullScreenWindow(this, false);
}
else { //only support hidden titlebar if maximized
this.setShowTitleBar(true);
updateState();
}
private void updateState() {
if (this.minimized) {
super.setExtendedState(Frame.ICONIFIED);
return;
}
updateBorder();
this.titleBar.handleMaximizedChanged(); //update icon and tooltip for maximize button
super.setExtendedState(Frame.NORMAL);
if (this.fullScreen) {
if (SDisplayUtil.setFullScreenWindow(this, true)) {
return; //nothing else needed if full-screen successful
}
this.fullScreen = false; //reset if full screen failed
updateBorder(); //ensure border updated for non-full screen if needed
}
if (this.maximized) {
this.setBounds(SDisplayUtil.getScreenMaximizedBounds(this.normalBounds));
}
else {
this.setBounds(this.normalBounds);
}
}
private void updateBorder() {
if (this.maximized) {
if (this.minimized || this.hideBorder == (this.maximized || this.fullScreen)) {
return; //don't update border if minimized or border visibility wouldn't change
}
this.hideBorder = !this.hideBorder;
if (this.hideBorder) {
FSkin.get(getRootPane()).removeBorder();
}
else {
@@ -179,6 +221,23 @@ public class FFrame extends JFrame {
}
}
//override normal state behavior
@Override
public void setState(int state) {
setMinimized(state == Frame.ICONIFIED);
if (state == Frame.MAXIMIZED_BOTH) {
this.setMaximized(true);
}
}
//override normal extended state behavior
@Override
public void setExtendedState(int state) {
this.minimized = (state & Frame.ICONIFIED) == Frame.ICONIFIED;
this.maximized = (state & Frame.MAXIMIZED_BOTH) == Frame.MAXIMIZED_BOTH;
updateState();
}
private void addMoveSupport() {
this.titleBar.addMouseListener(new MouseAdapter() {
@Override