Commit Graph
704 Commits
Author SHA1 Message Date
aiongg ee4103fe78 Merge pull request #337 from aiongg/lang-1-text
craft-text: hyphenation, line-break segmentation, bidi and line breaking
2026-10-25 23:08:26 -04:00
aiongg 1e6f127844 docking: a drop beside a too-narrow frame at its row's edge takes room from the other side (#353) 2026-10-25 22:13:01 -04:00
aiongg 9f89fe8f6d Add craft-menu: one menu definition, native on macOS and an egui bar on Windows, Linux and web (#66)
* 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
2026-10-25 21:59:22 -04:00
aiongg f85b11b8e4 docking: AppKit moves a native window for its bar drag on macOS (#341)
* docking: AppKit moves a native window for its bar drag on macOS

Moving a native window with OuterPosition every frame jitters on macOS:
the window's reported place and the pointer events disagree about where
the window is. On macOS the bar press (or the start of a drag of a
window's only tab or group) now sends ViewportCommand::StartDrag, which
winit turns into performWindowDragWithEvent, and the dock area sends no
OuterPosition for that drag.

egui keeps running frames during the move, but the window's reported
place lags it, so each frame takes the window's place as AppKit's
cursor (NSEvent::mouseLocation) less the pressed point. Drop targets,
the drag fade, cross-window routing and the drop use that point. The
drag ends on the first frame where NSEvent::pressedMouseButtons or egui
reports the release, and is resolved once; a desktop drop records
cursor less grab. Ctrl/Cmd is read from AppKit's modifier flags, since
AppKit sends no key events during the move, and Escape does not cancel
the move. A press released within the click distance docks nothing.
Windows, X11 and Wayland keep their paths.

The window system seam (WindowSystem, formerly DesktopCursor) tells the
cursor, the button, the modifiers and whether it moves windows.
NativeHost models AppKit's move (moves_windows, held): the window
follows the cursor at the grab and reports its place late. The
app-driven cursor path from #325 and its stale-pointer model are
removed, since macOS no longer takes that path.

* docs: a cancel during an AppKit bar drag leaves the window moving
2026-10-25 21:58:02 -04:00
aiongg f4c6718d68 craft-text: a locale's script must match the hyphenation patterns' script 2026-10-26 01:54:26 +00:00
skylar taylor-barrick cb53221767 fix(print): stop relying on inflate error output; bump miniz_oxide to 0.9 (#348)
* fix(print): do not treat early inflate errors as partial output

* chore(deps): bump miniz_oxide to 0.9
2026-10-25 20:37:49 -04:00
Dylan M. Taylor e3bcd5657f update(install): retry a busy disk-image attach for about a minute (#349) 2026-10-25 19:56:47 -04:00
Dylan M. Taylor c5a345e472 Merge pull request #347 from storytold/chore/deps-safe-refresh
chore(deps): safe dependency refresh (serde_json, fixed_decimal, roxmltree, miniz_oxide)
2026-10-25 19:03:41 -04:00
skylar 8e42b9ff4e fix(print): keep miniz_oxide at 0.8 2026-10-25 16:52:57 -06:00
skylar ce020c5811 chore(deps): bump roxmltree, miniz_oxide, serde_json, fixed_decimal
craft-print: roxmltree 0.20 -> 0.21 (locked 0.21.2, fixes a stack
overflow on deeply nested XML) and miniz_oxide 0.8 -> 0.9.
Lockfile only: serde_json 1.0.154, fixed_decimal 0.7.3.
2026-10-25 16:35:43 -06:00
Dylan M. Taylor 495e5971bc Merge pull request #344 from dylanmtaylor/fix/update-prompt-glyphs
fix(update): draw the prompt's close mark and version pill without glyphs a font may lack
2026-10-25 16:36:37 -04:00
Dylan M. Taylor 47edfe3255 fix(update): use egui::Role for the close button's accessibility info 2026-10-25 16:31:38 -04:00
Dylan M. Taylor 4f6fe25d77 fix(update): draw the prompt's close mark and version pill without glyphs a font may lack
The close button was a ✕ (U+2715) text glyph and the version pill used →, neither of which the bundled faces are guaranteed to have; apps that panic on a missing glyph took the whole prompt down. The close mark is two painted strokes, the pill reads 'v0.1.0 to 0.2.0', and a test asserts the prompt paints only ASCII text.

Fixes #343
2026-10-25 16:31:12 -04:00
aiongg a04bb9da2a craft-text: locale codes with a script or year subtag; Slovak data through feature unification 2026-10-25 19:37:05 +00:00
aiongg e3b9e239c5 docking: a window-edge drop beside an iconic column highlights only the edge (#336)
A group dragged to the window edge where an iconic column sits showed the
edge line and an outline around the column. The edge line is drawn just
inside the window, so it lay in the column's body, and paint_drop_line
outlines the iconic column a line lies in. That outline is for drops into
the column; a window-edge drop docks beside it, so it now shows the line
alone.
2026-10-25 15:20:32 -04:00
aiongg acf6f79739 craft-text: hyphenation, line-break segmentation, bidi and line breaking 2026-10-25 19:08:20 +00:00
Dylan M. Taylor db19c373c8 Merge pull request #333 from dylanmtaylor/fix/updater-pending-close
fix(update): keep the close request pending while the app holds unsaved work
2026-10-25 15:03:02 -04:00
aiongg 271594c081 window: the title bar's empty area takes no keyboard focus (#334) 2026-10-25 14:36:07 -04:00
Dylan M. Taylor 05c9f70e6e fix(update): keep the close request pending while the app holds unsaved work
A user can dismiss the prompt while the update installs and then edit. The close request is now kept until the app can close, so a close the user cancels is asked again once the work is saved. The installed message is set when the install finishes.
2026-10-25 14:05:40 -04:00
Dylan M. Taylor 5b0f3c572a Merge pull request #332 from dylanmtaylor/feat/updater-can-start
feat(update): Updater::show_if holds the install back while the app has unsaved work
2026-10-25 14:03:27 -04:00
aiongg c13668493c docking: test send_keys from a native input hook (#331) 2026-10-25 14:02:27 -04:00
Dylan M. Taylor 12f199b2b5 feat(update): Updater::show_if holds the install back while the app has unsaved work
The restart helper is scheduled before the app closes, so a close the user then cancels would leave it waiting for an exit that never comes. Apps pass whether it is safe to start.
2026-10-25 13:55:49 -04:00
Dylan M. Taylor b9ba47f589 Merge pull request #330 from storytold/fix/zip9-windows
fix(update): zip 9's entry name is fallible (Windows build)
2026-10-25 13:18:52 -04:00
Dylan M. Taylor f3540a1327 fix(update): check the executable's name before the app quits for an update
windows_precheck now refuses an executable that isn't the app's own, as the helper does, so the error is shown while the app can still show it. check_helper keeps the same check as the final guard.
2026-10-25 13:18:35 -04:00
Dylan M. Taylor 51da82901c test(update): a standalone executable updates from the portable zip
Detection now treats any executable as a standalone install, so the helper's test no longer expects a refusal once portable.txt is gone.
2026-10-25 13:12:18 -04:00
Dylan M. Taylor 8b7e8a13de fix(update): the update helper only restarts the app's own executable
check_helper accepted any existing file as a standalone install, so a differently named executable passed (the Windows test caught it). The program's name must be one of the app's own.
2026-10-25 13:05:31 -04:00
Dylan M. Taylor dbc86fdbf3 fix(update): zip 9's entry name is fallible
Fixes the Windows build after the move to zip 9: an entry whose name can't be read counts as unsafe.
2026-10-25 12:59:00 -04:00
Dylan M. Taylor a0916808f7 Merge pull request #268 from storytold/migration/egui-037-20261024
Migrate shared UI to egui 0.37 and preserve current docking and printing fixes
2026-10-25 12:57:14 -04:00
Dylan M. Taylor 9eba2520da fix(update): clean clippy on macOS and Windows
The macOS module imported staging_dir without using it, and the Windows helper never used its app argument.
2026-10-25 12:55:32 -04:00
Dylan M. Taylor 6ff2a458b4 Merge remote-tracking branch 'storytold/main' into migration/egui-037-20261024 2026-10-25 12:54:10 -04:00
Dylan M. Taylor 1c59a349f0 Merge pull request #327 from dylanmtaylor/fix/update-deny
fix(update): drop dirs, move to zip 9, add an Updater for egui apps
2026-10-25 12:50:44 -04:00
Dylan M. Taylor 13ceaf2f31 fix(update): keep Windows known-folder locations and never start a second install
Local AppData comes from the known-folder API (a redirected profile is honoured), with the environment variable as the fallback. Once an update is installed and waits for the app to exit, no check, offer or install starts, so a cancelled close cannot relaunch the app twice.
2026-10-25 12:41:38 -04:00
skylar taylor-barrick 42e30cd5b3 Reconcile native docking fixes with egui 0.37 input routing 2026-10-25 17:41:23 +01:00
Dylan M. Taylor 9285cbcd1f Merge pull request #177 from storytold/dependabot/github_actions/actions-e91bde37dc
ci(deps): bump the actions group across 1 directory with 2 updates
2026-10-25 12:30:06 -04:00
skylar taylor-barrick f71bccb65e Merge latest docking preview changes into egui 0.37 migration 2026-10-25 17:24:24 +01:00
aiongg 5bbe9f6254 docking: menu key equivalents reach the text field of a native panel window (#318)
* docking: menu key equivalents reach the focused text field of a native window

On macOS the menu bar takes key equivalents such as Cmd+C and Cmd+V before
the key window sees them, and an app's menu glue reports them in the main
window's input, where they ran on the document even while a text field in a
floating panel's own window had focus. Each native window notes its text
focus as it draws. While the window with the keyboard has a focused field,
the main window's input hands that window the keys a text field acts on
(Cmd+C/X/V/A, Cmd+Z and Shift+Cmd+Z, Cmd with the arrows, Cmd+Backspace and
Cmd+Delete, and Copy, Cut and Paste events); Cmd+C and Cmd+X arrive as Copy
and Cut, and Cmd+V asks the window for the clipboard text. Other command
keys stay with the main window, and nothing moves while input is blocked.

typing_window and send_keys expose the same routing to menus that run
commands themselves.

* docking: typing_window answers between frames with the last frame's focus
2026-10-25 12:23:34 -04:00
aiongg 8b19c44267 docking: a dragged window fades only while it highlights a drop target (#321)
* docking: a dragged native window fades only while a drop target is highlighted

The dragged window stays opaque over empty space and goes to 75% only
while the bar, only-tab or only-tab-row drag shows a drop preview.
paint_group_drag reports whether it highlighted a target; end_cross keeps
that for the next frame, since the dragged window sets its opacity before
the preview is drawn.

* docking: a dragged in-window floating window fades only while a drop target is highlighted

The rule for native windows covers in-window floating windows too. A
title bar drag draws its window at DRAG_OPACITY (75%) while the drag
highlights a drop target, and opaque over empty space and after a drop,
Escape or focus loss. finish_group_drag keeps paint_group_drag's result
for the next frame, since the window draws before the preview.
FloatingFrame takes an opacity for the whole frame, contents included.

* docking: a title-bar drag also fades over a preview the host draws
2026-10-25 12:21:53 -04:00
Dylan M. Taylor 9d2bea410e feat(update): Updater::offer, and dismissing a running check keeps it quiet 2026-10-25 12:18:20 -04:00
aiongg da5bc8df94 docking: native palettes stay shown while the app is inactive (#326)
* docking: keep native palettes shown while the app is inactive

Palettes no longer hide when none of the application's windows has focus.
They drop to WindowLevel::Normal while the application is inactive, so
they do not cover other applications, and go back to AlwaysOnTop when it
is active again. Files dragged from another application reach them, OS
sheets attached to them stay open, and they stay shown, dimmed, while an
app modal blocks input.

* docking: on Windows an inactive app's palettes go behind other windows

* docking: the viewer level test expects the inactive palette level
2026-10-25 12:18:10 -04:00
Dylan M. Taylor c2adf14ae3 feat(update): an Updater that owns the check, prompt and self-update flow for egui apps
One tested implementation of what each app had copied: results are picked up even after the prompt is dismissed, a dropped or panicking worker is an error, the UI is repainted while work runs, a failed install returns to the prompt with its reason, and success asks the app to close through its own close path instead of exiting the process.
2026-10-25 12:16:42 -04:00
aiongg 200b68fd6a docking: a macOS window bar drag follows AppKit's cursor (#325)
AppKit reports a native window's new place as soon as a move is
commanded, but measures the pointer events it sends against where the
window server had the window, which can be a move behind. Placing the
window from those events made it overshoot and step back while the
cursor moved smoothly.

On macOS the window now goes to NSEvent::mouseLocation, flipped against
the primary screen's height and divided by egui's zoom factor, less the
pressed point; drop targets are hit at the same point. Other platforms
keep placing the window from the frame's pointer move.

The cursor source is a DesktopCursor the test harness fixes. NativeHost
can tell the cursor (tells_cursor) and measure a window's pointer events
against its place some frames back (stale), through window_pointer.
2026-10-25 12:09:34 -04:00
aiongg c74116411d docking: a group drops on its own column's window edge, and window-edge drops preview a line (#324)
A group bar drag to a dock band named by one of the moving panels, such as the window edge of the column the group heads, anchors on another panel of that column, as a tab drag does; with none, the drop is a no-op. Before, the model rejected the move and the drop did nothing.

Drops on the bands along the layout's outer edge (a new dock, a new outermost column, the outer edge band) preview the line along that edge instead of the rect the panels take; drops beside groups keep the rect. The window-edge column line sits inside the window, like the new-dock line.
2026-10-25 12:08:55 -04:00
skylar taylor-barrick e5da4a7a68 M0: reconcile shared UI and updater changes with egui 0.37 2026-10-25 17:07:55 +01:00
aiongg bab74e1f4c docking: a canvas body takes no drop in the docking profile (#323)
With new_dock_zones (and without side_edge_zones), the body of a frame or
floating group that takes no tabs is no drop target, whatever its
accepts_splits. A floating window released there stays floating and a
docked panel floats as over empty space; the outer edge band, dock columns,
new docks and drops beside other groups still dock. Without new_dock_zones
the whole canvas body still goes to the nearest of drop_edges.
2026-10-25 12:07:15 -04:00
aiongg 7010c5e93e docking: a minimized native window shrinks to its bar (#320)
* docking: a minimized native window shrinks to its bar

A floating window shown in its own OS window kept its full size when
minimized, with an empty body under the tab strip. Minimizing now sizes
the OS window to its bar and the tab strip of its only group, at the
same width and top-left corner, and fixes its height with the window's
min and max inner size; expanding restores the saved size. The layout's
native and in-window rects keep the expanded height: the collapsed
height the window reports is not recorded, its position and width are.

* docking: a minimized native window's limits change before its size, and follow its bar height
2026-10-25 12:06:36 -04:00
Dylan M. Taylor fcd3441993 fix(update): drop the dirs crate and move to zip 9
dirs pulls in option-ext (MPL-2.0), which the apps' cargo-deny licence policy rejects, and zip 8.6 carries RUSTSEC-2026-0367, -0368 and -0369. The per-user folders now come from the environment.
2026-10-25 11:56:58 -04:00
aiongg 46d8cd1930 Merge pull request #313 from aiongg/dock-result-preview
docking: a drop beside something previews the rect the panel takes
2026-10-25 10:43:41 -04:00
skylar taylor-barrick 4e03c031de M-egui037: document shared UI behavior and regression contracts 2026-10-25 15:31:41 +01:00
aiongg b2928ca9ce docking: a dock column's result preview follows its scroll offset 2026-10-25 14:20:24 +00:00
aiongg 2ccf7ea3a2 Merge remote-tracking branch 'upstream/main' into dock-result-preview
# Conflicts:
#	crates/ui/src/docking/drop_tests.rs
#	crates/ui/src/docking/render/preview.rs
2026-10-25 14:01:23 +00:00