Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/app/apps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)),
Expand Down
28 changes: 26 additions & 2 deletions src/app/tile/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -698,12 +699,19 @@ fn open_window(height: f32) -> Task<Message> {

/// A helper function for resizing rustcast when only one result is found
fn single_item_resize_task(id: Id) -> Task<Message> {
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<Message> {
Task::done(Message::ResizeWindow(id, DEFAULT_WINDOW_HEIGHT))
resize_task(id, 0)
}

fn resize_task(id: Id, count: u32) -> Task<Message> {
Task::done(Message::ResizeWindow(
id,
(55 * count) as f32 + DEFAULT_WINDOW_HEIGHT,
))
}

fn resize_for_results_count(id: Id, count: usize) -> Task<Message> {
Expand Down Expand Up @@ -787,6 +795,12 @@ fn execute_query(tile: &mut Tile, id: Id) -> Task<Message> {
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);
Expand Down Expand Up @@ -864,6 +878,16 @@ fn execute_query(tile: &mut Tile, id: Id) -> Task<Message> {
}

tile.handle_search_query_changed();
if tile.query_lc.starts_with("quit") {
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() {
tile.results.par_sort_by_key(|x| -x.ranking);
Expand Down
11 changes: 11 additions & 0 deletions src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,15 @@ use crate::{
calculator::Expr,
clipboard::ClipBoardContentType,
config::Config,
quit::{terminate_all_apps, terminate_app},
};

/// The different functions that rustcast can perform
#[derive(Debug, Clone, PartialEq)]
pub enum Function {
OpenApp(String),
QuitApp(String),
QuitAllApps,
RunShellCommand(String),
OpenWebsite(String),
RandomVar(i32), // Easter egg function
Expand Down Expand Up @@ -48,6 +51,14 @@ impl Function {
.unwrap_or(());
}

Function::QuitAllApps => {
terminate_all_apps();
}

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);
Expand Down
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod commands;
mod config;
mod debounce;
mod platform;
mod quit;
mod styles;
mod unit_conversion;
mod utils;
Expand Down
4 changes: 2 additions & 2 deletions src/platform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down
76 changes: 76 additions & 0 deletions src/quit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
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<App> {
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: format!("quit {}", name.to_lowercase()),
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;
}
}
}

pub fn terminate_all_apps() {
let open_apps = NSWorkspace::sharedWorkspace().runningApplications();
for app in open_apps {
if app.activationPolicy() == NSApplicationActivationPolicy::Regular {
app.terminate();
}
}
}
Loading