GUI, preferences: added default gui size settings on app's first run (preset selected due screen resolution, part of #969)

This commit is contained in:
Oleg Agafonov 2024-08-14 13:31:47 +04:00
parent 9e6ffe7521
commit 3823e95c4f
4 changed files with 230 additions and 67 deletions

View file

@ -108,7 +108,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
private final ConnectDialog connectDialog; private final ConnectDialog connectDialog;
private final ErrorDialog errorDialog; private final ErrorDialog errorDialog;
private static CallbackClient callbackClient; private static CallbackClient callbackClient;
private static final Preferences PREFS = Preferences.userNodeForPackage(MageFrame.class); private static Preferences PREFS = null;
private final JPanel fakeTopPanel; private final JPanel fakeTopPanel;
private WhatsNewDialog whatsNewDialog; // can be null private WhatsNewDialog whatsNewDialog; // can be null
private JLabel title; private JLabel title;
@ -142,14 +142,16 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
private static long startTime; private static long startTime;
/**
* @return the session
*/
public static JDesktopPane getDesktop() { public static JDesktopPane getDesktop() {
return desktopPane; return desktopPane;
} }
// TODO: migrate to own preferences like MageSettings and add ready-only and fresh install modes support
// current workaround - delete or rename whole registry tree in HKEY_CURRENT_USER\Software\JavaSoft\Prefs\mage\client
public static Preferences getPreferences() { public static Preferences getPreferences() {
if (PREFS == null) {
PREFS = Preferences.userNodeForPackage(MageFrame.class);
}
return PREFS; return PREFS;
} }
@ -885,7 +887,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
} }
public boolean autoConnect() { public boolean autoConnect() {
boolean autoConnectParamValue = startUser != null || Boolean.parseBoolean(PREFS.get("autoConnect", "false")); boolean autoConnectParamValue = startUser != null || Boolean.parseBoolean(MageFrame.getPreferences().get("autoConnect", "false"));
boolean status = false; boolean status = false;
if (autoConnectParamValue) { if (autoConnectParamValue) {
LOGGER.info("Auto-connecting to " + MagePreferences.getServerAddress()); LOGGER.info("Auto-connecting to " + MagePreferences.getServerAddress());
@ -900,11 +902,11 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
int port = MagePreferences.getLastServerPort(); int port = MagePreferences.getLastServerPort();
String userName = MagePreferences.getLastServerUser(); String userName = MagePreferences.getLastServerUser();
String password = MagePreferences.getLastServerPassword(); String password = MagePreferences.getLastServerPassword();
String proxyServer = PREFS.get("proxyAddress", ""); String proxyServer = MageFrame.getPreferences().get("proxyAddress", "");
int proxyPort = Integer.parseInt(PREFS.get("proxyPort", "0")); int proxyPort = Integer.parseInt(MageFrame.getPreferences().get("proxyPort", "0"));
ProxyType proxyType = ProxyType.valueByText(PREFS.get("proxyType", "None")); ProxyType proxyType = ProxyType.valueByText(MageFrame.getPreferences().get("proxyType", "None"));
String proxyUsername = PREFS.get("proxyUsername", ""); String proxyUsername = MageFrame.getPreferences().get("proxyUsername", "");
String proxyPassword = PREFS.get("proxyPassword", ""); String proxyPassword = MageFrame.getPreferences().get("proxyPassword", "");
setCursor(new Cursor(Cursor.WAIT_CURSOR)); setCursor(new Cursor(Cursor.WAIT_CURSOR));
currentConnection = new Connection(); currentConnection = new Connection();
currentConnection.setUsername(userName); currentConnection.setUsername(userName);
@ -1559,6 +1561,35 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
splash.update(); splash.update();
} }
} }
// auto-update user settings here
// use case examples:
// - delete outdated data
// - migrate to new files formats
// - etc
int settingsVersion = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_SETTINGS_VERSION, 0);
if (settingsVersion == 0) {
// fresh install or first run after 2024-08-14
// find best GUI size settings due screen resolution and DPI
LOGGER.info("settings: it's a first run, trying to apply GUI size settings");
int screenDPI = Toolkit.getDefaultToolkit().getScreenResolution();
int screenHeight = Toolkit.getDefaultToolkit().getScreenSize().height;
LOGGER.info(String.format("settings: screen DPI - %d, screen height - %d", screenDPI, screenHeight));
// find preset for
String preset = PreferencesDialog.getDefaultSizeSettings().findBestPreset(screenDPI, screenHeight);
if (preset != null) {
LOGGER.info("settings: selected preset " + preset);
PreferencesDialog.getDefaultSizeSettings().applyPreset(preset);
} else {
LOGGER.info("settings: WARNING, can't find compatible preset, use Preferences - GUI Size to setup your app");
}
PreferencesDialog.saveValue(PreferencesDialog.KEY_SETTINGS_VERSION, String.valueOf(1));
}
// FIRST GUI CALL (create main window with all prepared frames, dialogs, etc)
try { try {
instance = new MageFrame(); instance = new MageFrame();
} catch (Throwable e) { } catch (Throwable e) {

View file

@ -160,9 +160,6 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
jToggleCardView.setToolTipText(jToggleCardView.getToolTipText() + " (works only up to " + CardGrid.MAX_IMAGES + " cards)."); jToggleCardView.setToolTipText(jToggleCardView.getToolTipText() + " (works only up to " + CardGrid.MAX_IMAGES + " cards).");
} }
/**
* Free all references
*/
public void cleanUp() { public void cleanUp() {
this.cardGrid.clear(); this.cardGrid.clear();
this.mainModel.clear(); this.mainModel.clear();

View file

@ -34,6 +34,7 @@ import java.util.List;
import java.util.*; import java.util.*;
import java.util.prefs.BackingStoreException; import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences; import java.util.prefs.Preferences;
import java.util.stream.Collectors;
import static mage.client.constants.Constants.AUTO_TARGET_NON_FEEL_BAD; import static mage.client.constants.Constants.AUTO_TARGET_NON_FEEL_BAD;
import static mage.constants.Constants.*; import static mage.constants.Constants.*;
@ -323,6 +324,9 @@ public class PreferencesDialog extends javax.swing.JDialog {
public static final String KEY_CONNECT_AUTO_CONNECT = "autoConnect"; public static final String KEY_CONNECT_AUTO_CONNECT = "autoConnect";
public static final String KEY_CONNECT_FLAG = "connectFlag"; public static final String KEY_CONNECT_FLAG = "connectFlag";
// auto-update settings on first run
public static final String KEY_SETTINGS_VERSION = "settingsVersion";
private static final Map<String, String> CACHE = new HashMap<>(); private static final Map<String, String> CACHE = new HashMap<>();
public static final String OPEN_CONNECTION_TAB = "Open-Connection-Tab"; public static final String OPEN_CONNECTION_TAB = "Open-Connection-Tab";
@ -344,12 +348,119 @@ public class PreferencesDialog extends javax.swing.JDialog {
private static boolean isLoadingSizes = false; private static boolean isLoadingSizes = false;
private static boolean isLoadingTheme = false; private static boolean isLoadingTheme = false;
// GUI size default settings // GUI default size settings
private final Map<String, DefaultSizeSetting> defaultSizeSettings = new LinkedHashMap<>(); private static final DefaultSizeSettings defaultSizeSettings = new DefaultSizeSettings();
static class DefaultSizeSetting { public static class DefaultSizeSettings {
String name;
Map<String, Integer> values; // settings key, value private final List<String> settingKeys = new ArrayList<>();
private final Map<String, List<Integer>> presetValues = new LinkedHashMap<>();
private final Map<String, Integer> presetMinHeights = new LinkedHashMap<>(); // preset name, minimum screen height
public DefaultSizeSettings() {
// prepare default size settings
// warning, make sure it use same order as createSizeSetting below
settingKeys.add(KEY_GUI_DIALOG_FONT_SIZE);
settingKeys.add(KEY_GUI_CHAT_FONT_SIZE);
settingKeys.add(KEY_GUI_CARD_EDITOR_SIZE);
settingKeys.add(KEY_GUI_TOOLTIP_SIZE);
//
settingKeys.add(KEY_GUI_PLAYER_PANEL_SIZE);
settingKeys.add(KEY_GUI_CARD_BATTLEFIELD_SIZE);
settingKeys.add(KEY_GUI_CARD_HAND_SIZE);
settingKeys.add(KEY_GUI_CARD_OTHER_ZONES_SIZE);
// x6 groups allowed here
// minimum system requirements: screen height > 750
// lower settings possible, but it's hard to use due low text and image quality
presetMinHeights.put("1366 x 768", 768);
presetValues.put("1366 x 768", Arrays.asList(
10, 15, 17, 15,
10, 22, 14, 13
));
presetMinHeights.put("1920 x 1080", 1080);
presetValues.put("1920 x 1080", Arrays.asList(
17, 18, 23, 20,
14, 30, 22, 21
));
presetMinHeights.put("2560 x 1440", 1440);
presetValues.put("2560 x 1440", Arrays.asList(
23, 25, 35, 31,
18, 42, 31, 28
));
presetMinHeights.put("3840 x 2160", 2160);
presetValues.put("3840 x 2160", Arrays.asList(
34, 37, 50, 55,
27, 64, 50, 44
));
}
public List<String> getAllPresets() {
return new ArrayList<>(presetValues.keySet());
}
public List<Integer> getPresetValues(String presetName) {
List<Integer> res = presetValues.getOrDefault(presetName, null);
if (res == null) {
throw new IllegalArgumentException("Wrong code usage: unknown size settings preset name " + presetName);
}
return res;
}
public String findBestPreset() {
int screenDPI = Toolkit.getDefaultToolkit().getScreenResolution();
int screenHeight = Toolkit.getDefaultToolkit().getScreenSize().height;
return findBestPreset(screenDPI, screenHeight);
}
public String findBestPreset(int screenDPI, int screenHeight) {
// TODO: add java HiDPI monitors support here (or do not use preset on gui scale?)
// TODO: test with windows DPI settings
// TODO: test with windows compatibility settings https://github.com/magefree/mage/issues/969#issuecomment-2016809163
// TODO: test with java 9 scale command line params https://github.com/magefree/mage/issues/969#issuecomment-671055642
// find min preset (for too small screens)
String minPossiblePreset = null;
int minPossibleRes = Integer.MAX_VALUE;
for (String preset : presetMinHeights.keySet()) {
int res = presetMinHeights.get(preset);
if (res < minPossibleRes) {
minPossibleRes = res;
minPossiblePreset = preset;
}
}
if (minPossiblePreset == null) {
throw new IllegalArgumentException("must found min preset all the time");
}
// find max preset
String maxPossiblePreset = null;
int maxPossibleRes = Integer.MIN_VALUE;
for (String preset : presetMinHeights.keySet()) {
int res = presetMinHeights.get(preset);
if (res <= screenHeight && res > maxPossibleRes) {
maxPossibleRes = res;
maxPossiblePreset = preset;
}
}
return maxPossiblePreset != null ? maxPossiblePreset : minPossiblePreset;
}
public String getSettingsKeyByIndex(int index) {
return settingKeys.get(index);
}
public void applyPreset(String presetName) {
// WARNING, it's apply settings directly to storage and cache, so opened preferences dialog will be outdated
// so usage example: app's starting routine
List<Integer> values = getPresetValues(presetName);
for (int i = 0; i < values.size(); i++) {
String settingsKey = getSettingsKeyByIndex(i);
int settingsValue = values.get(i);
saveValue(settingsKey, String.valueOf(settingsValue));
}
}
} }
// GUI size settings // GUI size settings
@ -566,7 +677,8 @@ public class PreferencesDialog extends javax.swing.JDialog {
addAvatars(); addAvatars();
// prepare size table (you can change settings order by new position index) // prepare size table (you can change settings order by new position index)
// warning, if you change default values then make sure calculateGUISizes uses same // WARNING, if you change default values then make sure calculateGUISizes uses same
// WARNING, make sure DefaultSizeSettings uses same settings keys
// App's elements (from position 1) // App's elements (from position 1)
createSizeSetting(1, KEY_GUI_DIALOG_FONT_SIZE, 14, false, "Font in dialogs and menu", "The size of the font of messages, menu, dialogs and other windows"); createSizeSetting(1, KEY_GUI_DIALOG_FONT_SIZE, 14, false, "Font in dialogs and menu", "The size of the font of messages, menu, dialogs and other windows");
createSizeSetting(2, KEY_GUI_CHAT_FONT_SIZE, 14, false, "Font in logs and chats", "The size of the font used to display the chat text"); createSizeSetting(2, KEY_GUI_CHAT_FONT_SIZE, 14, false, "Font in logs and chats", "The size of the font used to display the chat text");
@ -578,55 +690,40 @@ public class PreferencesDialog extends javax.swing.JDialog {
createSizeSetting(10, KEY_GUI_CARD_HAND_SIZE, 14, false, "Size of cards in hand and stack", "The size of the card images in hand and on the stack"); createSizeSetting(10, KEY_GUI_CARD_HAND_SIZE, 14, false, "Size of cards in hand and stack", "The size of the card images in hand and on the stack");
createSizeSetting(11, KEY_GUI_CARD_OTHER_ZONES_SIZE, 14, false, "Size of cards in other zones", "The size of card in other game zone (e.g. graveyard, revealed cards etc.)"); createSizeSetting(11, KEY_GUI_CARD_OTHER_ZONES_SIZE, 14, false, "Size of cards in other zones", "The size of card in other game zone (e.g. graveyard, revealed cards etc.)");
// protection from wrong keys amount
if (sizeSettings.size() != defaultSizeSettings.presetValues.values().stream().findFirst().get().size()) {
throw new IllegalArgumentException("Wrong code usage: size and default size settings must contains same records");
} else {
// protection from wrong keys order
List<String> keys = new ArrayList<>(sizeSettings.keySet());
for (int i = 0; i < keys.size(); i++) {
if (!defaultSizeSettings.getSettingsKeyByIndex(i).equals(keys.get(i))) {
throw new IllegalArgumentException("Wrong code usage: size and default size settings must use same ordered keys");
}
}
}
// hide unused controls // hide unused controls
hideUnusedSizeSettings(); hideUnusedSizeSettings();
// prepare default size settings // prepare default size settings
// warning, make sure it use same order as createSizeSetting above
List<String> codes = new ArrayList<>();
codes.add(KEY_GUI_DIALOG_FONT_SIZE);
codes.add(KEY_GUI_CHAT_FONT_SIZE);
codes.add(KEY_GUI_CARD_EDITOR_SIZE);
codes.add(KEY_GUI_TOOLTIP_SIZE);
//
codes.add(KEY_GUI_PLAYER_PANEL_SIZE);
codes.add(KEY_GUI_CARD_BATTLEFIELD_SIZE);
codes.add(KEY_GUI_CARD_HAND_SIZE);
codes.add(KEY_GUI_CARD_OTHER_ZONES_SIZE);
// x6 groups allowed here
// minimum system requirements: screen height > 750
// lower settings possible, but it's hard to use due low text and image quality
Map<String, List<Integer>> sizes = new LinkedHashMap<>();
sizes.put("1366 x 768", Arrays.asList(
10, 15, 17, 15,
10, 22, 14, 13
));
sizes.put("1920 x 1080", Arrays.asList(
17, 18, 23, 20,
14, 30, 22, 21
));
sizes.put("2560 x 1440", Arrays.asList(
23, 25, 35, 31,
18, 42, 31, 28
));
sizes.put("3840 x 2160", Arrays.asList(
34, 37, 50, 55,
27, 64, 50, 44
));
// set new settings on button clicks // set new settings on button clicks
int position = 0; int position = 0;
for (String groupName : sizes.keySet()) { String recommendedPreset = defaultSizeSettings.findBestPreset();
for (String presetName : defaultSizeSettings.getAllPresets()) {
position++; position++;
JButton button = GUISizeHelper.getComponentByFieldName(this, "buttonSizeDefault" + position); JButton button = GUISizeHelper.getComponentByFieldName(this, "buttonSizeDefault" + position);
button.setText(groupName); String buttonName = presetName;
if (presetName.equals(recommendedPreset)) {
buttonName += " (recommended)";
}
button.setText(buttonName);
button.addActionListener(e -> { button.addActionListener(e -> {
isLoadingSizes = true; isLoadingSizes = true;
try { try {
List<Integer> values = sizes.get(groupName); List<Integer> values = defaultSizeSettings.getPresetValues(presetName);
for (int i = 0; i < values.size(); i++) { for (int i = 0; i < values.size(); i++) {
sizeSettings.get(codes.get(i)).slider.setValue(values.get(i)); sizeSettings.get(defaultSizeSettings.getSettingsKeyByIndex(i)).slider.setValue(values.get(i));
} }
} finally { } finally {
isLoadingSizes = false; isLoadingSizes = false;
@ -3791,9 +3888,8 @@ public class PreferencesDialog extends javax.swing.JDialog {
prefs.put(key, value); prefs.put(key, value);
try { try {
prefs.flush(); prefs.flush();
} catch (BackingStoreException ex) { } catch (BackingStoreException e) {
ex.printStackTrace(); logger.error("Can't save preferences " + key + " due " + e, e);
JOptionPane.showMessageDialog(null, "Error: couldn't save preferences. Please try once again.");
} }
updateCache(key, value); updateCache(key, value);
} }
@ -3917,6 +4013,10 @@ public class PreferencesDialog extends javax.swing.JDialog {
); );
} }
public static DefaultSizeSettings getDefaultSizeSettings() {
return defaultSizeSettings;
}
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JScrollPane avatarPane; private javax.swing.JScrollPane avatarPane;
private javax.swing.JPanel avatarPanel; private javax.swing.JPanel avatarPanel;

View file

@ -1,6 +1,8 @@
package mage.client.preference; package mage.client.preference;
import mage.client.dialog.PreferencesDialog;
import org.junit.After; import org.junit.After;
import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -43,4 +45,37 @@ public class MagePreferencesTest {
assertFalse(MagePreferences.ignoreList("test.com.xx").contains("test")); assertFalse(MagePreferences.ignoreList("test.com.xx").contains("test"));
assertFalse(MagePreferences.ignoreList("test.com.xx").contains("lul")); assertFalse(MagePreferences.ignoreList("test.com.xx").contains("lul"));
} }
@Test
public void testGuiSizeRecommends() {
// possible presets
// 1366 x 768
// 1920 x 1080
// 2560 x 1440
// 3840 x 2160
PreferencesDialog.DefaultSizeSettings defaultSizeSettings = PreferencesDialog.getDefaultSizeSettings();
String needPreset = "1366 x 768";
Assert.assertEquals(needPreset, defaultSizeSettings.findBestPreset(96, 0));
Assert.assertEquals(needPreset, defaultSizeSettings.findBestPreset(96, 100));
Assert.assertEquals(needPreset, defaultSizeSettings.findBestPreset(96, 767));
Assert.assertEquals(needPreset, defaultSizeSettings.findBestPreset(96, 768));
Assert.assertEquals(needPreset, defaultSizeSettings.findBestPreset(96, 769));
Assert.assertEquals(needPreset, defaultSizeSettings.findBestPreset(96, 1079));
needPreset = "1920 x 1080";
Assert.assertEquals(needPreset, defaultSizeSettings.findBestPreset(96, 1080));
Assert.assertEquals(needPreset, defaultSizeSettings.findBestPreset(96, 1081));
Assert.assertEquals(needPreset, defaultSizeSettings.findBestPreset(96, 1439));
needPreset = "2560 x 1440";
Assert.assertEquals(needPreset, defaultSizeSettings.findBestPreset(96, 1440));
Assert.assertEquals(needPreset, defaultSizeSettings.findBestPreset(96, 1441));
Assert.assertEquals(needPreset, defaultSizeSettings.findBestPreset(96, 2159));
needPreset = "3840 x 2160";
Assert.assertEquals(needPreset, defaultSizeSettings.findBestPreset(96, 2160));
Assert.assertEquals(needPreset, defaultSizeSettings.findBestPreset(96, 5000));
Assert.assertEquals(needPreset, defaultSizeSettings.findBestPreset(96, 5000));
}
} }