refactor(dtmm): Use commands instead of notifications

This commit is contained in:
Lucas Schwiderski 2023-02-18 10:56:20 +01:00
parent b8804409a3
commit 4b7f12e487
Signed by: lucas
GPG key ID: AA12679AAA6DF4D8
3 changed files with 90 additions and 110 deletions

View file

@ -18,7 +18,7 @@ use tracing_subscriber::prelude::*;
use tracing_subscriber::EnvFilter;
use crate::engine::deploy_mods;
use crate::state::{AsyncAction, Delegate, State, COMMAND_FINISH_DEPLOY};
use crate::state::{AsyncAction, Delegate, State, ACTION_FINISH_DEPLOY};
mod controller;
mod engine;
@ -45,7 +45,7 @@ fn work_thread(
event_sink
.write()
.await
.submit_command(COMMAND_FINISH_DEPLOY, (), Target::Auto)
.submit_command(ACTION_FINISH_DEPLOY, (), Target::Auto)
.expect("failed to send command");
}),
};

View file

@ -5,9 +5,10 @@ use druid::widget::{
};
use druid::{lens, Insets, LensExt, Widget, WidgetExt, WindowDesc};
use crate::state::{ModInfo, PathBufFormatter, State, View, ACTION_ADD_MOD};
use crate::state::{
ModInfo, PathBufFormatter, State, StateController, View, ACTION_DELETE_SELECTED_MOD,
ACTION_SELECTED_MOD_DOWN, ACTION_SELECTED_MOD_UP, ACTION_SELECT_MOD, COMMAND_START_DEPLOY,
ACTION_DELETE_SELECTED_MOD, ACTION_SELECTED_MOD_DOWN, ACTION_SELECTED_MOD_UP,
ACTION_SELECT_MOD, ACTION_START_DEPLOY,
};
use crate::theme;
use crate::widget::ExtraWidgetExt;
@ -51,7 +52,7 @@ fn build_top_bar() -> impl Widget<State> {
.with_child(
Button::new("Deploy Mods")
.on_click(|ctx, _state: &mut State, _env| {
ctx.submit_command(COMMAND_START_DEPLOY);
ctx.submit_command(ACTION_START_DEPLOY);
})
.disabled_if(|data, _| !data.can_deploy_mods()),
)
@ -83,7 +84,7 @@ fn build_mod_list() -> impl Widget<State> {
.lens(lens!((usize, ModInfo), 1).then(ModInfo::enabled)),
)
.with_child(Label::raw().lens(lens!((usize, ModInfo), 1).then(ModInfo::name)))
.on_click(|ctx, (i, _info), _env| ctx.submit_notification(ACTION_SELECT_MOD.with(*i)))
.on_click(|ctx, (i, _info), _env| ctx.submit_command(ACTION_SELECT_MOD.with(*i)))
});
let scroll = Scroll::new(list)
@ -123,11 +124,11 @@ fn build_mod_details() -> impl Widget<State> {
.lens(State::selected_mod);
let button_move_up = Button::new("Move Up")
.on_click(|ctx, _state, _env| ctx.submit_notification(ACTION_SELECTED_MOD_UP))
.on_click(|ctx, _state, _env| ctx.submit_command(ACTION_SELECTED_MOD_UP))
.disabled_if(|state: &State, _env: &druid::Env| !state.can_move_mod_up());
let button_move_down = Button::new("Move Down")
.on_click(|ctx, _state, _env| ctx.submit_notification(ACTION_SELECTED_MOD_DOWN))
.on_click(|ctx, _state, _env| ctx.submit_command(ACTION_SELECTED_MOD_DOWN))
.disabled_if(|state: &State, _env: &druid::Env| !state.can_move_mod_down());
let button_toggle_mod = Maybe::new(
@ -150,14 +151,11 @@ fn build_mod_details() -> impl Widget<State> {
.disabled_if(|info: &Option<ModInfo>, _env: &druid::Env| info.is_none())
.lens(State::selected_mod);
let button_add_mod = Button::new("Add Mod").on_click(|_ctx, state: &mut State, _env| {
// TODO: Implement properly
let info = ModInfo::new();
state.add_mod(info);
});
let button_add_mod = Button::new("Add Mod")
.on_click(|ctx, _state: &mut State, _env| ctx.submit_command(ACTION_ADD_MOD));
let button_delete_mod = Button::new("Delete Mod")
.on_click(|ctx, _state, _env| ctx.submit_notification(ACTION_DELETE_SELECTED_MOD))
.on_click(|ctx, _state, _env| ctx.submit_command(ACTION_DELETE_SELECTED_MOD))
.disabled_if(|info: &Option<ModInfo>, _env: &druid::Env| info.is_none())
.lens(State::selected_mod);
@ -250,5 +248,4 @@ fn build_window() -> impl Widget<State> {
.must_fill_main_axis(true)
.with_child(build_top_bar())
.with_flex_child(build_main(), 1.0)
.controller(StateController::new())
}

View file

@ -3,20 +3,18 @@ use std::sync::Arc;
use druid::im::Vector;
use druid::text::Formatter;
use druid::widget::Controller;
use druid::{
AppDelegate, Command, Data, DelegateCtx, Env, Event, EventCtx, Handled, Lens, Selector, Target,
Widget,
};
use druid::{AppDelegate, Command, Data, DelegateCtx, Env, Handled, Lens, Selector, Target};
use tokio::sync::mpsc::UnboundedSender;
pub const ACTION_SELECT_MOD: Selector<usize> = Selector::new("dtmm.action..select-mod");
pub const ACTION_SELECT_MOD: Selector<usize> = Selector::new("dtmm.action.select-mod");
pub const ACTION_SELECTED_MOD_UP: Selector = Selector::new("dtmm.action.selected-mod-up");
pub const ACTION_SELECTED_MOD_DOWN: Selector = Selector::new("dtmm.action.selected-mod-down");
pub const ACTION_DELETE_SELECTED_MOD: Selector = Selector::new("dtmm.action.delete-selected-mod");
pub const COMMAND_FINISH_DEPLOY: Selector = Selector::new("dtmm.command.finish-deploy");
pub const COMMAND_START_DEPLOY: Selector = Selector::new("dtmm.command.start-deploy");
pub const ACTION_START_DEPLOY: Selector = Selector::new("dtmm.action.start-deploy");
pub const ACTION_FINISH_DEPLOY: Selector = Selector::new("dtmm.action.finish-deploy");
pub const ACTION_ADD_MOD: Selector = Selector::new("dtmm.action.add-mod");
#[derive(Copy, Clone, Data, Debug, PartialEq)]
pub(crate) enum View {
@ -236,75 +234,6 @@ impl<T: Data> Lens<Vector<T>, Vector<(usize, T)>> for IndexedVectorLens {
}
}
pub struct StateController {}
impl StateController {
pub fn new() -> Self {
Self {}
}
}
// TODO: Turn notifications into commands on the AppDelegate
impl<W: Widget<State>> Controller<State, W> for StateController {
#[tracing::instrument(name = "StateController::event", skip_all)]
fn event(
&mut self,
child: &mut W,
ctx: &mut EventCtx,
event: &Event,
state: &mut State,
env: &Env,
) {
match event {
Event::Notification(notif) if notif.is(ACTION_SELECT_MOD) => {
ctx.set_handled();
let index = notif
.get(ACTION_SELECT_MOD)
.expect("notification type didn't match after check");
state.select_mod(*index);
}
Event::Notification(notif) if notif.is(ACTION_SELECTED_MOD_UP) => {
ctx.set_handled();
let Some(i) = state.selected_mod_index else {
return;
};
let len = state.mods.len();
if len == 0 || i == 0 {
return;
}
state.mods.swap(i, i - 1);
state.selected_mod_index = Some(i - 1);
}
Event::Notification(notif) if notif.is(ACTION_SELECTED_MOD_DOWN) => {
ctx.set_handled();
let Some(i) = state.selected_mod_index else {
return;
};
let len = state.mods.len();
if len == 0 || i == usize::MAX || i >= len - 1 {
return;
}
state.mods.swap(i, i + 1);
state.selected_mod_index = Some(i + 1);
}
Event::Notification(notif) if notif.is(ACTION_DELETE_SELECTED_MOD) => {
ctx.set_handled();
let Some(index) = state.selected_mod_index else {
return;
};
state.mods.remove(index);
}
_ => child.event(ctx, event, state, env),
}
}
}
pub(crate) enum AsyncAction {
DeployMods(State),
}
@ -326,29 +255,83 @@ impl AppDelegate<State> for Delegate {
_ctx: &mut DelegateCtx,
_target: Target,
cmd: &Command,
data: &mut State,
state: &mut State,
_env: &Env,
) -> Handled {
if cmd.is(COMMAND_START_DEPLOY) {
match cmd {
cmd if cmd.is(ACTION_START_DEPLOY) => {
if self
.sender
.send(AsyncAction::DeployMods(data.clone()))
.send(AsyncAction::DeployMods(state.clone()))
.is_ok()
{
data.is_deployment_in_progress = true;
state.is_deployment_in_progress = true;
} else {
tracing::error!("Failed to queue action to deploy mods");
}
Handled::Yes
} else if cmd.is(COMMAND_FINISH_DEPLOY) {
data.is_deployment_in_progress = false;
}
cmd if cmd.is(ACTION_START_DEPLOY) => {
state.is_deployment_in_progress = false;
Handled::Yes
} else {
}
cmd if cmd.is(ACTION_SELECT_MOD) => {
let index = cmd
.get(ACTION_SELECT_MOD)
.expect("command type matched but didn't contain the expected value");
state.select_mod(*index);
Handled::Yes
}
cmd if cmd.is(ACTION_SELECTED_MOD_UP) => {
let Some(i) = state.selected_mod_index else {
return Handled::No;
};
let len = state.mods.len();
if len == 0 || i == 0 {
return Handled::No;
}
state.mods.swap(i, i - 1);
state.selected_mod_index = Some(i - 1);
Handled::Yes
}
cmd if cmd.is(ACTION_SELECTED_MOD_DOWN) => {
let Some(i) = state.selected_mod_index else {
return Handled::No;
};
let len = state.mods.len();
if len == 0 || i == usize::MAX || i >= len - 1 {
return Handled::No;
}
state.mods.swap(i, i + 1);
state.selected_mod_index = Some(i + 1);
Handled::Yes
}
cmd if cmd.is(ACTION_DELETE_SELECTED_MOD) => {
let Some(index) = state.selected_mod_index else {
return Handled::No;
};
state.mods.remove(index);
Handled::Yes
}
cmd if cmd.is(ACTION_ADD_MOD) => {
// TODO: Implement properly
let info = ModInfo::new();
state.add_mod(info);
Handled::Yes
}
cmd => {
tracing::debug!("Unknown command: {:?}", cmd);
Handled::No
}
}
}
}
pub(crate) struct PathBufFormatter;