From ce3a1c456cbfe6c7761cce5903975c8256f76a5c Mon Sep 17 00:00:00 2001 From: Federico Terzi Date: Sun, 27 Jun 2021 18:04:21 +0200 Subject: [PATCH] feat(core): wire up Welcome screen --- espanso/src/cli/daemon/mod.rs | 7 +++ espanso/src/cli/daemon/ui.rs | 36 +++++++++++++ espanso/src/cli/modulo/mod.rs | 6 +++ espanso/src/cli/modulo/welcome.rs | 48 ++++++++++++++++++ espanso/src/icon.rs | 4 ++ espanso/src/main.rs | 10 ++++ espanso/src/preferences/default.rs | 12 ++++- espanso/src/preferences/mod.rs | 5 +- .../src/res/windows/tray_explain_image.png | Bin 0 -> 15464 bytes 9 files changed, 125 insertions(+), 3 deletions(-) create mode 100644 espanso/src/cli/daemon/ui.rs create mode 100644 espanso/src/cli/modulo/welcome.rs create mode 100644 espanso/src/res/windows/tray_explain_image.png diff --git a/espanso/src/cli/daemon/mod.rs b/espanso/src/cli/daemon/mod.rs index 1a7793b..f770583 100644 --- a/espanso/src/cli/daemon/mod.rs +++ b/espanso/src/cli/daemon/mod.rs @@ -35,6 +35,7 @@ use crate::{VERSION, exit_code::{ use super::{CliModule, CliModuleArgs}; mod ipc; +mod ui; mod watcher; pub fn new() -> CliModule { @@ -55,6 +56,8 @@ fn daemon_main(args: CliModuleArgs) -> i32 { let config_store = args .config_store .expect("missing config store in worker main"); + let preferences = crate::preferences::get_default(&paths.runtime).expect("unable to obtain preferences"); + let cli_args = args.cli_args.expect("missing cli_args in daemon main"); // Make sure only one instance of the daemon is running let lock_file = acquire_daemon_lock(&paths.runtime); @@ -95,6 +98,10 @@ fn daemon_main(args: CliModuleArgs) -> i32 { .expect("unable to initialize config watcher thread"); } + if cli_args.is_present("show-welcome") { + ui::show_welcome_screen(&preferences); + } + loop { select! { recv(watcher_signal) -> _ => { diff --git a/espanso/src/cli/daemon/ui.rs b/espanso/src/cli/daemon/ui.rs new file mode 100644 index 0000000..9d0ff5a --- /dev/null +++ b/espanso/src/cli/daemon/ui.rs @@ -0,0 +1,36 @@ +/* + * This file is part of espanso. + * + * Copyright (C) 2019-2021 Federico Terzi + * + * espanso is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * espanso is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with espanso. If not, see . + */ + +use crate::preferences::Preferences; + +#[cfg(feature = "modulo")] +pub fn show_welcome_screen(preferences: &impl Preferences) { + if preferences.should_display_welcome() { + let espanso_exe_path = std::env::current_exe().expect("unable to determine executable path"); + let mut command = std::process::Command::new(&espanso_exe_path.to_string_lossy().to_string()); + command.args(&["modulo", "welcome"]); + + command.spawn().expect("unable to show welcome screen"); + } +} + +#[cfg(not(feature = "modulo"))] +pub fn show_welcome_screen(_: &impl Preferences) { + // NOOP +} \ No newline at end of file diff --git a/espanso/src/cli/modulo/mod.rs b/espanso/src/cli/modulo/mod.rs index 4b444c7..753b2ba 100644 --- a/espanso/src/cli/modulo/mod.rs +++ b/espanso/src/cli/modulo/mod.rs @@ -23,6 +23,8 @@ use super::{CliModule, CliModuleArgs}; mod form; #[cfg(feature = "modulo")] mod search; +#[cfg(feature = "modulo")] +mod welcome; pub fn new() -> CliModule { #[allow(clippy::needless_update)] @@ -49,6 +51,10 @@ fn modulo_main(args: CliModuleArgs) -> i32 { return search::search_main(matches, &icon_paths); } + if let Some(_) = cli_args.subcommand_matches("welcome") { + return welcome::welcome_main(&paths, &icon_paths); + } + 0 } diff --git a/espanso/src/cli/modulo/welcome.rs b/espanso/src/cli/modulo/welcome.rs new file mode 100644 index 0000000..e559ec4 --- /dev/null +++ b/espanso/src/cli/modulo/welcome.rs @@ -0,0 +1,48 @@ +/* + * This file is part of espanso. + * + * Copyright (C) 2019-2021 Federico Terzi + * + * espanso is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * espanso is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with espanso. If not, see . + */ + +use crate::icon::IconPaths; +use crate::preferences::Preferences; +use espanso_modulo::welcome::*; +use espanso_path::Paths; + +pub fn welcome_main(paths: &Paths, icon_paths: &IconPaths) -> i32 { + let preferences = + crate::preferences::get_default(&paths.runtime).expect("unable to initialize preferences"); + + let dont_show_again_handler = Box::new(move |dont_show: bool| { + preferences.set_should_display_welcome(!dont_show); + }); + + espanso_modulo::welcome::show(WelcomeOptions{ + window_icon_path: icon_paths + .wizard_icon + .as_ref() + .map(|path| path.to_string_lossy().to_string()), + tray_image_path: icon_paths + .tray_explain_image + .as_ref() + .map(|path| path.to_string_lossy().to_string()), + handlers: WelcomeHandlers { + dont_show_again_changed: Some(dont_show_again_handler), + }, + }); + + 0 +} diff --git a/espanso/src/icon.rs b/espanso/src/icon.rs index 8ae39e5..3ba5e9f 100644 --- a/espanso/src/icon.rs +++ b/espanso/src/icon.rs @@ -30,6 +30,8 @@ const WINDOWS_NORMAL_DARK_ICO_BINARY: &[u8] = include_bytes!("res/windows/normal const WINDOWS_DISABLED_DARK_ICO_BINARY: &[u8] = include_bytes!("res/windows/disabled_dark.ico"); #[cfg(target_os = "windows")] const WINDOWS_LOGO_ICO_BINARY: &[u8] = include_bytes!("res/windows/logo.ico"); +#[cfg(target_os = "windows")] +const WINDOWS_TRAY_EXPLAIN_IMAGE: &[u8] = include_bytes!("res/windows/tray_explain_image.png"); #[cfg(target_os = "macos")] @@ -55,6 +57,7 @@ pub struct IconPaths { pub accessibility_image_1: Option, pub accessibility_image_2: Option, + pub tray_explain_image: Option, pub logo: Option, pub logo_no_background: Option, @@ -70,6 +73,7 @@ pub fn load_icon_paths(runtime_dir: &Path) -> Result { tray_icon_disabled: Some(extract_icon(WINDOWS_DISABLED_DARK_ICO_BINARY, &runtime_dir.join("disabled.ico"))?), logo: Some(extract_icon(ICON_BINARY, &runtime_dir.join("icon.png"))?), logo_no_background: Some(extract_icon(LOGO_NO_BACKGROUND_BINARY, &runtime_dir.join("icon_no_background.png"))?), + tray_explain_image: Some(extract_icon(WINDOWS_TRAY_EXPLAIN_IMAGE, &runtime_dir.join("tray_explain_image.png"))?), ..Default::default() }) } diff --git a/espanso/src/main.rs b/espanso/src/main.rs index 4269b7e..7f8f1eb 100644 --- a/espanso/src/main.rs +++ b/espanso/src/main.rs @@ -163,6 +163,12 @@ fn main() { .subcommand( SubCommand::with_name("daemon") .setting(AppSettings::Hidden) + .arg( + Arg::with_name("show-welcome") + .long("show-welcome") + .required(false) + .takes_value(false) + ) .about("Start the daemon without spawning a new process."), ) // .subcommand(SubCommand::with_name("register") @@ -207,6 +213,10 @@ fn main() { .takes_value(false) .help("Interpret the input data as JSON"), ), + ) + .subcommand( + SubCommand::with_name("welcome") + .about("Display the welcome screen") ), ) // .subcommand(SubCommand::with_name("start") diff --git a/espanso/src/preferences/default.rs b/espanso/src/preferences/default.rs index f58da01..03caf6e 100644 --- a/espanso/src/preferences/default.rs +++ b/espanso/src/preferences/default.rs @@ -19,13 +19,13 @@ use espanso_kvs::KVS; use serde::{de::DeserializeOwned, Serialize}; -use std::path::Path; use anyhow::Result; use super::Preferences; const HAS_COMPLETED_WIZARD_KEY: &str = "has_completed_wizard"; +const SHOULD_DISPLAY_WELCOME_KEY: &str = "should_display_welcome"; #[derive(Clone)] pub struct DefaultPreferences { @@ -33,7 +33,7 @@ pub struct DefaultPreferences { } impl DefaultPreferences { - pub fn new(runtime_dir: &Path, kvs: KVSType) -> Result { + pub fn new(kvs: KVSType) -> Result { Ok(Self { kvs }) } @@ -61,4 +61,12 @@ impl Preferences for DefaultPreferences { fn set_completed_wizard(&self, value: bool) { self.set(HAS_COMPLETED_WIZARD_KEY, value); } + + fn should_display_welcome(&self) -> bool { + self.get(SHOULD_DISPLAY_WELCOME_KEY).unwrap_or(true) + } + + fn set_should_display_welcome(&self, value: bool) { + self.set(SHOULD_DISPLAY_WELCOME_KEY, value); + } } diff --git a/espanso/src/preferences/mod.rs b/espanso/src/preferences/mod.rs index bc86a1b..ca19f89 100644 --- a/espanso/src/preferences/mod.rs +++ b/espanso/src/preferences/mod.rs @@ -25,9 +25,12 @@ mod default; pub trait Preferences: Send + Sync + Clone { fn has_completed_wizard(&self) -> bool; fn set_completed_wizard(&self, value: bool); + + fn should_display_welcome(&self) -> bool; + fn set_should_display_welcome(&self, value: bool); } pub fn get_default(runtime_dir: &Path) -> Result { let kvs = espanso_kvs::get_persistent(runtime_dir)?; - default::DefaultPreferences::new(runtime_dir, kvs) + default::DefaultPreferences::new(kvs) } \ No newline at end of file diff --git a/espanso/src/res/windows/tray_explain_image.png b/espanso/src/res/windows/tray_explain_image.png new file mode 100644 index 0000000000000000000000000000000000000000..1750b99955d9e84dea624cb9e362f6f08c5d888e GIT binary patch literal 15464 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 G=zjppW+)Z_ literal 0 HcmV?d00001