diff --git a/Cargo.lock b/Cargo.lock index 4a0eb30..a63c9c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4500,7 +4500,6 @@ dependencies = [ "clap", "gauntlet-client", "gauntlet-common", - "gauntlet-management-client", "gauntlet-plugin-runtime", "gauntlet-server", "gauntlet-utils", @@ -4598,21 +4597,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "gauntlet-management-client" -version = "0.0.0" -dependencies = [ - "anyhow", - "gauntlet-common", - "gauntlet-common-ui", - "gauntlet-utils", - "iced", - "iced_fonts", - "itertools 0.13.0", - "tracing", - "tracing-subscriber", -] - [[package]] name = "gauntlet-manifest-schema" version = "0.0.0" diff --git a/Cargo.toml b/Cargo.toml index 724ba55..eb719e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,6 @@ repository = "https://github.com/project-gauntlet/gauntlet" [workspace] members = [ - "rust/management_client", "rust/client", "rust/server", "rust/common", @@ -35,7 +34,6 @@ gauntlet-common = { path = "./rust/common" } gauntlet-common-ui = { path = "./rust/common_ui" } gauntlet-common-plugin-runtime = { path = "./rust/common_plugin_runtime" } gauntlet-plugin-runtime = { path = "./rust/plugin_runtime" } -gauntlet-management-client = { path = "./rust/management_client" } gauntlet-client = { path = "./rust/client" } gauntlet-server = { path = "./rust/server" } gauntlet-utils = { path = "./rust/utils" } diff --git a/rust/cli/Cargo.toml b/rust/cli/Cargo.toml index b5a08e2..1043dda 100644 --- a/rust/cli/Cargo.toml +++ b/rust/cli/Cargo.toml @@ -4,7 +4,6 @@ edition.workspace = true [dependencies] # workspaces -gauntlet-management-client.workspace = true gauntlet-client.workspace = true gauntlet-server.workspace = true gauntlet-plugin-runtime.workspace = true diff --git a/rust/cli/src/lib.rs b/rust/cli/src/lib.rs index 259e827..2d79552 100644 --- a/rust/cli/src/lib.rs +++ b/rust/cli/src/lib.rs @@ -7,10 +7,10 @@ use std::time::UNIX_EPOCH; use clap::Parser; use gauntlet_common::cli::is_server_running; +use gauntlet_common::cli::open_settings_window; use gauntlet_common::cli::open_window; use gauntlet_common::cli::run_action; use gauntlet_common::dirs::Dirs; -use gauntlet_management_client::start_management_client; use gauntlet_server::PLUGIN_CONNECT_ENV; use gauntlet_server::PLUGIN_UUID_ENV; use tracing_subscriber::EnvFilter; @@ -110,7 +110,7 @@ pub fn init() { Some(command) => { match command { Commands::Open => open_window(), - Commands::Settings => start_management_client(), + Commands::Settings => open_settings_window(), Commands::Run { plugin_id, entrypoint_id, diff --git a/rust/client/src/ui/mod.rs b/rust/client/src/ui/mod.rs index d015c67..2c8e063 100644 --- a/rust/client/src/ui/mod.rs +++ b/rust/client/src/ui/mod.rs @@ -52,6 +52,7 @@ use iced::widget::text; use iced::widget::text::Shaping; use iced::widget::text_input; use iced::widget::text_input::focus; +use iced::widget::themer; use iced::window; use iced_fonts::BOOTSTRAP_FONT_BYTES; @@ -68,6 +69,7 @@ mod custom_widgets; mod grid_navigation; mod scroll_handle; mod search_list; +mod settings; mod state; #[cfg(any(target_os = "macos", target_os = "windows"))] mod sys_tray; @@ -89,6 +91,13 @@ use crate::ui::scenario_runner::ScenarioRunnerMsg; use crate::ui::scenario_runner::handle_scenario_runner_msg; use crate::ui::scroll_handle::ScrollHandle; use crate::ui::server::handle_server_message; +use crate::ui::settings::theme::GauntletSettingsTheme; +use crate::ui::settings::ui::SettingsMsg; +use crate::ui::settings::ui::SettingsParams; +use crate::ui::settings::ui::SettingsWindowState; +use crate::ui::settings::ui::subscription_settings; +use crate::ui::settings::ui::update_settings; +use crate::ui::settings::ui::view_settings; use crate::ui::state::ErrorViewData; use crate::ui::state::Focus; use crate::ui::state::GlobalState; @@ -100,8 +109,8 @@ use crate::ui::widget::action_panel::ActionPanel; use crate::ui::widget::action_panel::ActionPanelItem; use crate::ui::widget::events::ComponentWidgetEvent; use crate::ui::widget::root::render_root; +use crate::ui::windows::MainWindowState; use crate::ui::windows::WindowActionMsg; -use crate::ui::windows::WindowState; #[cfg(target_os = "linux")] use crate::ui::windows::x11_focus::x11_linux_focus_change_subscription; @@ -112,7 +121,8 @@ pub struct AppModel { #[cfg(any(target_os = "macos", target_os = "windows"))] _tray_icon: tray_icon::TrayIcon, theme: GauntletComplexTheme, - window: WindowState, + main_window_state: MainWindowState, + settings_window_state: SettingsWindowState, // ephemeral state prompt: String, @@ -197,10 +207,7 @@ pub enum AppMsg { plugin_preferences_required: bool, entrypoint_preferences_required: bool, }, - OpenSettingsPreferences { - plugin_id: PluginId, - entrypoint_id: Option, - }, + OpenSettings(SettingsParams), OnOpenView { action_shortcuts: HashMap, }, @@ -278,6 +285,7 @@ pub enum AppMsg { display: String, }, HandleScenario(ScenarioRunnerMsg), + Settings(SettingsMsg), } pub fn run(minimized: bool, scenario_runner_data: Option) { @@ -333,7 +341,7 @@ fn new(minimized: bool, #[allow(unused)] scenario_runner_data: Option String { - if Some(window) == state.window.main_window_id { - "Gauntlet".to_owned() - } else { - "Gauntlet HUD".to_owned() + match window { + _ if Some(window) == state.main_window_state.main_window_id => "Gauntlet".to_owned(), + _ if Some(window) == state.settings_window_state.settings_window_id => "Gauntlet Settings".to_owned(), + _ => "Gauntlet HUD".to_owned(), } } @@ -648,7 +664,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { } } AppMsg::IcedEvent(window_id, Event::Keyboard(event)) => { - let Some(main_window_id) = state.window.main_window_id else { + let Some(main_window_id) = state.main_window_state.main_window_id else { return Task::none(); }; @@ -742,13 +758,20 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { } } AppMsg::IcedEvent(window_id, Event::Window(window::Event::Focused)) => { - state.window.handle_focused_event(window_id) + state.main_window_state.handle_focused_event(window_id) } AppMsg::IcedEvent(window_id, Event::Window(window::Event::Unfocused)) => { - state.window.handle_unfocused_event(window_id) + state.main_window_state.handle_unfocused_event(window_id) } AppMsg::IcedEvent(window_id, Event::Window(window::Event::Moved(point))) => { - state.window.handle_move_event(window_id, point) + state.main_window_state.handle_move_event(window_id, point) + } + AppMsg::IcedEvent(window_id, Event::Window(window::Event::Closed)) => { + if state.settings_window_state.settings_window_id == Some(window_id) { + Task::done(AppMsg::Settings(SettingsMsg::WindowDestroyed)) + } else { + Task::none() + } } AppMsg::IcedEvent(_, _) => Task::none(), AppMsg::WidgetEvent { @@ -807,10 +830,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { }, ) } - AppMsg::OpenSettingsPreferences { - plugin_id, - entrypoint_id, - } => state.open_settings_window_preferences(plugin_id, entrypoint_id), + AppMsg::OpenSettings(params) => Task::done(AppMsg::Settings(SettingsMsg::OpenSettings(params))), AppMsg::OnOpenView { action_shortcuts } => { match &mut state.global_state { GlobalState::MainView { @@ -1086,7 +1106,7 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { Task::none() } AppMsg::FocusPluginViewSearchBar { widget_id } => state.client_context.focus_search_bar(widget_id), - AppMsg::WindowAction(action) => state.window.handle_action(action), + AppMsg::WindowAction(action) => state.main_window_state.handle_action(action), AppMsg::SetTheme { theme } => { state.theme = GauntletComplexTheme::new(theme); @@ -1192,17 +1212,28 @@ fn update(state: &mut AppModel, message: AppMsg) -> Task { Task::none() } AppMsg::HandleScenario(msg) => { - handle_scenario_runner_msg(msg, state.application_manager.clone(), state.window.main_window_id) - .map(AppMsg::HandleScenario) + handle_scenario_runner_msg( + msg, + state.application_manager.clone(), + state.main_window_state.main_window_id, + ) + .map(AppMsg::HandleScenario) + } + AppMsg::Settings(msg) => { + update_settings(&mut state.settings_window_state, &state.global_hotkey_manager, msg).map(AppMsg::Settings) } } } fn view(state: &AppModel, window: window::Id) -> Element<'_, AppMsg> { - if Some(window) == state.window.main_window_id { - view_main(state) - } else { - view_hud(state) + match window { + _ if Some(window) == state.main_window_state.main_window_id => view_main(state), + _ if Some(window) == state.settings_window_state.settings_window_id => { + let themer: Element<_> = themer(GauntletSettingsTheme, view_settings(&state.settings_window_state)).into(); + + themer.map(AppMsg::Settings) + } + _ => view_hud(state), } } @@ -1257,27 +1288,25 @@ fn view_main(state: &AppModel) -> Element<'_, AppMsg> { "Before using, plugin and entrypoint preferences need to be specified"; // note: // we open plugin view and not entrypoint even though both need to be specified - let msg = AppMsg::OpenSettingsPreferences { + let msg = AppMsg::OpenSettings(SettingsParams::PluginPreferences { plugin_id: plugin_id.clone(), - entrypoint_id: None, - }; + }); (description_text, msg) } (false, true) => { // TODO do not show "entrypoint" name to user let description_text = "Before using, entrypoint preferences need to be specified"; - let msg = AppMsg::OpenSettingsPreferences { + let msg = AppMsg::OpenSettings(SettingsParams::EntrypointPreferences { plugin_id: plugin_id.clone(), - entrypoint_id: Some(entrypoint_id.clone()), - }; + entrypoint_id: entrypoint_id.clone(), + }); (description_text, msg) } (true, false) => { let description_text = "Before using, plugin preferences need to be specified"; - let msg = AppMsg::OpenSettingsPreferences { + let msg = AppMsg::OpenSettings(SettingsParams::PluginPreferences { plugin_id: plugin_id.clone(), - entrypoint_id: None, - }; + }); (description_text, msg) } (false, false) => unreachable!(), @@ -1744,7 +1773,9 @@ fn view_main(state: &AppModel) -> Element<'_, AppMsg> { } fn subscription(#[allow(unused)] state: &AppModel) -> Subscription { - let events_subscription = event::listen_with(|event, status, window_id| { + let mut subscriptions = vec![]; + + subscriptions.push(event::listen_with(|event, status, window_id| { match status { event::Status::Ignored => Some(AppMsg::IcedEvent(window_id, event)), event::Status::Captured => { @@ -1757,25 +1788,23 @@ fn subscription(#[allow(unused)] state: &AppModel) -> Subscription { } } } - }); + })); - #[allow(unused_mut)] - let mut subscriptions = vec![ - Subscription::run(|| { - stream::channel(10, async move |sender| { - register_global_shortcut_listener(sender.clone()); + subscriptions.push(Subscription::run(|| { + stream::channel(10, async move |sender| { + register_global_shortcut_listener(sender.clone()); - std::future::pending::<()>().await; + std::future::pending::<()>().await; - unreachable!() - }) - .map(AppMsg::HandleGlobalShortcut) - }), - events_subscription, - ]; + unreachable!() + }) + .map(AppMsg::HandleGlobalShortcut) + })); + + subscriptions.push(subscription_settings(&state.settings_window_state).map(AppMsg::Settings)); #[cfg(target_os = "linux")] - if !state.window.wayland { + if !state.main_window_state.wayland { subscriptions.push(x11_linux_focus_change_subscription()) } @@ -1951,17 +1980,6 @@ impl AppModel { Task::done(msg) } - fn open_settings_window_preferences( - &self, - plugin_id: PluginId, - entrypoint_id: Option, - ) -> Task { - self.application_manager - .open_settings_window_preferences(plugin_id, entrypoint_id); - - Task::none() - } - fn inline_view_shortcuts(&self) -> Task { let result = self .application_manager @@ -1998,11 +2016,7 @@ impl AppModel { modifier_control: cfg!(not(target_os = "macos")), modifier_alt: false, modifier_meta: cfg!(target_os = "macos"), - }) => { - self.application_manager.open_settings_window(); - - Task::none() - } + }) => Task::done(AppMsg::OpenSettings(SettingsParams::Default)), Some(PhysicalShortcut { physical_key: PhysicalKey::KeyK, modifier_shift: false, diff --git a/rust/client/src/ui/server.rs b/rust/client/src/ui/server.rs index 91ae342..6c3066c 100644 --- a/rust/client/src/ui/server.rs +++ b/rust/client/src/ui/server.rs @@ -2,7 +2,6 @@ use std::ops::Deref; use std::sync::Arc; use std::sync::Mutex; -use anyhow::anyhow; use gauntlet_common::model::UiSetupData; use gauntlet_common::rpc::frontend_api::FrontendApiRequestData; use gauntlet_common::rpc::frontend_api::FrontendApiResponseData; @@ -22,6 +21,7 @@ use tokio::sync::RwLock as TokioRwLock; use crate::ui::AppModel; use crate::ui::AppMsg; +use crate::ui::settings::ui::SettingsParams; #[cfg(target_os = "linux")] use crate::ui::wayland::layer_shell_supported; use crate::ui::windows::WindowActionMsg; @@ -234,6 +234,11 @@ async fn request_loop( action_index, } } + FrontendApiRequestData::ShowSettings {} => { + responder.respond(Ok(FrontendApiResponseData::ShowSettings { data: () })); + + AppMsg::OpenSettings(SettingsParams::Default) + } } }; @@ -260,9 +265,7 @@ pub fn handle_server_message( ServerGrpcApiRequestData::ShowSettingsWindow {} => { responder.respond(Ok(ServerGrpcApiResponseData::ShowSettingsWindow { data: () })); - state.application_manager.open_settings_window(); - - Task::none() + Task::done(AppMsg::OpenSettings(SettingsParams::Default)) } ServerGrpcApiRequestData::RunAction { plugin_id, @@ -293,230 +296,6 @@ pub fn handle_server_message( responder.respond(result); - Task::none() - } - ServerGrpcApiRequestData::Plugins {} => { - let result = state - .application_manager - .plugins() - .map(|data| ServerGrpcApiResponseData::Plugins { data }); - - responder.respond(result.into()); - - Task::none() - } - ServerGrpcApiRequestData::SetPluginState { plugin_id, enabled } => { - let result = state - .application_manager - .set_plugin_state(plugin_id.clone(), *enabled) - .map(|data| ServerGrpcApiResponseData::SetPluginState { data }); - - responder.respond(result.into()); - - Task::none() - } - ServerGrpcApiRequestData::SetEntrypointState { - plugin_id, - entrypoint_id, - enabled, - } => { - let result = state - .application_manager - .set_entrypoint_state(plugin_id.clone(), entrypoint_id.clone(), *enabled) - .map(|data| ServerGrpcApiResponseData::SetEntrypointState { data }); - - responder.respond(result); - - Task::none() - } - ServerGrpcApiRequestData::SetGlobalShortcut { shortcut } => { - let Some(global_hotkey_manager) = &state.global_hotkey_manager else { - responder.respond(Err(anyhow!("Global hotkey manager is disabled"))); - return Task::none(); - }; - - let result = state - .application_manager - .set_global_shortcut(global_hotkey_manager, shortcut.clone()); - - responder.respond(Ok(ServerGrpcApiResponseData::SetGlobalShortcut { data: result })); - - Task::none() - } - ServerGrpcApiRequestData::GetGlobalShortcut {} => { - let result = state - .application_manager - .get_global_shortcut() - .map(|data| ServerGrpcApiResponseData::GetGlobalShortcut { data }); - - responder.respond(result); - - Task::none() - } - ServerGrpcApiRequestData::SetGlobalEntrypointShortcut { - plugin_id, - entrypoint_id, - shortcut, - } => { - let Some(global_hotkey_manager) = &state.global_hotkey_manager else { - responder.respond(Err(anyhow!("Global hotkey manager is disabled"))); - return Task::none(); - }; - - let result = state - .application_manager - .set_global_entrypoint_shortcut( - global_hotkey_manager, - plugin_id.clone(), - entrypoint_id.clone(), - shortcut.clone(), - ) - .map(|data| ServerGrpcApiResponseData::SetGlobalEntrypointShortcut { data }); - - responder.respond(result); - - Task::none() - } - ServerGrpcApiRequestData::GetGlobalEntrypointShortcuts {} => { - let result = state - .application_manager - .get_global_entrypoint_shortcut() - .map(|data| ServerGrpcApiResponseData::GetGlobalEntrypointShortcuts { data }); - - responder.respond(result); - - Task::none() - } - ServerGrpcApiRequestData::SetEntrypointSearchAlias { - plugin_id, - entrypoint_id, - alias, - } => { - let result = state - .application_manager - .set_entrypoint_search_alias(plugin_id.clone(), entrypoint_id.clone(), alias.clone()) - .map(|data| ServerGrpcApiResponseData::SetEntrypointSearchAlias { data }); - - responder.respond(result); - - Task::none() - } - ServerGrpcApiRequestData::GetEntrypointSearchAliases {} => { - let result = state - .application_manager - .get_entrypoint_search_aliases() - .map(|data| ServerGrpcApiResponseData::GetEntrypointSearchAliases { data }); - - responder.respond(result); - - Task::none() - } - ServerGrpcApiRequestData::SetTheme { theme } => { - let application_manager = state.application_manager.clone(); - let theme = theme.clone(); - - Task::future(async move { - let result = application_manager - .set_theme(theme) - .await - .map(|data| ServerGrpcApiResponseData::SetTheme { data }); - - responder.respond(result); - - AppMsg::Noop - }) - } - ServerGrpcApiRequestData::GetTheme {} => { - let result = state - .application_manager - .get_theme() - .map(|data| ServerGrpcApiResponseData::GetTheme { data }); - - responder.respond(result); - - Task::none() - } - ServerGrpcApiRequestData::SetWindowPositionMode { mode } => { - let application_manager = state.application_manager.clone(); - let mode = mode.clone(); - - Task::future(async move { - let result = application_manager - .set_window_position_mode(mode) - .await - .map(|data| ServerGrpcApiResponseData::SetWindowPositionMode { data }); - - responder.respond(result); - - AppMsg::Noop - }) - } - ServerGrpcApiRequestData::GetWindowPositionMode {} => { - let result = state - .application_manager - .get_window_position_mode() - .map(|data| ServerGrpcApiResponseData::GetWindowPositionMode { data }); - - responder.respond(result); - - Task::none() - } - ServerGrpcApiRequestData::SetPreferenceValue { - plugin_id, - entrypoint_id, - preference_id, - preference_value, - } => { - let result = state - .application_manager - .set_preference_value( - plugin_id.clone(), - entrypoint_id.clone(), - preference_id.clone(), - preference_value.clone(), - ) - .map(|data| ServerGrpcApiResponseData::SetPreferenceValue { data }); - - responder.respond(result); - - Task::none() - } - ServerGrpcApiRequestData::DownloadPlugin { plugin_id } => { - let result = state - .application_manager - .download_plugin(plugin_id.clone()) - .map(|data| ServerGrpcApiResponseData::DownloadPlugin { data }); - - responder.respond(result); - - Task::none() - } - ServerGrpcApiRequestData::DownloadStatus {} => { - let data = state.application_manager.download_status(); - - responder.respond(Ok(ServerGrpcApiResponseData::DownloadStatus { data })); - - Task::none() - } - ServerGrpcApiRequestData::RemovePlugin { plugin_id } => { - let result = state - .application_manager - .remove_plugin(plugin_id.clone()) - .map(|data| ServerGrpcApiResponseData::RemovePlugin { data }); - - responder.respond(result); - - Task::none() - } - ServerGrpcApiRequestData::WaylandGlobalShortcutsEnabled {} => { - let result = state.application_manager.config().map(|data| { - ServerGrpcApiResponseData::WaylandGlobalShortcutsEnabled { - data: data.wayland_use_legacy_x11_api, - } - }); - - responder.respond(result); - Task::none() } } diff --git a/rust/management_client/src/components/mod.rs b/rust/client/src/ui/settings/components/mod.rs similarity index 100% rename from rust/management_client/src/components/mod.rs rename to rust/client/src/ui/settings/components/mod.rs diff --git a/rust/management_client/src/components/shortcut_selector.rs b/rust/client/src/ui/settings/components/shortcut_selector.rs similarity index 98% rename from rust/management_client/src/components/shortcut_selector.rs rename to rust/client/src/ui/settings/components/shortcut_selector.rs index 277ea19..fe56abc 100644 --- a/rust/management_client/src/components/shortcut_selector.rs +++ b/rust/client/src/ui/settings/components/shortcut_selector.rs @@ -33,10 +33,10 @@ use iced::widget::tooltip; use iced::widget::tooltip::Position; use iced_fonts::bootstrap::exclamation_triangle_fill; -use crate::theme::Element; -use crate::theme::GauntletSettingsTheme; -use crate::theme::container::ContainerStyle; -use crate::theme::text::TextStyle; +use crate::ui::settings::theme::Element; +use crate::ui::settings::theme::GauntletSettingsTheme; +use crate::ui::settings::theme::container::ContainerStyle; +use crate::ui::settings::theme::text::TextStyle; pub struct ShortcutData { pub shortcut: Option, diff --git a/rust/management_client/src/components/spinner.rs b/rust/client/src/ui/settings/components/spinner.rs similarity index 100% rename from rust/management_client/src/components/spinner.rs rename to rust/client/src/ui/settings/components/spinner.rs diff --git a/rust/client/src/ui/settings/mod.rs b/rust/client/src/ui/settings/mod.rs new file mode 100644 index 0000000..5fb2004 --- /dev/null +++ b/rust/client/src/ui/settings/mod.rs @@ -0,0 +1,4 @@ +mod components; +pub mod theme; +pub mod ui; +pub mod views; diff --git a/rust/management_client/src/theme.rs b/rust/client/src/ui/settings/theme.rs similarity index 83% rename from rust/management_client/src/theme.rs rename to rust/client/src/ui/settings/theme.rs index e3f1134..f7987fc 100644 --- a/rust/management_client/src/theme.rs +++ b/rust/client/src/ui/settings/theme.rs @@ -1,7 +1,3 @@ -use iced::theme; -use iced::theme::Palette; -use iced::theme::Style; - pub mod button; pub mod checkbox; pub mod container; @@ -14,22 +10,9 @@ pub mod text_input; pub type Element<'a, Message> = iced::Element<'a, Message, GauntletSettingsTheme>; -#[derive(Default)] +#[derive(Default, Clone)] pub struct GauntletSettingsTheme; -impl theme::Base for GauntletSettingsTheme { - fn base(&self) -> Style { - Style { - background_color: BACKGROUND_DARKEST.to_iced(), - text_color: TEXT_LIGHTEST.to_iced(), - } - } - - fn palette(&self) -> Option { - Some(Palette::FERRA) - } -} - // keep colors more or less in sync with main ui #[allow(unused)] pub const NOT_INTENDED_TO_BE_USED: ThemeColor = ThemeColor::new(0xAF5BFF, 1.0); diff --git a/rust/management_client/src/theme/button.rs b/rust/client/src/ui/settings/theme/button.rs similarity index 92% rename from rust/management_client/src/theme/button.rs rename to rust/client/src/ui/settings/theme/button.rs index 5e05950..5595e27 100644 --- a/rust/management_client/src/theme/button.rs +++ b/rust/client/src/ui/settings/theme/button.rs @@ -3,16 +3,16 @@ use iced::widget::button; use iced::widget::button::Status; use iced::widget::button::Style; -use crate::theme::BACKGROUND_DARKER; -use crate::theme::BACKGROUND_LIGHTER; -use crate::theme::BUTTON_BORDER_RADIUS; -use crate::theme::DANGER; -use crate::theme::GauntletSettingsTheme; -use crate::theme::PRIMARY; -use crate::theme::PRIMARY_HOVERED; -use crate::theme::SUCCESS; -use crate::theme::TEXT_DARKEST; -use crate::theme::TEXT_LIGHTEST; +use crate::ui::settings::theme::BACKGROUND_DARKER; +use crate::ui::settings::theme::BACKGROUND_LIGHTER; +use crate::ui::settings::theme::BUTTON_BORDER_RADIUS; +use crate::ui::settings::theme::DANGER; +use crate::ui::settings::theme::GauntletSettingsTheme; +use crate::ui::settings::theme::PRIMARY; +use crate::ui::settings::theme::PRIMARY_HOVERED; +use crate::ui::settings::theme::SUCCESS; +use crate::ui::settings::theme::TEXT_DARKEST; +use crate::ui::settings::theme::TEXT_LIGHTEST; pub enum ButtonStyle { Primary, diff --git a/rust/management_client/src/theme/checkbox.rs b/rust/client/src/ui/settings/theme/checkbox.rs similarity index 85% rename from rust/management_client/src/theme/checkbox.rs rename to rust/client/src/ui/settings/theme/checkbox.rs index 4e744d6..b9b9cf6 100644 --- a/rust/management_client/src/theme/checkbox.rs +++ b/rust/client/src/ui/settings/theme/checkbox.rs @@ -3,12 +3,12 @@ use iced::widget::checkbox; use iced::widget::checkbox::Status; use iced::widget::checkbox::Style; -use crate::theme::BACKGROUND_DARKER; -use crate::theme::BACKGROUND_DARKEST; -use crate::theme::BACKGROUND_LIGHTER; -use crate::theme::GauntletSettingsTheme; -use crate::theme::PRIMARY; -use crate::theme::PRIMARY_HOVERED; +use crate::ui::settings::theme::BACKGROUND_DARKER; +use crate::ui::settings::theme::BACKGROUND_DARKEST; +use crate::ui::settings::theme::BACKGROUND_LIGHTER; +use crate::ui::settings::theme::GauntletSettingsTheme; +use crate::ui::settings::theme::PRIMARY; +use crate::ui::settings::theme::PRIMARY_HOVERED; impl checkbox::Catalog for GauntletSettingsTheme { type Class<'a> = (); diff --git a/rust/management_client/src/theme/container.rs b/rust/client/src/ui/settings/theme/container.rs similarity index 72% rename from rust/management_client/src/theme/container.rs rename to rust/client/src/ui/settings/theme/container.rs index 692b5f3..e416a5a 100644 --- a/rust/management_client/src/theme/container.rs +++ b/rust/client/src/ui/settings/theme/container.rs @@ -1,15 +1,19 @@ +use iced::Background; use iced::Border; use iced::Color; use iced::widget::container; use iced::widget::container::Style; -use crate::theme::BACKGROUND_DARKER; -use crate::theme::BACKGROUND_LIGHTER; -use crate::theme::DANGER; -use crate::theme::GauntletSettingsTheme; -use crate::theme::TRANSPARENT; +use crate::ui::settings::theme::BACKGROUND_DARKER; +use crate::ui::settings::theme::BACKGROUND_DARKEST; +use crate::ui::settings::theme::BACKGROUND_LIGHTER; +use crate::ui::settings::theme::DANGER; +use crate::ui::settings::theme::GauntletSettingsTheme; +use crate::ui::settings::theme::TEXT_LIGHTEST; +use crate::ui::settings::theme::TRANSPARENT; pub enum ContainerStyle { + WindowRoot, Transparent, Box, TextInputMissingValue, @@ -60,6 +64,13 @@ impl container::Catalog for GauntletSettingsTheme { ..Default::default() } } + ContainerStyle::WindowRoot => { + Style { + background: Some(Background::Color(BACKGROUND_DARKEST.to_iced())), + text_color: Some(TEXT_LIGHTEST.to_iced()), + ..Default::default() + } + } } } } diff --git a/rust/management_client/src/theme/pick_list.rs b/rust/client/src/ui/settings/theme/pick_list.rs similarity index 82% rename from rust/management_client/src/theme/pick_list.rs rename to rust/client/src/ui/settings/theme/pick_list.rs index bd6c159..7edcf56 100644 --- a/rust/management_client/src/theme/pick_list.rs +++ b/rust/client/src/ui/settings/theme/pick_list.rs @@ -2,14 +2,14 @@ use iced::Border; use iced::overlay; use iced::widget::pick_list; -use crate::theme::BACKGROUND_DARKER; -use crate::theme::BACKGROUND_DARKEST; -use crate::theme::BUTTON_BORDER_RADIUS; -use crate::theme::GauntletSettingsTheme; -use crate::theme::PRIMARY; -use crate::theme::PRIMARY_HOVERED; -use crate::theme::TEXT_DARKEST; -use crate::theme::TEXT_LIGHTEST; +use crate::ui::settings::theme::BACKGROUND_DARKER; +use crate::ui::settings::theme::BACKGROUND_DARKEST; +use crate::ui::settings::theme::BUTTON_BORDER_RADIUS; +use crate::ui::settings::theme::GauntletSettingsTheme; +use crate::ui::settings::theme::PRIMARY; +use crate::ui::settings::theme::PRIMARY_HOVERED; +use crate::ui::settings::theme::TEXT_DARKEST; +use crate::ui::settings::theme::TEXT_LIGHTEST; impl pick_list::Catalog for GauntletSettingsTheme { type Class<'a> = (); diff --git a/rust/management_client/src/theme/rule.rs b/rust/client/src/ui/settings/theme/rule.rs similarity index 81% rename from rust/management_client/src/theme/rule.rs rename to rust/client/src/ui/settings/theme/rule.rs index ae9ea52..35a9124 100644 --- a/rust/management_client/src/theme/rule.rs +++ b/rust/client/src/ui/settings/theme/rule.rs @@ -1,8 +1,8 @@ use iced::widget::rule; use iced::widget::rule::Style; -use crate::theme::BACKGROUND_DARKER; -use crate::theme::GauntletSettingsTheme; +use crate::ui::settings::theme::BACKGROUND_DARKER; +use crate::ui::settings::theme::GauntletSettingsTheme; impl rule::Catalog for GauntletSettingsTheme { type Class<'a> = (); diff --git a/rust/management_client/src/theme/scrollable.rs b/rust/client/src/ui/settings/theme/scrollable.rs similarity index 96% rename from rust/management_client/src/theme/scrollable.rs rename to rust/client/src/ui/settings/theme/scrollable.rs index fafed5f..e4d3343 100644 --- a/rust/management_client/src/theme/scrollable.rs +++ b/rust/client/src/ui/settings/theme/scrollable.rs @@ -6,8 +6,8 @@ use iced::widget::scrollable; use iced::widget::scrollable::Status; use iced::widget::scrollable::Style; -use crate::theme::GauntletSettingsTheme; -use crate::theme::PRIMARY; +use crate::ui::settings::theme::GauntletSettingsTheme; +use crate::ui::settings::theme::PRIMARY; impl scrollable::Catalog for GauntletSettingsTheme { type Class<'a> = (); diff --git a/rust/management_client/src/theme/shortcut_selector.rs b/rust/client/src/ui/settings/theme/shortcut_selector.rs similarity index 79% rename from rust/management_client/src/theme/shortcut_selector.rs rename to rust/client/src/ui/settings/theme/shortcut_selector.rs index 09dc383..c416ae0 100644 --- a/rust/management_client/src/theme/shortcut_selector.rs +++ b/rust/client/src/ui/settings/theme/shortcut_selector.rs @@ -1,14 +1,14 @@ use iced::Border; use iced::widget::container::Style; -use crate::components::shortcut_selector; -use crate::components::shortcut_selector::Status; -use crate::theme::BACKGROUND_DARKER; -use crate::theme::BACKGROUND_LIGHTER; -use crate::theme::BUTTON_BORDER_RADIUS; -use crate::theme::GauntletSettingsTheme; -use crate::theme::PRIMARY; -use crate::theme::TRANSPARENT; +use crate::ui::settings::components::shortcut_selector; +use crate::ui::settings::components::shortcut_selector::Status; +use crate::ui::settings::theme::BACKGROUND_DARKER; +use crate::ui::settings::theme::BACKGROUND_LIGHTER; +use crate::ui::settings::theme::BUTTON_BORDER_RADIUS; +use crate::ui::settings::theme::GauntletSettingsTheme; +use crate::ui::settings::theme::PRIMARY; +use crate::ui::settings::theme::TRANSPARENT; impl shortcut_selector::Catalog for GauntletSettingsTheme { type Class<'a> = (); diff --git a/rust/management_client/src/theme/text.rs b/rust/client/src/ui/settings/theme/text.rs similarity index 82% rename from rust/management_client/src/theme/text.rs rename to rust/client/src/ui/settings/theme/text.rs index be46ec7..a32fd55 100644 --- a/rust/management_client/src/theme/text.rs +++ b/rust/client/src/ui/settings/theme/text.rs @@ -1,10 +1,10 @@ use iced::widget::text; use iced::widget::text::Style; -use crate::theme::DANGER_BRIGHT; -use crate::theme::GauntletSettingsTheme; -use crate::theme::SUCCESS; -use crate::theme::TEXT_DARKER; +use crate::ui::settings::theme::DANGER_BRIGHT; +use crate::ui::settings::theme::GauntletSettingsTheme; +use crate::ui::settings::theme::SUCCESS; +use crate::ui::settings::theme::TEXT_DARKER; pub enum TextStyle { Default, diff --git a/rust/management_client/src/theme/text_input.rs b/rust/client/src/ui/settings/theme/text_input.rs similarity index 86% rename from rust/management_client/src/theme/text_input.rs rename to rust/client/src/ui/settings/theme/text_input.rs index cf58f28..8e35f7e 100644 --- a/rust/management_client/src/theme/text_input.rs +++ b/rust/client/src/ui/settings/theme/text_input.rs @@ -4,14 +4,14 @@ use iced::widget::text_input; use iced::widget::text_input::Status; use iced::widget::text_input::Style; -use crate::theme::BACKGROUND_DARKER; -use crate::theme::BACKGROUND_LIGHTER; -use crate::theme::BACKGROUND_LIGHTEST; -use crate::theme::BUTTON_BORDER_RADIUS; -use crate::theme::GauntletSettingsTheme; -use crate::theme::TEXT_DARKER; -use crate::theme::TEXT_LIGHTEST; -use crate::theme::TRANSPARENT; +use crate::ui::settings::theme::BACKGROUND_DARKER; +use crate::ui::settings::theme::BACKGROUND_LIGHTER; +use crate::ui::settings::theme::BACKGROUND_LIGHTEST; +use crate::ui::settings::theme::BUTTON_BORDER_RADIUS; +use crate::ui::settings::theme::GauntletSettingsTheme; +use crate::ui::settings::theme::TEXT_DARKER; +use crate::ui::settings::theme::TEXT_LIGHTEST; +use crate::ui::settings::theme::TRANSPARENT; pub enum TextInputStyle { FormInput, diff --git a/rust/management_client/src/ui.rs b/rust/client/src/ui/settings/ui.rs similarity index 67% rename from rust/management_client/src/ui.rs rename to rust/client/src/ui/settings/ui.rs index 2948fcc..18bb420 100644 --- a/rust/management_client/src/ui.rs +++ b/rust/client/src/ui/settings/ui.rs @@ -1,29 +1,23 @@ use std::collections::HashMap; +use std::sync::Arc; use std::time::Duration; use gauntlet_common::model::DownloadStatus; use gauntlet_common::model::EntrypointId; -use gauntlet_common::model::PhysicalShortcut; use gauntlet_common::model::PluginId; -use gauntlet_common::model::SettingsTheme; -use gauntlet_common::model::WindowPositionMode; -use gauntlet_common::rpc::backend_api::BackendForSettingsApi; -use gauntlet_common::rpc::backend_api::BackendForSettingsApiProxy; -use gauntlet_common::rpc::backend_api::GrpcBackendApi; use gauntlet_common_ui::padding; +use gauntlet_server::global_hotkey::GlobalHotKeyManager; +use gauntlet_server::plugins::ApplicationManager; use gauntlet_utils::channel::RequestError; use gauntlet_utils::channel::RequestResult; use iced::Alignment; use iced::Length; use iced::Padding; -use iced::Renderer; use iced::Size; use iced::Subscription; use iced::Task; use iced::advanced::text::Shaping; use iced::alignment; -use iced::font; -use iced::futures; use iced::padding; use iced::time; use iced::widget::button; @@ -37,54 +31,73 @@ use iced::widget::scrollable; use iced::widget::stack; use iced::widget::text; use iced::window; -use iced_fonts::BOOTSTRAP_FONT_BYTES; use iced_fonts::bootstrap::exclamation_triangle_fill; use iced_fonts::bootstrap::gear_fill; use iced_fonts::bootstrap::patch_check_fill; use iced_fonts::bootstrap::puzzle_fill; use itertools::Itertools; -use crate::components::spinner::Spinner; -use crate::theme::Element; -use crate::theme::GauntletSettingsTheme; -use crate::theme::button::ButtonStyle; -use crate::theme::container::ContainerStyle; -use crate::theme::text::TextStyle; -use crate::views::general::ManagementAppGeneralMsgIn; -use crate::views::general::ManagementAppGeneralMsgOut; -use crate::views::general::ManagementAppGeneralState; -use crate::views::plugins::ManagementAppPluginMsgIn; -use crate::views::plugins::ManagementAppPluginMsgOut; -use crate::views::plugins::ManagementAppPluginsState; +use crate::ui::settings::components::spinner::Spinner; +use crate::ui::settings::theme::Element; +use crate::ui::settings::theme::button::ButtonStyle; +use crate::ui::settings::theme::container::ContainerStyle; +use crate::ui::settings::theme::text::TextStyle; +use crate::ui::settings::views::general::SettingsGeneralMsgIn; +use crate::ui::settings::views::general::SettingsGeneralMsgOut; +use crate::ui::settings::views::general::SettingsGeneralState; +use crate::ui::settings::views::plugins::SelectedItem; +use crate::ui::settings::views::plugins::SettingsPluginMsgIn; +use crate::ui::settings::views::plugins::SettingsPluginMsgOut; +use crate::ui::settings::views::plugins::SettingsPluginsState; -pub fn run() { - iced::application::(new, update, view) - .title("Gauntlet Settings") - .window(window::Settings { - size: Size::new(1150.0, 700.0), - ..Default::default() - }) - .subscription(subscription) - .theme(|_| GauntletSettingsTheme::default()) - .run() - .expect("Unable to start settings application"); -} - -struct ManagementAppModel { - backend_api: Option, +pub struct SettingsWindowState { + pub settings_window_id: Option, + application_manager: Arc, + wayland: bool, error_view: Option, downloads_info: HashMap, download_info_shown: bool, current_settings_view: SettingsView, - general_state: ManagementAppGeneralState, - plugins_state: ManagementAppPluginsState, + general_state: SettingsGeneralState, + plugins_state: SettingsPluginsState, +} + +impl SettingsWindowState { + pub fn new(application_manager: Arc, wayland: bool) -> SettingsWindowState { + SettingsWindowState { + settings_window_id: None, + application_manager: application_manager.clone(), + wayland, + error_view: None, + downloads_info: HashMap::new(), + download_info_shown: false, + current_settings_view: SettingsView::Plugins, + general_state: SettingsGeneralState::new(application_manager.clone()), + plugins_state: SettingsPluginsState::new(application_manager.clone()), + } + } +} + +#[derive(Clone, Debug)] +pub enum SettingsParams { + Default, + PluginPreferences { + plugin_id: PluginId, + }, + EntrypointPreferences { + plugin_id: PluginId, + entrypoint_id: EntrypointId, + }, } #[derive(Debug, Clone)] -pub enum ManagementAppMsg { - FontLoaded(Result<(), font::Error>), - General(ManagementAppGeneralMsgIn), - Plugin(ManagementAppPluginMsgIn), +pub enum SettingsMsg { + Refresh, + OpenSettings(SettingsParams), + WindowCreated(window::Id), + WindowDestroyed, + General(SettingsGeneralMsgIn), + Plugin(SettingsPluginMsgIn), SwitchView(SettingsView), DownloadStatus { plugins: HashMap }, HandleBackendError(RequestError), @@ -113,130 +126,34 @@ pub enum DownloadInfo { Successful, } -fn new() -> (ManagementAppModel, Task) { - let backend_api = futures::executor::block_on(async { - anyhow::Ok(BackendForSettingsApiProxy::new(GrpcBackendApi::new().await?)) - }) - .inspect_err(|err| tracing::error!("Unable to connect to server: {:?}", err)) - .ok(); - - let wayland = if cfg!(target_os = "linux") { - std::env::var("WAYLAND_DISPLAY") - .or_else(|_| std::env::var("WAYLAND_SOCKET")) - .is_ok() - } else { - false - }; - - ( - ManagementAppModel { - backend_api: backend_api.clone(), - error_view: None, - downloads_info: HashMap::new(), - download_info_shown: false, - current_settings_view: SettingsView::Plugins, - general_state: ManagementAppGeneralState::new(backend_api.clone()), - plugins_state: ManagementAppPluginsState::new(backend_api.clone()), - }, - Task::batch([ - font::load(BOOTSTRAP_FONT_BYTES).map(ManagementAppMsg::FontLoaded), - Task::done(ManagementAppMsg::Plugin(ManagementAppPluginMsgIn::FetchPlugins)), - Task::future(async { - match backend_api { - Some(backend_api) => Some(init_data(backend_api).await), - None => None, - } - }) - .then(move |init_data| { - match init_data { - None => Task::done(ManagementAppMsg::General(ManagementAppGeneralMsgIn::Noop)), - Some(init) => { - match init { - Ok(init) => { - Task::batch([ - Task::done(ManagementAppMsg::General(ManagementAppGeneralMsgIn::InitSetting { - theme: init.theme, - window_position_mode: init.window_position_mode, - shortcut: init.global_shortcut, - shortcut_error: init.global_shortcut_error, - global_shortcuts_unsupported: wayland && !init.wayland_global_shortcuts_enabled, - })), - Task::done(ManagementAppMsg::Plugin(ManagementAppPluginMsgIn::InitSetting { - global_entrypoint_shortcuts: init.global_entrypoint_shortcuts, - show_global_shortcuts: !wayland || init.wayland_global_shortcuts_enabled, - })), - ]) - } - Err(err) => Task::done(ManagementAppMsg::HandleBackendError(err)), - } - } - } - }), - ]), - ) -} - -struct InitSettingsData { - global_shortcut: Option, - global_shortcut_error: Option, - theme: SettingsTheme, - window_position_mode: WindowPositionMode, - global_entrypoint_shortcuts: HashMap<(PluginId, EntrypointId), (PhysicalShortcut, Option)>, - wayland_global_shortcuts_enabled: bool, -} - -async fn init_data(backend_api: impl BackendForSettingsApi) -> RequestResult { - let (global_shortcut, global_shortcut_error) = backend_api.get_global_shortcut().await?; - let global_entrypoint_shortcuts = backend_api.get_global_entrypoint_shortcuts().await?; - - let theme = backend_api.get_theme().await?; - - let window_position_mode = backend_api.get_window_position_mode().await?; - let wayland_global_shortcuts_enabled = backend_api.wayland_global_shortcuts_enabled().await?; - - Ok(InitSettingsData { - global_shortcut, - global_shortcut_error, - global_entrypoint_shortcuts, - theme, - window_position_mode, - wayland_global_shortcuts_enabled, - }) -} - -fn update(state: &mut ManagementAppModel, message: ManagementAppMsg) -> Task { - let backend_api = match &state.backend_api { - Some(backend_api) => backend_api.clone(), - None => return Task::none(), - }; - +pub fn update_settings( + state: &mut SettingsWindowState, + global_hotkey_manager: &Option, + message: SettingsMsg, +) -> Task { match message { - ManagementAppMsg::Plugin(message) => { - state.plugins_state.update(message).map(|msg| { + SettingsMsg::Plugin(message) => { + state.plugins_state.update(global_hotkey_manager, message).map(|msg| { match msg { - ManagementAppPluginMsgOut::Inner(msg) => ManagementAppMsg::Plugin(msg), - ManagementAppPluginMsgOut::Outer(msg) => msg, + SettingsPluginMsgOut::Inner(msg) => SettingsMsg::Plugin(msg), + SettingsPluginMsgOut::Outer(msg) => msg, } }) } - ManagementAppMsg::General(message) => { - state.general_state.update(message).map(|msg| { + SettingsMsg::General(message) => { + state.general_state.update(global_hotkey_manager, message).map(|msg| { match msg { - ManagementAppGeneralMsgOut::Inner(msg) => ManagementAppMsg::General(msg), - ManagementAppGeneralMsgOut::Outer(msg) => msg, + SettingsGeneralMsgOut::Inner(msg) => SettingsMsg::General(msg), + SettingsGeneralMsgOut::Outer(msg) => msg, } }) } - ManagementAppMsg::FontLoaded(result) => { - result.expect("unable to load font"); - Task::none() - } - ManagementAppMsg::SwitchView(view) => { + SettingsMsg::SwitchView(view) => { state.current_settings_view = view; Task::none() } - ManagementAppMsg::HandleBackendError(err) => { + SettingsMsg::HandleBackendError(err) => { state.error_view = Some(match err { RequestError::Timeout => ErrorView::Timeout, RequestError::Other { display } => ErrorView::UnknownError { display }, @@ -249,7 +166,7 @@ fn update(state: &mut ManagementAppModel, message: ManagementAppMsg) -> Task { + SettingsMsg::DownloadStatus { plugins } => { for (plugin, status) in plugins { match status { DownloadStatus::InProgress => { @@ -266,13 +183,13 @@ fn update(state: &mut ManagementAppModel, message: ManagementAppMsg) -> Task Task Task { + SettingsMsg::CheckDownloadStatus => { if state.downloads_info.is_empty() { Task::none() } else { - let backend_client = backend_api.clone(); + let plugins = state.application_manager.download_status(); - Task::perform( - async move { - let plugins = backend_client.download_status().await?; - - Ok(plugins) - }, - |result| handle_backend_error(result, |plugins| ManagementAppMsg::DownloadStatus { plugins }), - ) + Task::done(SettingsMsg::DownloadStatus { plugins }) } } - ManagementAppMsg::DownloadPlugin { plugin_id } => { - let backend_client = backend_api.clone(); + SettingsMsg::DownloadPlugin { plugin_id } => { + let backend_client = state.application_manager.clone(); let already_downloading = state .downloads_info @@ -317,39 +227,97 @@ fn update(state: &mut ManagementAppModel, message: ManagementAppMsg) -> Task Task::none(), - ManagementAppMsg::ToggleDownloadInfo => { + SettingsMsg::Noop => Task::none(), + SettingsMsg::ToggleDownloadInfo => { state.download_info_shown = !state.download_info_shown; Task::none() } + SettingsMsg::Refresh => { + fn run(state: &mut SettingsWindowState) -> anyhow::Result> { + let (global_shortcut, global_shortcut_error) = + state.application_manager.get_global_shortcut().map(|data| { + data.map(|(shortcut, error)| (Some(shortcut), error)) + .unwrap_or((None, None)) + })?; + + let global_entrypoint_shortcuts = state.application_manager.get_global_entrypoint_shortcuts()?; + + let theme = state.application_manager.get_theme()?; + + let window_position_mode = state.application_manager.get_window_position_mode()?; + let wayland_global_shortcuts_enabled = state.application_manager.config()?.wayland_use_legacy_x11_api; + + Ok(Task::batch([ + Task::done(SettingsMsg::General(SettingsGeneralMsgIn::InitSetting { + theme, + window_position_mode, + shortcut: global_shortcut, + shortcut_error: global_shortcut_error, + global_shortcuts_unsupported: state.wayland && !wayland_global_shortcuts_enabled, + })), + Task::done(SettingsMsg::Plugin(SettingsPluginMsgIn::InitSetting { + global_entrypoint_shortcuts, + show_global_shortcuts: !state.wayland || wayland_global_shortcuts_enabled, + })), + ])) + } + + run(state).unwrap_or_else(|err| Task::done(SettingsMsg::HandleBackendError(err.into()))) + } + SettingsMsg::WindowCreated(window_id) => { + state.settings_window_id = Some(window_id); + + Task::none() + } + SettingsMsg::WindowDestroyed => { + state.settings_window_id = None; + + Task::none() + } + SettingsMsg::OpenSettings(settings_params) => { + let item = match settings_params { + SettingsParams::Default => SelectedItem::None, + SettingsParams::PluginPreferences { plugin_id } => SelectedItem::Plugin { plugin_id }, + SettingsParams::EntrypointPreferences { + plugin_id, + entrypoint_id, + } => { + SelectedItem::Entrypoint { + plugin_id, + entrypoint_id, + } + } + }; + + let open = match state.settings_window_id { + None => { + let settings = window::Settings { + size: Size::new(1150.0, 700.0), + ..Default::default() + }; + let (_, open) = window::open(settings); + open.map(SettingsMsg::WindowCreated) + } + Some(window_id) => window::gain_focus(window_id), + }; + + Task::batch([ + open, + Task::done(SettingsMsg::Plugin(SettingsPluginMsgIn::FetchPlugins)), + Task::done(SettingsMsg::Refresh), + Task::done(SettingsMsg::SwitchView(SettingsView::Plugins)), + Task::done(SettingsMsg::Plugin(SettingsPluginMsgIn::SelectItem(item))), + ]) + } } } -fn view(state: &ManagementAppModel) -> Element<'_, ManagementAppMsg> { - if let None = &state.backend_api { - let description: Element<_> = - text("Unable to connect to server. Please check if you have Gauntlet running on your PC").into(); - - let content: Element<_> = container(description) - .align_x(Alignment::Center) - .align_y(Alignment::Center) - .width(Length::Fill) - .height(Length::Fill) - .into(); - - return content; - } - +pub fn view_settings(state: &SettingsWindowState) -> Element<'_, SettingsMsg> { if let Some(err) = &state.error_view { return match err { ErrorView::Timeout => { @@ -421,8 +389,8 @@ fn view(state: &ManagementAppModel) -> Element<'_, ManagementAppMsg> { } let content = match state.current_settings_view { - SettingsView::General => state.general_state.view().map(|msg| ManagementAppMsg::General(msg)), - SettingsView::Plugins => state.plugins_state.view().map(|msg| ManagementAppMsg::Plugin(msg)), + SettingsView::General => state.general_state.view().map(|msg| SettingsMsg::General(msg)), + SettingsView::Plugins => state.plugins_state.view().map(|msg| SettingsMsg::Plugin(msg)), }; let icon_general: Element<_> = gear_fill() @@ -445,7 +413,7 @@ fn view(state: &ManagementAppModel) -> Element<'_, ManagementAppMsg> { .into(); let general_button: Element<_> = button(general_button) - .on_press(ManagementAppMsg::SwitchView(SettingsView::General)) + .on_press(SettingsMsg::SwitchView(SettingsView::General)) .height(Length::Fill) .width(80) .class( @@ -479,7 +447,7 @@ fn view(state: &ManagementAppModel) -> Element<'_, ManagementAppMsg> { .into(); let plugins_button: Element<_> = button(plugins_button) - .on_press(ManagementAppMsg::SwitchView(SettingsView::Plugins)) + .on_press(SettingsMsg::SwitchView(SettingsView::Plugins)) .height(Length::Fill) .width(80) .class( @@ -587,7 +555,7 @@ fn view(state: &ManagementAppModel) -> Element<'_, ManagementAppMsg> { let top_bar_right: Element<_> = button(top_bar_right) .class(ButtonStyle::DownloadInfo) - .on_press(ManagementAppMsg::ToggleDownloadInfo) + .on_press(SettingsMsg::ToggleDownloadInfo) .padding(Padding::from([4, 8])) .height(Length::Fill) .into(); @@ -735,12 +703,17 @@ fn view(state: &ManagementAppModel) -> Element<'_, ManagementAppMsg> { .into() }; + let content = container(content) + .width(Length::Fill) + .height(Length::Fill) + .class(ContainerStyle::WindowRoot); + let content: Element<_> = mouse_area(content) .on_press( if state.download_info_shown { - ManagementAppMsg::ToggleDownloadInfo + SettingsMsg::ToggleDownloadInfo } else { - ManagementAppMsg::Noop + SettingsMsg::Noop }, ) .into(); @@ -754,16 +727,16 @@ fn view(state: &ManagementAppModel) -> Element<'_, ManagementAppMsg> { stack(content).into() } -fn subscription(_state: &ManagementAppModel) -> Subscription { - time::every(Duration::from_millis(300)).map(|_| ManagementAppMsg::CheckDownloadStatus) -} - -pub fn handle_backend_error( - result: RequestResult, - convert: impl FnOnce(T) -> ManagementAppMsg, -) -> ManagementAppMsg { - match result { - Ok(val) => convert(val), - Err(err) => ManagementAppMsg::HandleBackendError(err), +pub fn subscription_settings(state: &SettingsWindowState) -> Subscription { + match state.settings_window_id { + None => Subscription::none(), + Some(_) => time::every(Duration::from_millis(300)).map(|_| SettingsMsg::CheckDownloadStatus), + } +} + +pub fn handle_backend_error(result: RequestResult, convert: impl FnOnce(T) -> SettingsMsg) -> SettingsMsg { + match result { + Ok(val) => convert(val), + Err(err) => SettingsMsg::HandleBackendError(err), } } diff --git a/rust/management_client/src/views/general.rs b/rust/client/src/ui/settings/views/general.rs similarity index 66% rename from rust/management_client/src/views/general.rs rename to rust/client/src/ui/settings/views/general.rs index 5679bd0..19179eb 100644 --- a/rust/management_client/src/views/general.rs +++ b/rust/client/src/ui/settings/views/general.rs @@ -1,8 +1,10 @@ +use std::sync::Arc; + use gauntlet_common::model::PhysicalShortcut; use gauntlet_common::model::SettingsTheme; use gauntlet_common::model::WindowPositionMode; -use gauntlet_common::rpc::backend_api::BackendForSettingsApi; -use gauntlet_common::rpc::backend_api::BackendForSettingsApiProxy; +use gauntlet_server::global_hotkey::GlobalHotKeyManager; +use gauntlet_server::plugins::ApplicationManager; use gauntlet_utils::channel::RequestResult; use iced::Alignment; use iced::Font; @@ -20,15 +22,15 @@ use iced::widget::row; use iced::widget::text; use iced::widget::text::Shaping; -use crate::components::shortcut_selector::ShortcutData; -use crate::components::shortcut_selector::render_shortcut_error; -use crate::components::shortcut_selector::shortcut_selector; -use crate::theme::Element; -use crate::theme::container::ContainerStyle; -use crate::ui::ManagementAppMsg; +use crate::ui::settings::components::shortcut_selector::ShortcutData; +use crate::ui::settings::components::shortcut_selector::render_shortcut_error; +use crate::ui::settings::components::shortcut_selector::shortcut_selector; +use crate::ui::settings::theme::Element; +use crate::ui::settings::theme::container::ContainerStyle; +use crate::ui::settings::ui::SettingsMsg; -pub struct ManagementAppGeneralState { - backend_api: Option, +pub struct SettingsGeneralState { + application_manager: Arc, theme: SettingsTheme, window_position_mode: WindowPositionMode, current_shortcut: ShortcutData, @@ -36,7 +38,7 @@ pub struct ManagementAppGeneralState { } #[derive(Debug, Clone)] -pub enum ManagementAppGeneralMsgIn { +pub enum SettingsGeneralMsgIn { ShortcutCaptured(Option), ThemeChanged(SettingsTheme), WindowPositionModeChanged(WindowPositionMode), @@ -51,19 +53,18 @@ pub enum ManagementAppGeneralMsgIn { shortcut_error: Option, global_shortcuts_unsupported: bool, }, - Noop, } #[derive(Debug, Clone)] -pub enum ManagementAppGeneralMsgOut { - Inner(ManagementAppGeneralMsgIn), - Outer(ManagementAppMsg), +pub enum SettingsGeneralMsgOut { + Inner(SettingsGeneralMsgIn), + Outer(SettingsMsg), } -impl ManagementAppGeneralState { - pub fn new(backend_api: Option) -> Self { +impl SettingsGeneralState { + pub fn new(application_manager: Arc) -> Self { Self { - backend_api, + application_manager, theme: SettingsTheme::AutoDetect, window_position_mode: WindowPositionMode::Static, current_shortcut: ShortcutData { @@ -74,40 +75,29 @@ impl ManagementAppGeneralState { } } - pub fn update(&mut self, message: ManagementAppGeneralMsgIn) -> Task { - let backend_api = match &self.backend_api { - Some(backend_api) => backend_api.clone(), - None => return Task::none(), - }; - + pub fn update( + &mut self, + global_hotkey_manager: &Option, + message: SettingsGeneralMsgIn, + ) -> Task { match message { - ManagementAppGeneralMsgIn::ShortcutCaptured(shortcut) => { - let backend_api = backend_api.clone(); + SettingsGeneralMsgIn::ShortcutCaptured(shortcut) => { + let Some(global_hotkey_manager) = &global_hotkey_manager else { + return Task::none(); + }; - Task::perform( - { - let shortcut = shortcut.clone(); + let error = self + .application_manager + .set_global_shortcut(global_hotkey_manager, shortcut.clone()); - async move { - let error = backend_api.set_global_shortcut(shortcut).await?; - - Ok(error) - } + Task::done(SettingsGeneralMsgOut::Inner( + SettingsGeneralMsgIn::HandleShortcutResponse { + shortcut, + shortcut_error: error, }, - move |result| { - let shortcut = shortcut.clone(); - - handle_backend_error(result, move |shortcut_error| { - ManagementAppGeneralMsgOut::Inner(ManagementAppGeneralMsgIn::HandleShortcutResponse { - shortcut, - shortcut_error, - }) - }) - }, - ) + )) } - ManagementAppGeneralMsgIn::Noop => Task::none(), - ManagementAppGeneralMsgIn::InitSetting { + SettingsGeneralMsgIn::InitSetting { theme, window_position_mode, shortcut, @@ -124,39 +114,35 @@ impl ManagementAppGeneralState { Task::none() } - ManagementAppGeneralMsgIn::ThemeChanged(theme) => { + SettingsGeneralMsgIn::ThemeChanged(theme) => { self.theme = theme.clone(); - let backend_api = backend_api.clone(); + let application_manager = self.application_manager.clone(); Task::perform( async move { - backend_api.set_theme(theme).await?; + application_manager.set_theme(theme).await?; Ok(()) }, - |result| { - handle_backend_error(result, |()| ManagementAppGeneralMsgOut::Outer(ManagementAppMsg::Noop)) - }, + |result| handle_backend_error(result, |()| SettingsGeneralMsgOut::Outer(SettingsMsg::Noop)), ) } - ManagementAppGeneralMsgIn::WindowPositionModeChanged(mode) => { + SettingsGeneralMsgIn::WindowPositionModeChanged(mode) => { self.window_position_mode = mode.clone(); - let backend_api = backend_api.clone(); + let application_manager = self.application_manager.clone(); Task::perform( async move { - backend_api.set_window_position_mode(mode).await?; + application_manager.set_window_position_mode(mode).await?; Ok(()) }, - |result| { - handle_backend_error(result, |()| ManagementAppGeneralMsgOut::Outer(ManagementAppMsg::Noop)) - }, + |result| handle_backend_error(result, |()| SettingsGeneralMsgOut::Outer(SettingsMsg::Noop)), ) } - ManagementAppGeneralMsgIn::HandleShortcutResponse { + SettingsGeneralMsgIn::HandleShortcutResponse { shortcut, shortcut_error, } => { @@ -170,7 +156,7 @@ impl ManagementAppGeneralState { } } - pub fn view(&self) -> Element { + pub fn view(&self) -> Element { let global_shortcut_selector: Element<_> = if self.global_shortcuts_unsupported { let text = text("Not supported").font(Font { style: Style::Italic, @@ -186,7 +172,7 @@ impl ManagementAppGeneralState { } else { shortcut_selector( &self.current_shortcut, - move |shortcut| ManagementAppGeneralMsgIn::ShortcutCaptured(shortcut), + move |shortcut| SettingsGeneralMsgIn::ShortcutCaptured(shortcut), ContainerStyle::Box, false, ) @@ -221,7 +207,7 @@ impl ManagementAppGeneralState { content } - fn theme_field(&self) -> Element { + fn theme_field(&self) -> Element { let theme_field = match &self.theme { SettingsTheme::ThemeFile => { let theme_field: Element<_> = text("Unable to change because theme config file is present ") @@ -250,7 +236,7 @@ impl ManagementAppGeneralState { ]; let theme_field: Element<_> = pick_list(theme_items, Some(self.theme.clone()), move |item| { - ManagementAppGeneralMsgIn::ThemeChanged(item) + SettingsGeneralMsgIn::ThemeChanged(item) }) .into(); @@ -266,11 +252,11 @@ impl ManagementAppGeneralState { } #[allow(unused)] - fn window_position_mode_field(&self) -> Element { + fn window_position_mode_field(&self) -> Element { let items = [WindowPositionMode::Static, WindowPositionMode::ActiveMonitor]; let field: Element<_> = pick_list(items, Some(self.window_position_mode.clone()), move |item| { - ManagementAppGeneralMsgIn::WindowPositionModeChanged(item) + SettingsGeneralMsgIn::WindowPositionModeChanged(item) }) .into(); @@ -284,9 +270,9 @@ impl ManagementAppGeneralState { fn view_field<'a>( &'a self, label: &'a str, - input: Element<'a, ManagementAppGeneralMsgIn>, - after: Option>, - ) -> Element<'a, ManagementAppGeneralMsgIn> { + input: Element<'a, SettingsGeneralMsgIn>, + after: Option>, + ) -> Element<'a, SettingsGeneralMsgIn> { let label: Element<_> = text(label) .shaping(Shaping::Advanced) .align_x(Horizontal::Right) @@ -306,7 +292,7 @@ impl ManagementAppGeneralState { row } - fn shortcut_capture_after(&self) -> Element { + fn shortcut_capture_after(&self) -> Element { if let Some(current_shortcut_error) = &self.current_shortcut.error { let content = render_shortcut_error(current_shortcut_error.clone()); @@ -325,10 +311,10 @@ impl ManagementAppGeneralState { fn handle_backend_error( result: RequestResult, - convert: impl FnOnce(T) -> ManagementAppGeneralMsgOut, -) -> ManagementAppGeneralMsgOut { + convert: impl FnOnce(T) -> SettingsGeneralMsgOut, +) -> SettingsGeneralMsgOut { match result { Ok(val) => convert(val), - Err(err) => ManagementAppGeneralMsgOut::Outer(ManagementAppMsg::HandleBackendError(err)), + Err(err) => SettingsGeneralMsgOut::Outer(SettingsMsg::HandleBackendError(err)), } } diff --git a/rust/management_client/src/views/mod.rs b/rust/client/src/ui/settings/views/mod.rs similarity index 100% rename from rust/management_client/src/views/mod.rs rename to rust/client/src/ui/settings/views/mod.rs diff --git a/rust/management_client/src/views/plugins.rs b/rust/client/src/ui/settings/views/plugins.rs similarity index 66% rename from rust/management_client/src/views/plugins.rs rename to rust/client/src/ui/settings/views/plugins.rs index 3132904..551f4d5 100644 --- a/rust/management_client/src/views/plugins.rs +++ b/rust/client/src/ui/settings/views/plugins.rs @@ -1,18 +1,16 @@ use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; +use std::sync::Arc; -use gauntlet_common::SETTINGS_ENV; -use gauntlet_common::SettingsEnvData; use gauntlet_common::model::EntrypointId; use gauntlet_common::model::PhysicalShortcut; use gauntlet_common::model::PluginId; use gauntlet_common::model::PluginPreferenceUserData; use gauntlet_common::model::SettingsEntrypointType; use gauntlet_common::model::SettingsPlugin; -use gauntlet_common::rpc::backend_api::BackendForSettingsApi; -use gauntlet_common::rpc::backend_api::BackendForSettingsApiProxy; -use gauntlet_common::settings_env_data_from_string; +use gauntlet_server::global_hotkey::GlobalHotKeyManager; +use gauntlet_server::plugins::ApplicationManager; use gauntlet_utils::channel::RequestResult; use iced::Alignment; use iced::Length; @@ -30,22 +28,22 @@ use iced::widget::text_input; use iced::widget::vertical_rule; use iced_fonts::bootstrap::plus; -use crate::theme::Element; -use crate::theme::button::ButtonStyle; -use crate::theme::text::TextStyle; -use crate::ui::ManagementAppMsg; -use crate::views::plugins::preferences::PluginPreferencesMsg; -use crate::views::plugins::preferences::SelectItem; -use crate::views::plugins::preferences::preferences_ui; -use crate::views::plugins::table::PluginTableMsgIn; -use crate::views::plugins::table::PluginTableMsgOut; -use crate::views::plugins::table::PluginTableState; +use crate::ui::settings::theme::Element; +use crate::ui::settings::theme::button::ButtonStyle; +use crate::ui::settings::theme::text::TextStyle; +use crate::ui::settings::ui::SettingsMsg; +use crate::ui::settings::views::plugins::preferences::PluginPreferencesMsg; +use crate::ui::settings::views::plugins::preferences::SelectItem; +use crate::ui::settings::views::plugins::preferences::preferences_ui; +use crate::ui::settings::views::plugins::table::PluginTableMsgIn; +use crate::ui::settings::views::plugins::table::PluginTableMsgOut; +use crate::ui::settings::views::plugins::table::PluginTableState; mod preferences; mod table; #[derive(Debug, Clone)] -pub enum ManagementAppPluginMsgIn { +pub enum SettingsPluginMsgIn { InitSetting { global_entrypoint_shortcuts: HashMap<(PluginId, EntrypointId), (PhysicalShortcut, Option)>, show_global_shortcuts: bool, @@ -74,13 +72,13 @@ pub enum ManagementAppPluginMsgIn { SelectItem(SelectedItem), } -pub enum ManagementAppPluginMsgOut { - Inner(ManagementAppPluginMsgIn), - Outer(ManagementAppMsg), +pub enum SettingsPluginMsgOut { + Inner(SettingsPluginMsgIn), + Outer(SettingsMsg), } -pub struct ManagementAppPluginsState { - backend_api: Option, +pub struct SettingsPluginsState { + application_manager: Arc, table_state: PluginTableState, plugin_data: Rc>, preference_user_data: HashMap<(PluginId, Option, String), PluginPreferenceUserDataState>, @@ -89,52 +87,27 @@ pub struct ManagementAppPluginsState { entrypoint_search_aliases: HashMap<(PluginId, EntrypointId), String>, } -impl ManagementAppPluginsState { - pub fn new(backend_api: Option) -> Self { - let settings_env_data = std::env::var(SETTINGS_ENV) - .ok() - .filter(|value| !value.is_empty()) - .map(|val| settings_env_data_from_string(val)); - - let select_item = match settings_env_data { - None => SelectedItem::None, - Some(SettingsEnvData::OpenEntrypointPreferences { - plugin_id, - entrypoint_id, - }) => { - SelectedItem::Entrypoint { - plugin_id: PluginId::from_string(plugin_id), - entrypoint_id: EntrypointId::from_string(entrypoint_id), - } - } - Some(SettingsEnvData::OpenPluginPreferences { plugin_id }) => { - SelectedItem::Plugin { - plugin_id: PluginId::from_string(plugin_id), - } - } - }; - - tracing::debug!("Opening selected item: {:?}", select_item); - +impl SettingsPluginsState { + pub fn new(application_manager: Arc) -> Self { Self { - backend_api: backend_api.clone(), + application_manager: application_manager.clone(), plugin_data: Rc::new(RefCell::new(PluginDataContainer::new())), preference_user_data: HashMap::new(), - selected_item: select_item, + selected_item: SelectedItem::None, table_state: PluginTableState::new(), global_entrypoint_shortcuts: HashMap::new(), entrypoint_search_aliases: HashMap::new(), } } - pub fn update(&mut self, message: ManagementAppPluginMsgIn) -> Task { - let backend_api = match &self.backend_api { - Some(backend_api) => backend_api.clone(), - None => return Task::none(), - }; - + pub fn update( + &mut self, + global_hotkey_manager: &Option, + message: SettingsPluginMsgIn, + ) -> Task { + let application_manager = self.application_manager.clone(); match message { - ManagementAppPluginMsgIn::InitSetting { + SettingsPluginMsgIn::InitSetting { global_entrypoint_shortcuts, show_global_shortcuts, } => { @@ -143,156 +116,162 @@ impl ManagementAppPluginsState { Task::none() } - ManagementAppPluginMsgIn::PluginTableMsg(message) => { - self.table_state.update(message).then(move |msg| { - match msg { - PluginTableMsgOut::SetPluginState { enabled, plugin_id } => { - let backend_client = backend_api.clone(); + SettingsPluginMsgIn::PluginTableMsg(message) => { + let application_manager = application_manager.clone(); + match self.table_state.update(message) { + PluginTableMsgOut::SetPluginState { enabled, plugin_id } => { + let application_manager = application_manager.clone(); - Task::perform( - async move { - backend_client.set_plugin_state(plugin_id, enabled).await?; + Task::perform( + async move { + application_manager.set_plugin_state(plugin_id, enabled)?; - let plugins = backend_client.plugins().await?; - let global_entrypoint_shortcuts = - backend_client.get_global_entrypoint_shortcuts().await?; - let entrypoint_aliases = backend_client.get_entrypoint_search_aliases().await?; + let plugins = application_manager.plugins()?; + let global_entrypoint_shortcuts = + application_manager.get_global_entrypoint_shortcuts()?; + let entrypoint_aliases = application_manager.get_entrypoint_search_aliases()?; - Ok((plugins, global_entrypoint_shortcuts, entrypoint_aliases)) - }, - |result| { - handle_backend_error( - result, - |(plugins, global_entrypoint_shortcuts, entrypoint_aliases)| { - ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::PluginsReloaded( - plugins, - global_entrypoint_shortcuts, - entrypoint_aliases, - )) - }, - ) - }, - ) - } - PluginTableMsgOut::SetEntrypointState { - enabled, + Ok((plugins, global_entrypoint_shortcuts, entrypoint_aliases)) + }, + |result| { + handle_backend_error( + result, + |(plugins, global_entrypoint_shortcuts, entrypoint_aliases)| { + SettingsPluginMsgOut::Inner(SettingsPluginMsgIn::PluginsReloaded( + plugins, + global_entrypoint_shortcuts, + entrypoint_aliases, + )) + }, + ) + }, + ) + } + PluginTableMsgOut::SetEntrypointState { + enabled, + plugin_id, + entrypoint_id, + } => { + let application_manager = application_manager.clone(); + + Task::perform( + async move { + application_manager.set_entrypoint_state(plugin_id, entrypoint_id, enabled)?; + + let plugins = application_manager.plugins()?; + let global_entrypoint_shortcuts = + application_manager.get_global_entrypoint_shortcuts()?; + let entrypoint_aliases = application_manager.get_entrypoint_search_aliases()?; + + Ok((plugins, global_entrypoint_shortcuts, entrypoint_aliases)) + }, + |result| { + handle_backend_error( + result, + |(plugins, global_entrypoint_shortcuts, entrypoint_aliases)| { + SettingsPluginMsgOut::Inner(SettingsPluginMsgIn::PluginsReloaded( + plugins, + global_entrypoint_shortcuts, + entrypoint_aliases, + )) + }, + ) + }, + ) + } + PluginTableMsgOut::SelectItem(selected_item) => { + Task::done(SettingsPluginMsgOut::Inner(SettingsPluginMsgIn::SelectItem( + selected_item, + ))) + } + PluginTableMsgOut::ToggleShowEntrypoints { plugin_id } => { + Task::done(SettingsPluginMsgOut::Inner(SettingsPluginMsgIn::ToggleShowEntrypoint { plugin_id, - entrypoint_id, - } => { - let backend_client = backend_api.clone(); + })) + } + PluginTableMsgOut::ToggleShowGeneratedEntrypoints { + plugin_id, + entrypoint_id, + } => { + Task::done(SettingsPluginMsgOut::Inner( + SettingsPluginMsgIn::ToggleShowGeneratedEntrypoint { + plugin_id, + entrypoint_id, + }, + )) + } + PluginTableMsgOut::ShortcutCaptured(plugin_id, entrypoint_id, shortcut) => { + let Some(global_hotkey_manager) = &global_hotkey_manager else { + return Task::none(); + }; - Task::perform( - async move { - backend_client - .set_entrypoint_state(plugin_id, entrypoint_id, enabled) - .await?; + fn run( + application_manager: &ApplicationManager, + global_hotkey_manager: &GlobalHotKeyManager, + plugin_id: PluginId, + entrypoint_id: EntrypointId, + shortcut: Option, + ) -> anyhow::Result { + application_manager.set_global_entrypoint_shortcut( + global_hotkey_manager, + plugin_id, + entrypoint_id, + shortcut, + )?; - let plugins = backend_client.plugins().await?; - let global_entrypoint_shortcuts = - backend_client.get_global_entrypoint_shortcuts().await?; - let entrypoint_aliases = backend_client.get_entrypoint_search_aliases().await?; + let plugins = application_manager.plugins()?; + let global_entrypoint_shortcuts = application_manager.get_global_entrypoint_shortcuts()?; + let entrypoint_aliases = application_manager.get_entrypoint_search_aliases()?; - Ok((plugins, global_entrypoint_shortcuts, entrypoint_aliases)) - }, - |result| { - handle_backend_error( - result, - |(plugins, global_entrypoint_shortcuts, entrypoint_aliases)| { - ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::PluginsReloaded( - plugins, - global_entrypoint_shortcuts, - entrypoint_aliases, - )) - }, - ) - }, - ) - } - PluginTableMsgOut::SelectItem(selected_item) => { - Task::done(ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::SelectItem( - selected_item, + Ok(SettingsPluginMsgOut::Inner(SettingsPluginMsgIn::PluginsReloaded( + plugins, + global_entrypoint_shortcuts, + entrypoint_aliases, ))) } - PluginTableMsgOut::ToggleShowEntrypoints { plugin_id } => { - Task::done(ManagementAppPluginMsgOut::Inner( - ManagementAppPluginMsgIn::ToggleShowEntrypoint { plugin_id }, - )) - } - PluginTableMsgOut::ToggleShowGeneratedEntrypoints { + + let msg_out = run( + &application_manager, + global_hotkey_manager, plugin_id, entrypoint_id, - } => { - Task::done(ManagementAppPluginMsgOut::Inner( - ManagementAppPluginMsgIn::ToggleShowGeneratedEntrypoint { - plugin_id, - entrypoint_id, - }, - )) - } - PluginTableMsgOut::ShortcutCaptured(plugin_id, entrypoint_id, shortcut) => { - let backend_client = backend_api.clone(); + shortcut, + ) + .unwrap_or_else(|err| SettingsPluginMsgOut::Outer(SettingsMsg::HandleBackendError(err.into()))); - Task::perform( - async move { - backend_client - .set_global_entrypoint_shortcut(plugin_id, entrypoint_id, shortcut) - .await?; - - let plugins = backend_client.plugins().await?; - let global_entrypoint_shortcuts = - backend_client.get_global_entrypoint_shortcuts().await?; - let entrypoint_aliases = backend_client.get_entrypoint_search_aliases().await?; - - Ok((plugins, global_entrypoint_shortcuts, entrypoint_aliases)) - }, - |result| { - handle_backend_error( - result, - |(plugins, global_entrypoint_shortcuts, entrypoint_aliases)| { - ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::PluginsReloaded( - plugins, - global_entrypoint_shortcuts, - entrypoint_aliases, - )) - }, - ) - }, - ) - } - PluginTableMsgOut::AliasChanged(plugin_id, entrypoint_id, shortcut) => { - let backend_client = backend_api.clone(); - - Task::perform( - async move { - backend_client - .set_entrypoint_search_alias(plugin_id, entrypoint_id, shortcut) - .await?; - - let plugins = backend_client.plugins().await?; - let global_entrypoint_shortcuts = - backend_client.get_global_entrypoint_shortcuts().await?; - let entrypoint_aliases = backend_client.get_entrypoint_search_aliases().await?; - - Ok((plugins, global_entrypoint_shortcuts, entrypoint_aliases)) - }, - |result| { - handle_backend_error( - result, - |(plugins, global_entrypoint_shortcuts, entrypoint_aliases)| { - ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::PluginsReloaded( - plugins, - global_entrypoint_shortcuts, - entrypoint_aliases, - )) - }, - ) - }, - ) - } + Task::done(msg_out) } - }) + PluginTableMsgOut::AliasChanged(plugin_id, entrypoint_id, shortcut) => { + let application_manager = application_manager.clone(); + + Task::perform( + async move { + application_manager.set_entrypoint_search_alias(plugin_id, entrypoint_id, shortcut)?; + + let plugins = application_manager.plugins()?; + let global_entrypoint_shortcuts = + application_manager.get_global_entrypoint_shortcuts()?; + let entrypoint_aliases = application_manager.get_entrypoint_search_aliases()?; + + Ok((plugins, global_entrypoint_shortcuts, entrypoint_aliases)) + }, + |result| { + handle_backend_error( + result, + |(plugins, global_entrypoint_shortcuts, entrypoint_aliases)| { + SettingsPluginMsgOut::Inner(SettingsPluginMsgIn::PluginsReloaded( + plugins, + global_entrypoint_shortcuts, + entrypoint_aliases, + )) + }, + ) + }, + ) + } + } } - ManagementAppPluginMsgIn::ToggleShowEntrypoint { plugin_id } => { + SettingsPluginMsgIn::ToggleShowEntrypoint { plugin_id } => { let plugins = { let mut plugin_data = self.plugin_data.borrow_mut(); let settings_plugin_data = plugin_data.plugins_state.get_mut(&plugin_id).unwrap(); @@ -309,7 +288,7 @@ impl ManagementAppPluginsState { Task::none() } - ManagementAppPluginMsgIn::ToggleShowGeneratedEntrypoint { + SettingsPluginMsgIn::ToggleShowGeneratedEntrypoint { plugin_id, entrypoint_id, } => { @@ -334,7 +313,7 @@ impl ManagementAppPluginsState { Task::none() } - ManagementAppPluginMsgIn::PluginPreferenceMsg(msg) => { + SettingsPluginMsgIn::PluginPreferenceMsg(msg) => { match msg { PluginPreferencesMsg::UpdatePreferenceValue { plugin_id, @@ -347,39 +326,38 @@ impl ManagementAppPluginsState { user_data.clone(), ); - let backend_api = backend_api.clone(); + let application_manager = application_manager.clone(); Task::perform( async move { - backend_api - .set_preference_value(plugin_id, entrypoint_id, id, user_data.to_user_data()) - .await?; + application_manager.set_preference_value( + plugin_id, + entrypoint_id, + id, + user_data.to_user_data(), + )?; Ok(()) }, - |result| { - handle_backend_error(result, |()| { - ManagementAppPluginMsgOut::Outer(ManagementAppMsg::Noop) - }) - }, + |result| handle_backend_error(result, |()| SettingsPluginMsgOut::Outer(SettingsMsg::Noop)), ) } } } - ManagementAppPluginMsgIn::FetchPlugins => { - let backend_api = backend_api.clone(); + SettingsPluginMsgIn::FetchPlugins => { + let application_manager = self.application_manager.clone(); Task::perform( async move { - let plugins = backend_api.plugins().await?; - let global_entrypoint_shortcuts = backend_api.get_global_entrypoint_shortcuts().await?; - let entrypoint_aliases = backend_api.get_entrypoint_search_aliases().await?; + let plugins = application_manager.plugins()?; + let global_entrypoint_shortcuts = application_manager.get_global_entrypoint_shortcuts()?; + let entrypoint_aliases = application_manager.get_entrypoint_search_aliases()?; Ok((plugins, global_entrypoint_shortcuts, entrypoint_aliases)) }, |result| { handle_backend_error(result, |(plugins, global_entrypoint_shortcuts, entrypoint_aliases)| { - ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::PluginsReloaded( + SettingsPluginMsgOut::Inner(SettingsPluginMsgIn::PluginsReloaded( plugins, global_entrypoint_shortcuts, entrypoint_aliases, @@ -388,29 +366,29 @@ impl ManagementAppPluginsState { }, ) } - ManagementAppPluginMsgIn::PluginsReloaded(plugins, shortcuts, entrypoint_aliases) => { + SettingsPluginMsgIn::PluginsReloaded(plugins, shortcuts, entrypoint_aliases) => { self.apply_plugin_fetch(plugins, shortcuts, entrypoint_aliases); Task::none() } - ManagementAppPluginMsgIn::RemovePlugin { plugin_id } => { + SettingsPluginMsgIn::RemovePlugin { plugin_id } => { self.selected_item = SelectedItem::None; - let backend_client = backend_api.clone(); + let application_manager = application_manager.clone(); Task::perform( async move { - backend_client.remove_plugin(plugin_id).await?; + application_manager.remove_plugin(plugin_id)?; - let plugins = backend_client.plugins().await?; - let global_entrypoint_shortcuts = backend_client.get_global_entrypoint_shortcuts().await?; - let entrypoint_aliases = backend_client.get_entrypoint_search_aliases().await?; + let plugins = application_manager.plugins()?; + let global_entrypoint_shortcuts = application_manager.get_global_entrypoint_shortcuts()?; + let entrypoint_aliases = application_manager.get_entrypoint_search_aliases()?; Ok((plugins, global_entrypoint_shortcuts, entrypoint_aliases)) }, |result| { handle_backend_error(result, |(plugins, global_entrypoint_shortcuts, entrypoint_aliases)| { - ManagementAppPluginMsgOut::Inner(ManagementAppPluginMsgIn::PluginsReloaded( + SettingsPluginMsgOut::Inner(SettingsPluginMsgIn::PluginsReloaded( plugins, global_entrypoint_shortcuts, entrypoint_aliases, @@ -419,12 +397,10 @@ impl ManagementAppPluginsState { }, ) } - ManagementAppPluginMsgIn::DownloadPlugin { plugin_id } => { - Task::done(ManagementAppPluginMsgOut::Outer(ManagementAppMsg::DownloadPlugin { - plugin_id, - })) + SettingsPluginMsgIn::DownloadPlugin { plugin_id } => { + Task::done(SettingsPluginMsgOut::Outer(SettingsMsg::DownloadPlugin { plugin_id })) } - ManagementAppPluginMsgIn::SelectItem(selected_item) => { + SettingsPluginMsgIn::SelectItem(selected_item) => { self.selected_item = selected_item; Task::none() @@ -522,11 +498,11 @@ impl ManagementAppPluginsState { ) } - pub fn view(&self) -> Element { + pub fn view(&self) -> Element { let table: Element<_> = self .table_state .view() - .map(|msg| ManagementAppPluginMsgIn::PluginTableMsg(msg)); + .map(|msg| SettingsPluginMsgIn::PluginTableMsg(msg)); let table: Element<_> = container(table).padding(Padding::new(8.0)).into(); @@ -593,7 +569,7 @@ impl ManagementAppPluginsState { column_content.push( preferences_ui(plugin_id.clone(), None, &plugin.preferences, &self.preference_user_data) - .map(|msg| ManagementAppPluginMsgIn::PluginPreferenceMsg(msg)), + .map(|msg| SettingsPluginMsgIn::PluginPreferenceMsg(msg)), ); let content: Element<_> = column(column_content).spacing(12).into(); @@ -614,7 +590,7 @@ impl ManagementAppPluginsState { let check_for_updates_button: Element<_> = button(check_for_updates_text_container) .width(Length::Fill) .class(ButtonStyle::Primary) - .on_press(ManagementAppPluginMsgIn::DownloadPlugin { + .on_press(SettingsPluginMsgIn::DownloadPlugin { plugin_id: plugin.plugin_id.clone(), }) .into(); @@ -632,7 +608,7 @@ impl ManagementAppPluginsState { let remove_button: Element<_> = button(remove_button_text_container) .width(Length::Fill) .class(ButtonStyle::Destructive) - .on_press(ManagementAppPluginMsgIn::RemovePlugin { + .on_press(SettingsPluginMsgIn::RemovePlugin { plugin_id: plugin.plugin_id.clone(), }) .into(); @@ -699,7 +675,7 @@ impl ManagementAppPluginsState { &entrypoint.preferences, &self.preference_user_data, ) - .map(|msg| ManagementAppPluginMsgIn::PluginPreferenceMsg(msg)), + .map(|msg| SettingsPluginMsgIn::PluginPreferenceMsg(msg)), ); let column: Element<_> = column(column_content).spacing(12).into(); @@ -717,9 +693,9 @@ impl ManagementAppPluginsState { SelectedItem::NewPlugin { repository_url } => { let url_input: Element<_> = text_input("Enter Git Repository URL", &repository_url) .on_input(|value| { - ManagementAppPluginMsgIn::SelectItem(SelectedItem::NewPlugin { repository_url: value }) + SettingsPluginMsgIn::SelectItem(SelectedItem::NewPlugin { repository_url: value }) }) - .on_submit(ManagementAppPluginMsgIn::DownloadPlugin { + .on_submit(SettingsPluginMsgIn::DownloadPlugin { plugin_id: PluginId::from_string(repository_url), }) .into(); @@ -805,12 +781,12 @@ impl ManagementAppPluginsState { let top_button_action = match plugin_url { Some(plugin_url) => { - ManagementAppPluginMsgIn::DownloadPlugin { + SettingsPluginMsgIn::DownloadPlugin { plugin_id: PluginId::from_string(plugin_url), } } None => { - ManagementAppPluginMsgIn::SelectItem(SelectedItem::NewPlugin { + SettingsPluginMsgIn::SelectItem(SelectedItem::NewPlugin { repository_url: Default::default(), }) } @@ -960,10 +936,10 @@ impl PluginPreferenceUserDataState { pub fn handle_backend_error( result: RequestResult, - convert: impl FnOnce(T) -> ManagementAppPluginMsgOut, -) -> ManagementAppPluginMsgOut { + convert: impl FnOnce(T) -> SettingsPluginMsgOut, +) -> SettingsPluginMsgOut { match result { Ok(val) => convert(val), - Err(err) => ManagementAppPluginMsgOut::Outer(ManagementAppMsg::HandleBackendError(err)), + Err(err) => SettingsPluginMsgOut::Outer(SettingsMsg::HandleBackendError(err)), } } diff --git a/rust/management_client/src/views/plugins/preferences.rs b/rust/client/src/ui/settings/views/plugins/preferences.rs similarity index 98% rename from rust/management_client/src/views/plugins/preferences.rs rename to rust/client/src/ui/settings/views/plugins/preferences.rs index 103519d..d1c8dd6 100644 --- a/rust/management_client/src/views/plugins/preferences.rs +++ b/rust/client/src/ui/settings/views/plugins/preferences.rs @@ -19,11 +19,11 @@ use iced::widget::text_input; use iced_fonts::bootstrap::dash; use iced_fonts::bootstrap::plus; -use crate::theme::Element; -use crate::theme::button::ButtonStyle; -use crate::theme::container::ContainerStyle; -use crate::theme::text::TextStyle; -use crate::views::plugins::PluginPreferenceUserDataState; +use crate::ui::settings::theme::Element; +use crate::ui::settings::theme::button::ButtonStyle; +use crate::ui::settings::theme::container::ContainerStyle; +use crate::ui::settings::theme::text::TextStyle; +use crate::ui::settings::views::plugins::PluginPreferenceUserDataState; #[derive(Debug, Clone)] pub enum PluginPreferencesMsg { diff --git a/rust/management_client/src/views/plugins/table.rs b/rust/client/src/ui/settings/views/plugins/table.rs similarity index 95% rename from rust/management_client/src/views/plugins/table.rs rename to rust/client/src/ui/settings/views/plugins/table.rs index b9ca674..803d3b2 100644 --- a/rust/management_client/src/views/plugins/table.rs +++ b/rust/client/src/ui/settings/views/plugins/table.rs @@ -9,7 +9,6 @@ use gauntlet_common::model::SettingsEntrypointType; use gauntlet_common::model::SettingsPlugin; use iced::Alignment; use iced::Length; -use iced::Task; use iced::advanced::text::Shaping; use iced::padding; use iced::widget::Space; @@ -25,15 +24,15 @@ use iced::widget::text_input; use iced_fonts::bootstrap::caret_down; use iced_fonts::bootstrap::caret_right; -use crate::components::shortcut_selector::ShortcutData; -use crate::components::shortcut_selector::shortcut_selector; -use crate::theme::Element; -use crate::theme::button::ButtonStyle; -use crate::theme::container::ContainerStyle; -use crate::theme::text_input::TextInputStyle; -use crate::views::plugins::PluginDataContainer; -use crate::views::plugins::SelectedItem; -use crate::views::plugins::SettingsPluginData; +use crate::ui::settings::components::shortcut_selector::ShortcutData; +use crate::ui::settings::components::shortcut_selector::shortcut_selector; +use crate::ui::settings::theme::Element; +use crate::ui::settings::theme::button::ButtonStyle; +use crate::ui::settings::theme::container::ContainerStyle; +use crate::ui::settings::theme::text_input::TextInputStyle; +use crate::ui::settings::views::plugins::PluginDataContainer; +use crate::ui::settings::views::plugins::SelectedItem; +use crate::ui::settings::views::plugins::SettingsPluginData; #[derive(Debug, Clone)] pub enum PluginTableMsgIn { @@ -85,47 +84,47 @@ impl PluginTableState { } } - pub fn update(&mut self, message: PluginTableMsgIn) -> Task { + pub fn update(&mut self, message: PluginTableMsgIn) -> PluginTableMsgOut { match message { PluginTableMsgIn::EnabledToggleItem(item) => { match item { EnabledItem::Plugin { enabled, plugin_id } => { - Task::done(PluginTableMsgOut::SetPluginState { enabled, plugin_id }) + PluginTableMsgOut::SetPluginState { enabled, plugin_id } } EnabledItem::Entrypoint { enabled, plugin_id, entrypoint_id, } => { - Task::done(PluginTableMsgOut::SetEntrypointState { + PluginTableMsgOut::SetEntrypointState { enabled, plugin_id, entrypoint_id, - }) + } } } } - PluginTableMsgIn::SelectItem(item) => Task::done(PluginTableMsgOut::SelectItem(item)), + PluginTableMsgIn::SelectItem(item) => PluginTableMsgOut::SelectItem(item), PluginTableMsgIn::ToggleShowEntrypoints { plugin_id } => { - Task::done(PluginTableMsgOut::ToggleShowEntrypoints { plugin_id }) + PluginTableMsgOut::ToggleShowEntrypoints { plugin_id } } PluginTableMsgIn::ToggleShowGeneratedEntrypoints { plugin_id, entrypoint_id, } => { - Task::done(PluginTableMsgOut::ToggleShowGeneratedEntrypoints { + PluginTableMsgOut::ToggleShowGeneratedEntrypoints { plugin_id, entrypoint_id, - }) + } } PluginTableMsgIn::ShortcutCaptured(plugin_id, entrypoint_id, shortcut) => { - Task::done(PluginTableMsgOut::ShortcutCaptured(plugin_id, entrypoint_id, shortcut)) + PluginTableMsgOut::ShortcutCaptured(plugin_id, entrypoint_id, shortcut) } PluginTableMsgIn::AliasChanged(plugin_id, entrypoint_id, alias) => { let alias = alias.trim().to_owned(); let alias = if alias.is_empty() { None } else { Some(alias) }; - Task::done(PluginTableMsgOut::AliasChanged(plugin_id, entrypoint_id, alias)) + PluginTableMsgOut::AliasChanged(plugin_id, entrypoint_id, alias) } } } diff --git a/rust/client/src/ui/windows/mod.rs b/rust/client/src/ui/windows/mod.rs index 6821ec5..bcae969 100644 --- a/rust/client/src/ui/windows/mod.rs +++ b/rust/client/src/ui/windows/mod.rs @@ -16,7 +16,7 @@ pub mod hud; #[cfg(target_os = "linux")] pub mod x11_focus; -pub struct WindowState { +pub struct MainWindowState { pub main_window_id: Option, focused: bool, #[cfg(target_os = "linux")] @@ -31,14 +31,14 @@ pub struct WindowState { open_position: Position, } -impl WindowState { +impl MainWindowState { pub fn new( window_position_file: Option, close_on_unfocus: bool, window_position_mode: WindowPositionMode, #[cfg(target_os = "linux")] wayland: bool, #[cfg(target_os = "linux")] layer_shell: bool, - ) -> WindowState { + ) -> MainWindowState { let open_position = window_position_file .as_ref() .map(|window_position_file| fs::read_to_string(window_position_file).ok()) @@ -73,7 +73,7 @@ impl WindowState { } } -impl WindowState { +impl MainWindowState { pub fn handle_action(&mut self, action: WindowActionMsg) -> Task { match action { WindowActionMsg::SetWindowPositionMode { mode } => { diff --git a/rust/common/src/lib.rs b/rust/common/src/lib.rs index 7bdf57d..12fd388 100644 --- a/rust/common/src/lib.rs +++ b/rust/common/src/lib.rs @@ -1,25 +1,5 @@ -use serde::Deserialize; -use serde::Serialize; - pub mod cli; pub mod detached_process; pub mod dirs; pub mod model; pub mod rpc; - -pub const SETTINGS_ENV: &'static str = "__GAUNTLET_INTERNAL_SETTINGS__"; - -#[derive(Debug, Deserialize, Serialize)] -#[serde(tag = "type")] -pub enum SettingsEnvData { - OpenPluginPreferences { plugin_id: String }, - OpenEntrypointPreferences { plugin_id: String, entrypoint_id: String }, -} - -pub fn settings_env_data_to_string(data: SettingsEnvData) -> String { - serde_json::to_string(&data).expect("unable to serialize settings env data") -} - -pub fn settings_env_data_from_string(data: String) -> SettingsEnvData { - serde_json::from_str(&data).expect("unable to serialize settings env data") -} diff --git a/rust/common/src/rpc/backend_api.rs b/rust/common/src/rpc/backend_api.rs index 788fe2e..1b9e67d 100644 --- a/rust/common/src/rpc/backend_api.rs +++ b/rust/common/src/rpc/backend_api.rs @@ -1,4 +1,3 @@ -use std::collections::HashMap; use std::sync::Arc; use gauntlet_utils::channel::RequestResult; @@ -7,15 +6,9 @@ use tokio::sync::Mutex; use tonic::Request; use tonic::transport::Channel; -use crate::model::DownloadStatus; use crate::model::EntrypointId; use crate::model::LocalSaveData; -use crate::model::PhysicalShortcut; use crate::model::PluginId; -use crate::model::PluginPreferenceUserData; -use crate::model::SettingsPlugin; -use crate::model::SettingsTheme; -use crate::model::WindowPositionMode; use crate::rpc::grpc::RpcBincode; use crate::rpc::grpc::RpcSaveLocalPluginRequest; use crate::rpc::grpc::rpc_backend_client::RpcBackendClient; @@ -42,69 +35,6 @@ pub trait BackendForToolsApi { async fn save_local_plugin(&self, path: String) -> RequestResult; } -#[boundary_gen(bincode, grpc)] -#[tonic::async_trait] -pub trait BackendForSettingsApi { - async fn wayland_global_shortcuts_enabled(&self) -> RequestResult; - - async fn plugins(&self) -> RequestResult>; - - async fn set_plugin_state(&self, plugin_id: PluginId, enabled: bool) -> RequestResult<()>; - - async fn set_entrypoint_state( - &self, - plugin_id: PluginId, - entrypoint_id: EntrypointId, - enabled: bool, - ) -> RequestResult<()>; - - async fn set_global_shortcut(&self, shortcut: Option) -> RequestResult>; - - async fn get_global_shortcut(&self) -> RequestResult<(Option, Option)>; - - async fn set_global_entrypoint_shortcut( - &self, - plugin_id: PluginId, - entrypoint_id: EntrypointId, - shortcut: Option, - ) -> RequestResult<()>; - - async fn get_global_entrypoint_shortcuts( - &self, - ) -> RequestResult)>>; - - async fn set_entrypoint_search_alias( - &self, - plugin_id: PluginId, - entrypoint_id: EntrypointId, - alias: Option, - ) -> RequestResult<()>; - - async fn get_entrypoint_search_aliases(&self) -> RequestResult>; - - async fn set_theme(&self, theme: SettingsTheme) -> RequestResult<()>; - - async fn get_theme(&self) -> RequestResult; - - async fn set_window_position_mode(&self, mode: WindowPositionMode) -> RequestResult<()>; - - async fn get_window_position_mode(&self) -> RequestResult; - - async fn set_preference_value( - &self, - plugin_id: PluginId, - entrypoint_id: Option, - preference_id: String, - preference_value: PluginPreferenceUserData, - ) -> RequestResult<()>; - - async fn download_plugin(&self, plugin_id: PluginId) -> RequestResult<()>; - - async fn download_status(&self) -> RequestResult>; - - async fn remove_plugin(&self, plugin_id: PluginId) -> RequestResult<()>; -} - #[derive(Debug, Clone)] pub struct GrpcBackendApi { client: Arc>>, @@ -117,20 +47,6 @@ impl GrpcBackendApi { }) } - pub async fn backend_for_settings_api(&self, bytes: Vec) -> RequestResult> { - let request = RpcBincode { data: bytes }; - - let mut client = self.client.lock().await; - - let response = client - .backend_for_settings_api(Request::new(request)) - .await? - .into_inner() - .data; - - Ok(response) - } - pub async fn backend_for_cli_api(&self, bytes: Vec) -> RequestResult> { let request = RpcBincode { data: bytes }; diff --git a/rust/common/src/rpc/backend_server.rs b/rust/common/src/rpc/backend_server.rs index 36e702c..4f8ea1c 100644 --- a/rust/common/src/rpc/backend_server.rs +++ b/rust/common/src/rpc/backend_server.rs @@ -8,10 +8,8 @@ use tonic::Status; use tonic::transport::Server; use crate::rpc::backend_api::BackendForCliApi; -use crate::rpc::backend_api::BackendForSettingsApi; use crate::rpc::backend_api::BackendForToolsApi; use crate::rpc::backend_api::handle_grpc_request_backend_for_cli_api; -use crate::rpc::backend_api::handle_grpc_request_backend_for_settings_api; use crate::rpc::grpc::RpcBincode; use crate::rpc::grpc::RpcSaveLocalPluginRequest; use crate::rpc::grpc::RpcSaveLocalPluginResponse; @@ -33,12 +31,11 @@ pub async fn wait_for_backend_server() { pub async fn start_backend_server( cli: Box, tools: Box, - settings: Box, ) { let addr = "127.0.0.1:42320".parse().unwrap(); Server::builder() - .add_service(RpcBackendServer::new(RpcBackendServerImpl::new(cli, tools, settings))) + .add_service(RpcBackendServer::new(RpcBackendServerImpl::new(cli, tools))) .serve(addr) .await .expect("unable to start backend server"); @@ -47,16 +44,11 @@ pub async fn start_backend_server( struct RpcBackendServerImpl { cli: Box, tools: Box, - settings: Box, } impl RpcBackendServerImpl { - pub fn new( - cli: Box, - tools: Box, - settings: Box, - ) -> Self { - Self { cli, settings, tools } + pub fn new(cli: Box, tools: Box) -> Self { + Self { cli, tools } } } @@ -70,14 +62,6 @@ impl RpcBackend for RpcBackendServerImpl { Ok(Response::new(RpcBincode { data: encoded })) } - async fn backend_for_settings_api(&self, request: Request) -> Result, Status> { - let data = request.into_inner().data; - - let encoded = handle_grpc_request_backend_for_settings_api(self.settings.as_ref(), data).await?; - - Ok(Response::new(RpcBincode { data: encoded })) - } - async fn save_local_plugin( &self, request: Request, diff --git a/rust/common/src/rpc/frontend_api.rs b/rust/common/src/rpc/frontend_api.rs index 908b242..2e93435 100644 --- a/rust/common/src/rpc/frontend_api.rs +++ b/rust/common/src/rpc/frontend_api.rs @@ -32,6 +32,8 @@ pub trait FrontendApi { async fn hide_window(&self) -> RequestResult<()>; + async fn show_settings(&self) -> RequestResult<()>; + async fn show_preference_required_view( &self, plugin_id: PluginId, diff --git a/rust/common/src/rpc/server_grpc_api.rs b/rust/common/src/rpc/server_grpc_api.rs index df1941b..6271848 100644 --- a/rust/common/src/rpc/server_grpc_api.rs +++ b/rust/common/src/rpc/server_grpc_api.rs @@ -1,17 +1,9 @@ -use std::collections::HashMap; - use gauntlet_utils::channel::RequestResult; use gauntlet_utils_macros::boundary_gen; -use crate::model::DownloadStatus; use crate::model::EntrypointId; use crate::model::LocalSaveData; -use crate::model::PhysicalShortcut; use crate::model::PluginId; -use crate::model::PluginPreferenceUserData; -use crate::model::SettingsPlugin; -use crate::model::SettingsTheme; -use crate::model::WindowPositionMode; #[allow(async_fn_in_trait)] #[boundary_gen(in_process)] @@ -28,63 +20,4 @@ pub trait ServerGrpcApi { ) -> RequestResult<()>; async fn save_local_plugin(&self, path: String) -> RequestResult; - - async fn plugins(&self) -> RequestResult>; - - async fn set_plugin_state(&self, plugin_id: PluginId, enabled: bool) -> RequestResult<()>; - - async fn set_entrypoint_state( - &self, - plugin_id: PluginId, - entrypoint_id: EntrypointId, - enabled: bool, - ) -> RequestResult<()>; - - async fn set_global_shortcut(&self, shortcut: Option) -> RequestResult>; - - async fn get_global_shortcut(&self) -> RequestResult)>>; - - async fn set_global_entrypoint_shortcut( - &self, - plugin_id: PluginId, - entrypoint_id: EntrypointId, - shortcut: Option, - ) -> RequestResult<()>; - - async fn get_global_entrypoint_shortcuts( - &self, - ) -> RequestResult)>>; - - async fn set_entrypoint_search_alias( - &self, - plugin_id: PluginId, - entrypoint_id: EntrypointId, - alias: Option, - ) -> RequestResult<()>; - - async fn get_entrypoint_search_aliases(&self) -> RequestResult>; - - async fn set_theme(&self, theme: SettingsTheme) -> RequestResult<()>; - - async fn get_theme(&self) -> RequestResult; - - async fn set_window_position_mode(&self, mode: WindowPositionMode) -> RequestResult<()>; - - async fn get_window_position_mode(&self) -> RequestResult; - - async fn set_preference_value( - &self, - plugin_id: PluginId, - entrypoint_id: Option, - preference_id: String, - preference_value: PluginPreferenceUserData, - ) -> RequestResult<()>; - - async fn download_plugin(&self, plugin_id: PluginId) -> RequestResult<()>; - - async fn download_status(&self) -> RequestResult>; - - async fn remove_plugin(&self, plugin_id: PluginId) -> RequestResult<()>; - - async fn wayland_global_shortcuts_enabled(&self) -> RequestResult; } diff --git a/rust/common_plugin_runtime/src/api.rs b/rust/common_plugin_runtime/src/api.rs index 33376c0..42268f9 100644 --- a/rust/common_plugin_runtime/src/api.rs +++ b/rust/common_plugin_runtime/src/api.rs @@ -35,6 +35,7 @@ pub trait BackendForPluginRuntimeApi { async fn ui_update_loading_bar(&self, entrypoint_id: EntrypointId, show: bool) -> RequestResult<()>; async fn ui_show_hud(&self, display: String) -> RequestResult<()>; async fn ui_hide_window(&self) -> RequestResult<()>; + async fn ui_show_settings(&self) -> RequestResult<()>; async fn ui_get_action_id_for_shortcut( &self, entrypoint_id: EntrypointId, diff --git a/rust/management_client/Cargo.toml b/rust/management_client/Cargo.toml deleted file mode 100644 index 15dfb9c..0000000 --- a/rust/management_client/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "gauntlet-management-client" -edition.workspace = true - -[dependencies] -# workspaces -gauntlet-common.workspace = true -gauntlet-common-ui.workspace = true -gauntlet-utils.workspace = true - -# shared -anyhow.workspace = true -iced.workspace = true -iced_fonts.workspace = true -tracing.workspace = true -tracing-subscriber.workspace = true -itertools.workspace = true diff --git a/rust/management_client/src/lib.rs b/rust/management_client/src/lib.rs deleted file mode 100644 index 7746327..0000000 --- a/rust/management_client/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -mod components; -mod theme; -mod ui; -mod views; - -pub fn start_management_client() { - ui::run(); -} diff --git a/rust/management_client/src/main.rs b/rust/management_client/src/main.rs deleted file mode 100644 index ec3b659..0000000 --- a/rust/management_client/src/main.rs +++ /dev/null @@ -1,5 +0,0 @@ -fn main() { - tracing_subscriber::fmt::init(); - - gauntlet_management_client::start_management_client(); -} diff --git a/rust/plugin_runtime/src/plugins/settings.rs b/rust/plugin_runtime/src/plugins/settings.rs index 1a89536..ad86cdf 100644 --- a/rust/plugin_runtime/src/plugins/settings.rs +++ b/rust/plugin_runtime/src/plugins/settings.rs @@ -1,17 +1,22 @@ -use anyhow::anyhow; +use std::cell::RefCell; +use std::rc::Rc; + +use deno_core::OpState; use deno_core::op2; -use gauntlet_common::detached_process::CommandExt; +use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApi; +use gauntlet_common_plugin_runtime::api::BackendForPluginRuntimeApiProxy; use crate::deno::GauntletJsError; -#[op2(fast)] -pub fn open_settings() -> Result<(), GauntletJsError> { - let current_exe = std::env::current_exe().map_err(|err| anyhow!(err))?; +#[op2(async)] +pub async fn open_settings(state: Rc>) -> Result<(), GauntletJsError> { + let api = { + let state = state.borrow(); - std::process::Command::new(current_exe) - .args(["settings"]) - .spawn_detached() - .map_err(|err| anyhow!(err))?; + let api = state.borrow::().clone(); - Ok(()) + api + }; + + api.ui_show_settings().await.map_err(Into::into) } diff --git a/rust/server/src/plugins/data_db_repository.rs b/rust/server/src/plugins/data_db_repository.rs index fa4f712..f571ecf 100644 --- a/rust/server/src/plugins/data_db_repository.rs +++ b/rust/server/src/plugins/data_db_repository.rs @@ -412,7 +412,6 @@ impl DataDbRepository { } pub fn list_plugins_and_entrypoints(&self) -> anyhow::Result)>> { - // language=SQLite let plugins = self.list_plugins()?; let result = plugins diff --git a/rust/server/src/plugins/js.rs b/rust/server/src/plugins/js.rs index 0fef45a..1eab75a 100644 --- a/rust/server/src/plugins/js.rs +++ b/rust/server/src/plugins/js.rs @@ -961,6 +961,12 @@ impl BackendForPluginRuntimeApi for BackendForPluginRuntimeApiImpl { Ok(()) } + async fn ui_show_settings(&self) -> RequestResult<()> { + self.frontend_api.show_settings().await?; + + Ok(()) + } + async fn ui_get_action_id_for_shortcut( &self, entrypoint_id: EntrypointId, diff --git a/rust/server/src/plugins/loader.rs b/rust/server/src/plugins/loader.rs index c5366da..845cac7 100644 --- a/rust/server/src/plugins/loader.rs +++ b/rust/server/src/plugins/loader.rs @@ -56,7 +56,7 @@ impl PluginLoader { self.download_status_holder.download_status() } - pub fn download_plugin(&self, plugin_id: PluginId) -> anyhow::Result<()> { + pub fn download_plugin(&self, plugin_id: PluginId) { let download_status_guard = self.download_status_holder.download_started(plugin_id.clone()); let data_db_repository = self.db_repository.clone(); @@ -104,8 +104,6 @@ impl PluginLoader { }) }) .expect("failed to spawn thread"); - - Ok(()) } pub fn save_local_plugin(&self, path: &str) -> anyhow::Result { diff --git a/rust/server/src/plugins/mod.rs b/rust/server/src/plugins/mod.rs index 97e73ce..d7e2d0a 100644 --- a/rust/server/src/plugins/mod.rs +++ b/rust/server/src/plugins/mod.rs @@ -1,9 +1,6 @@ use std::collections::HashMap; use anyhow::anyhow; -use gauntlet_common::SETTINGS_ENV; -use gauntlet_common::SettingsEnvData; -use gauntlet_common::detached_process::CommandExt; use gauntlet_common::dirs::Dirs; use gauntlet_common::model::DownloadStatus; use gauntlet_common::model::EntrypointId; @@ -30,7 +27,6 @@ use gauntlet_common::rpc::frontend_api::FrontendApi; use gauntlet_common::rpc::frontend_api::FrontendApiProxy; use gauntlet_common::rpc::frontend_api::FrontendApiRequestData; use gauntlet_common::rpc::frontend_api::FrontendApiResponseData; -use gauntlet_common::settings_env_data_to_string; use gauntlet_common_plugin_runtime::model::JsPluginCode; use gauntlet_common_plugin_runtime::model::JsPluginPermissionsExec; use gauntlet_common_plugin_runtime::model::JsPluginPermissionsFileSystem; @@ -174,7 +170,7 @@ impl ApplicationManager { }) } - pub fn download_plugin(&self, plugin_id: PluginId) -> anyhow::Result<()> { + pub fn download_plugin(&self, plugin_id: PluginId) { self.plugin_downloader.download_plugin(plugin_id) } @@ -571,7 +567,7 @@ impl ApplicationManager { .set_global_entrypoint_shortcut(global_hotkey_manager, plugin_id, entrypoint_id, shortcut) } - pub fn get_global_entrypoint_shortcut( + pub fn get_global_entrypoint_shortcuts( &self, ) -> anyhow::Result)>> { self.settings.global_entrypoint_shortcuts() @@ -812,36 +808,6 @@ impl ApplicationManager { .expect("failed to toggle window"); } - pub fn open_settings_window(&self) { - let current_exe = std::env::current_exe().expect("unable to get current_exe"); - - std::process::Command::new(current_exe) - .args(["settings"]) - .spawn_detached() - .expect("failed to execute settings process"); - } - - pub fn open_settings_window_preferences(&self, plugin_id: PluginId, entrypoint_id: Option) { - let data = if let Some(entrypoint_id) = entrypoint_id { - SettingsEnvData::OpenEntrypointPreferences { - plugin_id: plugin_id.to_string(), - entrypoint_id: entrypoint_id.to_string(), - } - } else { - SettingsEnvData::OpenPluginPreferences { - plugin_id: plugin_id.to_string(), - } - }; - - let current_exe = std::env::current_exe().expect("unable to get current_exe"); - - std::process::Command::new(current_exe) - .args(["settings"]) - .env(SETTINGS_ENV, settings_env_data_to_string(data)) - .spawn_detached() - .expect("failed to execute settings process"); // this can fail in dev if binary was replaced by more recent compilation - } - fn reload_plugin(&self, plugin_id: PluginId) -> anyhow::Result<()> { tracing::info!(target = "plugin", "Reloading plugin with id: {:?}", plugin_id); diff --git a/rust/server/src/rpc.rs b/rust/server/src/rpc.rs index 9b68e61..5d5ec35 100644 --- a/rust/server/src/rpc.rs +++ b/rust/server/src/rpc.rs @@ -1,16 +1,7 @@ -use std::collections::HashMap; - -use gauntlet_common::model::DownloadStatus; use gauntlet_common::model::EntrypointId; use gauntlet_common::model::LocalSaveData; -use gauntlet_common::model::PhysicalShortcut; use gauntlet_common::model::PluginId; -use gauntlet_common::model::PluginPreferenceUserData; -use gauntlet_common::model::SettingsPlugin; -use gauntlet_common::model::SettingsTheme; -use gauntlet_common::model::WindowPositionMode; use gauntlet_common::rpc::backend_api::BackendForCliApi; -use gauntlet_common::rpc::backend_api::BackendForSettingsApi; use gauntlet_common::rpc::backend_api::BackendForToolsApi; use gauntlet_common::rpc::backend_server::start_backend_server; use gauntlet_common::rpc::server_grpc_api::ServerGrpcApi; @@ -33,7 +24,6 @@ pub async fn run_grpc_server(grpc_api: ServerGrpcApiProxy) { start_backend_server( Box::new(BackendServerImpl::new(grpc_api.clone())), Box::new(BackendServerImpl::new(grpc_api.clone())), - Box::new(BackendServerImpl::new(grpc_api.clone())), ) .await } @@ -77,197 +67,3 @@ impl BackendForToolsApi for BackendServerImpl { Ok(result) } } - -#[tonic::async_trait] -impl BackendForSettingsApi for BackendServerImpl { - async fn wayland_global_shortcuts_enabled(&self) -> RequestResult { - self.proxy.wayland_global_shortcuts_enabled().await - } - - async fn plugins(&self) -> RequestResult> { - self.proxy.plugins().await - } - - async fn set_plugin_state(&self, plugin_id: PluginId, enabled: bool) -> RequestResult<()> { - let result = self.proxy.set_plugin_state(plugin_id, enabled).await; - - if let Err(err) = &result { - tracing::warn!( - target = "rpc", - "error occurred when handling 'set_plugin_state' request {:?}", - err - ) - } - - Ok(()) - } - - async fn set_entrypoint_state( - &self, - plugin_id: PluginId, - entrypoint_id: EntrypointId, - enabled: bool, - ) -> RequestResult<()> { - let result = self.proxy.set_entrypoint_state(plugin_id, entrypoint_id, enabled).await; - - if let Err(err) = &result { - tracing::warn!( - target = "rpc", - "error occurred when handling 'set_entrypoint_state' request {:?}", - err - ) - } - - Ok(()) - } - - async fn set_global_shortcut(&self, shortcut: Option) -> RequestResult> { - let result = self.proxy.set_global_shortcut(shortcut).await; - - if let Err(err) = &result { - tracing::warn!( - target = "rpc", - "error occurred when handling 'set_global_shortcut' request {:?}", - err - ) - } - - result - } - - async fn get_global_shortcut(&self) -> RequestResult<(Option, Option)> { - let result = self - .proxy - .get_global_shortcut() - .await? - .map(|(shortcut, error)| (Some(shortcut), error)) - .unwrap_or((None, None)); - - Ok(result) - } - - async fn set_global_entrypoint_shortcut( - &self, - plugin_id: PluginId, - entrypoint_id: EntrypointId, - shortcut: Option, - ) -> RequestResult<()> { - let result = self - .proxy - .set_global_entrypoint_shortcut(plugin_id, entrypoint_id, shortcut) - .await; - - if let Err(err) = &result { - tracing::warn!( - target = "rpc", - "error occurred when handling 'set_global_entrypoint_shortcut' request {:?}", - err - ) - } - - result - } - - async fn get_global_entrypoint_shortcuts( - &self, - ) -> RequestResult)>> { - self.proxy.get_global_entrypoint_shortcuts().await - } - - async fn set_entrypoint_search_alias( - &self, - plugin_id: PluginId, - entrypoint_id: EntrypointId, - alias: Option, - ) -> RequestResult<()> { - let result = self - .proxy - .set_entrypoint_search_alias(plugin_id, entrypoint_id, alias) - .await; - - if let Err(err) = &result { - tracing::warn!( - target = "rpc", - "error occurred when handling 'set_entrypoint_search_alias' request {:?}", - err - ) - } - - result - } - - async fn get_entrypoint_search_aliases(&self) -> RequestResult> { - self.proxy.get_entrypoint_search_aliases().await - } - - async fn set_theme(&self, theme: SettingsTheme) -> RequestResult<()> { - self.proxy.set_theme(theme).await - } - - async fn get_theme(&self) -> RequestResult { - self.proxy.get_theme().await - } - - async fn set_window_position_mode(&self, mode: WindowPositionMode) -> RequestResult<()> { - self.proxy.set_window_position_mode(mode).await - } - - async fn get_window_position_mode(&self) -> RequestResult { - self.proxy.get_window_position_mode().await - } - - async fn set_preference_value( - &self, - plugin_id: PluginId, - entrypoint_id: Option, - preference_id: String, - preference_value: PluginPreferenceUserData, - ) -> RequestResult<()> { - let result = self - .proxy - .set_preference_value(plugin_id, entrypoint_id, preference_id, preference_value) - .await; - - if let Err(err) = &result { - tracing::warn!( - target = "rpc", - "error occurred when handling 'set_preference_value' request {:?}", - err - ) - } - - Ok(()) - } - - async fn download_plugin(&self, plugin_id: PluginId) -> RequestResult<()> { - let result = self.proxy.download_plugin(plugin_id).await; - - if let Err(err) = &result { - tracing::warn!( - target = "rpc", - "error occurred when handling 'download_plugin' request {:?}", - err - ) - } - - Ok(()) - } - - async fn download_status(&self) -> RequestResult> { - self.proxy.download_status().await - } - - async fn remove_plugin(&self, plugin_id: PluginId) -> RequestResult<()> { - let result = self.proxy.remove_plugin(plugin_id).await; - - if let Err(err) = &result { - tracing::warn!( - target = "rpc", - "error occurred when handling 'remove_plugin' request {:?}", - err - ) - } - - Ok(()) - } -} diff --git a/schema/backend.proto b/schema/backend.proto index 9643b3d..f908d28 100644 --- a/schema/backend.proto +++ b/schema/backend.proto @@ -4,9 +4,6 @@ service RpcBackend { // cli rpc BackendForCliApi(RpcBincode) returns (RpcBincode); - // settings - rpc BackendForSettingsApi(RpcBincode) returns (RpcBincode); - // dev tools, screenshot gen rpc SaveLocalPlugin (RpcSaveLocalPluginRequest) returns (RpcSaveLocalPluginResponse); }