Commit Graph
82 Commits
Author SHA1 Message Date
Dylan M. Taylor 6a66b95cbc Enforce Rust coding standards via Clippy (#372)
* chore(lints): enforce workspace Clippy standards

Deny cognitive_complexity (threshold 30, rust:S3776), too_many_arguments and wildcard_imports workspace-wide. Existing violations are fixed in follow-up commits.

* refactor(keymap): isolate shortcut row rendering

* test(ui): cover dock command contract cases

* fix(keymap): remove unused shortcut layout state

* refactor(media): simplify container serialization tests

* docs: document workspace lint inheritance for new crates

* refactor(text): split Knuth-Plass line costing and path extraction

Brings knuth_plass under the workspace cognitive complexity limit.

* test(font-store): split lookup test by concern
2026-10-27 13:49:38 -04:00
aiongg 92d392cff1 craft-ui: ComboBox, egui's combo box with a menu that grows back (#387)
* craft-ui: combo boxes whose menu grows back with its contents

egui's ComboBox lays its menu out in the height it had on the previous pass,
and its scroll area takes no more than that. A filtered list that shrinks (a
search with no matches) never grows back when the search is cleared
(emilk/egui#5225, #5138). craft_ui::combo::menu offers its contents the full
height on every pass; craft_ui::combo::combo draws the combo box button
around it. The Font Manager's Language, Script and Sample menus use it.

* craft-ui: combo and menu take the close behaviour

A menu with a search field that closes itself on a pick (DesignCraft's font
menu) closes on a click outside only.

* craft-ui: ComboBox, a drop-in for egui's combo box, with header menus

craft_ui::ComboBox has egui 0.37's ComboBox builder and button (same
atoms, style classes and icon, pixel-identical in a test) with a menu that
grows back. show_ui_with_header and show_rows_with_header keep a search
field in place and scroll only the list; combo::Menu is the same menu under
a field an app draws itself. clippy.toml disallows egui::ComboBox.

The Font Manager's Language menu, the keymap dialog's combo boxes and the
print dialog's printer and duplex choices use it.

* craft-ui: combo menus keep to the room on their side, and footers

A menu is never taller than the room on the side of its field it opens on,
so an unbounded height fits on screen and scrolls. The side is settled once
the menu is measured and kept while it is open, so a menu that shrinks or
grows while typing doesn't jump to the other side of its field.

Footer forms keep a row under the scrolling list. Menu::new takes the
field's Ui for its default height and frame, and Menu::open shows the menu
under a text field that keeps the focus while typing.

* craft-ui: combo menus open by the app keep their side, and take picks

Menu::open takes &mut bool: the menu clears it when it closes (close
behavior, Escape, ui.close(), the field losing the focus to anything but
the menu), so a click on a row under a focused text field is picked.

The side settles only once the menu was shown and measured, also for
menus the app opens (Popup::open_id). A footer that changes height
remeasures the menu before it is shown. The popup frame's margins come
from the popup style; only above/below alignments are tried.

* craft-ui: a resting pointer over a combo menu doesn't keep it open

The menu counts the pointer as having moved into it only on a press or
click. A footer height change also requests a repaint, in case the
discard is denied.

* craft-ui: a footer menu opens without a second sizing pass

A footer height change no longer discards the pass or forces another
sizing pass: the menu measures again on the next pass and settles its
side then. A forced sizing pass ran a search field's focus request in a
pass where the field can't take the focus, so a footer menu opened with
the search unfocused.

* craft-ui: tests use Ui::scope_id

* craft-ui: hide the measuring pass of a footer menu, cap remeasures

The pass that measures a menu before its side is settled is laid out but
drawn fully transparent, so no frame shows a side or height the menu then
leaves. A footer that changes height on every pass remeasures twice per
open, then settles.

* craft-ui: tests satisfy clippy
2026-10-27 09:21:01 -04:00
aiongg 39d134fb54 keymap ui: Assign takes a held shortcut in one step (#352)
Assign in the detail pane adds the recorded chord to the command and takes
it from the commands listed under "Currently assigned to", as one undo
step, without opening the conflict bar. ConflictMode::Keep keeps both.
A chord recorded in a row's cell still asks with the conflict bar.
2026-10-25 23:25:35 -04:00
aiongg edc41f68fa craft-menu: shrink-to-fit bar, bar-run Quit, Window list opt-out on macOS; parse the minus sign (#354)
* Add craft-menu: Mac menu-bar layout and native-menu driver

A platform-free menu model (built directly or from flat rows), the macOS
layout (app menu, Window, Help, system-key clash resolution, translatable
labels), and NativeMenu, which feeds a backend's key equivalents to egui as
key presses, runs Minimize and craft-quit's Quit, and re-syncs only when the
app's state changes. Generalised from PhotoCraft's native_menu.rs.

* craft-menu: macOS menu bar through muda

* craft-menu: clipboard keys, layout-assigned shortcuts, Hide Others and atomic rebuilds

* craft-menu: menu key equivalents run while a text field has the keys

* craft-menu: commands as CommandRef, item kinds, craft-keymap chords

Items and rows run a craft_keymap::CommandRef and take a craft_keymap::Chord
shortcut; crates/menu/src/chord.rs is gone. Native ids are the command's key,
made unique with #2, #3 when a command is shown twice. Item.checked becomes
ItemKind { Plain, Check, Radio } (radio shows as a check item on macOS).
The built-in items' commands are Builtin::{Hide, HideOthers, Minimize, Quit}.
MenuRole gains Edit, View and Other; tag_submenu finds its menu by role.
While text editing, the keys left to the field are the clipboard keys and
craft_keymap::text_editing_chords. Adds the Platforms, Document and Gate types
(data only) and MenuBar::documents.

* craft-menu: Gate::allows, the one rule for what may run from the menus

* craft-menu: Windows, Linux and web layouts, documents in Window, keymap adapter

layout(bar, Target, options) dispatches to mac_layout, desktop_layout or
web_layout. Every layout drops what the target's Platforms leave out and lists
MenuBar::documents as radio items last in the Window menu (ItemRole::Document
is gone). Windows and Linux put About in Help, Settings in Edit as
Preferences and Quit in File (Exit on Windows); the web layout has no Quit and
takes the browser's own shortcuts off the items, reporting clashes. tidy drops
empty submenus.

apply_keymap sets shortcuts and toggle check items from a craft-keymap Keymap;
apply_state sets enabled and checked per frame.

* craft-menu: the egui-drawn menu bar for Windows, Linux and the web

* craft-menu: gate, command hook and one muda handler for the native menu

- NativeMenu::set_gate: app items greyed in place while modal or recording a
  shortcut; take_clicks drops clicks the gate forbids when they are taken.
  While recording, key equivalents reach egui as plain key presses.
- NativeMenu::on_command: a hook called from the menu event handler for each
  allowed app click; take_clicks still returns every command.
- Dispatch, shared between the driver and the backend's handler
  (Backend::attach replaces Backend::set_text_editing).
- Only Cmd or Ctrl chords become key equivalents; other shortcuts are not
  shown natively (building an attributed title needs unsafe objc2 calls).
- mac: one muda event handler per process, routed to the latest install;
  mac::install_app_menu ports craft-quit's minimal app menu onto it.
- craft_quit::install_app_menu is deprecated in favor of
  craft_menu::mac::install_app_menu.

* craft-menu: the bar takes Target, README section and NOTICE credit

* craft-menu: bar tests beside the layout and native ones; only a click outside closes the menus

* craft-menu: clippy (complex types, Copy clones, elided lifetimes)

* craft-menu: keyboard access to the egui bar

A tap on Alt alone or F10 focuses the bar, Alt+letter opens a menu and
a letter in an open menu runs or opens the row with that mnemonic, as
in a Windows menu bar. Mnemonics are assigned per level from the shown
labels, underlined only while the keyboard drives the bar, and a label
without an ASCII letter shows its English letter after it ("文件(F)")
through the optional english hook. Preferred letters come from
MenuBarUi::mnemonics. A Mac target has none, and a dialog or the
shortcut recorder keeps the keys. Ported from PhotoCraft's menu_keys.rs
and menu_keys_tests.rs.

* craft-menu: one installer or the other; sort mnemonics before checking uniqueness

* craft-menu: egui 0.37

* craft-menu: text fields keep their Edit items; web names by the browser's system; sync per frame; stable check kinds; handled hook clicks

* craft-menu: sync reports a clash change on an unchanged bar

* craft-menu: shrink-to-fit bar, bar runs Quit and Minimize, mnemonic before an ellipsis documented; craft-keymap: parse the minus sign

* craft-keymap: test the arrow glyphs as keys

* craft-menu: mac::install_with and MacOptions::window_list, for apps that list their documents themselves

* craft-menu: a negative fit limit draws no bar; Window list docs name the Window menu
2026-10-25 23:10:52 -04:00
skylar taylor-barrick 4e03c031de M-egui037: document shared UI behavior and regression contracts 2026-10-25 15:31:41 +01:00
skylar taylor-barrick 253c425e2f Document migrated UI contracts and regression coverage 2026-10-25 14:26:08 +01:00
skylartaylor 0f46902e32 Reconcile egui 0.37 with current shared libraries
Preserve the reviewed shared UI tree and the frozen app dependency commit while
bringing in current main. Port the new keymap UI APIs and use verified central
fonts in its strict UI tests and snapshots.
2026-10-24 07:20:20 +02:00
aiongg 332a4456dd Merge #186: keymap: Keyboard Shortcuts dialog, named shortcut sets and the shortcut editor
# Conflicts:
#	crates/keymap/src/lib.rs
2026-10-24 02:29:57 +00:00
aiongg 135981286f keymap: a layout switch forgets what letter positions typed, and Layout::forget_letters does so on request 2026-10-23 08:03:27 +00:00
aiongg 5050d5dead Merge remote-tracking branch 'ao/craft-keymap' into keymap-layout-rule 2026-10-23 08:02:46 +00:00
aiongg fe4a4450f7 keymap: compile Keymap::twins_in only with the ui feature, its one user 2026-10-23 07:28:24 +00:00
aiongg 0ccb6a9304 keymap: ForeignSet is non_exhaustive and built with ForeignSet::new, with_name and with_truncated 2026-10-23 07:19:55 +00:00
aiongg b43a39df65 keymap ui: the failed-change test sets the import's truncated count 2026-10-21 18:14:58 +00:00
aiongg 3c8c3309a0 Merge branch 'craft-keymap' into keymap-ui 2026-10-21 18:14:23 +00:00
aiongg 8ce872938f keymap: a failed import change keeps its shortcuts when a later change frees room
The cleanup after an import could remove a chord from a command whose own change had failed, once a later change freed an overrides entry. That command now keeps its bindings, and the change that would have taken its chord is dropped and reported, as when the removal itself fails.
2026-10-21 18:14:21 +00:00
aiongg 315af428c7 keymap: a key whose Latin layout character is no letter runs no letter binding
The suite's egui reports, per key pressed, the character it types on the
Latin layout shortcuts follow (InputState::latin_layout_char). A letter
makes the press that letter; any other character means the key has no
Latin letter, so egui's position fallback no longer turns it into one: on
Dvorak + Russian the key that types й is Dvorak's ', not Ctrl+Q. Without a
report (crates.io egui) the key's position still decides.
2026-10-21 18:05:18 +00:00
aiongg cec769a889 keymap: on layouts that are not Latin, take the letter the toolkit reports
The suite's egui-winit reports a letter key on a layout that is not Latin
as the Latin layout's letter, so on Dvorak + Russian the с key is
Dvorak's J. resolve takes that letter before the key's position; with
crates.io egui the letter is the position, as before.
2026-10-21 17:23:16 +00:00
aiongg cdc3b45496 keymap: take whether the layout is Latin from egui when it reports it
The suite's egui reports it from egui-winit on Linux. A report sets the
layout's Latin-ness; frames without one keep learning from typing. On
crates.io egui a private fallback trait reports nothing.
2026-10-21 06:57:03 +00:00
aiongg 1a0e7e7bbf keymap: learn nothing from presses while right Alt is held
egui-winit reports AltGr with neither Ctrl nor Alt, so AltGr text read as plain
typing: German AltGr+M (µ) marked the layout as not Latin and Ctrl+Z resolved
by position as Ctrl+Y. µ, ª and º no longer count as a sign of another script.
2026-10-21 06:08:30 +00:00
aiongg 9c75eda598 keymap: block the position fallback only at positions seen typing something else
AZERTY's Ctrl+Shift+E works before an e has been typed.
2026-10-21 05:49:41 +00:00
aiongg d0acf36588 keymap: Shift presses egui reports as their position run nothing on moved layouts
Dvorak's ⇧, types <, which egui has no key for, so egui reports the
press as W and Ctrl+Shift+, ran Ctrl+Shift+W. Once typing shows the
layout's letters are not on QWERTY positions, such a press resolves to
nothing unless the position is known to type its own letter.
2026-10-21 05:47:51 +00:00
aiongg 86248331bb keymap: read presses by the keyboard layout
Matching, the recorder and labels share one rule (craft-libs#198, track 4):
letters by the layout's Latin letter, or by position on layouts that are not
Latin; digits as typed, else by position; punctuation as typed, without the
Shift the layout needs for it. Layout holds whether the layout is Latin and
its characters at punctuation positions, reported by the platform or learned
from typing. KeyInput holds what the toolkit reports for one press, so a
toolkit that knows the virtual-key code, the macOS command table or the
Shift level can pass it on.
2026-10-21 04:57:06 +00:00
aiongg f9a65e813f keymap ui: the chip-click test uses the node's rect as is 2026-10-20 21:42:25 +00:00
aiongg 5a8f119a70 keymap ui: test that one pointer click on another row's chip records 2026-10-20 21:30:40 +00:00
aiongg 1aef62823a keymap ui: no command id in the row tooltip; say who draws the title
The detail pane already shows the command id. Options::title is drawn
by show(); with ui() the host's container draws its own title.
2026-10-20 21:06:07 +00:00
aiongg a03f6ab0c3 keymap ui: app errors in the dialog, and the reader's dropped entries
DialogState::show_error shows an app's message under the table until the
user dismisses it (a new Dismiss button on every message) or the next
action replaces it. ForeignSet::truncated carries the entries the app's
reader dropped; plan counts them in ImportReport::truncated, which the
import review shows.
2026-10-20 21:05:56 +00:00
aiongg 636fc8b850 keymap: import-only paths on Bindable
Bindable::import_paths (with_import_path) are read by import matching
like menu paths, and make no areas or groups in the dialog, so a tool's
["Tools", label] path no longer shows a Tools Menu entry.
2026-10-20 20:59:43 +00:00
aiongg 02ba09ee9a keymap ui: Options::visible hides catalog entries from the table
Hidden entries keep their bindings and still count in conflicts; they are
left out of the rows, search and the Show entries.
2026-10-20 20:53:13 +00:00
aiongg 0bff6f4c96 keymap ui: commands that share a default key are no conflict in the dialog
The dialog's conflict count, Conflicts filter and row warnings leave out
pairs whose commands both have the chord among their defaults, as
Keymap::clashing does.
2026-10-20 20:48:54 +00:00
aiongg d9b1a89950 Merge branch 'craft-keymap' into keymap-ui
# Conflicts:
#	.github/workflows/keymap.yml
#	crates/keymap/README.md
#	crates/keymap/src/egui.rs
2026-10-20 16:01:22 +00:00
aiongg b778a764af keymap: format the profiles test 2026-10-20 15:52:45 +00:00
aiongg f501cae73b keymap: settle the public API before release
Outcome, ConflictMode, ProfileError and Options are non_exhaustive, with Options::with for
options built in one expression. Profiles keeps its fields private behind active() and sets(),
lists the sets loading dropped in problems(), and stores a format version (a store without one
reads as version 1).
2026-10-20 15:49:51 +00:00
aiongg 60daca3770 keymap: name the selected row to screen readers, announce conflicts and messages, and make group headers and key caps Tab stops 2026-10-20 15:46:56 +00:00
aiongg 9aaf9dfd38 keymap: format, and keep the dispatcher's Default clear of clippy's derive lint 2026-10-20 15:46:00 +00:00
aiongg 6d96b4f684 keymap: mark public enums and report structs that will grow non_exhaustive 2026-10-20 15:29:50 +00:00
aiongg ad79cbcf31 keymap: translate shortcut parse errors and give translators whole sentences with placeholders 2026-10-20 15:29:42 +00:00
aiongg 501e2cf1fc keymap: while typing, text-editing chords go to the text field unless an active context binds them 2026-10-20 15:21:54 +00:00
aiongg 423524c1bc keymap: clipboard chords with more modifiers run their bindings with clipboard events off 2026-10-20 15:18:05 +00:00
aiongg 5160611bad keymap: while typing, a Ctrl+Alt press runs unless it typed text in the frame 2026-10-20 15:11:46 +00:00
aiongg 79747aeaae keymap: remove the Text event a bound press typed, also when the web build sends it first 2026-10-20 15:11:21 +00:00
aiongg fd2386b2ba keymap: hold the set row, table and edit actions while a prompt or the import review is open
Switching sets closes an open prompt, deleting a set reloads only when it was the active one, and
deleting the last set with no built-ins loads no overrides.
2026-10-20 15:10:58 +00:00
aiongg 339aaa41df keymap: show that an app with sets stores only Profiles and loads the active set 2026-10-20 15:09:14 +00:00
aiongg f4b04e4ad1 keymap: a double press needs both presses in the same panel 2026-10-20 15:09:01 +00:00
aiongg 0039a5c866 keymap: report Changed after a Cancel that undid edits, so apps rebuild their menus 2026-10-20 15:08:46 +00:00
aiongg 056a709d0f keymap: refuse scope names with outer whitespace, which binding text trims 2026-10-20 15:08:43 +00:00
aiongg 2abdcd53ab keymap: give Tab back to focus navigation after the New Shortcut field or Search by key records 2026-10-20 15:07:36 +00:00
aiongg 29ee24ab51 Merge branch 'craft-keymap' into keymap-ui 2026-10-20 06:32:28 +00:00
aiongg 9b0182a3e0 keymap: a held web clipboard key with repeats off leaves its clipboard events in place 2026-10-20 04:18:35 +00:00
aiongg 4674ab7c85 keymap: use US spelling in the shortcuts dialog 2026-10-20 03:28:24 +00:00
aiongg cf9e10a90a Merge branch 'craft-keymap' into keymap-ui
# Conflicts:
#	crates/keymap/README.md
#	crates/keymap/src/lib.rs
2026-10-20 03:26:15 +00:00