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
47 changes: 47 additions & 0 deletions src/dashboard/data_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ use crate::plugins::fav::{load_favs, FavEntry, FAV_FILE};
use crate::plugins::note::{load_notes, Note};
use crate::plugins::snippets::{load_snippets, SnippetEntry, SNIPPETS_FILE};
use crate::plugins::todo::{load_todos, TodoEntry, TODO_FILE};
use crate::mouse_gestures::db::{load_gestures, GestureDb, GESTURES_FILE};
use crate::mouse_gestures::usage::{load_usage, GestureUsageEntry, GESTURES_USAGE_FILE};
use crate::{launcher, launcher::RecycleBinInfo};
use chrono::Local;
use std::sync::{Arc, Mutex};
Expand Down Expand Up @@ -40,6 +42,12 @@ impl From<RecycleBinInfo> for RecycleBinSnapshot {
}
}

#[derive(Clone, Debug, Default)]
pub struct GestureSnapshot {
pub db: Arc<GestureDb>,
pub usage: Arc<Vec<GestureUsageEntry>>,
}

#[derive(Clone)]
pub struct DashboardDataSnapshot {
pub clipboard_history: Arc<Vec<String>>,
Expand All @@ -49,6 +57,7 @@ pub struct DashboardDataSnapshot {
pub calendar: Arc<CalendarSnapshot>,
pub processes: Arc<Vec<Action>>,
pub favorites: Arc<Vec<FavEntry>>,
pub gestures: Arc<GestureSnapshot>,
pub process_error: Option<String>,
pub system_status: Option<SystemStatusSnapshot>,
pub recycle_bin: Option<RecycleBinSnapshot>,
Expand All @@ -64,6 +73,7 @@ impl Default for DashboardDataSnapshot {
calendar: Arc::new(CalendarSnapshot::default()),
processes: Arc::new(Vec::new()),
favorites: Arc::new(Vec::new()),
gestures: Arc::new(GestureSnapshot::default()),
process_error: None,
system_status: None,
recycle_bin: None,
Expand All @@ -81,6 +91,7 @@ impl DashboardDataSnapshot {
calendar: Arc::clone(&self.calendar),
processes: Arc::clone(&self.processes),
favorites: Arc::clone(&self.favorites),
gestures: Arc::clone(&self.gestures),
process_error: self.process_error.clone(),
system_status: self.system_status.clone(),
recycle_bin: self.recycle_bin.clone(),
Expand All @@ -96,6 +107,7 @@ impl DashboardDataSnapshot {
calendar: Arc::clone(&self.calendar),
processes: Arc::clone(&self.processes),
favorites: Arc::clone(&self.favorites),
gestures: Arc::clone(&self.gestures),
process_error: self.process_error.clone(),
system_status: self.system_status.clone(),
recycle_bin: self.recycle_bin.clone(),
Expand All @@ -111,6 +123,7 @@ impl DashboardDataSnapshot {
calendar: Arc::clone(&self.calendar),
processes: Arc::clone(&self.processes),
favorites: Arc::clone(&self.favorites),
gestures: Arc::clone(&self.gestures),
process_error: self.process_error.clone(),
system_status: self.system_status.clone(),
recycle_bin: self.recycle_bin.clone(),
Expand All @@ -126,6 +139,7 @@ impl DashboardDataSnapshot {
calendar: Arc::clone(&self.calendar),
processes: Arc::clone(&self.processes),
favorites: Arc::clone(&self.favorites),
gestures: Arc::clone(&self.gestures),
process_error: self.process_error.clone(),
system_status: self.system_status.clone(),
recycle_bin: self.recycle_bin.clone(),
Expand All @@ -141,6 +155,7 @@ impl DashboardDataSnapshot {
calendar: Arc::clone(&self.calendar),
processes: Arc::clone(&self.processes),
favorites: Arc::new(favorites),
gestures: Arc::clone(&self.gestures),
process_error: self.process_error.clone(),
system_status: self.system_status.clone(),
recycle_bin: self.recycle_bin.clone(),
Expand All @@ -156,6 +171,7 @@ impl DashboardDataSnapshot {
calendar: Arc::clone(&self.calendar),
processes: Arc::new(processes),
favorites: Arc::clone(&self.favorites),
gestures: Arc::clone(&self.gestures),
process_error,
system_status: self.system_status.clone(),
recycle_bin: self.recycle_bin.clone(),
Expand All @@ -171,6 +187,7 @@ impl DashboardDataSnapshot {
calendar: Arc::clone(&self.calendar),
processes: Arc::clone(&self.processes),
favorites: Arc::clone(&self.favorites),
gestures: Arc::clone(&self.gestures),
process_error: self.process_error.clone(),
system_status,
recycle_bin: self.recycle_bin.clone(),
Expand All @@ -186,6 +203,7 @@ impl DashboardDataSnapshot {
calendar: Arc::clone(&self.calendar),
processes: Arc::clone(&self.processes),
favorites: Arc::clone(&self.favorites),
gestures: Arc::clone(&self.gestures),
process_error: self.process_error.clone(),
system_status: self.system_status.clone(),
recycle_bin,
Expand All @@ -201,6 +219,26 @@ impl DashboardDataSnapshot {
calendar: Arc::new(calendar),
processes: Arc::clone(&self.processes),
favorites: Arc::clone(&self.favorites),
gestures: Arc::clone(&self.gestures),
process_error: self.process_error.clone(),
system_status: self.system_status.clone(),
recycle_bin: self.recycle_bin.clone(),
}
}

fn with_gestures(&self, db: GestureDb, usage: Vec<GestureUsageEntry>) -> Self {
Self {
clipboard_history: Arc::clone(&self.clipboard_history),
snippets: Arc::clone(&self.snippets),
notes: Arc::clone(&self.notes),
todos: Arc::clone(&self.todos),
calendar: Arc::clone(&self.calendar),
processes: Arc::clone(&self.processes),
favorites: Arc::clone(&self.favorites),
gestures: Arc::new(GestureSnapshot {
db: Arc::new(db),
usage: Arc::new(usage),
}),
process_error: self.process_error.clone(),
system_status: self.system_status.clone(),
recycle_bin: self.recycle_bin.clone(),
Expand Down Expand Up @@ -259,6 +297,7 @@ impl DashboardDataCache {
self.refresh_todos();
self.refresh_calendar();
self.refresh_favorites();
self.refresh_gestures();
self.refresh_processes(plugins);
self.refresh_system_status();
self.refresh_recycle_bin();
Expand Down Expand Up @@ -316,6 +355,14 @@ impl DashboardDataCache {
}
}

pub fn refresh_gestures(&self) {
let db = load_gestures(GESTURES_FILE).unwrap_or_default();
let usage = load_usage(GESTURES_USAGE_FILE);
if let Ok(mut state) = self.state.lock() {
state.snapshot = Arc::new(state.snapshot.with_gestures(db, usage));
}
}

pub fn maybe_refresh_processes(&self, plugins: &PluginManager, interval: Duration) {
let should_refresh = self
.state
Expand Down
181 changes: 181 additions & 0 deletions src/dashboard/widgets/gesture_cheat_sheet.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
use super::{
edit_typed_settings, gesture_focus_action, gesture_toggle_action, Widget, WidgetAction,
WidgetSettingsContext, WidgetSettingsUiResult,
};
use crate::dashboard::dashboard::{DashboardContext, WidgetActivation};
use crate::mouse_gestures::db::{format_tokens, BindingEntry, GestureEntry};
use crate::mouse_gestures::engine::DirMode;
use eframe::egui;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

fn default_count() -> usize {
5
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GestureCheatSheetConfig {
#[serde(default = "default_count")]
pub count: usize,
#[serde(default)]
pub show_disabled: bool,
}

impl Default for GestureCheatSheetConfig {
fn default() -> Self {
Self {
count: default_count(),
show_disabled: false,
}
}
}

pub struct GestureCheatSheetWidget {
cfg: GestureCheatSheetConfig,
}

impl GestureCheatSheetWidget {
pub fn new(cfg: GestureCheatSheetConfig) -> Self {
Self { cfg }
}

pub fn settings_ui(
ui: &mut egui::Ui,
value: &mut serde_json::Value,
ctx: &WidgetSettingsContext<'_>,
) -> WidgetSettingsUiResult {
edit_typed_settings(
ui,
value,
ctx,
|ui, cfg: &mut GestureCheatSheetConfig, _ctx| {
let mut changed = false;
ui.horizontal(|ui| {
ui.label("Count");
changed |= ui
.add(egui::DragValue::new(&mut cfg.count).clamp_range(1..=50))
.changed();
});
changed |= ui.checkbox(&mut cfg.show_disabled, "Show disabled").changed();
changed
},
)
}

fn primary_binding_label(bindings: &[BindingEntry]) -> String {
bindings
.iter()
.find(|binding| binding.enabled)
.or_else(|| bindings.first())
.map(|binding| binding.label.clone())
.unwrap_or_else(|| "Unbound".into())
}

fn usage_counts(
usage: &[crate::mouse_gestures::usage::GestureUsageEntry],
) -> HashMap<(String, String, DirMode), usize> {
let mut counts: HashMap<(String, String, DirMode), usize> = HashMap::new();
for entry in usage {
*counts
.entry((
entry.gesture_label.clone(),
entry.tokens.clone(),
entry.dir_mode,
))
.or_insert(0) += 1;
}
counts
}
}

impl Default for GestureCheatSheetWidget {
fn default() -> Self {
Self::new(GestureCheatSheetConfig::default())
}
}

impl Widget for GestureCheatSheetWidget {
fn render(
&mut self,
ui: &mut egui::Ui,
ctx: &DashboardContext<'_>,
_activation: WidgetActivation,
) -> Option<WidgetAction> {
let snapshot = ctx.data_cache.snapshot();
let gestures = &snapshot.gestures.db.gestures;
let usage = &snapshot.gestures.usage;
let counts = Self::usage_counts(usage);
let mut rows: Vec<(GestureEntry, usize)> = gestures
.iter()
.filter(|gesture| !gesture.bindings.is_empty())
.cloned()
.map(|gesture| {
let count = counts
.get(&(gesture.label.clone(), gesture.tokens.clone(), gesture.dir_mode))
.copied()
.unwrap_or(0);
(gesture, count)
})
.collect();

if !self.cfg.show_disabled {
rows.retain(|(gesture, _)| gesture.enabled);
}

rows.sort_by(|a, b| {
b.1.cmp(&a.1)
.then_with(|| a.0.label.cmp(&b.0.label))
});

let mut clicked = None;
let count = self.cfg.count.max(1);
if rows.is_empty() {
ui.label("No bound gestures configured.");
return None;
}

egui::Grid::new("gesture_cheat_sheet")
.striped(true)
.show(ui, |ui| {
ui.label("On");
ui.label("Gesture");
ui.label("Tokens");
ui.label("Primary binding");
ui.end_row();

for (gesture, _) in rows.into_iter().take(count) {
let mut enabled = gesture.enabled;
if ui.checkbox(&mut enabled, "").changed() {
clicked = Some(gesture_toggle_action(
&gesture.label,
&gesture.tokens,
gesture.dir_mode,
enabled,
));
}
if ui
.selectable_label(false, gesture.label.clone())
.clicked()
{
clicked = Some(gesture_focus_action(
&gesture.label,
&gesture.tokens,
gesture.dir_mode,
None,
));
}
ui.label(format_tokens(&gesture.tokens));
ui.label(Self::primary_binding_label(&gesture.bindings));
ui.end_row();
}
});

clicked
}

fn on_config_updated(&mut self, settings: &serde_json::Value) {
if let Ok(cfg) = serde_json::from_value::<GestureCheatSheetConfig>(settings.clone()) {
self.cfg = cfg;
}
}
}
Loading