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:
@@ -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