diff --git a/src/main/java/forge/control/FControl.java b/src/main/java/forge/control/FControl.java index ff3e7e6d87c..402754a7f68 100644 --- a/src/main/java/forge/control/FControl.java +++ b/src/main/java/forge/control/FControl.java @@ -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(); } }); diff --git a/src/main/java/forge/gui/framework/SDisplayUtil.java b/src/main/java/forge/gui/framework/SDisplayUtil.java index 30e8d5e620b..57853b3217a 100644 --- a/src/main/java/forge/gui/framework/SDisplayUtil.java +++ b/src/main/java/forge/gui/framework/SDisplayUtil.java @@ -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; + } } diff --git a/src/main/java/forge/gui/framework/SLayoutIO.java b/src/main/java/forge/gui/framework/SLayoutIO.java index 9eb7709c877..3b027571d4d 100644 --- a/src/main/java/forge/gui/framework/SLayoutIO.java +++ b/src/main/java/forge/gui/framework/SLayoutIO.java @@ -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); } } } diff --git a/src/main/java/forge/gui/menus/LayoutMenu.java b/src/main/java/forge/gui/menus/LayoutMenu.java index 44e90d5a319..d6fe844da78 100644 --- a/src/main/java/forge/gui/menus/LayoutMenu.java +++ b/src/main/java/forge/gui/menus/LayoutMenu.java @@ -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()); + } + }; + } } diff --git a/src/main/java/forge/view/FFrame.java b/src/main/java/forge/view/FFrame.java index bdd1a76dfce..1047b28e7f0 100644 --- a/src/main/java/forge/view/FFrame.java +++ b/src/main/java/forge/view/FFrame.java @@ -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