From 1564a77e5feb27f6d180ecbbd70e75a2b22464bd Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Thu, 5 Feb 2026 10:34:58 -0700 Subject: [PATCH] Shortcut design updates and fix escape behavior --- src/main.rs | 246 +++++++++++++++++++++----------------------- src/terminal_box.rs | 6 +- 2 files changed, 117 insertions(+), 135 deletions(-) diff --git a/src/main.rs b/src/main.rs index 8897b33..a60157a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,9 +4,9 @@ use alacritty_terminal::tty::Options; use alacritty_terminal::{event::Event as TermEvent, term, term::color::Colors as TermColors, tty}; use cosmic::iced::clipboard::dnd::DndAction; +use cosmic::iced_core::keyboard::key::Named; use cosmic::widget::menu::action::MenuAction; use cosmic::widget::menu::key_bind::KeyBind; -use cosmic::iced_core::keyboard::key::Named; use cosmic::{ Application, ApplicationExt, Element, action, app::{Core, Settings, Task, context_drawer}, @@ -368,7 +368,6 @@ pub enum Message { FindNext, FindPrevious, FindSearchValueChanged(String), - KeyboardShortcuts(bool), MiddleClick(pane_grid::Pane, Option), FocusFollowMouse(bool), Key(Modifiers, Key), @@ -440,6 +439,7 @@ pub enum Message { pub enum ContextPage { About, ColorSchemes(ColorSchemeKind), + KeyboardShortcuts, Profiles, Settings, #[cfg(feature = "password_manager")] @@ -495,7 +495,6 @@ pub struct App { color_scheme_tab_model: widget::segmented_button::SingleSelectModel, profile_expanded: Option, show_advanced_font_settings: bool, - show_keyboard_shortcuts: bool, shortcut_capture: Option, shortcut_conflict: Option, shortcut_conflict_overlay_restore: Option, @@ -574,10 +573,9 @@ impl App { self.config.shortcuts_custom = self.shortcuts_config.custom.clone(); match &self.config_handler { Some(config_handler) => { - if let Err(err) = config_handler.set( - "shortcuts_custom", - &self.config.shortcuts_custom, - ) { + if let Err(err) = + config_handler.set("shortcuts_custom", &self.config.shortcuts_custom) + { log::warn!("failed to save shortcuts custom config: {}", err); } } @@ -946,6 +944,88 @@ impl App { widget::settings::view_column(sections).into() } + fn keyboard_shortcuts(&self) -> Element<'_, Message> { + let cosmic_theme::Spacing { + space_xxs, + space_xs, + space_m, + .. + } = self.core().system_theme().cosmic().spacing; + + let pad_m = [space_xxs, space_m]; + let div_m = 16; + let pad_l = [space_xxs, space_m + 32]; + let div_l = div_m + 32; + + let mut groups = Vec::new(); + for group in shortcuts::shortcut_groups() { + let mut list = widget::list::list_column(); + + for action in group.actions { + let bindings = self.shortcuts_config.bindings_for_action(action); + + list = list.list_item_padding(pad_m); + list = list.add( + widget::settings::item::builder(shortcuts::action_label(action)).control( + widget::button::custom(icon_cache_get("list-add-symbolic", 16)) + .class(style::Button::Icon) + .on_press(Message::ShortcutCaptureStart(action)), + ), + ); + list = list.divider_padding(div_m); + + if bindings.is_empty() { + list = list.list_item_padding(pad_l); + list = list.add(widget::text::body(fl!("no-shortcuts"))); + list = list.divider_padding(div_l); + } else { + for resolved in bindings { + list = list.list_item_padding(pad_l); + list = list.add( + widget::settings::item::builder(shortcuts::binding_display( + &resolved.binding, + )) + .control( + widget::button::custom(icon_cache_get("edit-delete-symbolic", 16)) + .class(style::Button::Icon) + .on_press(Message::ShortcutRemove( + resolved.binding.clone(), + resolved.source, + )), + ), + ); + list = list.divider_padding(div_l); + } + } + + if self.shortcut_capture == Some(action) { + list = list.list_item_padding(pad_l); + list = list.add( + widget::row::with_children(vec![ + widget::text::body(fl!("shortcut-capture-hint")).into(), + widget::horizontal_space().into(), + widget::button::standard(fl!("cancel")) + .on_press(Message::ShortcutCaptureCancel) + .into(), + ]) + .spacing(space_xxs), + ); + list = list.divider_padding(div_l); + } + } + + groups.push( + widget::settings::section::with_column(list) + .title(group.title) + .into(), + ); + } + + widget::column::with_children(groups) + .spacing(space_xs) + .into() + } + fn profiles(&self) -> Element<'_, Message> { let cosmic_theme::Spacing { space_s, @@ -1143,10 +1223,6 @@ impl App { } fn settings(&self) -> Element<'_, Message> { - let cosmic_theme::Spacing { - space_xxs, space_xs, .. - } = self.core().system_theme().cosmic().spacing; - let app_theme_selected = match self.config.app_theme { AppTheme::Dark => 1, AppTheme::Light => 2, @@ -1327,117 +1403,16 @@ impl App { .toggler(self.config.focus_follow_mouse, Message::FocusFollowMouse), ); - let mut shortcuts_section = widget::settings::section() + let shortcuts_section = widget::settings::section() .title(fl!("keyboard-shortcuts")) .add( widget::settings::item::builder(fl!("customize-shortcuts")).control( - if self.show_keyboard_shortcuts { - widget::button::custom(icon_cache_get("go-up-symbolic", 16)) - .on_press(Message::KeyboardShortcuts(false)) - } else { - widget::button::custom(icon_cache_get("go-down-symbolic", 16)) - .on_press(Message::KeyboardShortcuts(true)) - } - .class(style::Button::Icon), + widget::button::custom(icon_cache_get("go-next-symbolic", 16)) + .on_press(Message::ToggleContextPage(ContextPage::KeyboardShortcuts)) + .class(style::Button::Icon), ), ); - if self.show_keyboard_shortcuts { - let shortcuts_content = || { - let mut groups = Vec::new(); - - for group in shortcuts::shortcut_groups() { - let mut group_section = widget::settings::section().title(group.title); - - for action in group.actions { - let bindings = self.shortcuts_config.bindings_for_action(action); - let mut rows: Vec> = Vec::new(); - - if self.shortcut_capture == Some(action) { - rows.push( - widget::row::with_children(vec![ - widget::text::body(fl!("shortcut-capture-hint")) - .into(), - widget::horizontal_space().into(), - widget::button::standard(fl!("cancel")) - .on_press(Message::ShortcutCaptureCancel) - .into(), - ]) - .spacing(space_xxs) - .into(), - ); - } - - if bindings.is_empty() { - rows.push(widget::text::body(fl!("no-shortcuts")).into()); - } else { - for resolved in bindings { - let binding_text = widget::text::body( - shortcuts::binding_display(&resolved.binding), - ) - .width(Length::Fill) - .align_x(Alignment::End); - let binding_chip = widget::container( - widget::row::with_children(vec![ - binding_text.into(), - widget::button::custom(icon_cache_get( - "edit-delete-symbolic", - 16, - )) - .class(style::Button::Icon) - .on_press(Message::ShortcutRemove( - resolved.binding.clone(), - resolved.source, - )) - .into(), - ]) - .spacing(space_xxs) - .align_y(Alignment::Center) - .width(Length::Fill), - ) - .padding(Padding::new(6.0)) - .class(style::Container::Background) - .width(Length::Fill); - rows.push(binding_chip.into()); - } - } - - rows.push( - widget::row::with_children(vec![ - widget::horizontal_space().into(), - widget::button::standard(fl!("add-shortcut")) - .on_press(Message::ShortcutCaptureStart(action)) - .into(), - ]) - .into(), - ); - - let bindings_column = widget::column::with_children(rows) - .spacing(space_xxs) - .width(Length::Fill); - - group_section = group_section.add( - widget::settings::item::builder(shortcuts::action_label(action)) - .control(bindings_column), - ); - } - - groups.push(group_section.into()); - } - - widget::column::with_children(groups).spacing(space_xs) - }; - - let padding = Padding { - top: 0.0, - bottom: 0.0, - left: 12.0, - right: 12.0, - }; - shortcuts_section = - shortcuts_section.add(widget::container(shortcuts_content()).padding(padding)); - } - let advanced_section = widget::settings::section().title(fl!("advanced")).add( widget::settings::item::builder(fl!("show-headerbar")) .description(fl!("show-header-description")) @@ -1784,7 +1759,6 @@ impl Application for App { color_scheme_tab_model: widget::segmented_button::Model::default(), profile_expanded: None, show_advanced_font_settings: false, - show_keyboard_shortcuts: false, shortcut_capture: None, shortcut_conflict: None, shortcut_conflict_overlay_restore: None, @@ -1802,12 +1776,20 @@ impl Application for App { //TODO: currently the first escape unfocuses, and the second calls this function fn on_escape(&mut self) -> Task { if self.core.window.show_context { - // Close context drawer if open - self.core.window.show_context = false; - #[cfg(feature = "password_manager")] - if self.context_page == ContextPage::PasswordManager { - self.password_mgr.clear(); + // Handle keyboard shortcut page escape + if let ContextPage::KeyboardShortcuts = self.context_page { + // Cancel shortcut capture + if self.shortcut_capture.take().is_some() { + return Task::none(); + } + + // Cancel shortcut conflict dialog + if self.shortcut_conflict.take().is_some() { + return Task::none(); + } } + + return self.update(Message::ToggleContextPage(self.context_page)); } else if self.find { // Close find if open self.find = false; @@ -2069,8 +2051,7 @@ impl Application for App { } Message::Config(config) => { if config != self.config { - let shortcuts_changed = - config.shortcuts_custom != self.config.shortcuts_custom; + let shortcuts_changed = config.shortcuts_custom != self.config.shortcuts_custom; log::info!("update config"); //TODO: update syntax theme by clearing tabs, only if needed self.config = config; @@ -2308,12 +2289,6 @@ impl Application for App { Message::FindSearchValueChanged(value) => { self.find_search_value = value; } - Message::KeyboardShortcuts(show) => { - self.show_keyboard_shortcuts = show; - if !show { - self.shortcut_capture = None; - } - } Message::MiddleClick(pane, entity_opt) => { self.pane_model.set_focus(pane); return Task::batch([ @@ -2975,6 +2950,12 @@ impl Application for App { }); } + if let ContextPage::KeyboardShortcuts = context_page { + self.shortcut_capture = None; + self.shortcut_conflict = None; + self.shortcut_conflict_overlay_restore = None; + } + #[cfg(feature = "password_manager")] if ContextPage::PasswordManager == context_page { self.password_mgr.pane = Some(self.pane_model.focused()); @@ -3045,6 +3026,11 @@ impl Application for App { Message::ToggleContextPage(ContextPage::ColorSchemes(color_scheme_kind)), ) .title(fl!("color-schemes")), + ContextPage::KeyboardShortcuts => context_drawer::context_drawer( + self.keyboard_shortcuts(), + Message::ToggleContextPage(ContextPage::KeyboardShortcuts), + ) + .title(fl!("keyboard-shortcuts")), ContextPage::Profiles => context_drawer::context_drawer( self.profiles(), Message::ToggleContextPage(ContextPage::Profiles), diff --git a/src/terminal_box.rs b/src/terminal_box.rs index 7325bb0..3dc1b8d 100644 --- a/src/terminal_box.rs +++ b/src/terminal_box.rs @@ -46,11 +46,7 @@ use std::{ }; use crate::{ - Action, - Terminal, - TerminalScroll, - menu::MenuState, - mouse_reporter::MouseReporter, + Action, Terminal, TerminalScroll, menu::MenuState, mouse_reporter::MouseReporter, terminal::Metadata, };