[gnome-chess] Modernized preferences
- From: Michael Catanzaro <mcatanzaro src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-chess] Modernized preferences
- Date: Fri, 19 Aug 2022 21:18:49 +0000 (UTC)
commit f8aa4eab3d15fb1ed4a0af14c0b77cbc9cc542ae
Author: Nils Lück <nils luck outlook com>
Date: Fri Aug 19 21:18:48 2022 +0000
Modernized preferences
data/chess-window.ui | 2 +-
data/chess.gresource.xml | 3 +-
data/new-game-window.ui | 158 +++++++++++
data/preferences-window.ui | 84 ++++++
data/preferences.ui | 661 --------------------------------------------
lib/chess-clock.vala | 10 +-
lib/chess-pgn.vala | 2 +-
src/gnome-chess.vala | 80 +++---
src/meson.build | 4 +-
src/new-game-window.vala | 198 +++++++++++++
src/preferences-dialog.vala | 485 --------------------------------
src/preferences-window.vala | 60 ++++
src/preferences.vala | 607 ++++++++++++++++++++++++++++++++++++++++
13 files changed, 1162 insertions(+), 1192 deletions(-)
---
diff --git a/data/chess-window.ui b/data/chess-window.ui
index 9ea3418..72b9522 100644
--- a/data/chess-window.ui
+++ b/data/chess-window.ui
@@ -4,7 +4,7 @@
<menu id="app_menu">
<section>
<item>
- <attribute name="label" translatable="yes">_New Game</attribute>
+ <attribute name="label" translatable="yes">_New Game…</attribute>
<attribute name="action">app.new</attribute>
</item>
<item>
diff --git a/data/chess.gresource.xml b/data/chess.gresource.xml
index d027685..01c536b 100644
--- a/data/chess.gresource.xml
+++ b/data/chess.gresource.xml
@@ -2,7 +2,8 @@
<gresources>
<gresource prefix="/org/gnome/Chess/ui">
<file preprocess="xml-stripblanks">chess-window.ui</file>
- <file preprocess="xml-stripblanks">preferences.ui</file>
+ <file preprocess="xml-stripblanks">new-game-window.ui</file>
+ <file preprocess="xml-stripblanks">preferences-window.ui</file>
<file preprocess="xml-stripblanks">promotion-type-selector.ui</file>
</gresource>
<gresource prefix="/org/gnome/Chess/pieces">
diff --git a/data/new-game-window.ui b/data/new-game-window.ui
new file mode 100644
index 0000000..eea93ae
--- /dev/null
+++ b/data/new-game-window.ui
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk" version="4.0"/>
+ <template class="NewGameWindow" parent="AdwPreferencesWindow">
+ <property name="title" translatable="yes">New Game</property>
+ <property name="hide-on-close">true</property>
+ <property name="modal">true</property>
+ <property name="search-enabled">false</property>
+ <property name="default_width">400</property>
+ <property name="default_height">550</property>
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="AdwHeaderBar">
+ <property name="show-start-title-buttons">false</property>
+ <property name="show-end-title-buttons">false</property>
+ <child type="start">
+ <object class="GtkButton">
+ <property name="label" translatable="yes">_Cancel</property>
+ <property name="valign">center</property>
+ <property name="use-underline">true</property>
+ <signal name="clicked" handler="gtk_window_close" object="NewGameWindow" swapped="yes"/>
+ </object>
+ </child>
+ <child type="end">
+ <object class="GtkButton">
+ <property name="label" translatable="yes">_Start</property>
+ <property name="valign">center</property>
+ <property name="use-underline">true</property>
+ <signal name="clicked" handler="start_game_cb" />
+ <style>
+ <class name="suggested-action"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwPreferencesPage">
+ <child>
+ <object class="AdwPreferencesGroup">
+ <child>
+ <object class="AdwComboRow" id="opponent_combo">
+ <property name="title" translatable="yes">_Opposing Player</property>
+ <property name="use-underline">true</property>
+ </object>
+ </child>
+ <child>
+ <object class="AdwComboRow" id="play_as_combo">
+ <property name="title" translatable="yes">_Play As</property>
+ <property name="use-underline">true</property>
+ <property name="model">
+ <object class="AdwEnumListModel">
+ <property name="enum-type">PlayAs</property>
+ </object>
+ </property>
+ <property name="expression">
+ <closure type="gchararray" function="play_as_display_name_cb" swapped="true" />
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="AdwComboRow" id="difficulty_combo">
+ <property name="title" translatable="yes">_Difficulty</property>
+ <property name="use-underline">true</property>
+ <property name="model">
+ <object class="AdwEnumListModel">
+ <property name="enum-type">Difficulty</property>
+ </object>
+ </property>
+ <property name="expression">
+ <closure type="gchararray" function="difficulty_display_name_cb" swapped="true" />
+ </property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwPreferencesGroup">
+ <property name="title">Time Limit</property>
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">_Use Time Limit</property>
+ <property name="use-underline">true</property>
+ <property name="activatable_widget">time_limit_switch</property>
+ <child>
+ <object class="GtkSwitch" id="time_limit_switch">
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">_Minutes Per Side</property>
+ <property name="use-underline">true</property>
+ <property name="sensitive" bind-source="time_limit_switch" bind-property="active"
bind-flags="default|sync-create" />
+ <property name="activatable-widget">duration_spin</property>
+ <child>
+ <object class="GtkSpinButton" id="duration_spin">
+ <property name="valign">center</property>
+ <property name="adjustment">
+ <object class="GtkAdjustment">
+ <property name="lower">1</property>
+ <property name="step-increment">1</property>
+ <property name="upper">600</property>
+ </object>
+ </property>
+ <property name="value">5.0</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">_Increment In Seconds</property>
+ <property name="use-underline">true</property>
+ <property name="sensitive" bind-source="time_limit_switch" bind-property="active"
bind-flags="default|sync-create" />
+ <property name="activatable-widget">increment_spin</property>
+ <child>
+ <object class="GtkSpinButton" id="increment_spin">
+ <property name="valign">center</property>
+ <property name="adjustment">
+ <object class="GtkAdjustment">
+ <property name="lower">0</property>
+ <property name="step-increment">1</property>
+ <property name="upper">600</property>
+ </object>
+ </property>
+ <property name="value">0</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwComboRow" id="clock_type_combo" >
+ <property name="title" translatable="yes">Clock _Type</property>
+ <property name="use-underline">true</property>
+ <property name="sensitive" bind-source="time_limit_switch" bind-property="active"
bind-flags="default|sync-create" />
+ <property name="model">
+ <object class="AdwEnumListModel">
+ <property name="enum-type">ClockType</property>
+ </object>
+ </property>
+ <property name="expression">
+ <closure type="gchararray" function="clock_type_display_name_cb" swapped="true" />
+ </property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/data/preferences-window.ui b/data/preferences-window.ui
new file mode 100644
index 0000000..e4b6401
--- /dev/null
+++ b/data/preferences-window.ui
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk" version="4.0"/>
+ <template class="PreferencesWindow" parent="AdwPreferencesWindow">
+ <property name="search-enabled">false</property>
+ <property name="hide-on-close">true</property>
+ <property name="default_width">400</property>
+ <property name="default_height">380</property>
+ <child>
+ <object class="AdwPreferencesPage">
+ <child>
+ <object class="AdwPreferencesGroup">
+ <child>
+ <object class="AdwComboRow" id="board_orientation_combo">
+ <property name="title" translatable="yes">_Board Orientation</property>
+ <property name="use-underline">true</property>
+ <property name="model">
+ <object class="AdwEnumListModel">
+ <property name="enum-type">BoardOrientation</property>
+ </object>
+ </property>
+ <property name="expression">
+ <closure type="gchararray" function="board_orientation_display_name_cb" swapped="true" />
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="AdwComboRow" id="move_format_combo">
+ <property name="title" translatable="yes">_Move Format</property>
+ <property name="use-underline">true</property>
+ <property name="model">
+ <object class="AdwEnumListModel">
+ <property name="enum-type">MoveFormat</property>
+ </object>
+ </property>
+ <property name="expression">
+ <closure type="gchararray" function="move_format_display_name_cb" swapped="true" />
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="AdwComboRow" id="piece_style_combo">
+ <property name="title" translatable="yes">_Piece Style</property>
+ <property name="use-underline">true</property>
+ <property name="model">
+ <object class="AdwEnumListModel">
+ <property name="enum-type">PieceStyle</property>
+ </object>
+ </property>
+ <property name="expression">
+ <closure type="gchararray" function="piece_style_display_name_cb" swapped="true" />
+ </property>
+ </object>
+ </child>
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Board _Numbering</property>
+ <property name="use-underline">true</property>
+ <property name="activatable_widget">board_numbering_switch</property>
+ <child>
+ <object class="GtkSwitch" id="board_numbering_switch">
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Move _Hints</property>
+ <property name="use-underline">true</property>
+ <property name="activatable_widget">move_hints_switch</property>
+ <child>
+ <object class="GtkSwitch" id="move_hints_switch">
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/lib/chess-clock.vala b/lib/chess-clock.vala
index 9c0770f..fb63b86 100644
--- a/lib/chess-clock.vala
+++ b/lib/chess-clock.vala
@@ -11,7 +11,7 @@
* license.
*/
-public enum ClockType
+public enum ChessClockType
{
SIMPLE,
FISCHER,
@@ -33,7 +33,7 @@ public enum ClockType
}
}
- public static ClockType string_to_enum (string s)
+ public static ChessClockType string_to_enum (string s)
{
switch (s)
{
@@ -75,7 +75,7 @@ public class ChessClock : Object
get { return black_initial_seconds + black_extra_seconds - black_seconds_used; }
}
- public ClockType clock_type { get; set; default = ClockType.SIMPLE; }
+ public ChessClockType clock_type { get; set; default = ChessClockType.SIMPLE; }
private Color _active_color = Color.WHITE;
public Color active_color
@@ -206,13 +206,13 @@ public class ChessClock : Object
int white_move_used = 0, black_move_used = 0;
switch (clock_type)
{
- case ClockType.FISCHER:
+ case ChessClockType.FISCHER:
if (active_color == Color.WHITE)
white_extra_seconds += extra_seconds;
else
black_extra_seconds += extra_seconds;
break;
- case ClockType.BRONSTEIN:
+ case ChessClockType.BRONSTEIN:
white_move_used = white_seconds_used - white_prev_move_seconds;
black_move_used = black_seconds_used - black_prev_move_seconds;
if (active_color != Color.WHITE)
diff --git a/lib/chess-pgn.vala b/lib/chess-pgn.vala
index 2cade4b..a4ff25b 100644
--- a/lib/chess-pgn.vala
+++ b/lib/chess-pgn.vala
@@ -262,7 +262,7 @@ public class PGN : Object
game.tags.insert ("X-GNOME-BlackTimeLeft", tag_value);
break;
case "X-GNOME-ClockType":
- if (ClockType.string_to_enum (tag_value) == ClockType.INVALID)
+ if (ChessClockType.string_to_enum (tag_value) == ChessClockType.INVALID)
{
warning (_("Invalid clock type in PGN: %s, using a simple clock."), tag_value);
game.tags.insert ("X-GNOME-ClockType", "simple");
diff --git a/src/gnome-chess.vala b/src/gnome-chess.vala
index 3e8e68b..e66bfc3 100644
--- a/src/gnome-chess.vala
+++ b/src/gnome-chess.vala
@@ -30,6 +30,7 @@ public const string QUIT_ACTION_NAME = "quit";
public class ChessApplication : Adw.Application
{
private GLib.Settings settings;
+ private Preferences preferences;
public unowned ChessWindow window
{
@@ -46,7 +47,8 @@ public class ChessApplication : Adw.Application
get { return view.scene; }
}
- private PreferencesDialog? preferences_dialog = null;
+ private NewGameWindow? new_game_window = null;
+ private PreferencesWindow? preferences_window = null;
private Gtk.AboutDialog? about_dialog = null;
private Gtk.FileChooserNative? open_dialog = null;
private Gtk.FileChooserNative? save_dialog = null;
@@ -119,6 +121,7 @@ Copyright © 2015–2016 Sahil Sareen""";
base.startup ();
settings = new Settings ("org.gnome.Chess");
+ preferences = new Preferences (settings);
add_action_entries (action_entries, this);
set_accels_for_action ("app." + NEW_GAME_ACTION_NAME, { "<Control>n" });
@@ -137,16 +140,16 @@ Copyright © 2015–2016 Sahil Sareen""";
"<Control>w" });
window = new ChessWindow (this);
- window.set_default_size (settings.get_int ("width"), settings.get_int ("height"));
- if (settings.get_boolean ("maximized"))
+ window.set_default_size (settings.get_int (WIDTH_SETTINGS_KEY), settings.get_int
(HEIGHT_SETTINGS_KEY));
+ if (settings.get_boolean (MAXIMIZED_SETTINGS_KEY))
window.maximize ();
add_window (window);
- settings.bind ("show-move-hints", scene, "show-move-hints", SettingsBindFlags.GET);
- settings.bind ("show-numbering", scene, "show-numbering", SettingsBindFlags.GET);
- settings.bind ("piece-theme", scene, "theme-name", SettingsBindFlags.GET);
- settings.bind ("move-format", scene, "move-format", SettingsBindFlags.GET);
- settings.bind ("board-side", scene, "board-side", SettingsBindFlags.GET);
+ settings.bind (SHOW_MOVE_HINTS_SETTINGS_KEY, scene, "show-move-hints", SettingsBindFlags.GET);
+ settings.bind (SHOW_BOARD_NUMBERING_SETTINGS_KEY, scene, "show-numbering", SettingsBindFlags.GET);
+ settings.bind (PIECE_STYLE_SETTINGS_KEY, scene, "theme-name", SettingsBindFlags.GET);
+ settings.bind (MOVE_FORMAT_SETTINGS_KEY, scene, "move-format", SettingsBindFlags.GET);
+ settings.bind (BOARD_ORIENTATION_SETTINGS_KEY, scene, "board-side", SettingsBindFlags.GET);
scene.is_human.connect ((p) => { return p == human_player; });
scene.choose_promotion_type.connect (show_promotion_type_selector);
@@ -207,9 +210,9 @@ Copyright © 2015–2016 Sahil Sareen""";
/* Save window state */
settings.delay ();
- settings.set_int ("width", window.default_width);
- settings.set_int ("height", window.default_height);
- settings.set_boolean ("maximized", window.maximized);
+ settings.set_int (WIDTH_SETTINGS_KEY, window.default_width);
+ settings.set_int (HEIGHT_SETTINGS_KEY, window.default_height);
+ settings.set_boolean (MAXIMIZED_SETTINGS_KEY, window.maximized);
settings.apply ();
base.shutdown ();
@@ -275,7 +278,7 @@ Copyright © 2015–2016 Sahil Sareen""";
catch (Error e)
{
show_invalid_move_dialog (e.message);
- start_new_game ();
+ configure_new_game ();
return;
}
@@ -396,16 +399,16 @@ Copyright © 2015–2016 Sahil Sareen""";
timer_increment_adj_value = int.parse (pgn_game.timer_increment);
else
{
- timer_increment_adj_value = settings.get_int ("timer-increment");
+ timer_increment_adj_value = settings.get_int (INCREMENT_SETTINGS_KEY);
pgn_game.timer_increment = timer_increment_adj_value.to_string ();
}
- ClockType clock_type = ClockType.SIMPLE;
+ ChessClockType clock_type = ChessClockType.SIMPLE;
if (pgn_game.clock_type != null)
- clock_type = ClockType.string_to_enum (pgn_game.clock_type);
+ clock_type = ChessClockType.string_to_enum (pgn_game.clock_type);
else
{
- clock_type = ClockType.string_to_enum (settings.get_string ("clock-type"));
+ clock_type = ChessClockType.string_to_enum (settings.get_string (CLOCK_TYPE_SETTINGS_KEY));
pgn_game.clock_type = clock_type.to_string ();
}
@@ -689,8 +692,6 @@ Copyright © 2015–2016 Sahil Sareen""";
{
/* Warning: this callback is invoked several times when loading a game. */
- enable_action (NEW_GAME_ACTION_NAME);
-
/* Need to save after each move */
game_needs_saving = true;
@@ -755,7 +756,6 @@ Copyright © 2015–2016 Sahil Sareen""";
else
{
game_needs_saving = false;
- disable_action (NEW_GAME_ACTION_NAME);
disable_action (SAVE_GAME_ACTION_NAME);
disable_action (SAVE_GAME_AS_ACTION_NAME);
}
@@ -838,9 +838,6 @@ Copyright © 2015–2016 Sahil Sareen""";
disable_action (RESIGN_ACTION_NAME);
disable_action (PAUSE_RESUME_ACTION_NAME);
- /* In case of engine desync before the first move, or after undo */
- enable_action (NEW_GAME_ACTION_NAME);
-
game_needs_saving = false;
string what = "";
@@ -1060,7 +1057,7 @@ Copyright © 2015–2016 Sahil Sareen""";
{
prompt_save_game (_("Save this game before starting a new one?"), (cancelled) => {
if (!cancelled)
- start_new_game ();
+ configure_new_game ();
});
}
@@ -1162,17 +1159,29 @@ Copyright © 2015–2016 Sahil Sareen""";
scene.move_number = -1;
}
+ private void configure_new_game ()
+ {
+ if (new_game_window != null)
+ {
+ new_game_window.show ();
+ return;
+ }
+
+ new_game_window = new NewGameWindow (window, preferences, ai_profiles);
+ new_game_window.new_game_requested.connect (() => start_new_game ());
+ new_game_window.show ();
+ }
+
private void preferences_cb ()
{
- if (preferences_dialog != null)
+ if (preferences_window != null)
{
- preferences_dialog.show ();
+ preferences_window.show ();
return;
}
- preferences_dialog = new PreferencesDialog (window, settings, ai_profiles);
- preferences_dialog.response.connect (() => preferences_dialog.hide ());
- preferences_dialog.show ();
+ preferences_window = new PreferencesWindow (window, preferences);
+ preferences_window.show ();
}
public void help_cb ()
@@ -1463,21 +1472,20 @@ Copyright © 2015–2016 Sahil Sareen""";
{
game_file = null;
- disable_action (NEW_GAME_ACTION_NAME);
disable_action (SAVE_GAME_AS_ACTION_NAME);
pgn_game = new PGNGame ();
var now = new DateTime.now_local ();
pgn_game.date = now.format ("%Y.%m.%d");
pgn_game.time = now.format ("%H:%M:%S");
- var duration = settings.get_int ("duration");
+ var duration = settings.get_int (DURATION_SETTINGS_KEY);
if (duration > 0)
{
pgn_game.time_control = duration.to_string ();
pgn_game.white_time_left = duration.to_string ();
pgn_game.black_time_left = duration.to_string ();
}
- var engine_name = settings.get_string ("opponent");
+ var engine_name = settings.get_string (OPPONENT_SETTINGS_KEY);
if (engine_name == "")
{
if (ai_profiles != null)
@@ -1485,14 +1493,14 @@ Copyright © 2015–2016 Sahil Sareen""";
else
engine_name = "human";
}
- var engine_level = settings.get_string ("difficulty");
+ var engine_level = settings.get_string (DIFFICULTY_SETTINGS_KEY);
if (engine_name != null && engine_name != "human")
{
- var play_as = settings.get_string ("play-as");
+ var play_as = settings.get_string (PLAY_AS_SETTINGS_KEY);
if (play_as == "alternate")
{
- var last_side = settings.get_string ("last-played-as");
+ var last_side = settings.get_string (LAST_PLAYED_AS_SETTINGS_KEY);
play_as = (last_side == "white" ? "black" : "white");
}
@@ -1511,7 +1519,7 @@ Copyright © 2015–2016 Sahil Sareen""";
assert_not_reached ();
}
- settings.set_string ("last-played-as", play_as);
+ settings.set_string (LAST_PLAYED_AS_SETTINGS_KEY, play_as);
}
start_game ();
@@ -1519,8 +1527,6 @@ Copyright © 2015–2016 Sahil Sareen""";
private void load_game (File file)
{
- enable_action (NEW_GAME_ACTION_NAME);
-
try
{
var pgn = new PGN.from_file (file);
diff --git a/src/meson.build b/src/meson.build
index dc751ff..c9bcf8c 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -5,7 +5,9 @@ chess_sources = [
'chess-view.vala',
'chess-window.vala',
'gnome-chess.vala',
- 'preferences-dialog.vala',
+ 'new-game-window.vala',
+ 'preferences-window.vala',
+ 'preferences.vala',
'promotion-type-selector-dialog.vala',
]
diff --git a/src/new-game-window.vala b/src/new-game-window.vala
new file mode 100644
index 0000000..fbaec3c
--- /dev/null
+++ b/src/new-game-window.vala
@@ -0,0 +1,198 @@
+/* -*- Mode: vala; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * Copyright (C) 2022 Nils Lück
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+[GtkTemplate (ui = "/org/gnome/Chess/ui/new-game-window.ui")]
+public class NewGameWindow : Adw.PreferencesWindow
+{
+ private bool syncing_time_limit = false;
+ private Preferences preferences;
+ private unowned List<AIProfile> ai_profiles;
+ private List<Opponent> opponents = new List<Opponent> ();
+
+ public signal void new_game_requested ();
+
+ [GtkChild]
+ private unowned Adw.ComboRow play_as_combo;
+ [GtkChild]
+ private unowned Adw.ComboRow difficulty_combo;
+ [GtkChild]
+ private unowned Adw.ComboRow opponent_combo;
+ [GtkChild]
+ private unowned Adw.ComboRow clock_type_combo;
+ [GtkChild]
+ private unowned Gtk.SpinButton duration_spin;
+ [GtkChild]
+ private unowned Gtk.SpinButton increment_spin;
+ [GtkChild]
+ private unowned Gtk.Switch time_limit_switch;
+
+ public NewGameWindow (Gtk.Window window, Preferences preferences, List<AIProfile> ai_profiles)
+ {
+ transient_for = window;
+
+ this.preferences = preferences;
+ this.ai_profiles = ai_profiles;
+ initialize_opponents (ai_profiles);
+
+ preferences.bind_property ("play-as", play_as_combo, "selected", BindingFlags.BIDIRECTIONAL |
BindingFlags.SYNC_CREATE, null, null);
+ preferences.bind_property ("difficulty", difficulty_combo, "selected", BindingFlags.BIDIRECTIONAL |
BindingFlags.SYNC_CREATE, null, null);
+
+ preferences.bind_property (
+ "opponent",
+ opponent_combo,
+ "selected",
+ BindingFlags.BIDIRECTIONAL | BindingFlags.SYNC_CREATE,
+ (binding, from_value, ref to_value) =>
+ {
+ var opponent = (Opponent) from_value.get_object ();
+ var position = get_opponent_index (opponent.display_name);
+ to_value.set_uint (position);
+ return true;
+ },
+ (binding, from_value, ref to_value) =>
+ {
+ var position = from_value.get_uint ();
+ to_value.set_object (opponents.nth_data (position));
+ return true;
+ });
+
+ time_limit_settings_changed_cb ();
+ time_limit_switch.notify["active"].connect (time_limit_controls_changed_cb);
+ clock_type_combo.notify["selected"].connect (time_limit_controls_changed_cb);
+ duration_spin.value_changed.connect (time_limit_controls_changed_cb);
+ increment_spin.value_changed.connect (time_limit_controls_changed_cb);
+ preferences.notify["time-limit"].connect (time_limit_settings_changed_cb);
+
+ opponent_combo.bind_property (
+ "selected",
+ difficulty_combo,
+ "sensitive",
+ BindingFlags.DEFAULT | BindingFlags.SYNC_CREATE,
+ (binding, from_value, ref to_value) =>
+ {
+ var opponent = opponents.nth_data (from_value.get_uint ());
+ to_value.set_boolean (!opponent.is_human);
+ return true;
+ },
+ null);
+
+ opponent_combo.bind_property (
+ "selected",
+ play_as_combo,
+ "sensitive",
+ BindingFlags.DEFAULT | BindingFlags.SYNC_CREATE,
+ (binding, from_value, ref to_value) =>
+ {
+ var opponent = opponents.nth_data (from_value.get_uint ());
+ to_value.set_boolean (!opponent.is_human);
+ return true;
+ },
+ null);
+ }
+
+ [GtkCallback]
+ private void start_game_cb ()
+ {
+ close ();
+ new_game_requested ();
+ }
+
+ [GtkCallback]
+ private string play_as_display_name_cb (Adw.EnumListItem item)
+ {
+ var value = (PlayAs) item.value;
+ return value.display_name ();
+ }
+
+ [GtkCallback]
+ private string difficulty_display_name_cb (Adw.EnumListItem item)
+ {
+ var value = (Difficulty) item.value;
+ return value.display_name ();
+ }
+
+ [GtkCallback]
+ private string clock_type_display_name_cb (Adw.EnumListItem item)
+ {
+ var value = (ClockType) item.value;
+ return value.display_name ();
+ }
+
+ private void time_limit_settings_changed_cb ()
+ {
+ if (syncing_time_limit)
+ return;
+ syncing_time_limit = true;
+
+ if (preferences.time_limit == null)
+ {
+ time_limit_switch.active = false;
+ duration_spin.value = 5;
+ increment_spin.value = 0;
+ clock_type_combo.selected = (int) ClockType.FISCHER;
+ }
+ else
+ {
+ time_limit_switch.active = true;
+ duration_spin.value = preferences.time_limit.duration_in_seconds / 60.0;
+ increment_spin.value = preferences.time_limit.increment_in_seconds;
+ clock_type_combo.selected = (int) preferences.time_limit.clock_type;
+ }
+
+ syncing_time_limit = false;
+ }
+
+ private void time_limit_controls_changed_cb ()
+ {
+ if (syncing_time_limit)
+ return;
+ syncing_time_limit = true;
+
+ if (time_limit_switch.active)
+ {
+ var duration_in_seconds = (int) duration_spin.value * 60;
+ var increment_in_seconds = (int) increment_spin.value;
+ var clock_type = (ClockType) clock_type_combo.selected;
+ preferences.time_limit = new TimeLimit (duration_in_seconds, increment_in_seconds, clock_type);
+ }
+ else
+ preferences.time_limit = null;
+
+ syncing_time_limit = false;
+ }
+
+ private void initialize_opponents (List<AIProfile> ai_profiles)
+ {
+ opponents.append (Opponent.human);
+ foreach (var ai_profile in ai_profiles)
+ {
+ opponents.append (Opponent.from_ai_profile (ai_profile));
+ }
+ var opponents_model = new Gtk.StringList (null);
+ foreach (var opponent in opponents)
+ {
+ opponents_model.append(opponent.display_name);
+ }
+ opponent_combo.model = opponents_model;
+ }
+
+ private uint get_opponent_index (string display_name)
+ {
+ var i = 0;
+ foreach (var opponent in opponents)
+ {
+ if (opponent.display_name == display_name)
+ return i;
+ i++;
+ }
+ return 0;
+ }
+}
\ No newline at end of file
diff --git a/src/preferences-window.vala b/src/preferences-window.vala
new file mode 100644
index 0000000..f2f7b2b
--- /dev/null
+++ b/src/preferences-window.vala
@@ -0,0 +1,60 @@
+/* -*- Mode: vala; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * Copyright (C) 2022 Nils Lück
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+[GtkTemplate (ui = "/org/gnome/Chess/ui/preferences-window.ui")]
+public class PreferencesWindow : Adw.PreferencesWindow
+{
+ private Preferences preferences;
+
+ [GtkChild]
+ private unowned Adw.ComboRow board_orientation_combo;
+ [GtkChild]
+ private unowned Adw.ComboRow move_format_combo;
+ [GtkChild]
+ private unowned Adw.ComboRow piece_style_combo;
+ [GtkChild]
+ private unowned Gtk.Switch board_numbering_switch;
+ [GtkChild]
+ private unowned Gtk.Switch move_hints_switch;
+
+ public PreferencesWindow (Gtk.Window window, Preferences preferences)
+ {
+ transient_for = window;
+ this.preferences = preferences;
+
+ preferences.bind_property ("show-board-numbering", board_numbering_switch, "active",
BindingFlags.BIDIRECTIONAL | BindingFlags.SYNC_CREATE, null, null);
+ preferences.bind_property ("show-move-hints", move_hints_switch, "active",
BindingFlags.BIDIRECTIONAL | BindingFlags.SYNC_CREATE, null, null);
+ preferences.bind_property ("piece-style", piece_style_combo, "selected", BindingFlags.BIDIRECTIONAL
| BindingFlags.SYNC_CREATE, null, null);
+ preferences.bind_property ("move-format", move_format_combo, "selected", BindingFlags.BIDIRECTIONAL
| BindingFlags.SYNC_CREATE, null, null);
+ preferences.bind_property ("board-orientation", board_orientation_combo, "selected",
BindingFlags.BIDIRECTIONAL | BindingFlags.SYNC_CREATE, null, null);
+ }
+
+ [GtkCallback]
+ private string board_orientation_display_name_cb (Adw.EnumListItem item)
+ {
+ var value = (BoardOrientation) item.value;
+ return value.display_name ();
+ }
+
+ [GtkCallback]
+ private string move_format_display_name_cb (Adw.EnumListItem item)
+ {
+ var value = (MoveFormat) item.value;
+ return value.display_name ();
+ }
+
+ [GtkCallback]
+ private string piece_style_display_name_cb (Adw.EnumListItem item)
+ {
+ var value = (PieceStyle) item.value;
+ return value.display_name ();
+ }
+}
diff --git a/src/preferences.vala b/src/preferences.vala
new file mode 100644
index 0000000..eebfcfe
--- /dev/null
+++ b/src/preferences.vala
@@ -0,0 +1,607 @@
+/* -*- Mode: vala; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * Copyright (C) 2022 Nils Lück
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+public const string WIDTH_SETTINGS_KEY = "width";
+public const string HEIGHT_SETTINGS_KEY = "height";
+public const string MAXIMIZED_SETTINGS_KEY = "maximized";
+public const string PIECE_STYLE_SETTINGS_KEY = "piece-theme";
+public const string SHOW_MOVE_HINTS_SETTINGS_KEY = "show-move-hints";
+public const string SHOW_BOARD_NUMBERING_SETTINGS_KEY = "show-numbering";
+public const string MOVE_FORMAT_SETTINGS_KEY = "move-format";
+public const string BOARD_ORIENTATION_SETTINGS_KEY = "board-side";
+public const string DURATION_SETTINGS_KEY = "duration";
+public const string CLOCK_TYPE_SETTINGS_KEY = "clock-type";
+public const string INCREMENT_SETTINGS_KEY = "timer-increment";
+public const string PLAY_AS_SETTINGS_KEY = "play-as";
+public const string LAST_PLAYED_AS_SETTINGS_KEY = "last-played-as";
+public const string OPPONENT_SETTINGS_KEY = "opponent";
+public const string DIFFICULTY_SETTINGS_KEY = "difficulty";
+
+public class Preferences : Object
+{
+ private Settings settings;
+
+ public BoardOrientation board_orientation { get; set; }
+ public MoveFormat move_format { get; set; }
+ public PieceStyle piece_style { get; set; }
+ public bool show_board_numbering { get; set; }
+ public bool show_move_hints { get; set; }
+
+ public Opponent opponent { get; set; }
+ public PlayAs play_as { get; set; }
+ public Difficulty difficulty { get; set; }
+
+ private bool syncing_time_limit = false;
+ public TimeLimit? time_limit { get; set; }
+
+ public Preferences(Settings settings)
+ {
+ this.settings = settings;
+
+ settings.bind (SHOW_BOARD_NUMBERING_SETTINGS_KEY, this, "show-board-numbering",
SettingsBindFlags.DEFAULT);
+ settings.bind (SHOW_MOVE_HINTS_SETTINGS_KEY, this, "show-move-hints", SettingsBindFlags.DEFAULT);
+
+ settings.bind_with_mapping (
+ BOARD_ORIENTATION_SETTINGS_KEY,
+ this,
+ "board-orientation",
+ SettingsBindFlags.DEFAULT,
+ (to_value, from_value, user_data) =>
+ {
+ var value = BoardOrientation.from_setting (from_value.get_string ()) ??
BoardOrientation.HUMAN_SIDE;
+ to_value.set_enum (value);
+ return true;
+ },
+ (from_value, expected_type, user_data) =>
+ {
+ var value = (BoardOrientation) from_value.get_enum ();
+ return new Variant.string (value.to_setting ());
+ },
+ null,
+ null);
+
+ settings.bind_with_mapping (
+ MOVE_FORMAT_SETTINGS_KEY,
+ this,
+ "move-format",
+ SettingsBindFlags.DEFAULT,
+ (to_value, from_value, user_data) =>
+ {
+ var value = MoveFormat.from_setting (from_value.get_string ()) ?? MoveFormat.HUMAN;
+ to_value.set_enum (value);
+ return true;
+ },
+ (from_value, expected_type, user_data) =>
+ {
+ var value = (MoveFormat) from_value.get_enum ();
+ return new Variant.string (value.to_setting ());
+ },
+ null,
+ null);
+
+ settings.bind_with_mapping (
+ PIECE_STYLE_SETTINGS_KEY,
+ this,
+ "piece-style",
+ SettingsBindFlags.DEFAULT,
+ (to_value, from_value, user_data) =>
+ {
+ var value = PieceStyle.from_setting (from_value.get_string ()) ?? PieceStyle.SIMPLE;
+ to_value.set_enum (value);
+ return true;
+ },
+ (from_value, expected_type, user_data) =>
+ {
+ var value = (PieceStyle) from_value.get_enum ();
+ return new Variant.string (value.to_setting ());
+ },
+ null,
+ null);
+
+ settings.bind_with_mapping (
+ PLAY_AS_SETTINGS_KEY,
+ this,
+ "play-as",
+ SettingsBindFlags.DEFAULT,
+ (to_value, from_value, user_data) =>
+ {
+ var value = PlayAs.from_setting (from_value.get_string ()) ?? PlayAs.WHITE;
+ to_value.set_enum (value);
+ return true;
+ },
+ (from_value, expected_type, user_data) =>
+ {
+ var value = (PlayAs) from_value.get_enum ();
+ return new Variant.string (value.to_setting ());
+ },
+ null,
+ null);
+
+ settings.bind_with_mapping (
+ DIFFICULTY_SETTINGS_KEY,
+ this,
+ "difficulty",
+ SettingsBindFlags.DEFAULT,
+ (to_value, from_value, user_data) =>
+ {
+ var value = Difficulty.from_setting (from_value.get_string ()) ?? Difficulty.EASY;
+ to_value.set_enum (value);
+ return true;
+ },
+ (from_value, expected_type, user_data) =>
+ {
+ var value = (Difficulty) from_value.get_enum ();
+ return new Variant.string (value.to_setting ());
+ },
+ null,
+ null);
+
+ settings.bind_with_mapping (
+ OPPONENT_SETTINGS_KEY,
+ this,
+ "opponent",
+ SettingsBindFlags.DEFAULT,
+ (to_value, from_value, user_data) =>
+ {
+ var value = Opponent.from_setting (from_value.get_string ()) ?? Opponent.human;
+ to_value.set_object (value);
+ return true;
+ },
+ (from_value, expected_type, user_data) =>
+ {
+ var value = (Opponent) from_value.get_object ();
+ return new Variant.string (value.to_setting ());
+ },
+ null,
+ null);
+
+ time_limit_settings_changed_cb ();
+ settings.changed[DURATION_SETTINGS_KEY].connect(time_limit_settings_changed_cb);
+ settings.changed[INCREMENT_SETTINGS_KEY].connect(time_limit_settings_changed_cb);
+ settings.changed[CLOCK_TYPE_SETTINGS_KEY].connect(time_limit_settings_changed_cb);
+ notify["time-limit"].connect(time_limit_preferences_changed_cb);
+ }
+
+ private void time_limit_settings_changed_cb ()
+ {
+ if (syncing_time_limit)
+ return;
+ syncing_time_limit = true;
+
+ var duration = settings.get_int (DURATION_SETTINGS_KEY);
+ var increment = settings.get_int (INCREMENT_SETTINGS_KEY);
+ var clock_type = settings.get_string (CLOCK_TYPE_SETTINGS_KEY);
+ time_limit = TimeLimit.from_settings (duration, increment, clock_type);
+
+ syncing_time_limit = false;
+ }
+
+ private void time_limit_preferences_changed_cb ()
+ {
+ if (syncing_time_limit)
+ return;
+ syncing_time_limit = true;
+
+ int duration;
+ int increment;
+ string clock_type;
+ TimeLimit.to_settings (time_limit, out duration, out increment, out clock_type);
+ settings.set_int (DURATION_SETTINGS_KEY, duration);
+ settings.set_int (INCREMENT_SETTINGS_KEY, increment);
+ settings.set_string (CLOCK_TYPE_SETTINGS_KEY, clock_type);
+
+ syncing_time_limit = false;
+ }
+}
+
+public enum BoardOrientation
+{
+ HUMAN_SIDE,
+ WHITE_SIDE,
+ BLACK_SIDE,
+ CURRENT_PLAYER;
+
+ public string display_name ()
+ {
+ switch (this)
+ {
+ case WHITE_SIDE:
+ return C_("chess-side", "White Side");
+ case BLACK_SIDE:
+ return C_("chess-side", "Black Side");
+ case HUMAN_SIDE:
+ return C_("chess-side", "Human Side");
+ case CURRENT_PLAYER:
+ return C_("chess-side", "Current Player");
+ default:
+ assert_not_reached ();
+ }
+ }
+
+ public string to_setting ()
+ {
+ switch (this)
+ {
+ case WHITE_SIDE:
+ return "white";
+ case BLACK_SIDE:
+ return "black";
+ case HUMAN_SIDE:
+ return "human";
+ case CURRENT_PLAYER:
+ return "current";
+ default:
+ assert_not_reached ();
+ }
+ }
+
+ public static BoardOrientation? from_setting (string s)
+ {
+ switch (s)
+ {
+ case "white":
+ return WHITE_SIDE;
+ case "black":
+ return BLACK_SIDE;
+ case "human":
+ return HUMAN_SIDE;
+ case "current":
+ return CURRENT_PLAYER;
+ default:
+ return null;
+ }
+ }
+}
+
+public enum MoveFormat
+{
+ HUMAN,
+ STANDARD_ALGEBRAIC,
+ LONG_ALGEBRAIC,
+ FIGURINE;
+
+ public string display_name ()
+ {
+ switch (this)
+ {
+ case HUMAN:
+ return C_("chess-move-format", "Human");
+ case STANDARD_ALGEBRAIC:
+ return C_("chess-move-format", "Standard Algebraic");
+ case LONG_ALGEBRAIC:
+ return C_("chess-move-format", "Long Algebraic");
+ case FIGURINE:
+ return C_("chess-move-format", "Figurine");
+ default:
+ assert_not_reached ();
+ }
+ }
+
+ public string to_setting ()
+ {
+ switch (this)
+ {
+ case HUMAN:
+ return "human";
+ case STANDARD_ALGEBRAIC:
+ return "san";
+ case LONG_ALGEBRAIC:
+ return "lan";
+ case FIGURINE:
+ return "fan";
+ default:
+ assert_not_reached ();
+ }
+ }
+
+ public static MoveFormat? from_setting (string s)
+ {
+ switch (s)
+ {
+ case "human":
+ return HUMAN;
+ case "san":
+ return STANDARD_ALGEBRAIC;
+ case "lan":
+ return LONG_ALGEBRAIC;
+ case "fan":
+ return FIGURINE;
+ default:
+ return null;
+ }
+ }
+}
+
+public enum PieceStyle
+{
+ SIMPLE,
+ FANCY;
+
+ public string display_name ()
+ {
+ switch (this)
+ {
+ case SIMPLE:
+ return C_("chess-piece-style", "Simple");
+ case FANCY:
+ return C_("chess-piece-style", "Fancy");
+ default:
+ assert_not_reached ();
+ }
+ }
+
+ public string to_setting ()
+ {
+ switch (this)
+ {
+ case SIMPLE:
+ return "simple";
+ case FANCY:
+ return "fancy";
+ default:
+ assert_not_reached ();
+ }
+ }
+
+ public static PieceStyle? from_setting (string s)
+ {
+ switch (s)
+ {
+ case "simple":
+ return SIMPLE;
+ case "fancy":
+ return FANCY;
+ default:
+ return null;
+ }
+ }
+}
+
+public enum PlayAs
+{
+ WHITE,
+ BLACK,
+ ALTERNATE;
+
+ public string display_name ()
+ {
+ switch (this)
+ {
+ case WHITE:
+ return C_("chess-player", "White");
+ case BLACK:
+ return C_("chess-player", "Black");
+ case ALTERNATE:
+ return C_("chess-player", "Alternate");
+ default:
+ assert_not_reached ();
+ }
+ }
+
+ public string to_setting ()
+ {
+ switch (this)
+ {
+ case WHITE:
+ return "white";
+ case BLACK:
+ return "black";
+ case ALTERNATE:
+ return "alternate";
+ default:
+ assert_not_reached ();
+ }
+ }
+
+ public static PlayAs? from_setting (string s)
+ {
+ switch (s)
+ {
+ case "white":
+ return WHITE;
+ case "black":
+ return BLACK;
+ case "alternate":
+ return ALTERNATE;
+ default:
+ return null;
+ }
+ }
+}
+
+public enum Difficulty
+{
+ EASY,
+ NORMAL,
+ HARD;
+
+ public string display_name ()
+ {
+ switch (this)
+ {
+ case EASY:
+ return C_("difficulty", "Easy");
+ case NORMAL:
+ return C_("difficulty", "Normal");
+ case HARD:
+ return C_("difficulty", "Hard");
+ default:
+ assert_not_reached ();
+ }
+ }
+
+ public string to_setting ()
+ {
+ switch (this)
+ {
+ case EASY:
+ return "easy";
+ case NORMAL:
+ return "normal";
+ case HARD:
+ return "hard";
+ default:
+ assert_not_reached ();
+ }
+ }
+
+ public static Difficulty? from_setting (string s)
+ {
+ switch (s)
+ {
+ case "easy":
+ return EASY;
+ case "normal":
+ return NORMAL;
+ case "hard":
+ return HARD;
+ default:
+ return null;
+ }
+ }
+}
+
+public enum ClockType
+{
+ FISCHER,
+ BRONSTEIN;
+
+ public string display_name ()
+ {
+ switch (this)
+ {
+ case FISCHER:
+ return C_("clock-type", "Fischer");
+ case BRONSTEIN:
+ return C_("clock-type", "Bronstein");
+ default:
+ assert_not_reached ();
+ }
+ }
+
+ public string to_setting ()
+ {
+ switch (this)
+ {
+ case FISCHER:
+ return "fischer";
+ case BRONSTEIN:
+ return "bronstein";
+ default:
+ assert_not_reached ();
+ }
+ }
+
+ public static ClockType? from_setting (string s)
+ {
+ switch (s)
+ {
+ case "fischer":
+ return FISCHER;
+ case "bronstein":
+ return BRONSTEIN;
+ default:
+ return null;
+ }
+ }
+}
+
+public class Opponent : Object
+{
+ private const string HUMAN_NAME = "human";
+
+ private static Opponent _human;
+ public static Opponent human
+ {
+ get
+ {
+ if (_human == null)
+ _human = new Opponent (HUMAN_NAME, C_("chess-opponent", "Human"));
+ return _human;
+ }
+ }
+
+ public string name { get; private set; }
+ public string display_name { get; private set; }
+ public bool is_human { get { return name == HUMAN_NAME; } }
+
+ public Opponent (string name, string display_name)
+ {
+ this.name = name;
+ this.display_name = display_name;
+ }
+
+ public string to_setting ()
+ {
+ return name;
+ }
+
+ public static Opponent? from_setting (string s)
+ {
+ if (s == HUMAN_NAME)
+ return human;
+ else if (s == null || s.length == 0)
+ return null;
+ return new Opponent (s, s);
+ }
+
+ public static Opponent from_ai_profile (AIProfile ai_profile)
+ {
+ return new Opponent (ai_profile.name, ai_profile.name);
+ }
+}
+
+public class TimeLimit
+{
+ public int duration_in_seconds { get; private set; }
+ public int increment_in_seconds { get; private set; }
+ public ClockType clock_type { get; private set; }
+
+ public TimeLimit (int duration_in_seconds, int increment_in_seconds, ClockType clock_type)
+ {
+ assert_cmpint (duration_in_seconds, CompareOperator.GT, 0);
+ assert_cmpint (increment_in_seconds, CompareOperator.GE, 0);
+ this.duration_in_seconds = duration_in_seconds;
+ this.increment_in_seconds = increment_in_seconds;
+ this.clock_type = clock_type;
+ }
+
+ public static void to_settings (TimeLimit? time_limit, out int duration, out int increment, out string
clock_type)
+ {
+ if (time_limit == null)
+ {
+ duration = 0;
+ increment = 0;
+ clock_type = "simple";
+ return;
+ }
+
+ duration = time_limit.duration_in_seconds;
+ increment = time_limit.increment_in_seconds;
+
+ if (increment == 0)
+ clock_type = "simple";
+ else
+ clock_type = time_limit.clock_type.to_setting ();
+ }
+
+ public static TimeLimit? from_settings (int duration_setting, int increment_setting, string
clock_type_setting)
+ {
+ if (duration_setting <= 0)
+ return null;
+
+ var clock_type = ClockType.from_setting (clock_type_setting);
+ if (clock_type == null || increment_setting <= 0)
+ return new TimeLimit (duration_setting, 0, ClockType.FISCHER);
+
+ return new TimeLimit (duration_setting, increment_setting, clock_type);
+ }
+}
\ No newline at end of file
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]