Skip to content

Commit 29e13ff

Browse files
committed
app quiting with "quit"
1 parent 5d5f378 commit 29e13ff

5 files changed

Lines changed: 90 additions & 4 deletions

File tree

src/app/tile/update.rs

Lines changed: 14 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};
@@ -716,12 +717,19 @@ fn open_window(height: f32) -> Task<Message> {
716717

717718
/// A helper function for resizing rustcast when only one result is found
718719
fn single_item_resize_task(id: Id) -> Task<Message> {
719-
Task::done(Message::ResizeWindow(id, 55. + DEFAULT_WINDOW_HEIGHT))
720+
resize_task(id, 1)
720721
}
721722

722723
/// A helper function for resizing rustcast when zero results are found
723724
fn zero_item_resize_task(id: Id) -> Task<Message> {
724-
Task::done(Message::ResizeWindow(id, DEFAULT_WINDOW_HEIGHT))
725+
resize_task(id, 0)
726+
}
727+
728+
fn resize_task(id: Id, count: u32) -> Task<Message> {
729+
Task::done(Message::ResizeWindow(
730+
id,
731+
(55 * count) as f32 + DEFAULT_WINDOW_HEIGHT,
732+
))
725733
}
726734

727735
/// Handling the lemon easter egg icon
@@ -831,6 +839,10 @@ fn execute_query(tile: &mut Tile, id: Id) -> Task<Message> {
831839
}
832840

833841
tile.handle_search_query_changed();
842+
if tile.query_lc.starts_with("quit") {
843+
tile.results
844+
.extend(get_open_apps(tile.config.theme.show_icons));
845+
}
834846

835847
if !tile.results.is_empty() {
836848
tile.results.par_sort_by_key(|x| -x.ranking);

src/commands.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ use crate::{
1111
calculator::Expr,
1212
clipboard::ClipBoardContentType,
1313
config::Config,
14+
quit::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),
2022
RunShellCommand(String),
2123
OpenWebsite(String),
2224
RandomVar(i32), // Easter egg function
@@ -48,6 +50,10 @@ impl Function {
4850
.unwrap_or(());
4951
}
5052

53+
Function::QuitApp(name) => {
54+
terminate_app(name.to_owned());
55+
}
56+
5157
Function::GoogleSearch(query_string) => {
5258
let query_args = query_string.replace(" ", "+");
5359
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: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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: "Quit".to_string(),
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+
}

0 commit comments

Comments
 (0)