From 3252e66a3ff09f2438a03abce1f32ca64b4d3959 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 3 Mar 2023 14:35:35 +0100 Subject: [PATCH] feat(dtmm): Add indicator when a deployment is necessary Closes #32. --- crates/dtmm/src/state/data.rs | 2 ++ crates/dtmm/src/state/delegate.rs | 7 +++++++ crates/dtmm/src/ui/widget/controller.rs | 26 +++++++++++++++---------- crates/dtmm/src/ui/window/main.rs | 17 ++++++++++++---- 4 files changed, 38 insertions(+), 14 deletions(-) diff --git a/crates/dtmm/src/state/data.rs b/crates/dtmm/src/state/data.rs index c8dc3aa..2045ddc 100644 --- a/crates/dtmm/src/state/data.rs +++ b/crates/dtmm/src/state/data.rs @@ -78,6 +78,7 @@ pub(crate) struct State { pub current_view: View, pub mods: Vector, pub selected_mod_index: Option, + pub dirty: bool, pub is_deployment_in_progress: bool, pub is_reset_in_progress: bool, pub is_save_in_progress: bool, @@ -106,6 +107,7 @@ impl State { current_view: View::default(), mods: Vector::new(), selected_mod_index: None, + dirty: false, is_deployment_in_progress: false, is_reset_in_progress: false, is_save_in_progress: false, diff --git a/crates/dtmm/src/state/delegate.rs b/crates/dtmm/src/state/delegate.rs index 08d17b0..689332c 100644 --- a/crates/dtmm/src/state/delegate.rs +++ b/crates/dtmm/src/state/delegate.rs @@ -33,6 +33,8 @@ pub(crate) const ACTION_START_SAVE_SETTINGS: Selector = pub(crate) const ACTION_FINISH_SAVE_SETTINGS: Selector = Selector::new("dtmm.action.finish-save-settings"); +pub(crate) const ACTION_SET_DIRTY: Selector = Selector::new("dtmm.action.set-dirty"); + pub(crate) enum AsyncAction { DeployMods(State), ResetDeployment(State), @@ -81,6 +83,7 @@ impl AppDelegate for Delegate { } cmd if cmd.is(ACTION_FINISH_DEPLOY) => { state.is_deployment_in_progress = false; + state.dirty = false; Handled::Yes } cmd if cmd.is(ACTION_START_RESET_DEPLOYMENT) => { @@ -226,6 +229,10 @@ impl AppDelegate for Delegate { Handled::Yes } + cmd if cmd.is(ACTION_SET_DIRTY) => { + state.dirty = true; + Handled::Yes + } cmd => { if cfg!(debug_assertions) { tracing::warn!("Unknown command: {:?}", cmd); diff --git a/crates/dtmm/src/ui/widget/controller.rs b/crates/dtmm/src/ui/widget/controller.rs index ce18d5b..1bd7f6a 100644 --- a/crates/dtmm/src/ui/widget/controller.rs +++ b/crates/dtmm/src/ui/widget/controller.rs @@ -1,7 +1,7 @@ use druid::widget::{Button, Controller, Scroll}; use druid::{Data, Env, Event, EventCtx, Rect, UpdateCtx, Widget}; -use crate::state::{State, ACTION_START_SAVE_SETTINGS}; +use crate::state::{State, ACTION_SET_DIRTY, ACTION_START_SAVE_SETTINGS}; pub struct DisabledButtonController; @@ -57,11 +57,16 @@ impl> Controller> for AutoScrollController } } -/// A controller that submits the command to save settings every time its widget's -/// data changes. -pub struct SaveSettingsController; +macro_rules! compare_state_fields { + ($old:ident, $new:ident, $($field:ident),+) => { + $($old.$field != $new.$field) || + + } +} -impl> Controller for SaveSettingsController { +/// A controller that tracks state changes for certain fields and submits commands to handle them. +pub struct DirtyStateController; + +impl> Controller for DirtyStateController { fn update( &mut self, child: &mut W, @@ -70,13 +75,14 @@ impl> Controller for SaveSettingsController { data: &State, env: &Env, ) { - // Only filter for the values that actually go into the settings file. - if old_data.mods != data.mods - || old_data.game_dir != data.game_dir - || old_data.data_dir != data.data_dir - { + if compare_state_fields!(old_data, data, mods, game_dir, data_dir) { ctx.submit_command(ACTION_START_SAVE_SETTINGS); } + + if compare_state_fields!(old_data, data, mods, game_dir) { + ctx.submit_command(ACTION_SET_DIRTY); + } + child.update(ctx, old_data, data, env) } } diff --git a/crates/dtmm/src/ui/window/main.rs b/crates/dtmm/src/ui/window/main.rs index 25c1001..3b3ef00 100644 --- a/crates/dtmm/src/ui/window/main.rs +++ b/crates/dtmm/src/ui/window/main.rs @@ -15,7 +15,7 @@ use crate::state::{ ACTION_START_RESET_DEPLOYMENT, }; use crate::ui::theme; -use crate::ui::widget::controller::{AutoScrollController, SaveSettingsController}; +use crate::ui::widget::controller::{AutoScrollController, DirtyStateController}; use crate::ui::widget::PathBufFormatter; const TITLE: &str = "Darktide Mod Manager"; @@ -38,11 +38,20 @@ fn build_top_bar() -> impl Widget { state.current_view = View::Settings; }); - let deploy_button = Button::new("Deploy Mods") + let deploy_button = { + Button::dynamic(|state: &State, _| { + let mut s = String::new(); + if state.dirty { + s.push_str("! "); + } + s.push_str("Deploy Mods"); + s + }) .on_click(|ctx, _state: &mut State, _env| { ctx.submit_command(ACTION_START_DEPLOY); }) - .disabled_if(|data, _| data.is_deployment_in_progress || data.is_reset_in_progress); + .disabled_if(|data, _| data.is_deployment_in_progress || data.is_reset_in_progress) + }; let reset_button = Button::new("Reset Game") .on_click(|ctx, _state: &mut State, _env| { @@ -308,5 +317,5 @@ fn build_window() -> impl Widget { .with_child(build_top_bar()) .with_flex_child(build_main(), 1.0) .with_child(build_log_view()) - .controller(SaveSettingsController) + .controller(DirtyStateController) }