Skip to content

Commit

Permalink
refactor: add show manager shortcut plus refactoring (#419)
Browse files Browse the repository at this point in the history
* refactor: add show manager shortcut plus refactoring

* show-manager -> open-manager

* show-manager -> open-manager more
  • Loading branch information
Charlie-XIAO authored Feb 13, 2025
1 parent 0963a0f commit 53aace8
Show file tree
Hide file tree
Showing 10 changed files with 129 additions and 98 deletions.
11 changes: 1 addition & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ version = "0.0.1"

[workspace.dependencies]
anyhow = "1.0.87"
bincode = "1.3.3"
dunce = "1.0.5"
objc2 = "0.5.2"
once_cell = "1.20.2"
open = "5.3.1"
oxc = "0.43.0"
paste = "1.0.15"
path-clean = "1.0.1"
rolldown = { git = "https://github.com/rolldown/rolldown.git", tag = "v1.0.0-beta.1" }
rolldown_common = { git = "https://github.com/rolldown/rolldown.git", tag = "v1.0.0-beta.1" }
Expand Down
2 changes: 1 addition & 1 deletion crates/deskulpt-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ version = { workspace = true }

[dependencies]
anyhow = { workspace = true }
bincode = { workspace = true }
dunce = { workspace = true }
once_cell = { workspace = true }
open = { workspace = true, features = ["shellexecute-on-windows"] }
oxc = { workspace = true }
paste = { workspace = true }
path-clean = { workspace = true }
rolldown = { workspace = true }
rolldown_common = { workspace = true }
Expand Down
26 changes: 3 additions & 23 deletions crates/deskulpt-core/src/commands/update_shortcut.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,9 @@
use serde::Deserialize;
use tauri::{command, AppHandle, Runtime};

use super::error::CmdResult;
use crate::shortcuts::ShortcutsExt;
use crate::shortcuts::{ShortcutKey, ShortcutsExt};

/// The key of the shortcut to update.
///
/// This corresponds to `keyof Shortcuts` in TypeScript.
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum ShortcutKey {
ToggleCanvas,
}

/// Update a shortcut registered in the application.
///
/// This command will compare the old and new shortcuts and perform an update
/// only if it has changed. In that case, the old shortcut (if exists) will be
/// unregistered and the new shortcut (if exists) will be registered.
/// Wrapper of [`update_shortcut`](ShortcutsExt::update_shortcut).
///
/// ### Errors
///
Expand All @@ -31,12 +17,6 @@ pub async fn update_shortcut<R: Runtime>(
old_shortcut: Option<String>,
new_shortcut: Option<String>,
) -> CmdResult<()> {
match key {
ShortcutKey::ToggleCanvas => {
app_handle
.update_toggle_canvas_shortcut(old_shortcut.as_deref(), new_shortcut.as_deref())?;
},
}

app_handle.update_shortcut(key, old_shortcut.as_deref(), new_shortcut.as_deref())?;
Ok(())
}
21 changes: 18 additions & 3 deletions crates/deskulpt-core/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use anyhow::Result;
use serde::{Deserialize, Serialize};

/// The settings file name in the persistence directory.
static SETTINGS_FILE: &str = "settings.bin";
static SETTINGS_FILE: &str = "settings.json";

/// Light/dark theme of the application.
#[derive(Default, Deserialize, Serialize)]
Expand All @@ -28,16 +28,22 @@ enum Theme {
#[serde(rename_all = "camelCase")]
pub struct Shortcuts {
/// For toggling canvas click-through.
#[serde(default)]
pub toggle_canvas: Option<String>,
/// For opening the manager window.
#[serde(default)]
pub open_manager: Option<String>,
}

/// Application-wide settings.
#[derive(Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
struct AppSettings {
/// The application theme.
#[serde(default)]
theme: Theme,
/// The keyboard shortcuts.
#[serde(default)]
shortcuts: Shortcuts,
}

Expand All @@ -49,20 +55,29 @@ struct AppSettings {
#[serde(rename_all = "camelCase")]
struct WidgetSettings {
/// The leftmost x-coordinate in pixels.
#[serde(default)]
x: i32,
/// The topmost y-coordinate in pixels.
#[serde(default)]
y: i32,
/// The opacity in percentage.
#[serde(default = "default_opacity")]
opacity: i32,
}

fn default_opacity() -> i32 {
100
}

/// Full settings of the application.
#[derive(Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Settings {
/// Application-wide settings.
#[serde(default)]
app: AppSettings,
/// The mapping from widget IDs to their respective settings.
#[serde(default)]
widgets: HashMap<String, WidgetSettings>,
}

Expand All @@ -77,7 +92,7 @@ impl Settings {
}
let file = File::open(settings_path)?;
let reader = BufReader::new(file);
let settings: Settings = bincode::deserialize_from(reader)?;
let settings: Settings = serde_json::from_reader(reader)?;
Ok(settings)
}

Expand All @@ -92,7 +107,7 @@ impl Settings {
}
let file = File::create(persist_dir.join(SETTINGS_FILE))?;
let writer = BufWriter::new(file);
bincode::serialize_into(writer, self)?;
serde_json::to_writer(writer, self)?;
Ok(())
}

Expand Down
142 changes: 89 additions & 53 deletions crates/deskulpt-core/src/shortcuts.rs
Original file line number Diff line number Diff line change
@@ -1,78 +1,114 @@
//! Keyboard shortcut registration.
//! Keyboard shortcut management.
use anyhow::{bail, Result};
use paste::paste;
use serde::Deserialize;
use tauri::{App, AppHandle, Manager, Runtime};
use tauri_plugin_global_shortcut::{GlobalShortcutExt, ShortcutState};

use crate::settings::Settings;
use crate::states::StatesExtCanvasClickThrough;
use crate::WindowExt;

/// Implement a shortcut update function in [`ShortcutsExt`].
/// Implement [`ShortcutKey`] and [`ShortcutsExt`] for the given shortcuts.
///
/// The first argument is the name of the function. The second argument is the
/// top-level docstring. The third argument is the listener to be registered for
/// the shortcut.
macro_rules! impl_update_shortcut {
($method: ident, $shortcut: expr, $listener: expr) => {
#[doc = concat!("Update the keyboard shortcut `", $shortcut, "`.")]
///
/// This will compare the old and new shortcut, and update only when they
/// are different. For each changed shortcut, the old one (if exists) will
/// be unregistered and the new one (if exists) will be registered.
fn $method(&self, old_shortcut: Option<&str>, new_shortcut: Option<&str>) -> Result<()> {
if old_shortcut == new_shortcut {
return Ok(());
/// This macro takes a list of `key => listener` pairs, where `key` corresponds
/// to the keys of [`Shortcuts`](crate::settings::Shortcuts) and `listener` is
/// the corresponding shortcut handler callback.
macro_rules! impl_shortcuts {
($($key: ident => $listener: expr),* $(,)?) => {
paste! {
/// Keyboard shortcuts registered in the application.
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum ShortcutKey {
$(
[<$key:upper:camel>], // lower_snake_case => UpperCamelCase
)*
}
let manager = self.global_shortcut();
}

if let Some(shortcut) = old_shortcut {
if !manager.is_registered(shortcut) {
bail!("Failed to unregister '{shortcut}' because it is not registered yet");
/// Extension trait for keyboard shortcuts.
pub trait ShortcutsExt<R: Runtime>: Manager<R> + GlobalShortcutExt<R> {
/// Initialize keyboard shortcuts according to the initial settings.
///
/// If any shortcut fails to be registered, the initial settings will be
/// modified to remove that shortcut. This is to prevent the application
/// from panicking only due to non-critical failures, and also sync this
/// information to the frontend.
fn init_shortcuts(&self, settings: &mut Settings) {
let shortcuts = settings.shortcuts_mut();
paste! {
$(
if let Err(e) = self.update_shortcut(
ShortcutKey::[<$key:upper:camel>],
None,
shortcuts.$key.as_deref(),
) {
eprintln!("{}: {}", stringify!($key), e);
shortcuts.$key = None;
}
)*
}
manager.unregister(shortcut)?;
}

if let Some(shortcut) = new_shortcut {
if manager.is_registered(shortcut) {
bail!("Failed to register '{shortcut}' because it is already registered");
/// Update a shortcut registered in the application.
///
/// This function will compare the old and new shortcuts and perform an update
/// only if it has changed. In that case, the old shortcut (if exists) will be
/// unregistered and the new shortcut (if exists) will be registered.
fn update_shortcut(
&self,
key: ShortcutKey,
old_shortcut: Option<&str>,
new_shortcut: Option<&str>,
) -> Result<()> {
if old_shortcut == new_shortcut {
return Ok(());
}
manager.on_shortcut(shortcut, $listener)?;
}
let manager = self.global_shortcut();

Ok(())
}
};
}
if let Some(shortcut) = old_shortcut {
if !manager.is_registered(shortcut) {
bail!("Cannot unregister '{shortcut}': not registered yet");
}
manager.unregister(shortcut)?;
}

/// Extension trait for keyboard shortcuts.
pub trait ShortcutsExt<R: Runtime>: Manager<R> + GlobalShortcutExt<R> {
/// Initialize keyboard shortcuts according to the initial settings.
///
/// If any shortcut fails to be registered, the initial settings will be
/// modified to remove that shortcut. This is to prevent the application
/// from panicking only due to non-critical failures, and also sync this
/// information to the frontend.
fn init_shortcuts(&self, settings: &mut Settings) {
let shortcuts = settings.shortcuts_mut();
if let Some(shortcut) = new_shortcut {
if manager.is_registered(shortcut) {
bail!("Cannot register '{shortcut}': already registered");
}
paste! {
match key {
$(
ShortcutKey::[<$key:upper:camel>] => {
manager.on_shortcut(shortcut, $listener)?;
},
)*
}
}
}

if let Err(e) = self.update_toggle_canvas_shortcut(None, shortcuts.toggle_canvas.as_deref())
{
eprintln!("{e}");
shortcuts.toggle_canvas = None;
Ok(())
}
}
}
};
}

impl_update_shortcut!(
update_toggle_canvas_shortcut,
"toggle_canvas",
|app_handle, _, event| {
if event.state == ShortcutState::Pressed {
if let Err(e) = app_handle.toggle_canvas_click_through() {
eprintln!("Failed to toggle canvas click-through: {e}");
}
impl_shortcuts! {
toggle_canvas => |app_handle, _, event| {
if event.state == ShortcutState::Pressed {
if let Err(e) = app_handle.toggle_canvas_click_through() {
eprintln!("Failed to toggle canvas click-through: {e}");
}
}
);
},
open_manager => |app_handle, _, _| {
if let Err(e) = app_handle.open_manager() {
eprintln!("Failed to open the manager window: {e}");
}
},
}

impl<R: Runtime> ShortcutsExt<R> for App<R> {}
Expand Down
4 changes: 2 additions & 2 deletions crates/deskulpt-core/src/tray.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ fn on_menu_event<R: Runtime>(app_handle: &AppHandle<R>, event: MenuEvent) {
}
},
"tray-manage" => {
if let Err(e) = app_handle.show_manager() {
eprintln!("Error showing manager window: {}", e);
if let Err(e) = app_handle.open_manager() {
eprintln!("Error opening manager window: {}", e);
}
},
"tray-exit" => {
Expand Down
4 changes: 2 additions & 2 deletions crates/deskulpt-core/src/window/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ pub trait WindowExt<R: Runtime>: Manager<R> {
Ok(())
}

/// Show the manager window.
fn show_manager(&self) -> Result<()> {
/// Open the manager window.
fn open_manager(&self) -> Result<()> {
let manager = self
.get_webview_window("manager")
.ok_or(anyhow!("Manager window not found"))?;
Expand Down
Loading

0 comments on commit 53aace8

Please sign in to comment.