feat(settings): "Smart window size" toggle to opt out of monitor-relative
launch sizing Players who specifically prefer the literal 1280×800 baseline on every fresh-install launch had no way to opt out of the v0.19.0 smart-default sizer. Adds a Gameplay-section toggle (mirrors the "Winnable deals only" pattern) so they can flip it off. - New `Settings::disable_smart_default_size: bool` field with `#[serde(default)]` so legacy `settings.json` files load to the shipped behaviour (smart sizer enabled). - Settings panel gains a "Smart window size" row with ON/OFF label inverting the negative flag, and a tooltip clarifying that saved window geometry always wins over both branches. - `solitaire_app::main` reads the flag once at startup and skips the `apply_smart_default_window_size` registration when it's set. Mid-session changes apply on next launch (documented on the field). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -153,7 +153,12 @@ fn main() {
|
||||
// monitor opens the same 1280×800 window that a 1080p monitor
|
||||
// does — visually tiny relative to screen. Skipped entirely when
|
||||
// saved geometry was applied; the player's preference always wins.
|
||||
if !had_saved_geometry {
|
||||
//
|
||||
// Players who specifically want the literal 1280×800 baseline on
|
||||
// every fresh launch can flip `disable_smart_default_size` in
|
||||
// Settings to opt out. The flag is checked once at startup; a
|
||||
// mid-session change applies on the next launch.
|
||||
if !had_saved_geometry && !settings.disable_smart_default_size {
|
||||
app.add_systems(Update, apply_smart_default_window_size);
|
||||
}
|
||||
|
||||
|
||||
@@ -181,6 +181,20 @@ pub struct Settings {
|
||||
/// solver retry loop — see `solitaire_engine::handle_new_game`.
|
||||
#[serde(default)]
|
||||
pub winnable_deals_only: bool,
|
||||
/// When `true`, suppresses the launch-time
|
||||
/// `apply_smart_default_window_size` system: the window opens at
|
||||
/// the literal `(1280, 800)` default instead of resizing to ~70 %
|
||||
/// of the primary monitor's logical size on the first frame. For
|
||||
/// players who specifically prefer the 1280×800 baseline on every
|
||||
/// fresh launch (i.e. installs without saved geometry).
|
||||
///
|
||||
/// Older `settings.json` files written before this field existed
|
||||
/// deserialize cleanly to `false` via `#[serde(default)]`, which
|
||||
/// preserves the smart-default behaviour shipped in v0.19.0.
|
||||
/// Saved-geometry launches are unaffected by this flag — the
|
||||
/// player's last window size always wins.
|
||||
#[serde(default)]
|
||||
pub disable_smart_default_size: bool,
|
||||
/// Per-move duration during replay playback, in seconds. Range
|
||||
/// `[REPLAY_MOVE_INTERVAL_MIN_SECS, REPLAY_MOVE_INTERVAL_MAX_SECS]`;
|
||||
/// default mirrors `solitaire_engine::replay_playback::REPLAY_MOVE_INTERVAL_SECS`
|
||||
@@ -306,6 +320,7 @@ impl Default for Settings {
|
||||
tooltip_delay_secs: default_tooltip_delay(),
|
||||
time_bonus_multiplier: default_time_bonus_multiplier(),
|
||||
winnable_deals_only: false,
|
||||
disable_smart_default_size: false,
|
||||
replay_move_interval_secs: default_replay_move_interval_secs(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,6 +144,13 @@ struct ReplayMoveIntervalText;
|
||||
#[derive(Component, Debug)]
|
||||
struct WinnableDealsOnlyText;
|
||||
|
||||
/// Marks the `Text` node showing the current "Smart window size"
|
||||
/// state ("ON" / "OFF") in the Gameplay section. The flag is stored
|
||||
/// negatively in `Settings::disable_smart_default_size`, so the
|
||||
/// label inverts: "ON" = smart sizing enabled (the default).
|
||||
#[derive(Component, Debug)]
|
||||
struct SmartDefaultSizeText;
|
||||
|
||||
/// Marks the scrollable inner card so the mouse-wheel system can target it.
|
||||
#[derive(Component, Debug)]
|
||||
struct SettingsPanelScrollable;
|
||||
@@ -199,6 +206,13 @@ enum SettingsButton {
|
||||
/// [`solitaire_core::solver::try_solve`] until one is provably
|
||||
/// winnable (or the retry cap is hit). Off by default.
|
||||
ToggleWinnableDealsOnly,
|
||||
/// Toggle the inverse of [`Settings::disable_smart_default_size`].
|
||||
/// When the visible label reads "ON", the launch-time window
|
||||
/// sizer scales the window to ~70 % of the primary monitor on a
|
||||
/// fresh install; "OFF" pins the literal 1280×800 baseline. The
|
||||
/// flag only affects launches without saved geometry — the
|
||||
/// player's last window size always wins.
|
||||
ToggleSmartDefaultSize,
|
||||
SyncNow,
|
||||
Done,
|
||||
/// Select a specific card-back by index from the picker row.
|
||||
@@ -236,6 +250,8 @@ impl SettingsButton {
|
||||
// sits between TimeBonusUp (48) and the Cosmetic section.
|
||||
SettingsButton::ReplayMoveIntervalDown => 49,
|
||||
SettingsButton::ReplayMoveIntervalUp => 49,
|
||||
// Smart-default-size toggle — sits at the end of Gameplay.
|
||||
SettingsButton::ToggleSmartDefaultSize => 50,
|
||||
// Cosmetic section
|
||||
SettingsButton::ToggleTheme => 55,
|
||||
SettingsButton::ToggleColorBlind => 60,
|
||||
@@ -330,6 +346,7 @@ impl Plugin for SettingsPlugin {
|
||||
update_time_bonus_multiplier_text,
|
||||
update_replay_move_interval_text,
|
||||
update_winnable_deals_only_text,
|
||||
update_smart_default_size_text,
|
||||
attach_focusable_to_settings_buttons,
|
||||
scroll_focus_into_view,
|
||||
),
|
||||
@@ -600,6 +617,21 @@ fn update_winnable_deals_only_text(
|
||||
}
|
||||
}
|
||||
|
||||
/// Refreshes the live "Smart window size" toggle value whenever
|
||||
/// `SettingsResource` changes. The flag is stored negatively as
|
||||
/// `disable_smart_default_size`, so the label inverts.
|
||||
fn update_smart_default_size_text(
|
||||
settings: Res<SettingsResource>,
|
||||
mut text_nodes: Query<&mut Text, With<SmartDefaultSizeText>>,
|
||||
) {
|
||||
if !settings.is_changed() {
|
||||
return;
|
||||
}
|
||||
for mut text in &mut text_nodes {
|
||||
**text = smart_default_size_label(!settings.0.disable_smart_default_size);
|
||||
}
|
||||
}
|
||||
|
||||
/// Refreshes the live tooltip-delay value in the Gameplay section
|
||||
/// whenever `SettingsResource` changes (slider buttons, hand-edited
|
||||
/// settings.json reload, etc.).
|
||||
@@ -854,6 +886,17 @@ fn handle_settings_buttons(
|
||||
// The Text node is refreshed by `update_winnable_deals_only_text`
|
||||
// on the next frame via `settings.is_changed()`.
|
||||
}
|
||||
SettingsButton::ToggleSmartDefaultSize => {
|
||||
settings.0.disable_smart_default_size =
|
||||
!settings.0.disable_smart_default_size;
|
||||
persist(&path, &settings.0);
|
||||
changed.write(SettingsChangedEvent(settings.0.clone()));
|
||||
// The Text node is refreshed by
|
||||
// `update_smart_default_size_text` next frame. The
|
||||
// sizer system is gated only at startup, so flipping
|
||||
// this mid-session takes effect on the next launch —
|
||||
// documented on the field in `solitaire_data::Settings`.
|
||||
}
|
||||
SettingsButton::SelectCardBack(idx) => {
|
||||
settings.0.selected_card_back = *idx;
|
||||
persist(&path, &settings.0);
|
||||
@@ -915,6 +958,14 @@ fn winnable_deals_only_label(enabled: bool) -> String {
|
||||
if enabled { "ON".into() } else { "OFF".into() }
|
||||
}
|
||||
|
||||
/// Display string for the "Smart window size" toggle. The argument
|
||||
/// is the *enabled* state (i.e. the inverse of the underlying
|
||||
/// `disable_smart_default_size` field) so reading the label gives
|
||||
/// the player intuitive ON/OFF semantics.
|
||||
fn smart_default_size_label(enabled: bool) -> String {
|
||||
if enabled { "ON".into() } else { "OFF".into() }
|
||||
}
|
||||
|
||||
/// Formats the tooltip-hover delay for display in the Settings panel.
|
||||
/// `0.0` reads as `"Instant"` so the zero-delay case has a name; any
|
||||
/// other value prints as `"{n:.1} s"` (e.g. `"0.5 s"`, `"1.2 s"`).
|
||||
@@ -1303,6 +1354,17 @@ fn spawn_settings_panel(
|
||||
settings.replay_move_interval_secs,
|
||||
font_res,
|
||||
);
|
||||
toggle_row(
|
||||
body,
|
||||
"Smart window size",
|
||||
SmartDefaultSizeText,
|
||||
smart_default_size_label(!settings.disable_smart_default_size),
|
||||
SettingsButton::ToggleSmartDefaultSize,
|
||||
"When ON, fresh launches resize the window to ~70 % of the \
|
||||
monitor. OFF pins the 1280\u{00D7}800 baseline. Saved \
|
||||
window size always wins.",
|
||||
font_res,
|
||||
);
|
||||
|
||||
// --- Cosmetic ---
|
||||
section_label(body, "Cosmetic", font_res);
|
||||
|
||||
Reference in New Issue
Block a user