package games.strategy.triplea.settings;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.util.Arrays;
import java.util.List;
import javax.swing.Box;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.border.BevelBorder;
import games.strategy.engine.ClientContext;
import games.strategy.triplea.settings.ai.AiTab;
import games.strategy.triplea.settings.battle.calc.BattleCalcTab;
import games.strategy.triplea.settings.battle.options.BattleOptionsTab;
import games.strategy.triplea.settings.folders.FoldersTab;
import games.strategy.triplea.settings.scrolling.ScrollSettingsTab;
import games.strategy.ui.SwingComponents;
/**
* Window that contains a tabbed panel with preference categories, each tab contains fields that allow users to update
* game settings. The window handles generic logic around preferences, each tab will specify configuration values for
* the settings.
*
* <p>
* Overall layout:
* </p>
*
* <ul>
* <li>Primary element is a JTabbed pain, the contents are organized into rows, one row per option presented to the
* user.</li>
* </ul>
*
* <p>
* Each row consists of: label, swing input, detailed description
* </p>
*
* <p>
* Then we have some buttons:
* </p>
*
* <ul>
* <li>default (revert) settings</li>
* <li>save settings</li>
* <li>close window</li>
* </ul>
*/
public class SettingsWindow extends SwingComponents.ModalJDialog {
private static final long serialVersionUID = 8108714206041495198L;
private static final int MAX_WIDTH = 1100;
private static final int TEXT_LABEL_WIDTH = MAX_WIDTH / 4;
private static final int ROW_HEIGHT = 60;
/**
* Shows the settings window. The window is modal (a user therefore cannot open multiple at a time)
*/
public static void showWindow() {
SwingComponents.showWindow(new SettingsWindow(
new BattleOptionsTab(ClientContext.battleOptionsSettings()),
new BattleCalcTab(ClientContext.battleCalcSettings()),
new AiTab(ClientContext.aiSettings()),
new ScrollSettingsTab(ClientContext.scrollSettings()),
new FoldersTab(ClientContext.folderSettings())));
}
private SettingsWindow(final SettingsTab<?>... tabs) {
add(buildTabbedPane(tabs), BorderLayout.CENTER);
}
private JTabbedPane buildTabbedPane(final SettingsTab<?>... tabs) {
final JTabbedPane pane = SwingComponents.newJTabbedPane();
Arrays.asList(tabs).forEach(tab -> pane.addTab(tab.getTabTitle(), createTabWindow(tab)));
return pane;
}
private <T extends HasDefaults> Component createTabWindow(final SettingsTab<T> settingTab) {
final List<SettingInputComponent<T>> inputs = settingTab.getInputs();
final JPanel settingsPanel = SwingComponents.newJPanelWithVerticalBoxLayout();
final int topOfWindowPadding = 20;
settingsPanel.add(Box.createVerticalStrut(topOfWindowPadding));
inputs.forEach(input -> {
settingsPanel.add(createInputElementRow(input));
final int paddingBetweenRows = 15;
settingsPanel.add(Box.createVerticalStrut(paddingBetweenRows));
});
final JPanel panel = SwingComponents.newJPanelWithVerticalBoxLayout();
panel.add(new JScrollPane(settingsPanel));
panel.add(createButtonsPanel(settingTab));
return panel;
}
private static JPanel createInputElementRow(final SettingInputComponent<?> input) {
final JPanel contentRow = createContentRow(
createTextAndInputPanel(input),
createInputValueRangeDescription(input),
createInputDescription(input));
return SwingComponents.createRowWithTopAndBottomPadding(contentRow, 3, 5);
}
private static JPanel createContentRow(final JComponent textAndInputComponent,
final JComponent valueRangeDescriptionComponent, final JComponent descriptionComponent) {
final JPanel contentRow = SwingComponents.newJPanelWithHorizontalBoxLayout();
contentRow.setMaximumSize(new Dimension(MAX_WIDTH, ROW_HEIGHT));
final int leftHandPadding = 20;
contentRow.add(Box.createHorizontalStrut(leftHandPadding));
contentRow.add(textAndInputComponent);
// the vertical struct gives the row height
contentRow.add(Box.createVerticalStrut(ROW_HEIGHT));
contentRow.add(valueRangeDescriptionComponent);
contentRow.add(SwingComponents.newJScrollPane(descriptionComponent));
contentRow.setBorder(new BevelBorder(BevelBorder.LOWERED));
return contentRow;
}
private static JPanel createTextAndInputPanel(final SettingInputComponent<?> input) {
final JPanel labelInputPanel = SwingComponents.newJPanelWithGridLayout(1, 2);
final JLabel label = new JLabel(input.getLabel());
labelInputPanel.add(label);
final JPanel inputPanel = new JPanel();
inputPanel.add(input.getInputElement().getSwingComponent());
inputPanel.add(Box.createHorizontalGlue());
inputPanel.setMinimumSize(new Dimension(TEXT_LABEL_WIDTH, ROW_HEIGHT));
inputPanel.setPreferredSize(new Dimension(TEXT_LABEL_WIDTH, ROW_HEIGHT));
inputPanel.setMaximumSize(new Dimension(TEXT_LABEL_WIDTH, ROW_HEIGHT));
labelInputPanel.add(inputPanel);
return labelInputPanel;
}
private static JComponent createInputValueRangeDescription(final SettingInputComponent<?> input) {
return SwingComponents.newMultilineLabel(input.getValueRangeDescription(), 2, 10);
}
private static JTextArea createInputDescription(final SettingInputComponent<?> input) {
final JTextArea description = new JTextArea(input.getDescription(), 2, 50);
description.setLineWrap(true);
description.setWrapStyleWord(true);
description.setEditable(false);
return description;
}
/**
* Each element is arranged in a row, with glue in between every element.
*/
private <T extends HasDefaults> JPanel createButtonsPanel(final SettingsTab<T> settingTab) {
final JPanel buttonsPanel = SwingComponents.newJPanelWithHorizontalBoxLayout();
// instead of glue, use one vertical strut to give the buttons panel a minimum height
final int buttonPanelHeight = 50;
buttonsPanel.add(Box.createVerticalStrut(buttonPanelHeight));
buttonsPanel.add(SwingComponents.newJButton("Use Defaults",
e -> SwingComponents.promptUser("Revert to default settings?",
"Are you sure you would like revert '" + settingTab.getTabTitle() + "' back to default settings?", () -> {
settingTab.getSettingsObject().setToDefault();
SystemPreferences.flush();
dispose();
SwingComponents.showDialog("Defaults Restored",
"Reverted the '" + settingTab.getTabTitle() + "' settings back to defaults");
})));
buttonsPanel.add(Box.createHorizontalGlue());
buttonsPanel.add(SwingComponents.newJButton("Save", e -> {
settingTab.updateSettings(settingTab.getInputs());
SystemPreferences.flush();
}));
buttonsPanel.add(Box.createHorizontalGlue());
buttonsPanel.add(SwingComponents.newJButton("Close", e -> dispose()));
buttonsPanel.add(Box.createHorizontalGlue());
return buttonsPanel;
}
}