Skip to content

Commit c862de5

Browse files
authored
Merge pull request #223 from unsecretised/quitting-apps
app quiting with "quit"
2 parents 5930564 + 8b08518 commit c862de5

File tree

6 files changed

+124
-4
lines changed

6 files changed

+124
-4
lines changed

src/app/apps.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,14 @@ impl App {
108108
display_name: "Quit RustCast".to_string(),
109109
search_name: "quit".to_string(),
110110
},
111+
App {
112+
ranking: 0,
113+
open_command: AppCommand::Function(Function::QuitAllApps),
114+
desc: RUSTCAST_DESC_NAME.to_string(),
115+
icons: icons.clone(),
116+
display_name: "Quit All Apps".to_string(),
117+
search_name: "quit all apps".to_string(),
118+
},
111119
App {
112120
ranking: 0,
113121
open_command: AppCommand::Message(Message::SwitchToPage(Page::Settings)),

src/app/tile/update.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ use crate::calculator::Expr;
3333
use crate::commands::Function;
3434
use crate::config::Config;
3535
use crate::debounce::DebouncePolicy;
36+
use crate::quit::get_open_apps;
3637
use crate::unit_conversion;
3738
use crate::utils::is_valid_url;
3839
use crate::{app::ArrowKey, platform::focus_this_app};
@@ -714,12 +715,19 @@ fn open_window(height: f32) -> Task<Message> {
714715

715716
/// A helper function for resizing rustcast when only one result is found
716717
fn single_item_resize_task(id: Id) -> Task<Message> {
717-
Task::done(Message::ResizeWindow(id, 55. + DEFAULT_WINDOW_HEIGHT))
718+
resize_task(id, 1)
718719
}
719720

720721
/// A helper function for resizing rustcast when zero results are found
721722
fn zero_item_resize_task(id: Id) -> Task<Message> {
722-
Task::done(Message::ResizeWindow(id, DEFAULT_WINDOW_HEIGHT))
723+
resize_task(id, 0)
724+
}
725+
726+
fn resize_task(id: Id, count: u32) -> Task<Message> {
727+
Task::done(Message::ResizeWindow(
728+
id,
729+
(55 * count) as f32 + DEFAULT_WINDOW_HEIGHT,
730+
))
723731
}
724732

725733
fn resize_for_results_count(id: Id, count: usize) -> Task<Message> {
@@ -803,6 +811,12 @@ fn execute_query(tile: &mut Tile, id: Id) -> Task<Message> {
803811
return zero_item_resize_task(id);
804812
};
805813

814+
let quittables = if tile.query_lc.starts_with("quit") {
815+
get_open_apps(tile.config.theme.show_icons)
816+
} else {
817+
vec![]
818+
};
819+
806820
match tile.query_lc.as_str() {
807821
"randomvar" => {
808822
let rand_num = rand::random_range(0..100);
@@ -880,6 +894,16 @@ fn execute_query(tile: &mut Tile, id: Id) -> Task<Message> {
880894
}
881895

882896
tile.handle_search_query_changed();
897+
if tile.query_lc.starts_with("quit") {
898+
let query = tile.query_lc.clone();
899+
tile.results.extend(quittables.iter().filter_map(move |x| {
900+
if x.search_name.starts_with(&query) {
901+
Some(x.to_owned())
902+
} else {
903+
None
904+
}
905+
}))
906+
}
883907

884908
if !tile.results.is_empty() {
885909
tile.results.par_sort_by_key(|x| -x.ranking);

src/commands.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,15 @@ use crate::{
1111
calculator::Expr,
1212
clipboard::ClipBoardContentType,
1313
config::Config,
14+
quit::{terminate_all_apps, terminate_app},
1415
};
1516

1617
/// The different functions that rustcast can perform
1718
#[derive(Debug, Clone, PartialEq)]
1819
pub enum Function {
1920
OpenApp(String),
21+
QuitApp(String),
22+
QuitAllApps,
2023
RunShellCommand(String),
2124
OpenWebsite(String),
2225
RandomVar(i32), // Easter egg function
@@ -48,6 +51,14 @@ impl Function {
4851
.unwrap_or(());
4952
}
5053

54+
Function::QuitAllApps => {
55+
terminate_all_apps();
56+
}
57+
58+
Function::QuitApp(name) => {
59+
terminate_app(name.to_owned());
60+
}
61+
5162
Function::GoogleSearch(query_string) => {
5263
let query_args = query_string.replace(" ", "+");
5364
let query = config.search_url.replace("%s", &query_args);

src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ mod commands;
77
mod config;
88
mod debounce;
99
mod platform;
10+
mod quit;
1011
mod styles;
1112
mod unit_conversion;
1213
mod utils;

src/platform/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ use iced::wgpu::rwh::WindowHandle;
44
pub use self::cross::default_app_paths;
55
use crate::app::apps::App;
66

7-
mod cross;
7+
pub mod cross;
88
#[cfg(target_os = "macos")]
9-
mod macos;
9+
pub mod macos;
1010

1111
pub fn set_activation_policy_accessory() {
1212
#[cfg(target_os = "macos")]

src/quit.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
use std::io::Cursor;
2+
3+
use iced::widget::image::Handle;
4+
use objc2_app_kit::{NSApplicationActivationPolicy, NSWorkspace};
5+
use objc2_foundation::NSString;
6+
7+
use crate::{
8+
app::apps::{App, AppCommand},
9+
commands::Function,
10+
platform::macos::discovery::icon_of_path_ns,
11+
};
12+
13+
pub fn get_open_apps(store_icons: bool) -> Vec<App> {
14+
let open_apps = NSWorkspace::sharedWorkspace().runningApplications();
15+
16+
open_apps
17+
.iter()
18+
.filter_map(|app| {
19+
if app.activationPolicy() != NSApplicationActivationPolicy::Regular {
20+
return None;
21+
}
22+
23+
let name = app.localizedName().unwrap().to_string();
24+
25+
let icon = icon_of_path_ns(
26+
&app.bundleURL()
27+
.and_then(|x| x.path())
28+
.unwrap_or(NSString::new())
29+
.to_string(),
30+
)
31+
.unwrap_or(vec![]);
32+
let icons = if store_icons {
33+
image::ImageReader::new(Cursor::new(icon))
34+
.with_guessed_format()
35+
.unwrap()
36+
.decode()
37+
.ok()
38+
.map(|img| Handle::from_rgba(img.width(), img.height(), img.into_bytes()))
39+
} else {
40+
None
41+
};
42+
43+
Some(App {
44+
ranking: 0,
45+
open_command: AppCommand::Function(Function::QuitApp(name.clone())),
46+
display_name: format!("Quit {}", name),
47+
icons,
48+
search_name: format!("quit {}", name.to_lowercase()),
49+
desc: name.to_string(),
50+
})
51+
})
52+
.collect()
53+
}
54+
55+
pub fn terminate_app(name: String) {
56+
let open_apps = NSWorkspace::sharedWorkspace().runningApplications();
57+
58+
for app in open_apps {
59+
let is_regular_app = app.activationPolicy() == NSApplicationActivationPolicy::Regular;
60+
let name_matches = app.localizedName() == Some(NSString::from_str(&name));
61+
62+
if is_regular_app && name_matches {
63+
app.terminate();
64+
break;
65+
}
66+
}
67+
}
68+
69+
pub fn terminate_all_apps() {
70+
let open_apps = NSWorkspace::sharedWorkspace().runningApplications();
71+
for app in open_apps {
72+
if app.activationPolicy() == NSApplicationActivationPolicy::Regular {
73+
app.terminate();
74+
}
75+
}
76+
}

0 commit comments

Comments
 (0)