diff --git a/.gitattributes b/.gitattributes index 4e14ebe7d3c..01432ca7ab9 100644 --- a/.gitattributes +++ b/.gitattributes @@ -15260,12 +15260,11 @@ src/main/java/forge/gui/match/views/VPicture.java -text src/main/java/forge/gui/match/views/VPlayers.java -text src/main/java/forge/gui/match/views/VStack.java -text src/main/java/forge/gui/match/views/package-info.java svneol=native#text/plain -src/main/java/forge/gui/menubar/FMenuBar.java -text -src/main/java/forge/gui/menubar/IMenuProvider.java -text -src/main/java/forge/gui/menubar/MenuUtil.java -text src/main/java/forge/gui/menus/ForgeMenu.java -text src/main/java/forge/gui/menus/HelpMenu.java -text +src/main/java/forge/gui/menus/IMenuProvider.java -text src/main/java/forge/gui/menus/LayoutMenu.java -text +src/main/java/forge/gui/menus/MenuUtil.java -text src/main/java/forge/gui/package-info.java svneol=native#text/plain src/main/java/forge/gui/toolbox/CardFaceSymbols.java svneol=native#text/plain src/main/java/forge/gui/toolbox/FAbsolutePositioner.java -text @@ -15489,8 +15488,10 @@ src/main/java/forge/util/storage/StorageReaderFolder.java -text src/main/java/forge/util/storage/package-info.java -text src/main/java/forge/view/ButtonUtil.java svneol=native#text/plain src/main/java/forge/view/FFrame.java -text +src/main/java/forge/view/FNavigationBar.java -text src/main/java/forge/view/FStatusBar.java -text src/main/java/forge/view/FTitleBar.java -text +src/main/java/forge/view/FTitleBarBase.java -text src/main/java/forge/view/FView.java -text src/main/java/forge/view/Main.java -text src/main/java/forge/view/SplashFrame.java -text diff --git a/src/main/java/forge/control/FControl.java b/src/main/java/forge/control/FControl.java index cf93b7d88c6..ff3e7e6d87c 100644 --- a/src/main/java/forge/control/FControl.java +++ b/src/main/java/forge/control/FControl.java @@ -64,11 +64,9 @@ import forge.gui.match.controllers.CMessage; import forge.gui.match.controllers.CStack; import forge.gui.match.nonsingleton.VField; import forge.gui.match.views.VAntes; -import forge.gui.menubar.FMenuBar; -import forge.gui.menubar.MenuUtil; +import forge.gui.menus.ForgeMenu; import forge.gui.toolbox.FSkin; import forge.net.FServer; -import forge.properties.ForgePreferences; import forge.properties.ForgePreferences.FPref; import forge.properties.NewConstants; import forge.quest.QuestController; @@ -88,10 +86,11 @@ import forge.view.FView; public enum FControl implements KeyEventDispatcher { instance; - private FMenuBar menuBar; + private ForgeMenu forgeMenu; private List shortcuts; private JLayeredPane display; private Screens state = Screens.UNKNOWN; + private boolean altKeyLastDown; private WindowListener waDefault, waConcede, waLeaveBazaar, waLeaveEditor; @@ -174,7 +173,7 @@ public enum FControl implements KeyEventDispatcher { // Preloads skin components (using progress bar). FSkin.loadFull(true); - createMenuBar(); + forgeMenu = new ForgeMenu(); this.shortcuts = KeyboardShortcuts.attachKeyboardShortcuts(); this.display = FView.SINGLETON_INSTANCE.getLpnDocument(); @@ -217,13 +216,8 @@ public enum FControl implements KeyEventDispatcher { manager.addKeyEventDispatcher(this); } - private void createMenuBar() { - this.menuBar = new FMenuBar(Singletons.getView().getFrame()); - this.menuBar.setVisible(MenuUtil.isMenuBarVisible()); - } - - public FMenuBar getMenuBar() { - return this.menuBar; + public ForgeMenu getForgeMenu() { + return this.forgeMenu; } /** @@ -486,27 +480,33 @@ public enum FControl implements KeyEventDispatcher { */ @Override public boolean dispatchKeyEvent(KeyEvent e) { - // Toggle menu bar visibility using F1 key. - if (e.getKeyCode() == KeyEvent.VK_F1 && e.getID() == KeyEvent.KEY_RELEASED) { - toggleMenuBarVisibility(); - return true; - } else { - //Allow the event to be redispatched - return false; - } - } - - private void toggleMenuBarVisibility() { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - ForgePreferences prefs = Singletons.getModel().getPreferences(); - boolean isHidden = !prefs.getPrefBoolean(FPref.UI_HIDE_MENUBAR); - menuBar.setVisible(!isHidden); - prefs.setPref(FPref.UI_HIDE_MENUBAR, String.valueOf(isHidden)); - prefs.save(); + // Show Forge menu if Alt key pressed without modifiers and released without pressing any other keys in between + if (e.getKeyCode() == KeyEvent.VK_ALT) { + if (e.getID() == KeyEvent.KEY_RELEASED) { + if (altKeyLastDown) { + forgeMenu.show(true); + return true; + } } - }); + else if (e.getID() == KeyEvent.KEY_PRESSED && e.getModifiers() == KeyEvent.ALT_MASK) { + altKeyLastDown = true; + } + } + else { + altKeyLastDown = false; + if (e.getID() == KeyEvent.KEY_PRESSED) { + if (forgeMenu.handleKeyEvent(e)) { //give Forge menu the chance to handle the key event + return true; + } + } + else if (e.getID() == KeyEvent.KEY_RELEASED) { + if (e.getKeyCode() == KeyEvent.VK_CONTEXT_MENU) { + forgeMenu.show(); + } + } + } + //Allow the event to be redispatched + return false; } } diff --git a/src/main/java/forge/gui/SOverlayUtils.java b/src/main/java/forge/gui/SOverlayUtils.java index e9239794890..2cbc18f515f 100644 --- a/src/main/java/forge/gui/SOverlayUtils.java +++ b/src/main/java/forge/gui/SOverlayUtils.java @@ -126,7 +126,7 @@ public final class SOverlayUtils { private static Component prevFocusOwner; public static void showOverlay() { - Singletons.getControl().getMenuBar().setEnabled(false); + Singletons.getControl().getForgeMenu().setEnabled(false); prevFocusOwner = FocusManager.getCurrentKeyboardFocusManager().getPermanentFocusOwner(); FOverlay.SINGLETON_INSTANCE.getPanel().setVisible(true); // ensure no background element has focus @@ -138,7 +138,7 @@ public final class SOverlayUtils { * Removes child components and closes overlay. */ public static void hideOverlay() { - Singletons.getControl().getMenuBar().setEnabled(true); + Singletons.getControl().getForgeMenu().setEnabled(true); FOverlay.SINGLETON_INSTANCE.getPanel().removeAll(); FOverlay.SINGLETON_INSTANCE.getPanel().setVisible(false); if (null != prevFocusOwner) { diff --git a/src/main/java/forge/gui/deckeditor/CDeckEditorUI.java b/src/main/java/forge/gui/deckeditor/CDeckEditorUI.java index e8e2d78b9f4..0468e708650 100644 --- a/src/main/java/forge/gui/deckeditor/CDeckEditorUI.java +++ b/src/main/java/forge/gui/deckeditor/CDeckEditorUI.java @@ -58,7 +58,7 @@ import forge.gui.deckeditor.views.VCardCatalog; import forge.gui.framework.ICDoc; import forge.gui.match.controllers.CDetail; import forge.gui.match.controllers.CPicture; -import forge.gui.menubar.IMenuProvider; +import forge.gui.menus.IMenuProvider; import forge.gui.toolbox.FLabel; import forge.gui.toolbox.FSkin; import forge.gui.toolbox.itemmanager.ItemManager; @@ -554,7 +554,7 @@ public enum CDeckEditorUI implements ICDoc, IMenuProvider { */ @Override public void initialize() { - Singletons.getControl().getMenuBar().setupMenuBar(this); + Singletons.getControl().getForgeMenu().setProvider(this); } /* (non-Javadoc) diff --git a/src/main/java/forge/gui/framework/SResizingUtil.java b/src/main/java/forge/gui/framework/SResizingUtil.java index 098a5707883..5b8fb75c9ce 100644 --- a/src/main/java/forge/gui/framework/SResizingUtil.java +++ b/src/main/java/forge/gui/framework/SResizingUtil.java @@ -19,9 +19,13 @@ import java.util.Set; import javax.swing.JPanel; +import forge.Singletons; import forge.gui.FNetOverlay; import forge.gui.toolbox.FAbsolutePositioner; import forge.gui.toolbox.FOverlay; +import forge.properties.ForgePreferences; +import forge.properties.ForgePreferences.FPref; +import forge.view.FNavigationBar; import forge.view.FStatusBar; import forge.view.FView; @@ -115,19 +119,25 @@ public final class SResizingUtil { public static void resizeWindow() { final List cells = FView.SINGLETON_INSTANCE.getDragCells(); + final FNavigationBar navigationBar = FView.SINGLETON_INSTANCE.getNavigationBar(); final JPanel pnlContent = FView.SINGLETON_INSTANCE.getPnlContent(); final JPanel pnlInsets = FView.SINGLETON_INSTANCE.getPnlInsets(); final FStatusBar statusBar = FView.SINGLETON_INSTANCE.getStatusBar(); - Rectangle mainBounds = FView.SINGLETON_INSTANCE.getFrame().getInnerPane().getContentPane().getBounds(); + Rectangle mainBounds = FView.SINGLETON_INSTANCE.getFrame().getContentPane().getBounds(); mainBounds.y = 0; // Play nicely with MenuBar if visible or not. FAbsolutePositioner.SINGLETON_INSTANCE.containerResized(mainBounds); FOverlay.SINGLETON_INSTANCE.getPanel().setBounds(mainBounds); FNetOverlay.SINGLETON_INSTANCE.containerResized(mainBounds); - final int statusBarHeight = statusBar.getPreferredSize().height; + final ForgePreferences prefs = Singletons.getModel().getPreferences(); + final int navigationBarHeight = prefs.getPrefBoolean(FPref.UI_HIDE_TITLE_BAR) ? 0 : navigationBar.getPreferredSize().height; + final int statusBarHeight = prefs.getPrefBoolean(FPref.UI_HIDE_STATUS_BAR) ? 0 : statusBar.getPreferredSize().height; - pnlInsets.setBounds(mainBounds.x, mainBounds.y, mainBounds.width, mainBounds.height - statusBarHeight); + navigationBar.setBounds(mainBounds.x, mainBounds.y, mainBounds.width, navigationBarHeight); + navigationBar.validate(); + + pnlInsets.setBounds(mainBounds.x, mainBounds.y + navigationBarHeight, mainBounds.width, mainBounds.height - navigationBarHeight - statusBarHeight); pnlInsets.validate(); statusBar.setBounds(mainBounds.x, mainBounds.y + mainBounds.height - statusBarHeight, mainBounds.width, statusBarHeight); diff --git a/src/main/java/forge/gui/home/CHomeUI.java b/src/main/java/forge/gui/home/CHomeUI.java index 12c46a6fe52..bc8282db12d 100644 --- a/src/main/java/forge/gui/home/CHomeUI.java +++ b/src/main/java/forge/gui/home/CHomeUI.java @@ -13,8 +13,8 @@ import forge.gui.deckeditor.controllers.CEditorConstructed; import forge.gui.framework.EDocID; import forge.gui.framework.ICDoc; import forge.gui.home.sanctioned.VSubmenuConstructed; -import forge.gui.menubar.IMenuProvider; -import forge.gui.menubar.MenuUtil; +import forge.gui.menus.IMenuProvider; +import forge.gui.menus.MenuUtil; import forge.net.FServer; import forge.net.NetServer; import forge.properties.ForgePreferences; @@ -47,7 +47,7 @@ public enum CHomeUI implements ICDoc, IMenuProvider { if (previousDoc != null) { if (!previousDoc.equals(id0.getDoc().getLayoutControl())) { - MenuUtil.setupMenuBar(null); + MenuUtil.setMenuProvider(null); } } @@ -124,7 +124,7 @@ public enum CHomeUI implements ICDoc, IMenuProvider { } private void setupMyMenuBar() { - Singletons.getControl().getMenuBar().setupMenuBar(this); + Singletons.getControl().getForgeMenu().setProvider(this); } /* (non-Javadoc) diff --git a/src/main/java/forge/gui/home/sanctioned/CSubmenuConstructed.java b/src/main/java/forge/gui/home/sanctioned/CSubmenuConstructed.java index 4414a23c9b6..b12f36741c9 100644 --- a/src/main/java/forge/gui/home/sanctioned/CSubmenuConstructed.java +++ b/src/main/java/forge/gui/home/sanctioned/CSubmenuConstructed.java @@ -20,8 +20,8 @@ import forge.game.player.LobbyPlayer; import forge.gui.SOverlayUtils; import forge.gui.deckchooser.DecksComboBox.DeckType; import forge.gui.framework.ICDoc; -import forge.gui.menubar.IMenuProvider; -import forge.gui.menubar.MenuUtil; +import forge.gui.menus.IMenuProvider; +import forge.gui.menus.MenuUtil; import forge.gui.toolbox.FComboBox; import forge.net.FServer; import forge.net.Lobby; @@ -65,7 +65,7 @@ public enum CSubmenuConstructed implements ICDoc, IMenuProvider { @Override public void update() { - MenuUtil.setupMenuBar(this); + MenuUtil.setMenuProvider(this); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { view.getBtnStart().requestFocusInWindow(); } diff --git a/src/main/java/forge/gui/home/sanctioned/ConstructedGameMenu.java b/src/main/java/forge/gui/home/sanctioned/ConstructedGameMenu.java index 0dacbc6c81b..359e25796b3 100644 --- a/src/main/java/forge/gui/home/sanctioned/ConstructedGameMenu.java +++ b/src/main/java/forge/gui/home/sanctioned/ConstructedGameMenu.java @@ -9,7 +9,7 @@ import javax.swing.JMenu; import javax.swing.JMenuItem; import forge.Singletons; -import forge.gui.menubar.MenuUtil; +import forge.gui.menus.MenuUtil; import forge.properties.ForgePreferences; import forge.properties.ForgePreferences.FPref; diff --git a/src/main/java/forge/gui/match/CMatchUI.java b/src/main/java/forge/gui/match/CMatchUI.java index b52db1a3b47..86254444e50 100644 --- a/src/main/java/forge/gui/match/CMatchUI.java +++ b/src/main/java/forge/gui/match/CMatchUI.java @@ -60,7 +60,7 @@ import forge.gui.match.nonsingleton.VCommand; import forge.gui.match.nonsingleton.VField; import forge.gui.match.nonsingleton.VHand; import forge.gui.match.views.VPlayers; -import forge.gui.menubar.IMenuProvider; +import forge.gui.menus.IMenuProvider; import forge.gui.toolbox.FSkin; import forge.gui.toolbox.FSkin.SkinImage; import forge.gui.toolbox.special.PhaseLabel; @@ -422,7 +422,7 @@ public enum CMatchUI implements ICDoc, IMenuProvider { */ @Override public void initialize() { - Singletons.getControl().getMenuBar().setupMenuBar(this); + Singletons.getControl().getForgeMenu().setProvider(this); } /* (non-Javadoc) diff --git a/src/main/java/forge/gui/match/menus/CardOverlaysMenu.java b/src/main/java/forge/gui/match/menus/CardOverlaysMenu.java index 39aa1c8a1a6..caa4989a4ad 100644 --- a/src/main/java/forge/gui/match/menus/CardOverlaysMenu.java +++ b/src/main/java/forge/gui/match/menus/CardOverlaysMenu.java @@ -13,7 +13,7 @@ import javax.swing.SwingUtilities; import forge.Singletons; import forge.gui.match.CMatchUI; -import forge.gui.menubar.MenuUtil; +import forge.gui.menus.MenuUtil; import forge.properties.ForgePreferences; import forge.properties.ForgePreferences.FPref; diff --git a/src/main/java/forge/gui/match/menus/DevModeMenu.java b/src/main/java/forge/gui/match/menus/DevModeMenu.java index 520dc41d519..2c664a03118 100644 --- a/src/main/java/forge/gui/match/menus/DevModeMenu.java +++ b/src/main/java/forge/gui/match/menus/DevModeMenu.java @@ -12,7 +12,7 @@ import javax.swing.event.MenuListener; import forge.Singletons; import forge.gui.match.controllers.CDev; -import forge.gui.menubar.MenuUtil; +import forge.gui.menus.MenuUtil; import forge.properties.ForgePreferences; import forge.properties.ForgePreferences.FPref; @@ -93,11 +93,11 @@ public class DevModeMenu implements ActionListener { return new MenuListener() { @Override public void menuSelected(MenuEvent arg0) { - Singletons.getControl().getMenuBar().setStatusText("Options for testing during development"); + Singletons.getView().getStatusBar().setStatusText("Options for testing during development"); } @Override public void menuDeselected(MenuEvent arg0) { - Singletons.getControl().getMenuBar().setStatusText(""); + Singletons.getView().getStatusBar().setStatusText(""); } @Override public void menuCanceled(MenuEvent arg0) { } diff --git a/src/main/java/forge/gui/match/menus/GameMenu.java b/src/main/java/forge/gui/match/menus/GameMenu.java index f15169dea70..3afcf0b8767 100644 --- a/src/main/java/forge/gui/match/menus/GameMenu.java +++ b/src/main/java/forge/gui/match/menus/GameMenu.java @@ -13,7 +13,7 @@ import javax.swing.JRadioButtonMenuItem; import forge.Singletons; import forge.gui.match.controllers.CDock; -import forge.gui.menubar.MenuUtil; +import forge.gui.menus.MenuUtil; import forge.gui.toolbox.FSkin; import forge.gui.toolbox.FSkin.SkinIcon; import forge.gui.toolbox.FSkin.SkinProp; diff --git a/src/main/java/forge/gui/menubar/FMenuBar.java b/src/main/java/forge/gui/menubar/FMenuBar.java deleted file mode 100644 index 0e2c8b02609..00000000000 --- a/src/main/java/forge/gui/menubar/FMenuBar.java +++ /dev/null @@ -1,120 +0,0 @@ -package forge.gui.menubar; - -import java.awt.Component; -import java.awt.Dimension; -import java.util.List; - -import javax.swing.Box; -import javax.swing.JLabel; -import javax.swing.JMenu; -import javax.swing.JMenuBar; - -import forge.gui.menus.ForgeMenu; -import forge.gui.menus.HelpMenu; -import forge.gui.menus.LayoutMenu; -import forge.gui.toolbox.FSkin; -import forge.gui.toolbox.FSkin.JLabelSkin; -import forge.view.FFrame; - -@SuppressWarnings("serial") -public class FMenuBar extends JMenuBar { - private final FFrame frame; - private String statusText; - private JLabel lblStatus; - private IMenuProvider provider; - - public FMenuBar(FFrame f) { - this.frame = f; - setVisible(false); //hide by default until prefs decide whether to show it - super.setVisible(true); //ensure super is visible since setVisible overriden to only set height to 0 to hide - f.getInnerPane().setJMenuBar(this); - refresh(); - setStatusText(""); //set default status text - } - - public void setupMenuBar(IMenuProvider provider0) { - provider = provider0; - refresh(); - } - - public void refresh() { - removeAll(); - add(ForgeMenu.getMenu()); - addProviderMenus(); - add(LayoutMenu.getMenu()); - add(HelpMenu.getMenu()); - addStatusLabel(); - repaintMenuBar(); - } - - /** - * Ensures that MenuBar is repainted correctly. - *

- * !! Both statements are required - do not alter !! - */ - private void repaintMenuBar() { - validate(); - repaint(); - } - - /** - * Adds a label to the right-hand side of the MenuBar which can - * be used to show hints or status information. - */ - private void addStatusLabel() { - add(Box.createHorizontalGlue()); // align right hack/patch. - lblStatus = new JLabel(statusText); - JLabelSkin labelSkin = FSkin.get(lblStatus); - if (FSkin.isLookAndFeelSet()) { - labelSkin.setForeground(FSkin.getColor(FSkin.Colors.CLR_TEXT)); - } - else { //ensure status is visible on default menu bar - labelSkin.setForeground(getForeground()); - } - labelSkin.setFont(FSkin.getItalicFont(11)); - lblStatus.setOpaque(false); - add(lblStatus); - } - - public void setStatusText(String text) { - statusText = text.trim(); - if (statusText.isEmpty()) { - statusText = "F1 : hide menu"; //show shortcut to hide menu if no other status to show - } - statusText += " "; //add padding from right edge of menu bar - lblStatus.setText(statusText); - } - - private void addProviderMenus() { - if (provider != null) { - List menus = provider.getMenus(); - if (menus != null) { - for (JMenu m : menus) { - m.setBorderPainted(false); - add(m); - } - } - } - } - - /* (non-Javadoc) - * @see javax.swing.JComponent#setEnabled(boolean) - */ - @Override - public void setEnabled(boolean enabled) { - super.setEnabled(enabled); - for (Component c : getComponents()) { - c.setEnabled(enabled); - } - } - - /* (non-Javadoc) - * @see javax.swing.JComponent#setVisible(boolean) - */ - @Override - public void setVisible(boolean visible) { - //use height 0 to hide rather than setting visible to false to allow menu item accelerators to work - setPreferredSize(new Dimension(this.frame.getWidth(), visible ? 22 : 0)); - revalidate(); - } -} diff --git a/src/main/java/forge/gui/menus/ForgeMenu.java b/src/main/java/forge/gui/menus/ForgeMenu.java index 50019ff512c..b337e91c2e1 100644 --- a/src/main/java/forge/gui/menus/ForgeMenu.java +++ b/src/main/java/forge/gui/menus/ForgeMenu.java @@ -1,49 +1,110 @@ package forge.gui.menus; +import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; +import java.util.HashMap; +import java.util.List; + import javax.swing.JMenu; import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.KeyStroke; + import forge.Singletons; import forge.control.RestartUtil; import forge.control.FControl.Screens; -import forge.gui.menubar.MenuUtil; +import forge.util.TypeUtil; -public final class ForgeMenu { - private ForgeMenu() { } - - public static JMenu getMenu() { - JMenu menu = new JMenu("Forge"); - menu.setMnemonic(KeyEvent.VK_F); - menu.add(getMenuItem_Restart()); - menu.add(getMenuItem_Exit()); - return menu; +@SuppressWarnings("serial") +public class ForgeMenu extends JPopupMenu { + private static final int minItemWidth = 100; + private static final int itemHeight = 25; + + private IMenuProvider provider; + private static HashMap activeShortcuts = new HashMap(); + + public ForgeMenu() { + refresh(); } - private static JMenuItem getMenuItem_Exit() { - JMenuItem menuItem = new JMenuItem("Exit"); - menuItem.addActionListener(getExitAction()); - return menuItem; + @Override + public void show() { + show(false); } - private static ActionListener getExitAction() { - return new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - if (!isHomeScreenActive()) { - String userPrompt = "Please confirm you want to close Forge.\n\n"; - if (!MenuUtil.getUserConfirmation(userPrompt, "Exit Forge")) { - return; - } + @Override + public void show(boolean hideIfAlreadyShown) { + Singletons.getView().getNavigationBar().showForgeMenu(hideIfAlreadyShown); + } + + public void setProvider(IMenuProvider provider0) { + provider = provider0; + refresh(); + } + + public void refresh() { + activeShortcuts.clear(); + removeAll(); + if (provider != null) { + List menus = provider.getMenus(); + if (menus != null) { + for (JMenu m : menus) { + m.setBorderPainted(false); + add(m); } - System.exit(0); } - }; + } + add(LayoutMenu.getMenu()); + add(HelpMenu.getMenu()); + addSeparator(); + add(getMenuItem_Restart()); + add(getMenuItem_Exit()); + } + + @Override + public JMenuItem add(JMenuItem item) { + item = super.add(item); + setupItem(item); + return item; + } + + private void setupMenu(JMenu menu) { + for (int i = 0; i < menu.getItemCount(); i++) { + setupItem(menu.getItem(i)); + } + } + + private void setupItem(JMenuItem item) { + if (item == null) { return; } + + item.setPreferredSize(new Dimension(Math.max(item.getPreferredSize().width, minItemWidth), itemHeight)); + + KeyStroke shortcut = item.getAccelerator(); + if (shortcut != null) { + activeShortcuts.put(shortcut, item); + } + + JMenu subMenu = TypeUtil.safeCast(item, JMenu.class); + if (subMenu != null) { + setupMenu(subMenu); + } + } + + public boolean handleKeyEvent(KeyEvent e) { + JMenuItem item = activeShortcuts.get(KeyStroke.getKeyStrokeForEvent(e)); + if (item != null) { + setVisible(false); //ensure menu doesn't stay open if currently open + item.doClick(); + return true; + } + return false; } private static JMenuItem getMenuItem_Restart() { - JMenuItem menuItem = new JMenuItem("Restart"); + JMenuItem menuItem = new JMenuItem("Restart"); + menuItem.setMnemonic(KeyEvent.VK_R); menuItem.addActionListener(getRestartAction()); return menuItem; } @@ -63,8 +124,29 @@ public final class ForgeMenu { }; } + private static JMenuItem getMenuItem_Exit() { + JMenuItem menuItem = new JMenuItem("Exit"); + menuItem.setMnemonic(KeyEvent.VK_X); + menuItem.addActionListener(getExitAction()); + return menuItem; + } + + private static ActionListener getExitAction() { + return new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (!isHomeScreenActive()) { + String userPrompt = "Please confirm you want to close Forge.\n\n"; + if (!MenuUtil.getUserConfirmation(userPrompt, "Exit Forge")) { + return; + } + } + System.exit(0); + } + }; + } + private static boolean isHomeScreenActive() { return Singletons.getControl().getState() == Screens.HOME_SCREEN; } - } diff --git a/src/main/java/forge/gui/menus/HelpMenu.java b/src/main/java/forge/gui/menus/HelpMenu.java index b771160fb6d..1d864dd13cb 100644 --- a/src/main/java/forge/gui/menus/HelpMenu.java +++ b/src/main/java/forge/gui/menus/HelpMenu.java @@ -9,8 +9,8 @@ import java.io.IOException; import javax.swing.JMenu; import javax.swing.JMenuItem; +import javax.swing.KeyStroke; -import forge.gui.menubar.MenuUtil; import forge.util.FileUtil; public final class HelpMenu { @@ -47,7 +47,7 @@ public final class HelpMenu { JMenu mnu = new JMenu("Getting Started"); mnu.add(getMenuItem_HowToPlayFile()); mnu.addSeparator(); - mnu.add(getMenuItem_UrlLink("Forge Wiki", "http://www.slightlymagic.net/wiki/Forge")); + mnu.add(getMenuItem_UrlLink("Forge Wiki", "http://www.slightlymagic.net/wiki/Forge", KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0))); mnu.add(getMenuItem_UrlLink("What is Forge?", "http://www.slightlymagic.net/forum/viewtopic.php?f=26&t=468")); return mnu; } @@ -112,14 +112,19 @@ public final class HelpMenu { Desktop.getDesktop().open(file); } } - - + private static JMenuItem getMenuItem_UrlLink(String caption, String url) { JMenuItem menuItem = new JMenuItem(caption); menuItem.addActionListener(getLaunchUrlAction(url)); return menuItem; } + private static JMenuItem getMenuItem_UrlLink(String caption, String url, KeyStroke accelerator) { + JMenuItem menuItem = getMenuItem_UrlLink(caption, url); + menuItem.setAccelerator(accelerator); + return menuItem; + } + private static ActionListener getLaunchUrlAction(final String url) { return new ActionListener() { @Override diff --git a/src/main/java/forge/gui/menubar/IMenuProvider.java b/src/main/java/forge/gui/menus/IMenuProvider.java similarity index 92% rename from src/main/java/forge/gui/menubar/IMenuProvider.java rename to src/main/java/forge/gui/menus/IMenuProvider.java index 061aacf03c3..b43e575c558 100644 --- a/src/main/java/forge/gui/menubar/IMenuProvider.java +++ b/src/main/java/forge/gui/menus/IMenuProvider.java @@ -1,4 +1,4 @@ -package forge.gui.menubar; +package forge.gui.menus; import java.util.List; diff --git a/src/main/java/forge/gui/menus/LayoutMenu.java b/src/main/java/forge/gui/menus/LayoutMenu.java index e009fdc8139..7450e7135e8 100644 --- a/src/main/java/forge/gui/menus/LayoutMenu.java +++ b/src/main/java/forge/gui/menus/LayoutMenu.java @@ -17,8 +17,8 @@ import forge.control.FControl.Screens; import forge.gui.GuiChoose; import forge.gui.MouseUtil; import forge.gui.MouseUtil.MouseCursor; +import forge.gui.framework.SResizingUtil; import forge.gui.match.controllers.CDock; -import forge.gui.menubar.MenuUtil; import forge.gui.toolbox.FSkin; import forge.properties.ForgePreferences; import forge.properties.ForgePreferences.FPref; @@ -32,9 +32,9 @@ import forge.view.FView; public final class LayoutMenu { private LayoutMenu() { } - private static CDock controller = CDock.SINGLETON_INSTANCE; + private static final CDock controller = CDock.SINGLETON_INSTANCE; private static Screens currentScreen; - private static ForgePreferences prefs = Singletons.getModel().getPreferences(); + private static final ForgePreferences prefs = Singletons.getModel().getPreferences(); private static boolean showIcons = false; public static JMenu getMenu() { @@ -58,6 +58,7 @@ public final class LayoutMenu { private static JMenu getMenu_ViewOptions() { JMenu menu = new JMenu("View"); menu.add(getMenuItem_ShowTitleBar()); + menu.add(getMenuItem_ShowStatusBar()); if (currentScreen != Screens.HOME_SCREEN) { menu.add(getMenuItem_ShowTabs()); } @@ -139,14 +140,15 @@ public final class LayoutMenu { boolean showTabs = menuItem.getState(); FView.SINGLETON_INSTANCE.refreshAllCellLayouts(showTabs); prefs.setPref(FPref.UI_HIDE_GAME_TABS, !showTabs); + prefs.save(); } }; } private static JMenuItem getMenuItem_ShowTitleBar() { - final JCheckBoxMenuItem menuItem = new JCheckBoxMenuItem("Titlebar"); + final JCheckBoxMenuItem menuItem = new JCheckBoxMenuItem("Title Bar"); menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F11, 0)); - menuItem.setState(Singletons.getView().getFrame().getShowTitleBar()); + menuItem.setState(!prefs.getPrefBoolean(FPref.UI_HIDE_TITLE_BAR)); menuItem.addActionListener(getShowTitleBarAction(menuItem)); return menuItem; } @@ -155,10 +157,32 @@ public final class LayoutMenu { @Override public void actionPerformed(ActionEvent e) { boolean showTitleBar = menuItem.getState(); + prefs.setPref(FPref.UI_HIDE_TITLE_BAR, !showTitleBar); + prefs.save(); Singletons.getView().getFrame().setShowTitleBar(showTitleBar); } }; } + + private static JMenuItem getMenuItem_ShowStatusBar() { + final JCheckBoxMenuItem menuItem = new JCheckBoxMenuItem("Status Bar"); + menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F12, 0)); + menuItem.setState(!prefs.getPrefBoolean(FPref.UI_HIDE_STATUS_BAR)); + menuItem.addActionListener(getShowStatusBarAction(menuItem)); + return menuItem; + } + private static ActionListener getShowStatusBarAction(final JCheckBoxMenuItem menuItem) { + return new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + boolean showStatusBar = menuItem.getState(); + prefs.setPref(FPref.UI_HIDE_STATUS_BAR, !showStatusBar); + prefs.save(); + Singletons.getView().getStatusBar().setVisible(showStatusBar); + SResizingUtil.resizeWindow(); + } + }; + } private static JMenuItem getMenuItem_SaveLayout() { JMenuItem menuItem = new JMenuItem("Save Current Layout"); diff --git a/src/main/java/forge/gui/menubar/MenuUtil.java b/src/main/java/forge/gui/menus/MenuUtil.java similarity index 74% rename from src/main/java/forge/gui/menubar/MenuUtil.java rename to src/main/java/forge/gui/menus/MenuUtil.java index a45c06b4ae4..f6c333d2a92 100644 --- a/src/main/java/forge/gui/menubar/MenuUtil.java +++ b/src/main/java/forge/gui/menus/MenuUtil.java @@ -1,4 +1,4 @@ -package forge.gui.menubar; +package forge.gui.menus; import java.awt.Toolkit; import java.io.IOException; @@ -14,21 +14,12 @@ import forge.Singletons; import forge.gui.toolbox.FSkin; import forge.gui.toolbox.FSkin.SkinProp; import forge.gui.toolbox.imaging.ImageUtil; -import forge.properties.ForgePreferences; -import forge.properties.ForgePreferences.FPref; public final class MenuUtil { private MenuUtil() { } - private static ForgePreferences prefs = Singletons.getModel().getPreferences(); - // Get appropriate OS standard accelerator key for menu shortcuts. - private static final int DEFAULT_MenuShortcutKeyMask = - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); - - public static boolean isMenuBarVisible() { - return !prefs.getPrefBoolean(FPref.UI_HIDE_MENUBAR); - } + private static final int DEFAULT_MenuShortcutKeyMask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); public static void openUrlInBrowser(String url) { try { @@ -61,8 +52,8 @@ public final class MenuUtil { return (reply == JOptionPane.YES_OPTION); } - public static void setupMenuBar(IMenuProvider provider) { - Singletons.getControl().getMenuBar().setupMenuBar(provider); + public static void setMenuProvider(IMenuProvider provider) { + Singletons.getControl().getForgeMenu().setProvider(provider); } public static void setMenuHint(final JMenuItem menu, final String hint) { menu.addChangeListener(new ChangeListener() { @@ -70,9 +61,9 @@ public final class MenuUtil { public void stateChanged(ChangeEvent e) { JMenuItem item = (JMenuItem) e.getSource(); if (item.isArmed() || (item.isSelected() && e.getSource() instanceof JMenu)) { - Singletons.getControl().getMenuBar().setStatusText(hint); + Singletons.getView().getStatusBar().setStatusText(hint); } else { - Singletons.getControl().getMenuBar().setStatusText(""); + Singletons.getView().getStatusBar().setStatusText(""); } } }); diff --git a/src/main/java/forge/gui/toolbox/FSkin.java b/src/main/java/forge/gui/toolbox/FSkin.java index dc731135880..5e68569a0fe 100644 --- a/src/main/java/forge/gui/toolbox/FSkin.java +++ b/src/main/java/forge/gui/toolbox/FSkin.java @@ -1631,7 +1631,7 @@ public enum FSkin { ComponentSkin.reapplyAll(); //refresh certain components skinned via look and feel - Singletons.getControl().getMenuBar().refresh(); + Singletons.getControl().getForgeMenu().refresh(); FComboBoxWrapper.refreshAllSkins(); FComboBoxPanel.refreshAllSkins(); CSubmenuPreferences.SINGLETON_INSTANCE.updateCurrentSkin(); diff --git a/src/main/java/forge/properties/ForgePreferences.java b/src/main/java/forge/properties/ForgePreferences.java index baa00599e1e..5e40dee5996 100644 --- a/src/main/java/forge/properties/ForgePreferences.java +++ b/src/main/java/forge/properties/ForgePreferences.java @@ -66,7 +66,8 @@ public class ForgePreferences extends PreferencesStore { UI_CLONE_MODE_SOURCE ("false"), /** */ UI_MATCH_IMAGE_VISIBLE ("true"), UI_THEMED_COMBOBOX ("true"), - UI_HIDE_MENUBAR ("false"), // Dev setting only - cannot be set from GUI. + UI_HIDE_TITLE_BAR ("false"), + UI_HIDE_STATUS_BAR ("false"), UI_HIDE_GAME_TABS ("false"), // Visibility of tabs in match screen. UI_FOR_TOUCHSCREN("false"), diff --git a/src/main/java/forge/view/FFrame.java b/src/main/java/forge/view/FFrame.java index b90780aba91..509ec26b104 100644 --- a/src/main/java/forge/view/FFrame.java +++ b/src/main/java/forge/view/FFrame.java @@ -15,6 +15,7 @@ import javax.swing.JFrame; import javax.swing.JRootPane; import javax.swing.SwingUtilities; +import forge.gui.framework.SResizingUtil; import forge.gui.toolbox.FSkin; import forge.gui.toolbox.FSkin.Colors; import forge.gui.toolbox.FSkin.CompoundSkinBorder; @@ -27,27 +28,29 @@ public class FFrame extends JFrame { private Dimension sizeBeforeResize; private Point mouseDownLoc; private int resizeCursor; - private FTitleBar titleBar; + private FTitleBarBase titleBar; private boolean maximized, showTitleBar; - private final JRootPane innerPane = new JRootPane(); public FFrame() { setUndecorated(true); - setContentPane(innerPane); } public void initialize() { + initialize(new FTitleBar(this), true); //show titlebar by default + } + + public void initialize(FTitleBarBase titleBar0, boolean showTitleBar0) { // Frame border updateBorder(); addResizeSupport(); // Title bar - this.titleBar = new FTitleBar(this); - this.setShowTitleBar(true); //show titlebar by default + this.titleBar = titleBar0; + this.setShowTitleBar(showTitleBar0); addMoveSupport(); } - public FTitleBar getTitleBar() { + public FTitleBarBase getTitleBar() { return this.titleBar; } @@ -59,9 +62,12 @@ public class FFrame extends JFrame { if (this.showTitleBar == showTitleBar0) { return; } this.showTitleBar = showTitleBar0; this.titleBar.setVisible(showTitleBar0); - if (!showTitleBar0) { + if (!showTitleBar0 && !this.getMaximized()) { this.setMaximized(true); //only support hidden titlebar if maximized } + else { + SResizingUtil.resizeWindow(); //ensure window layout updated to account for showing titlebar + } } @Override @@ -152,10 +158,6 @@ public class FFrame extends JFrame { } } - public JRootPane getInnerPane() { - return innerPane; - } - private void addMoveSupport() { this.titleBar.addMouseListener(new MouseAdapter() { @Override diff --git a/src/main/java/forge/view/FNavigationBar.java b/src/main/java/forge/view/FNavigationBar.java new file mode 100644 index 00000000000..e71b181f9f2 --- /dev/null +++ b/src/main/java/forge/view/FNavigationBar.java @@ -0,0 +1,86 @@ +package forge.view; + +import java.awt.Dimension; +import java.awt.Image; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +import javax.swing.SpringLayout; +import javax.swing.event.PopupMenuEvent; +import javax.swing.event.PopupMenuListener; + +import forge.Singletons; +import forge.gui.menus.ForgeMenu; +import forge.gui.toolbox.FButton; +import forge.gui.toolbox.FSkin; + +@SuppressWarnings("serial") +public class FNavigationBar extends FTitleBarBase { + + private final FButton btnForge = new FButton("Forge"); + private final ForgeMenu forgeMenu = Singletons.getControl().getForgeMenu(); + private long timeMenuHidden = 0; + + public FNavigationBar(FFrame f) { + super(f); + btnForge.setFocusable(false); + btnForge.setPreferredSize(new Dimension(100, 23)); + setIconImage(this.frame.getIconImage()); //set default icon image based on frame icon image + FSkin.get(btnForge).setForeground(foreColor); + addControls(); + } + + @Override + protected void addControls() { + add(btnForge); + layout.putConstraint(SpringLayout.WEST, btnForge, 1, SpringLayout.WEST, this); + layout.putConstraint(SpringLayout.NORTH, btnForge, 1, SpringLayout.NORTH, this); + addForgeButtonListeners(); + super.addControls(); + } + + private void addForgeButtonListeners() { + btnForge.addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + if (System.currentTimeMillis() - timeMenuHidden > 250) { //time comparsion needed clicking button a second time to hide menu + showForgeMenu(true); + } + } + }); + forgeMenu.addPopupMenuListener(new PopupMenuListener() { + @Override + public void popupMenuWillBecomeInvisible(PopupMenuEvent popupMenuEvent) { + btnForge.setToggled(false); + timeMenuHidden = System.currentTimeMillis(); + } + @Override + public void popupMenuWillBecomeVisible(PopupMenuEvent popupMenuEvent) {} + @Override + public void popupMenuCanceled(PopupMenuEvent popupMenuEvent) {} + }); + } + + public void showForgeMenu(boolean hideIfAlreadyShown) { + if (!btnForge.isToggled()) { + btnForge.setToggled(true); + forgeMenu.show(this, 1, this.getHeight()); + } + else if (hideIfAlreadyShown) { + forgeMenu.setVisible(false); + } + } + + @Override + public String getTitle() { + return "Forge"; + } + + @Override + public void setTitle(String title) { + } + + @Override + public void setIconImage(Image image) { + } +} diff --git a/src/main/java/forge/view/FStatusBar.java b/src/main/java/forge/view/FStatusBar.java index 74998f1abb2..aab73d03d2a 100644 --- a/src/main/java/forge/view/FStatusBar.java +++ b/src/main/java/forge/view/FStatusBar.java @@ -25,7 +25,7 @@ public class FStatusBar extends JPanel { private final JLabel lblStatus = new JLabel(); private final FDigitalClock clock = new FDigitalClock(); - public FStatusBar(FFrame f) { + public FStatusBar(FFrame f, boolean visible0) { this.frame = f; setPreferredSize(new Dimension(f.getWidth(), 18)); setLayout(this.layout); @@ -42,6 +42,8 @@ public class FStatusBar extends JPanel { add(clock); layout.putConstraint(SpringLayout.EAST, clock, -4, SpringLayout.EAST, this); layout.putConstraint(SpringLayout.NORTH, clock, 0, SpringLayout.NORTH, lblStatus); + + this.setVisible(visible0); } public void setStatusText(String text) { diff --git a/src/main/java/forge/view/FTitleBar.java b/src/main/java/forge/view/FTitleBar.java index 94d9a3c9353..7f05a912e97 100644 --- a/src/main/java/forge/view/FTitleBar.java +++ b/src/main/java/forge/view/FTitleBar.java @@ -1,87 +1,43 @@ package forge.view; -import java.awt.BasicStroke; -import java.awt.Dimension; -import java.awt.Graphics; -import java.awt.Graphics2D; import java.awt.Image; -import java.awt.RenderingHints; -import java.awt.Toolkit; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.event.WindowEvent; - import javax.swing.ImageIcon; import javax.swing.JLabel; -import javax.swing.JMenuBar; import javax.swing.SpringLayout; -import javax.swing.SwingUtilities; - -import forge.gui.framework.ILocalRepaint; import forge.gui.toolbox.FSkin; -import forge.gui.toolbox.FSkin.Colors; -import forge.gui.toolbox.FSkin.JLabelSkin; -import forge.gui.toolbox.FSkin.SkinColor; @SuppressWarnings("serial") -public class FTitleBar extends JMenuBar { - private static final SkinColor foreColor = FSkin.getColor(Colors.CLR_TEXT); - private static final SkinColor backColor = FSkin.getColor(Colors.CLR_THEME2); - private static final SkinColor buttonHoverColor = backColor.stepColor(40); - private static final SkinColor buttonDownColor = backColor.stepColor(-40); - - private final FFrame frame; - private final SpringLayout layout = new SpringLayout(); +public class FTitleBar extends FTitleBarBase { private final JLabel lblTitle = new JLabel(); - private final MinimizeButton btnMinimize = new MinimizeButton(); - private final MaximizeButton btnMaximize = new MaximizeButton(); - private final CloseButton btnClose = new CloseButton(); - + public FTitleBar(FFrame f) { - this.frame = f; + super(f); f.setJMenuBar(this); - setPreferredSize(new Dimension(f.getWidth(), 26)); - setLayout(this.layout); - FSkin.get(this).setBackground(backColor); setTitle(f.getTitle()); //set default title based on frame title setIconImage(f.getIconImage()); //set default icon image based on frame icon image FSkin.get(lblTitle).setForeground(foreColor); - + addControls(); + } + + @Override + protected void addControls() { add(lblTitle); layout.putConstraint(SpringLayout.WEST, lblTitle, 1, SpringLayout.WEST, this); layout.putConstraint(SpringLayout.NORTH, lblTitle, 4, SpringLayout.NORTH, this); - - add(btnClose); - layout.putConstraint(SpringLayout.EAST, btnClose, 0, SpringLayout.EAST, this); - layout.putConstraint(SpringLayout.NORTH, btnClose, 0, SpringLayout.NORTH, this); - - add(btnMaximize); - layout.putConstraint(SpringLayout.EAST, btnMaximize, 0, SpringLayout.WEST, btnClose); - layout.putConstraint(SpringLayout.NORTH, btnMaximize, 0, SpringLayout.NORTH, btnClose); - - add(btnMinimize); - layout.putConstraint(SpringLayout.EAST, btnMinimize, 0, SpringLayout.WEST, btnMaximize); - layout.putConstraint(SpringLayout.NORTH, btnMinimize, 0, SpringLayout.NORTH, btnMaximize); - } - - public void handleMaximizedChanged() { - if (frame.getMaximized()) { - btnMaximize.setToolTipText("Restore Down"); - } - else { - btnMaximize.setToolTipText("Maximize"); - } - btnMaximize.repaintSelf(); + super.addControls(); } + @Override public String getTitle() { return this.lblTitle.getText(); } + @Override public void setTitle(String title) { this.lblTitle.setText(title); } + @Override public void setIconImage(Image image) { if (image != null) { this.lblTitle.setIcon(new ImageIcon(image.getScaledInstance(16, 16, Image.SCALE_AREA_AVERAGING))); @@ -90,168 +46,4 @@ public class FTitleBar extends JMenuBar { this.lblTitle.setIcon(null); } } - - public abstract class TitleBarButton extends JLabel implements ILocalRepaint { - protected JLabelSkin skin = FSkin.get(this); - private boolean pressed, hovered; - - private TitleBarButton() { - setPreferredSize(new Dimension(25, 25)); - addMouseListener(new MouseAdapter() { - @Override - public void mousePressed(MouseEvent e) { - if (SwingUtilities.isLeftMouseButton(e)) { - pressed = true; - repaintSelf(); - } - } - @Override - public void mouseReleased(MouseEvent e) { - if (SwingUtilities.isLeftMouseButton(e)) { - if (pressed) { - pressed = false; - if (hovered) { //only handle click if mouse released over button - repaintSelf(); - onClick(); - } - } - } - } - @Override - public void mouseEntered(MouseEvent e) { - hovered = true; - repaintSelf(); - } - @Override - public void mouseExited(MouseEvent e) { - hovered = false; - repaintSelf(); - } - }); - } - - protected abstract void onClick(); - - @Override - public void repaintSelf() { - final Dimension d = this.getSize(); - repaint(0, 0, d.width, d.height); - } - - @Override - public void paintComponent(Graphics g) { - if (hovered) { - if (pressed) { - skin.setGraphicsColor(g, buttonDownColor); - g.fillRect(0, 0, getWidth(), getHeight()); - g.translate(1, 1); //translate icon to give pressed button look - } - else { - skin.setGraphicsColor(g, buttonHoverColor); - g.fillRect(0, 0, getWidth(), getHeight()); - } - } - } - } - - public class MinimizeButton extends TitleBarButton { - private MinimizeButton() { - setToolTipText("Minimize"); - } - @Override - protected void onClick() { - frame.setMinimized(true); - } - @Override - public void paintComponent(Graphics g) { - super.paintComponent(g); - - int thickness = 2; - int offsetX = 8; - int offsetY = 7; - int x1 = offsetX; - int x2 = getWidth() - offsetX; - int y = getHeight() - offsetY - thickness; - - Graphics2D g2d = (Graphics2D) g; - skin.setGraphicsColor(g2d, foreColor); - g2d.setStroke(new BasicStroke(thickness)); - g2d.drawLine(x1, y, x2, y); - } - } - - public class MaximizeButton extends TitleBarButton { - private MaximizeButton() { - setToolTipText("Maximize"); - } - @Override - protected void onClick() { - frame.setMaximized(!frame.getMaximized()); - } - @Override - public void paintComponent(Graphics g) { - super.paintComponent(g); - - int thickness = 2; - int offsetX = 7; - int offsetY = 8; - int x = offsetX; - int y = offsetY; - int width = getWidth() - 2 * offsetX; - int height = getHeight() - 2 * offsetY; - - Graphics2D g2d = (Graphics2D) g; - skin.setGraphicsColor(g2d, foreColor); - g2d.setStroke(new BasicStroke(thickness)); - - if (frame.getMaximized()) { //draw 2 rectangles offset if icon to restore window - x -= 1; - y += 2; - width -= 1; - height -= 1; - g2d.drawRect(x, y, width, height); - x += 3; - y -= 3; - //draw back rectangle as 4 lines so front rectangle doesn't show back rectangle through it - g2d.drawLine(x, y, x, y + 2); - g2d.drawLine(x, y, x + width, y); - x += width; - g2d.drawLine(x, y, x, y + height); - y += height; - g2d.drawLine(x - 2, y, x, y); - } - else { //otherwise just draw 1 rectangle if icon to maximize window - g2d.drawRect(x, y, width, height); - } - } - } - - public class CloseButton extends TitleBarButton { - private CloseButton() { - setToolTipText("Close"); - } - @Override - protected void onClick() { - WindowEvent wev = new WindowEvent(frame, WindowEvent.WINDOW_CLOSING); - Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(wev); - } - @Override - public void paintComponent(Graphics g) { - super.paintComponent(g); - - int thickness = 2; - int offset = 7; - int x1 = offset; - int y1 = offset; - int x2 = getWidth() - offset - 1; - int y2 = getHeight() - offset - 1; - - Graphics2D g2d = (Graphics2D) g; - skin.setGraphicsColor(g2d, foreColor); - g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - g2d.setStroke(new BasicStroke(thickness)); - g2d.drawLine(x1, y1, x2, y2); - g2d.drawLine(x2, y1, x1, y2); - } - } -} +} \ No newline at end of file diff --git a/src/main/java/forge/view/FTitleBarBase.java b/src/main/java/forge/view/FTitleBarBase.java new file mode 100644 index 00000000000..715ab6277ae --- /dev/null +++ b/src/main/java/forge/view/FTitleBarBase.java @@ -0,0 +1,250 @@ +package forge.view; + +import java.awt.BasicStroke; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.RenderingHints; +import java.awt.Toolkit; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.WindowEvent; + +import javax.swing.JLabel; +import javax.swing.JMenuBar; +import javax.swing.SpringLayout; +import javax.swing.SwingUtilities; + +import forge.gui.framework.ILocalRepaint; +import forge.gui.toolbox.FSkin; +import forge.gui.toolbox.FSkin.Colors; +import forge.gui.toolbox.FSkin.JComponentSkin; +import forge.gui.toolbox.FSkin.JLabelSkin; +import forge.gui.toolbox.FSkin.SkinColor; + +@SuppressWarnings("serial") +public abstract class FTitleBarBase extends JMenuBar { + protected static final SkinColor foreColor = FSkin.getColor(Colors.CLR_TEXT); + protected static final SkinColor backColor = FSkin.getColor(Colors.CLR_THEME2); + protected static final SkinColor borderColor = backColor.stepColor(-80); + protected static final SkinColor buttonHoverColor = backColor.stepColor(40); + protected static final SkinColor buttonDownColor = backColor.stepColor(-40); + + protected final FFrame frame; + protected final JComponentSkin skin = FSkin.get(this); + protected final SpringLayout layout = new SpringLayout(); + private final MinimizeButton btnMinimize = new MinimizeButton(); + private final MaximizeButton btnMaximize = new MaximizeButton(); + private final CloseButton btnClose = new CloseButton(); + + protected FTitleBarBase(FFrame f) { + this.frame = f; + setVisible(true); //set to visible by default so preferred size set + setLayout(this.layout); + skin.setBackground(backColor); + skin.setMatteBorder(0, 0, 1, 0, borderColor); + } + + protected void addControls() { + add(btnClose); + layout.putConstraint(SpringLayout.EAST, btnClose, 0, SpringLayout.EAST, this); + layout.putConstraint(SpringLayout.NORTH, btnClose, 0, SpringLayout.NORTH, this); + + add(btnMaximize); + layout.putConstraint(SpringLayout.EAST, btnMaximize, 0, SpringLayout.WEST, btnClose); + layout.putConstraint(SpringLayout.NORTH, btnMaximize, 0, SpringLayout.NORTH, btnClose); + + add(btnMinimize); + layout.putConstraint(SpringLayout.EAST, btnMinimize, 0, SpringLayout.WEST, btnMaximize); + layout.putConstraint(SpringLayout.NORTH, btnMinimize, 0, SpringLayout.NORTH, btnMaximize); + } + + public abstract String getTitle(); + public abstract void setTitle(String title); + public abstract void setIconImage(Image image); + + public void handleMaximizedChanged() { + if (frame.getMaximized()) { + btnMaximize.setToolTipText("Restore Down"); + } + else { + btnMaximize.setToolTipText("Maximize"); + } + btnMaximize.repaintSelf(); + } + + /* (non-Javadoc) + * @see javax.swing.JComponent#setVisible(boolean) + */ + @Override + public void setVisible(boolean visible) { + //use height 0 to hide rather than setting visible to false to allow menu item accelerators to work + setPreferredSize(new Dimension(this.frame.getWidth(), visible ? 26 : 0)); + revalidate(); + } + + public abstract class TitleBarButton extends JLabel implements ILocalRepaint { + protected JLabelSkin skin = FSkin.get(this); + private boolean pressed, hovered; + + private TitleBarButton() { + setPreferredSize(new Dimension(25, 25)); + addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + if (SwingUtilities.isLeftMouseButton(e)) { + pressed = true; + repaintSelf(); + } + } + @Override + public void mouseReleased(MouseEvent e) { + if (SwingUtilities.isLeftMouseButton(e)) { + if (pressed) { + pressed = false; + if (hovered) { //only handle click if mouse released over button + repaintSelf(); + onClick(); + } + } + } + } + @Override + public void mouseEntered(MouseEvent e) { + hovered = true; + repaintSelf(); + } + @Override + public void mouseExited(MouseEvent e) { + hovered = false; + repaintSelf(); + } + }); + } + + protected abstract void onClick(); + + @Override + public void repaintSelf() { + final Dimension d = this.getSize(); + repaint(0, 0, d.width, d.height); + } + + @Override + public void paintComponent(Graphics g) { + if (hovered) { + if (pressed) { + skin.setGraphicsColor(g, buttonDownColor); + g.fillRect(0, 0, getWidth(), getHeight()); + g.translate(1, 1); //translate icon to give pressed button look + } + else { + skin.setGraphicsColor(g, buttonHoverColor); + g.fillRect(0, 0, getWidth(), getHeight()); + } + } + } + } + + public class MinimizeButton extends TitleBarButton { + private MinimizeButton() { + setToolTipText("Minimize"); + } + @Override + protected void onClick() { + frame.setMinimized(true); + } + @Override + public void paintComponent(Graphics g) { + super.paintComponent(g); + + int thickness = 2; + int offsetX = 8; + int offsetY = 7; + int x1 = offsetX; + int x2 = getWidth() - offsetX; + int y = getHeight() - offsetY - thickness; + + Graphics2D g2d = (Graphics2D) g; + skin.setGraphicsColor(g2d, foreColor); + g2d.setStroke(new BasicStroke(thickness)); + g2d.drawLine(x1, y, x2, y); + } + } + + public class MaximizeButton extends TitleBarButton { + private MaximizeButton() { + setToolTipText("Maximize"); + } + @Override + protected void onClick() { + frame.setMaximized(!frame.getMaximized()); + } + @Override + public void paintComponent(Graphics g) { + super.paintComponent(g); + + int thickness = 2; + int offsetX = 7; + int offsetY = 8; + int x = offsetX; + int y = offsetY; + int width = getWidth() - 2 * offsetX; + int height = getHeight() - 2 * offsetY; + + Graphics2D g2d = (Graphics2D) g; + skin.setGraphicsColor(g2d, foreColor); + g2d.setStroke(new BasicStroke(thickness)); + + if (frame.getMaximized()) { //draw 2 rectangles offset if icon to restore window + x -= 1; + y += 2; + width -= 1; + height -= 1; + g2d.drawRect(x, y, width, height); + x += 3; + y -= 3; + //draw back rectangle as 4 lines so front rectangle doesn't show back rectangle through it + g2d.drawLine(x, y, x, y + 2); + g2d.drawLine(x, y, x + width, y); + x += width; + g2d.drawLine(x, y, x, y + height); + y += height; + g2d.drawLine(x - 2, y, x, y); + } + else { //otherwise just draw 1 rectangle if icon to maximize window + g2d.drawRect(x, y, width, height); + } + } + } + + public class CloseButton extends TitleBarButton { + private CloseButton() { + setToolTipText("Close"); + } + @Override + protected void onClick() { + WindowEvent wev = new WindowEvent(frame, WindowEvent.WINDOW_CLOSING); + Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(wev); + } + @Override + public void paintComponent(Graphics g) { + super.paintComponent(g); + + int thickness = 2; + int offset = 7; + int x1 = offset; + int y1 = offset; + int x2 = getWidth() - offset - 1; + int y2 = getHeight() - offset - 1; + + Graphics2D g2d = (Graphics2D) g; + skin.setGraphicsColor(g2d, foreColor); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g2d.setStroke(new BasicStroke(thickness)); + g2d.drawLine(x1, y1, x2, y2); + g2d.drawLine(x2, y1, x1, y2); + } + } +} diff --git a/src/main/java/forge/view/FView.java b/src/main/java/forge/view/FView.java index cf04314f569..4dea9294552 100644 --- a/src/main/java/forge/view/FView.java +++ b/src/main/java/forge/view/FView.java @@ -56,7 +56,9 @@ import forge.gui.toolbox.FSkin; import forge.gui.toolbox.FSkin.JComponentSkin; import forge.gui.toolbox.FSkin.SkinCursor; import forge.model.BuildInfo; +import forge.properties.ForgePreferences; import forge.properties.NewConstants; +import forge.properties.ForgePreferences.FPref; /** */ public enum FView { @@ -65,6 +67,7 @@ public enum FView { /** */ public static final Integer TARGETING_LAYER = JLayeredPane.MODAL_LAYER - 1; + private final List allCells = new ArrayList(); private SplashFrame frmSplash; @@ -75,13 +78,15 @@ public enum FView { private final FFrame frmDocument = new FFrame(); // A layered pane is the frame's viewport, allowing overlay effects. private final DocumentPane lpnDocument = new DocumentPane(); + // The status bar to display at the bottom of the frame + private FNavigationBar navigationBar; // The content panel is placed in the layered pane. private final JPanel pnlContent = new JPanel(); - // The status bar to display at the bottom of the frame - private FStatusBar statusBar; // An insets panel neatly maintains a space from the edges of the window and // whatever layout is happening, without having to explicitly define a margin each time. private FPanel pnlInsets; + // The status bar to display at the bottom of the frame + private FStatusBar statusBar; // Preview panel is what is shown when a drag cell is being moved around private final JPanel pnlPreview = new PreviewPanel(); // Tab overflow is for the +X display for extra tabs. @@ -89,29 +94,35 @@ public enum FView { private FView() { frmSplash = new SplashFrame(); + frmDocument.setTitle("Forge: " + BuildInfo.getVersionString()); } /** */ public void initialize() { - // Insets panel has background image / texture, which - // must be instantiated after the skin is loaded. + final ForgePreferences prefs = Singletons.getModel().getPreferences(); + + // pnlInsets, navigationBar, and statusBar are skinned components + // which must be instantiated after the skin is loaded. pnlInsets = new FPanel(new BorderLayout()); pnlInsets.setBorderToggle(false); + navigationBar = new FNavigationBar(frmDocument); + statusBar = new FStatusBar(frmDocument, !prefs.getPrefBoolean(FPref.UI_HIDE_STATUS_BAR)); // Frame styling - frmDocument.initialize(); + frmDocument.initialize(navigationBar, !prefs.getPrefBoolean(FPref.UI_HIDE_TITLE_BAR)); frmDocument.setMinimumSize(new Dimension(800, 600)); frmDocument.setLocationRelativeTo(null); frmDocument.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); FSkin.get(frmDocument).setIconImage(FSkin.getIcon(FSkin.InterfaceIcons.ICO_FAVICON)); - frmDocument.setTitle("Forge: " + BuildInfo.getVersionString()); // Frame components - frmDocument.getInnerPane().setContentPane(lpnDocument); + frmDocument.setContentPane(lpnDocument); lpnDocument.add(pnlInsets, (Integer) 1); + lpnDocument.add(statusBar, (Integer) 1); //should always appear below pnlInsets, so put at same level FAbsolutePositioner.SINGLETON_INSTANCE.initialize(lpnDocument, (Integer) 2); lpnDocument.add(pnlPreview, (Integer) 3); lpnDocument.add(pnlTabOverflow, (Integer) 4); + lpnDocument.add(navigationBar, (Integer) 5); //ensure this appears over all other non-overlay layers lpnDocument.add(FOverlay.SINGLETON_INSTANCE.getPanel(), JLayeredPane.MODAL_LAYER); // Note: when adding new panels here, keep in mind that the layered pane // has a null layout, so new components will be W0 x H0 pixels - gotcha! @@ -128,11 +139,6 @@ public enum FView { pnlContent.setOpaque(false); pnlContent.setLayout(null); - // Status bar has foreground/background color which - // must be instantiated after the skin is loaded. - statusBar = new FStatusBar(frmDocument); - lpnDocument.add(statusBar, (Integer) 1); - FSkin.get(FOverlay.SINGLETON_INSTANCE.getPanel()).setBackground(FSkin.getColor(FSkin.Colors.CLR_OVERLAY)); // Populate all drag tab components. @@ -310,6 +316,11 @@ public enum FView { public DocumentPane getLpnDocument() { return lpnDocument; } + + /** @return {@link forge.view.FNavigationBar} */ + public FNavigationBar getNavigationBar() { + return navigationBar; + } /** @return {@link forge.gui.toolbox.FPanel} */ public FPanel getPnlInsets() {