diff --git a/forge-gui-desktop/src/main/java/forge/gui/SwingPrefBinders.java b/forge-gui-desktop/src/main/java/forge/gui/SwingPrefBinders.java new file mode 100644 index 00000000000..6a3df000df7 --- /dev/null +++ b/forge-gui-desktop/src/main/java/forge/gui/SwingPrefBinders.java @@ -0,0 +1,26 @@ +package forge.gui; + +import javax.swing.*; +import java.awt.event.ItemEvent; +import forge.localinstance.properties.ForgePreferences; +import forge.model.FPrefsBinder; + +public class SwingPrefBinders { + public static final class ComboBox extends FPrefsBinder, String> { + public ComboBox(ForgePreferences.FPref key, JComboBox box) { + super( + key, + box, + b -> (String) b.getSelectedItem(), + (b, s) -> b.setSelectedItem(s), + s -> s, + s -> s); + + box.addItemListener(e -> { + if (e.getStateChange() == ItemEvent.SELECTED) { + this.save(); + } + }); + } + } +} diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/CLobby.java b/forge-gui-desktop/src/main/java/forge/screens/home/CLobby.java index a637bf4c303..d255c9a4910 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/CLobby.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/CLobby.java @@ -54,6 +54,7 @@ public class CLobby { // General updates when switching back to this view view.getBtnStart().requestFocusInWindow(); }); + view.getGamesInMatchBinder().load(); } public void initialize() { diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/VLobby.java b/forge-gui-desktop/src/main/java/forge/screens/home/VLobby.java index ee4852447d4..a7093b3d3ae 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/VLobby.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/VLobby.java @@ -34,6 +34,7 @@ import forge.gamemodes.match.LobbySlotType; import forge.gamemodes.net.event.UpdateLobbyPlayerEvent; import forge.gui.CardDetailPanel; import forge.gui.GuiBase; +import forge.gui.SwingPrefBinders; import forge.gui.interfaces.ILobbyView; import forge.gui.util.SOptionPane; import forge.interfaces.IPlayerChangeListener; @@ -74,7 +75,9 @@ public class VLobby implements ILobbyView { private final StartButton btnStart = new StartButton(); private final JPanel pnlStart = new JPanel(new MigLayout("insets 0, gap 0, wrap 2")); - private final JComboBox gamesInMatch = new JComboBox(new String[] {"1","3","5"}); + private final JComboBox gamesInMatch = new JComboBox(new String[] {"1","3","5"}); + private final SwingPrefBinders.ComboBox gamesInMatchBinder = + new SwingPrefBinders.ComboBox(FPref.UI_MATCHES_PER_GAME, gamesInMatch); private final JPanel gamesInMatchFrame = new JPanel(new MigLayout("insets 0, gap 0, wrap 2")); private final JPanel constructedFrame = new JPanel(new MigLayout("insets 0, gap 0, wrap 2")); // Main content frame @@ -182,17 +185,19 @@ public class VLobby implements ILobbyView { btnStart.addActionListener(arg0 -> { Runnable startGame = lobby.startGame(); if (startGame != null) { - if (!gamesInMatch.getSelectedItem().equals(FPref.UI_MATCHES_PER_GAME)) { - FModel.getPreferences().setPref(FPref.UI_MATCHES_PER_GAME, (String) gamesInMatch.getSelectedItem()); - } startGame.run(); } }); } + String defaultGamesInMatch = FModel.getPreferences().getPref(FPref.UI_MATCHES_PER_GAME); + if (defaultGamesInMatch == null || defaultGamesInMatch.isEmpty()) { + defaultGamesInMatch = "3"; + } + gamesInMatchFrame.add(newLabel(localizer.getMessage("lblGamesInMatch")), "w 150px!, h 30px!"); gamesInMatchFrame.add(gamesInMatch, "w 50px!, h 30px!"); gamesInMatchFrame.setOpaque(false); - gamesInMatch.setSelectedItem("3"); + pnlStart.add(gamesInMatchFrame); } @@ -855,6 +860,11 @@ public class VLobby implements ILobbyView { return nonRandomAiAvatars; } + /** Return the gamesInMatchBinder */ + public SwingPrefBinders.ComboBox getGamesInMatchBinder() { + return gamesInMatchBinder; + } + /** Populate vanguard lists. */ private void populateVanguardLists() { humanListData.add("Use deck's default avatar (random if unavailable)"); diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/sanctioned/CSubmenuDraft.java b/forge-gui-desktop/src/main/java/forge/screens/home/sanctioned/CSubmenuDraft.java index d7e6e587cec..fca5a6aa053 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/sanctioned/CSubmenuDraft.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/sanctioned/CSubmenuDraft.java @@ -97,12 +97,15 @@ public enum CSubmenuDraft implements ICDoc { view.getBtnBuildDeck().requestFocusInWindow(); } }); + + view.getGamesInMatchBinder().load(); } private void startGame(final GameType gameType) { final Localizer localizer = Localizer.getInstance(); - final boolean gauntlet = VSubmenuDraft.SINGLETON_INSTANCE.isGauntlet(); - final DeckProxy humanDeck = VSubmenuDraft.SINGLETON_INSTANCE.getLstDecks().getSelectedItem(); + final VSubmenuDraft view = VSubmenuDraft.SINGLETON_INSTANCE; + final boolean gauntlet = view.isGauntlet(); + final DeckProxy humanDeck = view.getLstDecks().getSelectedItem(); if (humanDeck == null) { FOptionPane.showErrorDialog(localizer.getMessage("lblNoDeckSelected"), localizer.getMessage("lblNoDeck")); @@ -245,5 +248,4 @@ public enum CSubmenuDraft implements ICDoc { combo.addItem("5"); } } - } diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/sanctioned/VSubmenuDraft.java b/forge-gui-desktop/src/main/java/forge/screens/home/sanctioned/VSubmenuDraft.java index 907fd74edb7..d32b2637d60 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/sanctioned/VSubmenuDraft.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/sanctioned/VSubmenuDraft.java @@ -13,6 +13,7 @@ import forge.game.GameType; import forge.gui.framework.DragCell; import forge.gui.framework.DragTab; import forge.gui.framework.EDocID; +import forge.gui.SwingPrefBinders; import forge.itemmanager.DeckManager; import forge.itemmanager.ItemManagerContainer; import forge.screens.deckeditor.CDeckEditorUI; @@ -26,6 +27,8 @@ import forge.toolbox.FLabel; import forge.toolbox.FRadioButton; import forge.toolbox.FSkin; import forge.toolbox.JXButtonPanel; +import forge.localinstance.properties.ForgePreferences.FPref; +import forge.model.FModel; import forge.util.Localizer; import net.miginfocom.swing.MigLayout; @@ -57,6 +60,11 @@ public enum VSubmenuDraft implements IVSubmenu { private final JComboBox cbOpponent = new JComboBox<>(); + private final JComboBox gamesInMatch = new JComboBox(new String[] {"1","3","5"}); + private final SwingPrefBinders.ComboBox gamesInMatchBinder = + new SwingPrefBinders.ComboBox(FPref.UI_MATCHES_PER_GAME, gamesInMatch); + private final JPanel gamesInMatchFrame = new JPanel(new MigLayout("insets 0, gap 0, wrap 2")); + private final JLabel lblInfo = new FLabel.Builder() .fontAlign(SwingConstants.LEFT).fontSize(16).fontStyle(Font.BOLD) .text(localizer.getMessage("lblBuildorselectadeck")).build(); @@ -73,6 +81,10 @@ public enum VSubmenuDraft implements IVSubmenu { .text(localizer.getMessage("lblDraftText3")) .fontSize(12).build(); + private final FLabel lblGamesInMatch = new FLabel.Builder() + .text(localizer.getMessage("lblGamesInMatch")) + .fontSize(12).build(); + private final FLabel btnBuildDeck = new FLabel.ButtonBuilder().text(localizer.getMessage("lblNewBoosterDraftGame")).fontSize(16).build(); /** @@ -91,10 +103,22 @@ public enum VSubmenuDraft implements IVSubmenu { radSingle.setSelected(true); grpPanel.add(cbOpponent, "w 200px!, h 30px!"); - pnlStart.setLayout(new MigLayout("insets 0, gap 0, wrap 2")); + pnlStart.setLayout(new MigLayout("insets 0, gap 0", + "[grow][pref!]", + "[pref!][grow,fill][pref!]")); pnlStart.setOpaque(false); - pnlStart.add(grpPanel, "gapright 20"); - pnlStart.add(btnStart); + pnlStart.add(grpPanel, "cell 0 0 1 3, growy, gapright 20"); + + String defaultGamesInMatch = FModel.getPreferences().getPref(FPref.UI_MATCHES_PER_GAME); + if (defaultGamesInMatch == null || defaultGamesInMatch.isEmpty()) { + defaultGamesInMatch = "3"; + } + gamesInMatchFrame.add(lblGamesInMatch, "w 150px!, h 30px!"); + gamesInMatchFrame.add(gamesInMatch, "w 50px!, h 30px!"); + gamesInMatchFrame.setOpaque(false); + pnlStart.add(gamesInMatchFrame, "cell 1 0, alignx center, aligny top"); + + pnlStart.add(btnStart, "cell 1 2, alignx center, aligny bottom"); } /* (non-Javadoc) @@ -147,6 +171,7 @@ public enum VSubmenuDraft implements IVSubmenu { public JRadioButton getRadSingle() { return radSingle; } public JRadioButton getRadMultiple() { return radMultiple; } public JRadioButton getRadAll() { return radAll; } + public SwingPrefBinders.ComboBox getGamesInMatchBinder() { return gamesInMatchBinder; } //========== Overridden from IVDoc diff --git a/forge-gui-mobile/src/forge/screens/constructed/LobbyScreen.java b/forge-gui-mobile/src/forge/screens/constructed/LobbyScreen.java index a2b361f5a5c..c1e187aee64 100644 --- a/forge-gui-mobile/src/forge/screens/constructed/LobbyScreen.java +++ b/forge-gui-mobile/src/forge/screens/constructed/LobbyScreen.java @@ -51,6 +51,7 @@ import forge.toolbox.FScrollPane; import forge.util.MyRandom; import forge.util.TextUtil; import forge.util.Utils; +import forge.util.GuiPrefBinders; public abstract class LobbyScreen extends LaunchScreen implements ILobbyView { private static final ForgePreferences prefs = FModel.getPreferences(); @@ -72,6 +73,8 @@ public abstract class LobbyScreen extends LaunchScreen implements ILobbyView { // Max games in a match frame and variables private final FLabel lblGamesInMatch = new FLabel.Builder().text(Forge.getLocalizer().getMessage("lblMatch") + ":").font(VARIANTS_FONT).build(); private final FComboBox cbGamesInMatch = new FComboBox<>(); + private final GuiPrefBinders.ComboBox cbGamesInMatchBinder = + new GuiPrefBinders.ComboBox(FPref.UI_MATCHES_PER_GAME, cbGamesInMatch); private final List playerPanels = new ArrayList<>(MAX_PLAYERS); private final FScrollPane playersScroll = new FScrollPane() { @@ -133,8 +136,6 @@ public abstract class LobbyScreen extends LaunchScreen implements ILobbyView { cbGamesInMatch.addItem("1"); cbGamesInMatch.addItem("3"); cbGamesInMatch.addItem("5"); - cbGamesInMatch.setSelectedItem(FModel.getPreferences().getPref((FPref.UI_MATCHES_PER_GAME))); - cbGamesInMatch.setChangedHandler(event -> FModel.getPreferences().setPref(FPref.UI_MATCHES_PER_GAME, cbGamesInMatch.getSelectedItem())); add(lblVariants); add(cbVariants); @@ -588,6 +589,11 @@ public abstract class LobbyScreen extends LaunchScreen implements ILobbyView { } } + @Override + public void onActivate() { + cbGamesInMatchBinder.load(); + } + @Override public void update(final boolean fullUpdate) { int playerCount = lobby.getNumberOfSlots(); diff --git a/forge-gui-mobile/src/forge/screens/limited/LoadDraftScreen.java b/forge-gui-mobile/src/forge/screens/limited/LoadDraftScreen.java index b4bf46ab85e..712377114a2 100644 --- a/forge-gui-mobile/src/forge/screens/limited/LoadDraftScreen.java +++ b/forge-gui-mobile/src/forge/screens/limited/LoadDraftScreen.java @@ -20,6 +20,7 @@ import forge.gamemodes.match.HostedMatch; import forge.gui.FThreads; import forge.gui.GuiBase; import forge.gui.util.SGuiChoose; +import forge.util.GuiPrefBinders; import forge.itemmanager.DeckManager; import forge.itemmanager.ItemManagerConfig; import forge.itemmanager.filters.ItemFilter; @@ -32,6 +33,7 @@ import forge.screens.home.LoadGameMenu; import forge.toolbox.FComboBox; import forge.toolbox.FLabel; import forge.toolbox.FOptionPane; +import forge.util.Utils; public class LoadDraftScreen extends LaunchScreen { private final DeckManager lstDecks = add(new DeckManager(GameType.Draft)); @@ -44,6 +46,12 @@ public class LoadDraftScreen extends LaunchScreen { private final FLabel lblMode = add(new FLabel.Builder().text(Forge.getLocalizer().getMessage("lblMode")).font(GAME_MODE_FONT).build()); private final FComboBox cbMode = add(new FComboBox<>()); + // Max games in a match frame and variables + private final FLabel lblGamesInMatch = add(new FLabel.Builder().text(Forge.getLocalizer().getMessage("lblMatch") + ":").font(GAME_MODE_FONT).build()); + private final FComboBox cbGamesInMatch = add(new FComboBox<>()); + private final GuiPrefBinders.ComboBox cbGamesInMatchBinder = new GuiPrefBinders.ComboBox( + FPref.UI_MATCHES_PER_GAME, cbGamesInMatch); + public LoadDraftScreen() { super(null, LoadGameMenu.getMenu()); @@ -53,12 +61,18 @@ public class LoadDraftScreen extends LaunchScreen { lstDecks.setup(ItemManagerConfig.DRAFT_DECKS); lstDecks.setItemActivateHandler(event -> editSelectedDeck()); + + cbGamesInMatch.setFont(GAME_MODE_FONT); + cbGamesInMatch.addItem("1"); + cbGamesInMatch.addItem("3"); + cbGamesInMatch.addItem("5"); } @Override public void onActivate() { lstDecks.setPool(DeckProxy.getAllDraftDecks()); lstDecks.setSelectedString(DeckPreferences.getDraftDeck()); + cbGamesInMatchBinder.load(); } private void editSelectedDeck() { @@ -78,8 +92,16 @@ public class LoadDraftScreen extends LaunchScreen { float listHeight = height - labelHeight - y - FDeckChooser.PADDING; float comboBoxHeight = cbMode.getHeight(); - lblMode.setBounds(x, y, lblMode.getAutoSizeBounds().width + FDeckChooser.PADDING / 2, comboBoxHeight); - cbMode.setBounds(x + lblMode.getWidth(), y, w - lblMode.getWidth(), comboBoxHeight); + float x2 = x; + float w1 = lblMode.getAutoSizeBounds().width; + float w2 = lblGamesInMatch.getAutoSizeBounds().width; + lblMode.setBounds(x2, y, w1 + FDeckChooser.PADDING / 2, comboBoxHeight); + x2 += lblMode.getWidth(); + cbMode.setBounds(x2, y, w - x2 - w2 - Utils.AVG_FINGER_WIDTH, comboBoxHeight); + x2 += cbMode.getWidth(); + lblGamesInMatch.setBounds(x2, y, w2 + FDeckChooser.PADDING / 2, comboBoxHeight); + x2 += lblGamesInMatch.getWidth(); + cbGamesInMatch.setBounds(x2, y, Utils.AVG_FINGER_WIDTH, comboBoxHeight); y += comboBoxHeight + FDeckChooser.PADDING; lstDecks.setBounds(x, y, w, listHeight); y += listHeight + FDeckChooser.PADDING; diff --git a/forge-gui-mobile/src/forge/screens/limited/LoadSealedScreen.java b/forge-gui-mobile/src/forge/screens/limited/LoadSealedScreen.java index 27a4f1f176e..87f618ecd4d 100644 --- a/forge-gui-mobile/src/forge/screens/limited/LoadSealedScreen.java +++ b/forge-gui-mobile/src/forge/screens/limited/LoadSealedScreen.java @@ -20,6 +20,7 @@ import forge.gamemodes.match.HostedMatch; import forge.gui.FThreads; import forge.gui.GuiBase; import forge.gui.util.SGuiChoose; +import forge.util.GuiPrefBinders; import forge.itemmanager.DeckManager; import forge.itemmanager.ItemManagerConfig; import forge.itemmanager.filters.ItemFilter; @@ -32,6 +33,7 @@ import forge.screens.home.LoadGameMenu; import forge.toolbox.FComboBox; import forge.toolbox.FLabel; import forge.toolbox.FOptionPane; +import forge.util.Utils; public class LoadSealedScreen extends LaunchScreen { private final DeckManager lstDecks = add(new DeckManager(GameType.Draft)); @@ -44,6 +46,12 @@ public class LoadSealedScreen extends LaunchScreen { private final FLabel lblMode = add(new FLabel.Builder().text(Forge.getLocalizer().getMessage("lblMode")).font(GAME_MODE_FONT).build()); private final FComboBox cbMode = add(new FComboBox<>()); + // Max games in a match frame and variables + private final FLabel lblGamesInMatch = add(new FLabel.Builder().text(Forge.getLocalizer().getMessage("lblMatch") + ":").font(GAME_MODE_FONT).build()); + private final FComboBox cbGamesInMatch = add(new FComboBox<>()); + private final GuiPrefBinders.ComboBox cbGamesInMatchBinder = new GuiPrefBinders.ComboBox( + FPref.UI_MATCHES_PER_GAME, cbGamesInMatch); + public LoadSealedScreen() { super(null, LoadGameMenu.getMenu()); @@ -53,12 +61,18 @@ public class LoadSealedScreen extends LaunchScreen { lstDecks.setup(ItemManagerConfig.SEALED_DECKS); lstDecks.setItemActivateHandler(event -> editSelectedDeck()); + + cbGamesInMatch.setFont(GAME_MODE_FONT); + cbGamesInMatch.addItem("1"); + cbGamesInMatch.addItem("3"); + cbGamesInMatch.addItem("5"); } @Override public void onActivate() { lstDecks.setPool(DeckProxy.getAllSealedDecks()); lstDecks.setSelectedString(DeckPreferences.getSealedDeck()); + cbGamesInMatchBinder.load(); } private void editSelectedDeck() { @@ -78,8 +92,16 @@ public class LoadSealedScreen extends LaunchScreen { float listHeight = height - labelHeight - y - FDeckChooser.PADDING; float comboBoxHeight = cbMode.getHeight(); - lblMode.setBounds(x, y, lblMode.getAutoSizeBounds().width + FDeckChooser.PADDING / 2, comboBoxHeight); - cbMode.setBounds(x + lblMode.getWidth(), y, w - lblMode.getWidth(), comboBoxHeight); + float x2 = x; + float w1 = lblMode.getAutoSizeBounds().width; + float w2 = lblGamesInMatch.getAutoSizeBounds().width; + lblMode.setBounds(x2, y, w1 + FDeckChooser.PADDING / 2, comboBoxHeight); + x2 += lblMode.getWidth(); + cbMode.setBounds(x2, y, w - x2 - w2 - Utils.AVG_FINGER_WIDTH, comboBoxHeight); + x2 += cbMode.getWidth(); + lblGamesInMatch.setBounds(x2, y, w2 + FDeckChooser.PADDING / 2, comboBoxHeight); + x2 += lblGamesInMatch.getWidth(); + cbGamesInMatch.setBounds(x2, y, Utils.AVG_FINGER_WIDTH, comboBoxHeight); y += comboBoxHeight + FDeckChooser.PADDING; lstDecks.setBounds(x, y, w, listHeight); y += listHeight + FDeckChooser.PADDING; diff --git a/forge-gui-mobile/src/forge/util/GuiPrefBinders.java b/forge-gui-mobile/src/forge/util/GuiPrefBinders.java new file mode 100644 index 00000000000..20e4c456ff9 --- /dev/null +++ b/forge-gui-mobile/src/forge/util/GuiPrefBinders.java @@ -0,0 +1,23 @@ +package forge.util; + +import forge.toolbox.FComboBox; +import forge.localinstance.properties.ForgePreferences; +import forge.model.FPrefsBinder; + +public class GuiPrefBinders { + public static final class ComboBox extends FPrefsBinder, String> { + public ComboBox(ForgePreferences.FPref key, FComboBox box) { + super( + key, + box, + b -> (String) b.getSelectedItem(), + (b, s) -> b.setSelectedItem(s), + s -> s, + s -> s); + + box.setChangedHandler(e -> { + this.save(); + }); + } + } +} diff --git a/forge-gui/src/main/java/forge/model/FPrefsBinder.java b/forge-gui/src/main/java/forge/model/FPrefsBinder.java new file mode 100644 index 00000000000..2aa23be6afb --- /dev/null +++ b/forge-gui/src/main/java/forge/model/FPrefsBinder.java @@ -0,0 +1,56 @@ +package forge.model; + +import java.util.function.BiConsumer; +import java.util.function.Function; + +import forge.localinstance.properties.ForgePreferences; + +/** + * Binds any GUI component to a Forge preference key + */ + +public class FPrefsBinder implements ModelBinder { + private final ForgePreferences.FPref prefKey; + private final C component; + + /** Extracts a value from the component */ + private final Function extractor; + + /** Pushes a value back into the component */ + private final BiConsumer applier; + + /** conversions between pref store value and component value */ + private final Function toString; + private final Function fromString; + + public FPrefsBinder(ForgePreferences.FPref prefKey, + C component, + Function extractor, + BiConsumer applier, + Function toString, + Function fromString) { + this.prefKey = prefKey; + this.component = component; + this.extractor = extractor; + this.applier = applier; + this.toString = toString; + this.fromString = fromString; + } + + public void load() { + ForgePreferences prefs = FModel.getPreferences(); + String prefValue = prefs.getPref(prefKey); + if (prefValue != null) { + V value = fromString.apply(prefValue); + applier.accept(component, value); + } + } + + public void save() { + ForgePreferences prefs = FModel.getPreferences(); + V value = extractor.apply(component); + String prefValue = toString.apply(value); + prefs.setPref(prefKey, prefValue); + prefs.save(); + } +} diff --git a/forge-gui/src/main/java/forge/model/ModelBinder.java b/forge-gui/src/main/java/forge/model/ModelBinder.java new file mode 100644 index 00000000000..2d81c45d5ca --- /dev/null +++ b/forge-gui/src/main/java/forge/model/ModelBinder.java @@ -0,0 +1,10 @@ +package forge.model; + +/** + * Binds any GUI component to a Forge preference key + */ + +public interface ModelBinder { + public void load(); + public void save(); +}