//! Keyboard Shortcuts Reference window — fills the entire OS window. use crate::app::Message; use iced::widget::{column, container, row, scrollable, text, Space}; use iced::{Background, Element, Theme}; use std::borrow::Cow; /// Display name of the primary accelerator modifier on this platform. /// Mirrors the runtime binding in `app::view`, which uses /// `Modifiers::command()` — the Cmd key on macOS, Ctrl elsewhere — so the /// reference window shows the key the user actually presses. #[cfg(target_os = "macos")] const MOD: &str = "Cmd"; #[cfg(not(target_os = "macos"))] const MOD: &str = "Ctrl"; fn muted_style(theme: &Theme) -> iced::widget::text::Style { iced::widget::text::Style { color: Some(theme.palette().background.base.text.scale_alpha(0.68)), } } fn primary_style(theme: &Theme) -> iced::widget::text::Style { iced::widget::text::Style { color: Some(theme.palette().primary.base.color), } } fn hdivider<'a>(width: iced::Length) -> Element<'a, Message> { container(Space::new().width(width).height(1)) .width(width) .height(1) .style(|theme: &Theme| container::Style { background: Some(Background::Color( theme.palette().background.neutral.color, )), ..Default::default() }) .into() } fn shortcut_row<'a>( key: impl Into>, action: &'static str, ) -> Element<'a, Message> { row![ text(key.into()) .size(11) .style(primary_style) .font(iced::Font::MONOSPACE) .width(160), text(action).size(11), ] .spacing(8) .align_y(iced::Center) .padding([2, 0]) .into() } fn section<'a>(title: impl Into>) -> Element<'a, Message> { container(text(title.into()).size(11).style(muted_style)) .padding(iced::Padding { top: 6.0, right: 0.0, bottom: 2.0, left: 0.0, }) .into() } pub fn view_window<'a>( overrides: &'a rustc_hash::FxHashMap, sizing: crate::ui::modal::ModalSizing, ) -> Element<'a, Message> { // ── Toolbar ─────────────────────────────────────────────────────────── let toolbar = container( row![ text("Type SHORTCUTS SET to add custom shortcuts.") .size(10) .style(muted_style), ] .align_y(iced::Center), ) .style(|theme: &Theme| container::Style { background: Some(Background::Color( theme.palette().background.weakest.color, )), ..Default::default() }) .width(sizing.width) .padding([5, 10]); // ── Shortcut entries ────────────────────────────────────────────────── let mut rows: Vec> = vec![ section("── Function Keys ──────────────────────────────────────"), shortcut_row("F3", "Toggle Object Snap"), shortcut_row("F7", "Toggle Grid"), shortcut_row("F8", "Toggle Ortho"), shortcut_row("F9", "Toggle Grid Snap"), shortcut_row("F10", "Toggle Polar Tracking"), shortcut_row("F11", "Toggle Object Snap Tracking"), shortcut_row("F12", "Toggle Dynamic Input"), section(format!( "── {MOD} Shortcuts ──────────────────────────────────────" )), shortcut_row(format!("{MOD}+N"), "New Drawing"), shortcut_row(format!("{MOD}+O"), "Open File"), shortcut_row(format!("{MOD}+S"), "Save"), shortcut_row(format!("{MOD}+Shift+S"), "Save As"), shortcut_row(format!("{MOD}+Z"), "Undo"), shortcut_row(format!("{MOD}+Shift+Z / {MOD}+Y"), "Redo"), shortcut_row(format!("{MOD}+F / {MOD}+H"), "Find and Replace"), shortcut_row(format!("{MOD}+C"), "Copy to Clipboard"), shortcut_row(format!("{MOD}+X"), "Cut to Clipboard"), shortcut_row(format!("{MOD}+V"), "Paste from Clipboard"), section("── Other Keys ──────────────────────────────────────────"), shortcut_row("Enter / Space", "Finalize command / Repeat last"), shortcut_row("Escape", "Cancel active command"), shortcut_row("Delete", "Delete selected entities"), shortcut_row("↑ / ↓", "Command history navigation"), ]; // Custom overrides section rows.push(section( "── Custom Overrides (SHORTCUTS SET) ──────────────────", )); if overrides.is_empty() { rows.push( text(" (none — use: SHORTCUTS SET )") .size(11) .style(muted_style) .into(), ); } else { let mut sorted: Vec<_> = overrides.iter().collect(); sorted.sort_by_key(|(k, _)| k.as_str()); for (key, cmd) in sorted { rows.push( row![ text(key.as_str()) .size(11) .style(primary_style) .font(iced::Font::MONOSPACE) .width(160), text(cmd.as_str()).size(11), ] .spacing(8) .align_y(iced::Center) .padding([2, 0]) .into(), ); } } // ── Section headers styled separately ──────────────────────────────── let content = scrollable(column(rows).spacing(3).padding([12, 16])) .width(sizing.width) .height(sizing.height); // ── Header row with accent ──────────────────────────────────────────── let header = container( row![ text("Key").size(10).style(primary_style).width(160), text("Action").size(10).style(primary_style), ] .spacing(8) .padding([4, 16]), ) .style(|theme: &Theme| container::Style { background: Some(Background::Color( theme.palette().primary.weak.color, )), ..Default::default() }) .width(sizing.width); container( column![ toolbar, hdivider(sizing.width), header, hdivider(sizing.width), content ] .spacing(0), ) .style(|theme: &Theme| container::Style { background: Some(Background::Color( theme.palette().background.base.color, )), ..Default::default() }) .width(sizing.width) .height(sizing.height) .into() }