- New forge menu bar (part 1 of 2). Includes common Forge and Help menus.

This commit is contained in:
spr
2013-08-21 01:13:26 +00:00
parent f8f03a57ba
commit 5ee58d727a
11 changed files with 827 additions and 407 deletions

5
.gitattributes vendored
View File

@@ -14986,6 +14986,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/VPlayers.java -text
src/main/java/forge/gui/match/views/VStack.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/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/package-info.java svneol=native#text/plain 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/CardFaceSymbols.java svneol=native#text/plain
src/main/java/forge/gui/toolbox/FAbsolutePositioner.java -text src/main/java/forge/gui/toolbox/FAbsolutePositioner.java -text

View File

@@ -60,6 +60,8 @@ import forge.gui.match.controllers.CMessage;
import forge.gui.match.controllers.CStack; import forge.gui.match.controllers.CStack;
import forge.gui.match.nonsingleton.VField; import forge.gui.match.nonsingleton.VField;
import forge.gui.match.views.VAntes; import forge.gui.match.views.VAntes;
import forge.gui.menubar.FMenuBar;
import forge.gui.menubar.MenuUtil;
import forge.gui.toolbox.FSkin; import forge.gui.toolbox.FSkin;
import forge.net.FServer; import forge.net.FServer;
import forge.properties.ForgeLookAndFeel; import forge.properties.ForgeLookAndFeel;
@@ -82,6 +84,7 @@ import forge.view.FView;
public enum FControl { public enum FControl {
instance; instance;
private FMenuBar menuBar;
private List<Shortcut> shortcuts; private List<Shortcut> shortcuts;
private JLayeredPane display; private JLayeredPane display;
private Screens state = Screens.UNKNOWN; private Screens state = Screens.UNKNOWN;
@@ -172,6 +175,8 @@ public enum FControl {
FSkin.setProgessBarMessage("Setting look and feel..."); FSkin.setProgessBarMessage("Setting look and feel...");
setForgeLookAndFeel(); setForgeLookAndFeel();
createMenuBar();
this.shortcuts = KeyboardShortcuts.attachKeyboardShortcuts(); this.shortcuts = KeyboardShortcuts.attachKeyboardShortcuts();
this.display = FView.SINGLETON_INSTANCE.getLpnDocument(); this.display = FView.SINGLETON_INSTANCE.getLpnDocument();
@@ -211,6 +216,12 @@ public enum FControl {
laf.setForgeLookAndFeel(Singletons.getView().getFrame()); laf.setForgeLookAndFeel(Singletons.getView().getFrame());
} }
private void createMenuBar() {
if (MenuUtil.isMenuBarVisible()) {
this.menuBar = new FMenuBar(Singletons.getView().getFrame());
}
}
/** /**
* Switches between display states in top level JFrame. * Switches between display states in top level JFrame.
*/ */

View File

@@ -118,6 +118,7 @@ public final class SResizingUtil {
final JPanel pnlInsets = FView.SINGLETON_INSTANCE.getPnlInsets(); final JPanel pnlInsets = FView.SINGLETON_INSTANCE.getPnlInsets();
Rectangle mainBounds = FView.SINGLETON_INSTANCE.getFrame().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); FAbsolutePositioner.SINGLETON_INSTANCE.containerResized(mainBounds);
FOverlay.SINGLETON_INSTANCE.getPanel().setBounds(mainBounds); FOverlay.SINGLETON_INSTANCE.getPanel().setBounds(mainBounds);
FNetOverlay.SINGLETON_INSTANCE.containerResized(mainBounds); FNetOverlay.SINGLETON_INSTANCE.containerResized(mainBounds);

View File

@@ -0,0 +1,51 @@
package forge.gui.menubar;
import java.awt.Component;
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import forge.gui.menus.ForgeMenu;
import forge.gui.menus.HelpMenu;
@SuppressWarnings("serial")
public class FMenuBar extends JMenuBar {
public FMenuBar(JFrame f) {
f.setJMenuBar(this);
setPreferredSize(new Dimension(f.getWidth(), 26));
setupMenuBar(null);
}
public void setupMenuBar(IMenuProvider provider) {
removeAll();
add(ForgeMenu.getMenu());
addProviderMenus(provider);
add(HelpMenu.getMenu());
repaint();
}
private void addProviderMenus(IMenuProvider provider) {
if (provider != null && provider.getMenus() != null) {
for (JMenu m : provider.getMenus()) {
m.setBorderPainted(false);
add(m);
}
}
}
/**
* Enables or disables the MenuBar.
* <p>
* Note: Disabling a component does not disable its children.
*/
public void setMenuBarEnabled(boolean isEnabled) {
setEnabled(isEnabled);
for (Component c : getComponents()) {
c.setEnabled(isEnabled);
}
}
}

View File

@@ -0,0 +1,19 @@
package forge.gui.menubar;
import java.util.List;
import javax.swing.JMenu;
/**
* By implementing this interface a class can add menus to the menu bar
* by calling the {@code MenuBarManager.SetupMenuBar()} method.
*
*/
public interface IMenuProvider {
/**
* Returns a list of JMenu objects for display in MenuBar.
*/
List<JMenu> getMenus();
}

View File

@@ -0,0 +1,61 @@
package forge.gui.menubar;
import java.awt.Toolkit;
import java.io.IOException;
import javax.swing.ImageIcon;
import javax.swing.JOptionPane;
import javax.swing.KeyStroke;
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);
}
public static void openUrlInBrowser(String url) {
try {
java.awt.Desktop.getDesktop().browse(java.net.URI.create(url));
} catch (IOException e) {
// Auto-generated catch block ignores the exception, but sends it to System.err and probably forge.log.
e.printStackTrace();
}
}
public static ImageIcon getMenuIcon(SkinProp ico) {
return ImageUtil.getMenuIcon(FSkin.getIcon(ico));
}
public static KeyStroke getAcceleratorKey(int key) {
return KeyStroke.getKeyStroke(key, DEFAULT_MenuShortcutKeyMask);
}
public static boolean getUserConfirmation(String prompt, String dialogTitle) {
Object[] options = {"Yes", "No"};
int reply = JOptionPane.showOptionDialog(
null,
prompt,
dialogTitle,
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE,
null,
options,
options[1]);
return (reply == JOptionPane.YES_OPTION);
}
}

View File

@@ -0,0 +1,70 @@
package forge.gui.menus;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import forge.Singletons;
import forge.control.RestartUtil;
import forge.control.FControl.Screens;
import forge.gui.menubar.MenuUtil;
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;
}
private static JMenuItem getMenuItem_Exit() {
JMenuItem menuItem = new JMenuItem("Exit");
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 JMenuItem getMenuItem_Restart() {
JMenuItem menuItem = new JMenuItem("Restart");
menuItem.addActionListener(getRestartAction());
return menuItem;
}
private static ActionListener getRestartAction() {
return new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (!isHomeScreenActive()) {
String userPrompt = "Please confirm you want to restart Forge.\n\n";
if (!MenuUtil.getUserConfirmation(userPrompt, "Restart Forge")) {
return;
}
}
RestartUtil.restartApplication(null);
}
};
}
private static boolean isHomeScreenActive() {
return Singletons.getControl().getState() == Screens.HOME_SCREEN;
}
}

View File

@@ -0,0 +1,124 @@
package forge.gui.menus;
import java.awt.Desktop;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.IOException;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import forge.gui.menubar.MenuUtil;
import forge.util.FileUtil;
public final class HelpMenu {
private HelpMenu() { }
public static JMenu getMenu() {
JMenu menu = new JMenu("Help");
menu.setMnemonic(KeyEvent.VK_H);
menu.add(getMenu_GettingStarted());
menu.add(getMenu_Articles());
menu.add(getMenu_Troubleshooting());
menu.addSeparator();
menu.add(getMenuItem_ReleaseNotes());
menu.add(getMenuItem_License());
return menu;
}
private static JMenu getMenu_Troubleshooting() {
JMenu mnu = new JMenu("Troubleshooting");
mnu.add(getMenuItem_UrlLink("How to Provide a Useful Bug Report", "http://www.slightlymagic.net/forum/viewtopic.php?f=26&t=9621"));
mnu.addSeparator();
mnu.add(getMenuItem_ReadMeFile());
return mnu;
}
private static JMenu getMenu_Articles() {
JMenu mnu = new JMenu("Articles");
mnu.add(getMenuItem_UrlLink("HOW-TO: Customize your Sealed Deck games with fantasy blocks", "http://www.slightlymagic.net/forum/viewtopic.php?f=26&t=8164"));
mnu.add(getMenuItem_UrlLink("Quest Mode: Guide to Formats, Worlds, and everything", "http://www.slightlymagic.net/forum/viewtopic.php?f=26&t=9258"));
return mnu;
}
private static JMenu getMenu_GettingStarted() {
JMenu mnu = new JMenu("Getting Started");
mnu.add(getMenuItem_UrlLink("Forge Wiki", "http://www.slightlymagic.net/wiki/Forge"));
mnu.add(getMenuItem_UrlLink("What is Forge?", "http://www.slightlymagic.net/forum/viewtopic.php?f=26&t=468"));
return mnu;
}
private static JMenuItem getMenuItem_ReadMeFile() {
JMenuItem menuItem = new JMenuItem("README.txt");
menuItem.addActionListener(getOpenFileAction(getFile("README.txt")));
return menuItem;
}
private static JMenuItem getMenuItem_License() {
JMenuItem menuItem = new JMenuItem("Forge License");
menuItem.addActionListener(getOpenFileAction(getFile("LICENSE.txt")));
return menuItem;
}
private static JMenuItem getMenuItem_ReleaseNotes() {
JMenuItem menuItem = new JMenuItem("Release Notes");
menuItem.addActionListener(getOpenFileAction(getFile("CHANGES.txt")));
return menuItem;
}
private static ActionListener getOpenFileAction(final File file) {
return new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
openFile(file);
} catch (IOException e1) {
// Auto-generated catch block ignores the exception, but sends it to System.err and probably forge.log.
e1.printStackTrace();
}
}
};
}
protected static File getFile(String filename) {
// !! Linux is case-sensitive so file name and extension need to match exactly !!
File file = null;
String filePath = FileUtil.pathCombine(System.getProperty("user.dir"), filename);
if (FileUtil.doesFileExist(filePath)) {
file = new File(filePath);
}
return file;
}
/**
* @see http://stackoverflow.com/questions/6273221/open-a-text-file-in-the-default-text-editor-via-java
*/
private static void openFile(File file) throws IOException {
if (System.getProperty("os.name").toLowerCase().contains("windows")) {
String cmd = "rundll32 url.dll,FileProtocolHandler " + file.getCanonicalPath();
Runtime.getRuntime().exec(cmd);
}
else {
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 ActionListener getLaunchUrlAction(final String url) {
return new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
MenuUtil.openUrlInBrowser(url);
}
};
}
}

View File

@@ -19,6 +19,9 @@
package forge.gui.toolbox.imaging; package forge.gui.toolbox.imaging;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Image;
import javax.swing.ImageIcon;
/** /**
* Useful general imaging routines. * Useful general imaging routines.
@@ -29,6 +32,12 @@ import java.awt.Dimension;
public final class ImageUtil { public final class ImageUtil {
private ImageUtil() {} private ImageUtil() {}
public static ImageIcon getMenuIcon(ImageIcon sourceIcon) {
Image img = sourceIcon.getImage();
Image newimg = img.getScaledInstance(16, 16, java.awt.Image.SCALE_SMOOTH);
return new ImageIcon(newimg);
}
/** /**
* Gets the nearest rotation for a requested rotation. * Gets the nearest rotation for a requested rotation.
* <p> * <p>

View File

@@ -2,10 +2,15 @@ package forge.properties;
import java.awt.Color; import java.awt.Color;
import java.awt.Font; import java.awt.Font;
import java.util.ArrayList;
import javax.swing.BorderFactory;
import javax.swing.JFrame; import javax.swing.JFrame;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException; import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.Border;
import forge.Singletons; import forge.Singletons;
import forge.gui.toolbox.FSkin; import forge.gui.toolbox.FSkin;
import forge.properties.ForgePreferences.FPref; import forge.properties.ForgePreferences.FPref;
@@ -20,6 +25,8 @@ public final class ForgeLookAndFeel {
private Color FORE_COLOR = FSkin.getColor(FSkin.Colors.CLR_TEXT); private Color FORE_COLOR = FSkin.getColor(FSkin.Colors.CLR_TEXT);
private Color BACK_COLOR = FSkin.getColor(FSkin.Colors.CLR_THEME2); private Color BACK_COLOR = FSkin.getColor(FSkin.Colors.CLR_THEME2);
private Color HIGHLIGHT_COLOR = BACK_COLOR.brighter(); private Color HIGHLIGHT_COLOR = BACK_COLOR.brighter();
private Border LINE_BORDER = BorderFactory.createLineBorder(FORE_COLOR.darker(), 1);
private Border EMPTY_BORDER = BorderFactory.createEmptyBorder(2,2,2,2);
/** /**
* Sets the look and feel of the GUI based on the selected Forge theme. * Sets the look and feel of the GUI based on the selected Forge theme.
@@ -27,6 +34,7 @@ public final class ForgeLookAndFeel {
public void setForgeLookAndFeel(JFrame appFrame) { public void setForgeLookAndFeel(JFrame appFrame) {
if (isUIManagerEnabled()) { if (isUIManagerEnabled()) {
if (setMetalLookAndFeel(appFrame)) { if (setMetalLookAndFeel(appFrame)) {
setMenusLookAndFeel();
setComboBoxLookAndFeel(); setComboBoxLookAndFeel();
setTabbedPaneLookAndFeel(); setTabbedPaneLookAndFeel();
setButtonLookAndFeel(); setButtonLookAndFeel();
@@ -53,6 +61,56 @@ public final class ForgeLookAndFeel {
return isMetalLafSet; return isMetalLafSet;
} }
/**
* Sets the look and feel for a JMenuBar, JMenu, JMenuItem & variations.
*/
private void setMenusLookAndFeel() {
// JMenuBar
Color clrTheme = FSkin.getColor(FSkin.Colors.CLR_THEME);
Color backgroundColor = FSkin.stepColor(clrTheme, 0);
Color menuBarEdgeColor = FSkin.stepColor(clrTheme, -80);
UIManager.put("MenuBar.foreground", FORE_COLOR);
UIManager.put("MenuBar.gradient", getColorGradients(backgroundColor.darker(), backgroundColor));
UIManager.put("MenuBar.border", BorderFactory.createMatteBorder(0, 0, 1, 0, menuBarEdgeColor));
// JMenu
UIManager.put("Menu.foreground", FORE_COLOR);
UIManager.put("Menu.background", BACK_COLOR);
UIManager.put("Menu.borderPainted", false);
UIManager.put("Menu.selectionBackground", HIGHLIGHT_COLOR);
UIManager.put("Menu.selectionForeground", FORE_COLOR);
UIManager.put("Menu.border", EMPTY_BORDER);
UIManager.put("Menu.opaque", false);
// JPopupMenu
UIManager.put("PopupMenu.border", LINE_BORDER);
UIManager.put("PopupMenu.background", BACK_COLOR);
UIManager.put("PopupMenu.foreground", FORE_COLOR);
// JMenuItem
UIManager.put("MenuItem.foreground", FORE_COLOR);
UIManager.put("MenuItem.background", BACK_COLOR);
UIManager.put("MenuItem.border", EMPTY_BORDER);
UIManager.put("MenuItem.selectionBackground", HIGHLIGHT_COLOR);
UIManager.put("MenuItem.selectionForeground", FORE_COLOR);
UIManager.put("MenuItem.acceleratorForeground", FORE_COLOR.darker());
UIManager.put("MenuItem.opaque", true);
// JSeparator (needs to be opaque!).
UIManager.put("Separator.foreground", FORE_COLOR.darker());
UIManager.put("Separator.background", BACK_COLOR);
// JRadioButtonMenuItem
UIManager.put("RadioButtonMenuItem.foreground", FORE_COLOR);
UIManager.put("RadioButtonMenuItem.background", BACK_COLOR);
UIManager.put("RadioButtonMenuItem.selectionBackground", HIGHLIGHT_COLOR);
UIManager.put("RadioButtonMenuItem.selectionForeground", FORE_COLOR);
UIManager.put("RadioButtonMenuItem.border", EMPTY_BORDER);
UIManager.put("RadioButtonMenuItem.acceleratorForeground", FORE_COLOR.darker());
// JCheckboxMenuItem
UIManager.put("CheckBoxMenuItem.foreground", FORE_COLOR);
UIManager.put("CheckBoxMenuItem.background", BACK_COLOR);
UIManager.put("CheckBoxMenuItem.selectionBackground", HIGHLIGHT_COLOR);
UIManager.put("CheckBoxMenuItem.selectionForeground", FORE_COLOR);
UIManager.put("CheckBoxMenuItem.border", EMPTY_BORDER);
UIManager.put("CheckBoxMenuItem.acceleratorForeground", FORE_COLOR.darker());
}
private void setTabbedPaneLookAndFeel() { private void setTabbedPaneLookAndFeel() {
UIManager.put("TabbedPane.selected", HIGHLIGHT_COLOR); UIManager.put("TabbedPane.selected", HIGHLIGHT_COLOR);
UIManager.put("TabbedPane.contentOpaque", FSkin.getColor(FSkin.Colors.CLR_THEME)); UIManager.put("TabbedPane.contentOpaque", FSkin.getColor(FSkin.Colors.CLR_THEME));
@@ -95,4 +153,14 @@ public final class ForgeLookAndFeel {
return FSkin.getFont(UIManager.getFont(component).getSize()); return FSkin.getFont(UIManager.getFont(component).getSize());
} }
private ArrayList<Object> getColorGradients(Color bottom, Color top) {
ArrayList<Object> gradients = new ArrayList<>();
gradients.add(0.0);
gradients.add(0.0);
gradients.add(top);
gradients.add(bottom);
gradients.add(bottom);
return gradients;
}
} }

View File

@@ -61,6 +61,7 @@ public class ForgePreferences extends PreferencesStore<ForgePreferences.FPref> {
UI_CLONE_MODE_SOURCE ("false"), /** */ UI_CLONE_MODE_SOURCE ("false"), /** */
UI_MATCH_IMAGE_VISIBLE ("true"), UI_MATCH_IMAGE_VISIBLE ("true"),
UI_THEMED_COMBOBOX ("true"), UI_THEMED_COMBOBOX ("true"),
UI_HIDE_MENUBAR ("false"), // Dev setting only - cannot be set from GUI.
UI_FOR_TOUCHSCREN("false"), UI_FOR_TOUCHSCREN("false"),