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
386 changes: 348 additions & 38 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ egui-toast = "0.13"
dirs-next = "2"
shlex = "1.3"
sysinfo = "0.35"
chrono = "0.4"
notify-rust = "4"

[target.'cfg(target_os = "windows")'.dependencies]
rfd = { version = "0.15.3", default-features = false, features = ["common-controls-v6"] }
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ quickly open applications or files.
- Run shell commands without opening a terminal.
- Perform web searches or look up documentation directly.
- Jump to frequently used folders with the folders plugin.
- Set timers or alarms from the launcher. Type `timer` or `alarm` and press
<kbd>Enter</kbd> to open the creation dialog.


## Building
Expand Down Expand Up @@ -70,6 +72,7 @@ default hotkey is `F2`. To use a different key, set the `hotkey` value in
"runescape_search",
"weather",
"system",
"timer",
"history",
"help"
],
Expand Down Expand Up @@ -149,6 +152,10 @@ Built-in plugins and their command prefixes are:
- Shell commands (`sh echo hi`)
- System actions (`sys shutdown`)
- Process list (`ps`), providing "Switch to" and "Kill" actions
- Timers and alarms (`timer 5m tea`, `alarm 07:30`). Use `timer list` to view
remaining time. Pending alarms are saved to `alarms.json` and resume after
restarting the launcher. A plugin setting controls pop-up dialogs when a
timer completes.
- Search history (`hi`)
- Command overview (`help`)

Expand Down Expand Up @@ -186,6 +193,7 @@ Example:
"system",
"processes",
"weather",
"timer",
"history",
"help"
]
Expand Down
42 changes: 42 additions & 0 deletions src/gui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ use fuzzy_matcher::FuzzyMatcher;
use crate::visibility::apply_visibility;
use std::collections::HashMap;
use crate::help_window::HelpWindow;
use crate::timer_help_window::TimerHelpWindow;
use crate::timer_dialog::{TimerDialog, TimerCompletionDialog};

fn scale_ui<R>(ui: &mut egui::Ui, scale: f32, add_contents: impl FnOnce(&mut egui::Ui) -> R) -> R {
ui.scope(|ui| {
Expand Down Expand Up @@ -74,6 +76,9 @@ pub struct LauncherApp {
pub enable_toasts: bool,
alias_dialog: crate::alias_dialog::AliasDialog,
help_window: crate::help_window::HelpWindow,
timer_help: crate::timer_help_window::TimerHelpWindow,
timer_dialog: crate::timer_dialog::TimerDialog,
completion_dialog: crate::timer_dialog::TimerCompletionDialog,
pub help_flag: Arc<AtomicBool>,
pub hotkey_str: Option<String>,
pub quit_hotkey_str: Option<String>,
Expand Down Expand Up @@ -236,6 +241,9 @@ impl LauncherApp {
enable_toasts,
alias_dialog: crate::alias_dialog::AliasDialog::default(),
help_window: HelpWindow::default(),
timer_help: TimerHelpWindow::default(),
timer_dialog: TimerDialog::default(),
completion_dialog: TimerCompletionDialog::default(),
help_flag: help_flag.clone(),
hotkey_str: settings.hotkey.clone(),
quit_hotkey_str: settings.quit_hotkey.clone(),
Expand Down Expand Up @@ -284,6 +292,9 @@ impl LauncherApp {
pub fn search(&mut self) {
let mut res: Vec<(Action, f32)> = Vec::new();

let trimmed = self.query.trim();


if self.query.is_empty() {
res.extend(self.actions.iter().cloned().map(|a| (a, 0.0)));
} else {
Expand Down Expand Up @@ -391,6 +402,17 @@ impl eframe::App for LauncherApp {
if self.enable_toasts {
self.toasts.show(ctx);
}
if self
.enabled_capabilities
.as_ref()
.and_then(|m| m.get("timer"))
.map(|c| c.contains(&"completion_dialog".to_string()))
.unwrap_or(true)
{
for msg in crate::plugins::timer::take_finished_messages() {
self.completion_dialog.open_message(msg);
}
}
if let Some(rect) = ctx.input(|i| i.viewport().inner_rect) {
self.window_size = (rect.width() as i32, rect.height() as i32);
}
Expand Down Expand Up @@ -466,6 +488,9 @@ impl eframe::App for LauncherApp {
if ui.button("Command List").clicked() {
self.help_window.open = true;
}
if ui.button("Timer Plugin Help").clicked() {
self.timer_help.open = true;
}
});
});
});
Expand Down Expand Up @@ -524,6 +549,10 @@ impl eframe::App for LauncherApp {
let mut set_focus = false;
if a.action == "help:show" {
self.help_window.open = true;
} else if a.action == "timer:dialog:timer" {
self.timer_dialog.open_timer();
} else if a.action == "timer:dialog:alarm" {
self.timer_dialog.open_alarm();
} else if let Err(e) = launch_action(&a) {
self.error = Some(format!("Failed: {e}"));
if self.enable_toasts {
Expand Down Expand Up @@ -637,6 +666,10 @@ impl eframe::App for LauncherApp {
let current = self.query.clone();
if a.action == "help:show" {
self.help_window.open = true;
} else if a.action == "timer:dialog:timer" {
self.timer_dialog.open_timer();
} else if a.action == "timer:dialog:alarm" {
self.timer_dialog.open_alarm();
} else if let Err(e) = launch_action(&a) {
self.error = Some(format!("Failed: {e}"));
if self.enable_toasts {
Expand Down Expand Up @@ -714,6 +747,15 @@ impl eframe::App for LauncherApp {
let mut help = std::mem::take(&mut self.help_window);
help.ui(ctx, self);
self.help_window = help;
let mut timer_help = std::mem::take(&mut self.timer_help);
timer_help.ui(ctx);
self.timer_help = timer_help;
let mut timer_dlg = std::mem::take(&mut self.timer_dialog);
timer_dlg.ui(ctx, self);
self.timer_dialog = timer_dlg;
let mut comp = std::mem::take(&mut self.completion_dialog);
comp.ui(ctx);
self.completion_dialog = comp;
}

fn on_exit(&mut self, _gl: Option<&eframe::glow::Context>) {
Expand Down
1 change: 1 addition & 0 deletions src/help_window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ fn example_queries(name: &str) -> Option<&'static [&'static str]> {
"system" => Some(&["sys shutdown"]),
"weather" => Some(&["weather Berlin"]),
"history" => Some(&["hi"]),
"timer" => Some(&["timer 10s break", "timer list", "alarm 07:30"]),
"help" => Some(&["help"]),
_ => None,
}
Expand Down
29 changes: 29 additions & 0 deletions src/launcher.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::actions::Action;
use crate::plugins::bookmarks::{append_bookmark, remove_bookmark};
use crate::plugins::folders::{append_folder, remove_folder, FOLDERS_FILE};
use crate::plugins::timer;
use crate::history;
use sysinfo::System;
use arboard::Clipboard;
Expand Down Expand Up @@ -126,6 +127,34 @@ pub fn launch_action(action: &Action) -> anyhow::Result<()> {
}
return Ok(());
}
if let Some(id) = action.action.strip_prefix("timer:cancel:") {
if let Ok(id) = id.parse::<u64>() {
timer::cancel_timer(id);
}
return Ok(());
}
if let Some(arg) = action.action.strip_prefix("timer:start:") {
let (dur_str, name) = arg.split_once('|').unwrap_or((arg, ""));
if let Some(dur) = timer::parse_duration(dur_str) {
if name.is_empty() {
timer::start_timer(dur);
} else {
timer::start_timer_named(dur, Some(name.to_string()));
}
}
return Ok(());
}
if let Some(arg) = action.action.strip_prefix("alarm:set:") {
let (time_str, name) = arg.split_once('|').unwrap_or((arg, ""));
if let Some((h, m)) = timer::parse_hhmm(time_str) {
if name.is_empty() {
timer::start_alarm(h, m);
} else {
timer::start_alarm_named(h, m, Some(name.to_string()));
}
}
return Ok(());
}
let path = Path::new(&action.action);

// If it's an .exe or we have additional args, launch it directly
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ pub mod hotkey;
pub mod history;
pub mod visibility;
pub mod help_window;
pub mod timer_help_window;
pub mod timer_dialog;

pub mod window_manager;
pub mod workspace;
Expand Down
2 changes: 2 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ mod window_manager;
mod workspace;
mod plugins;
mod help_window;
mod timer_help_window;
mod timer_dialog;

use crate::actions::{load_actions, Action};
use crate::gui::LauncherApp;
Expand Down
3 changes: 3 additions & 0 deletions src/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use crate::plugins::help::HelpPlugin;
use crate::plugins::youtube::YoutubePlugin;
use crate::plugins::reddit::RedditPlugin;
use crate::plugins::weather::WeatherPlugin;
use crate::plugins::timer::TimerPlugin;

pub trait Plugin: Send + Sync {
/// Return actions based on the query string
Expand Down Expand Up @@ -61,6 +62,8 @@ impl PluginManager {
self.register(Box::new(ShellPlugin));
self.register(Box::new(HistoryPlugin));
self.register(Box::new(HelpPlugin));
self.register(Box::new(TimerPlugin));
crate::plugins::timer::load_saved_alarms();
self.register(Box::new(WeatherPlugin));
for dir in dirs {
tracing::debug!("loading plugins from {dir}");
Expand Down
1 change: 1 addition & 0 deletions src/plugins/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ pub mod youtube;
pub mod reddit;
pub mod processes;
pub mod weather;
pub mod timer;
Loading