From 29e13ff22726506785b8652ca375e1260fa0737e Mon Sep 17 00:00:00 2001 From: unsecretised Date: Wed, 25 Mar 2026 23:30:26 +0800 Subject: [PATCH 1/3] app quiting with "quit" --- src/app/tile/update.rs | 16 ++++++++-- src/commands.rs | 6 ++++ src/main.rs | 1 + src/platform/mod.rs | 4 +-- src/quit.rs | 67 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 90 insertions(+), 4 deletions(-) create mode 100644 src/quit.rs diff --git a/src/app/tile/update.rs b/src/app/tile/update.rs index 8f1933f..928aaf3 100644 --- a/src/app/tile/update.rs +++ b/src/app/tile/update.rs @@ -33,6 +33,7 @@ use crate::calculator::Expr; use crate::commands::Function; use crate::config::Config; use crate::debounce::DebouncePolicy; +use crate::quit::get_open_apps; use crate::unit_conversion; use crate::utils::is_valid_url; use crate::{app::ArrowKey, platform::focus_this_app}; @@ -716,12 +717,19 @@ fn open_window(height: f32) -> Task { /// A helper function for resizing rustcast when only one result is found fn single_item_resize_task(id: Id) -> Task { - Task::done(Message::ResizeWindow(id, 55. + DEFAULT_WINDOW_HEIGHT)) + resize_task(id, 1) } /// A helper function for resizing rustcast when zero results are found fn zero_item_resize_task(id: Id) -> Task { - Task::done(Message::ResizeWindow(id, DEFAULT_WINDOW_HEIGHT)) + resize_task(id, 0) +} + +fn resize_task(id: Id, count: u32) -> Task { + Task::done(Message::ResizeWindow( + id, + (55 * count) as f32 + DEFAULT_WINDOW_HEIGHT, + )) } /// Handling the lemon easter egg icon @@ -831,6 +839,10 @@ fn execute_query(tile: &mut Tile, id: Id) -> Task { } tile.handle_search_query_changed(); + if tile.query_lc.starts_with("quit") { + tile.results + .extend(get_open_apps(tile.config.theme.show_icons)); + } if !tile.results.is_empty() { tile.results.par_sort_by_key(|x| -x.ranking); diff --git a/src/commands.rs b/src/commands.rs index d50e56f..65f15a5 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -11,12 +11,14 @@ use crate::{ calculator::Expr, clipboard::ClipBoardContentType, config::Config, + quit::terminate_app, }; /// The different functions that rustcast can perform #[derive(Debug, Clone, PartialEq)] pub enum Function { OpenApp(String), + QuitApp(String), RunShellCommand(String), OpenWebsite(String), RandomVar(i32), // Easter egg function @@ -48,6 +50,10 @@ impl Function { .unwrap_or(()); } + Function::QuitApp(name) => { + terminate_app(name.to_owned()); + } + Function::GoogleSearch(query_string) => { let query_args = query_string.replace(" ", "+"); let query = config.search_url.replace("%s", &query_args); diff --git a/src/main.rs b/src/main.rs index 0165cde..cf1adbc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ mod commands; mod config; mod debounce; mod platform; +mod quit; mod styles; mod unit_conversion; mod utils; diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 6e10e1f..38de61e 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -4,9 +4,9 @@ use iced::wgpu::rwh::WindowHandle; pub use self::cross::default_app_paths; use crate::app::apps::App; -mod cross; +pub mod cross; #[cfg(target_os = "macos")] -mod macos; +pub mod macos; pub fn set_activation_policy_accessory() { #[cfg(target_os = "macos")] diff --git a/src/quit.rs b/src/quit.rs new file mode 100644 index 0000000..f27c7f3 --- /dev/null +++ b/src/quit.rs @@ -0,0 +1,67 @@ +use std::io::Cursor; + +use iced::widget::image::Handle; +use objc2_app_kit::{NSApplicationActivationPolicy, NSWorkspace}; +use objc2_foundation::NSString; + +use crate::{ + app::apps::{App, AppCommand}, + commands::Function, + platform::macos::discovery::icon_of_path_ns, +}; + +pub fn get_open_apps(store_icons: bool) -> Vec { + let open_apps = NSWorkspace::sharedWorkspace().runningApplications(); + + open_apps + .iter() + .filter_map(|app| { + if app.activationPolicy() != NSApplicationActivationPolicy::Regular { + return None; + } + + let name = app.localizedName().unwrap().to_string(); + + let icon = icon_of_path_ns( + &app.bundleURL() + .and_then(|x| x.path()) + .unwrap_or(NSString::new()) + .to_string(), + ) + .unwrap_or(vec![]); + let icons = if store_icons { + image::ImageReader::new(Cursor::new(icon)) + .with_guessed_format() + .unwrap() + .decode() + .ok() + .map(|img| Handle::from_rgba(img.width(), img.height(), img.into_bytes())) + } else { + None + }; + + Some(App { + ranking: 0, + open_command: AppCommand::Function(Function::QuitApp(name.clone())), + display_name: format!("Quit {}", name), + icons, + search_name: "Quit".to_string(), + desc: name.to_string(), + }) + }) + .collect() +} + +pub fn terminate_app(name: String) { + let open_apps = NSWorkspace::sharedWorkspace().runningApplications(); + + for app in open_apps { + let is_regular_app = app.activationPolicy() == NSApplicationActivationPolicy::Regular; + let name_matches = app.localizedName() == Some(NSString::from_str(&name)); + + if is_regular_app && name_matches { + app.terminate(); + break; + } + } +} From 69e36b9faa77dd575d6424e1ae47358e9d513d36 Mon Sep 17 00:00:00 2001 From: unsecretised Date: Wed, 25 Mar 2026 23:41:47 +0800 Subject: [PATCH 2/3] search through quit bits as well --- src/app/tile/update.rs | 16 ++++++++++++++-- src/quit.rs | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/app/tile/update.rs b/src/app/tile/update.rs index 928aaf3..6a621d9 100644 --- a/src/app/tile/update.rs +++ b/src/app/tile/update.rs @@ -762,6 +762,12 @@ fn execute_query(tile: &mut Tile, id: Id) -> Task { return zero_item_resize_task(id); }; + let quittables = if tile.query_lc.starts_with("quit") { + get_open_apps(tile.config.theme.show_icons) + } else { + vec![] + }; + match tile.query_lc.as_str() { "randomvar" => { let rand_num = rand::random_range(0..100); @@ -840,8 +846,14 @@ fn execute_query(tile: &mut Tile, id: Id) -> Task { tile.handle_search_query_changed(); if tile.query_lc.starts_with("quit") { - tile.results - .extend(get_open_apps(tile.config.theme.show_icons)); + let query = tile.query_lc.clone(); + tile.results.extend(quittables.iter().filter_map(move |x| { + if x.search_name.starts_with(&query) { + Some(x.to_owned()) + } else { + None + } + })) } if !tile.results.is_empty() { diff --git a/src/quit.rs b/src/quit.rs index f27c7f3..bb0b932 100644 --- a/src/quit.rs +++ b/src/quit.rs @@ -45,7 +45,7 @@ pub fn get_open_apps(store_icons: bool) -> Vec { open_command: AppCommand::Function(Function::QuitApp(name.clone())), display_name: format!("Quit {}", name), icons, - search_name: "Quit".to_string(), + search_name: format!("quit {}", name.to_lowercase()), desc: name.to_string(), }) }) From 049fc72833970d6e6108cda3951885c291edfe0d Mon Sep 17 00:00:00 2001 From: unsecretised Date: Thu, 26 Mar 2026 00:30:36 +0800 Subject: [PATCH 3/3] Add quit all apps --- src/app/apps.rs | 8 ++++++++ src/commands.rs | 7 ++++++- src/quit.rs | 9 +++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/app/apps.rs b/src/app/apps.rs index b3954d1..52f452c 100644 --- a/src/app/apps.rs +++ b/src/app/apps.rs @@ -108,6 +108,14 @@ impl App { display_name: "Quit RustCast".to_string(), search_name: "quit".to_string(), }, + App { + ranking: 0, + open_command: AppCommand::Function(Function::QuitAllApps), + desc: RUSTCAST_DESC_NAME.to_string(), + icons: icons.clone(), + display_name: "Quit All Apps".to_string(), + search_name: "quit all apps".to_string(), + }, App { ranking: 0, open_command: AppCommand::Message(Message::SwitchToPage(Page::Settings)), diff --git a/src/commands.rs b/src/commands.rs index 65f15a5..a4d6d29 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -11,7 +11,7 @@ use crate::{ calculator::Expr, clipboard::ClipBoardContentType, config::Config, - quit::terminate_app, + quit::{terminate_all_apps, terminate_app}, }; /// The different functions that rustcast can perform @@ -19,6 +19,7 @@ use crate::{ pub enum Function { OpenApp(String), QuitApp(String), + QuitAllApps, RunShellCommand(String), OpenWebsite(String), RandomVar(i32), // Easter egg function @@ -50,6 +51,10 @@ impl Function { .unwrap_or(()); } + Function::QuitAllApps => { + terminate_all_apps(); + } + Function::QuitApp(name) => { terminate_app(name.to_owned()); } diff --git a/src/quit.rs b/src/quit.rs index bb0b932..2678a15 100644 --- a/src/quit.rs +++ b/src/quit.rs @@ -65,3 +65,12 @@ pub fn terminate_app(name: String) { } } } + +pub fn terminate_all_apps() { + let open_apps = NSWorkspace::sharedWorkspace().runningApplications(); + for app in open_apps { + if app.activationPolicy() == NSApplicationActivationPolicy::Regular { + app.terminate(); + } + } +}