photocraft/docs/ui-design.md

4.2 KiB
Raw Permalink Blame History

UI design system

Themes

Theme Intent
Pro (default) Photoshop-style Spectrum dark: flat charcoal panels (#323232), dark tab strips, Spectrum blue accent (#378ef0), pill buttons, checkboxes, compact 12 px type
Studio Dark studio style: near-black, rounded cards, pill tabs, violet accent, toggles
Studio Light Studio on light surfaces
Classic Windows-2000 bevels, square corners, navy selection

Switch themes with the sun icon, Window → Theme, or ui.set {"theme":"classic"} over the control channel.

Rules

  • Colours and radii come from tokens. Read them with Tokens::get(ctx); never hard-code a colour in a widget.
  • Shared widgets live in widgets.rs: card (panel group; Pro renders a Photoshop tab strip), value_field, slider/slider_row, toggle/checkbox, primary_button/secondary_button, dropdown (with a chevron icon), hairline/vline.
  • Icons: Lucide SVGs in assets/icons (ISC licence), embedded via icon_data.rs. Regenerate that file when you add icons.
  • Fonts: Inter (UI) and JetBrains Mono (numbers), both OFL. Named families medium and semibold are available via theme::medium() and theme::semibold().
  • Photoshop layout grammar (Pro):
    • Essentials dock order: Color | Swatches, then Properties | Adjustments, then Layers | Channels | Paths (Layers fills the remaining height).
    • Options-bar labels end with a colon ("Size:").
    • Document tabs read "name @ 12.5% (RGB/8)".
    • Toolbar tool groups carry a corner triangle.
  • Verify every visual change with ui.screenshot, at several window sizes and in every theme.

Adding icons

curl -sfL -o assets/icons/<name>.svg https://raw.githubusercontent.com/lucide-icons/lucide/main/icons/<name>.svg
# regenerate the embedded table
{ printf '%s\n' '//! Lucide icons (ISC licence), embedded and tinted at runtime.' '' 'pub static ICONS: &[(&str, &[u8])] = &['; \
  for f in assets/icons/*.svg; do n=$(basename $f .svg); echo "    (\"$n\", include_bytes!(\"../../../assets/icons/$n.svg\")),"; done; echo '];'; } \
  > crates/ui-egui/src/icon_data.rs

Interaction models (match Photoshop CC)

Feature Module Behaviour
Type tool type_tool.rs Click: point text with the placeholder "Lorem Ipsum" selected. Drag: paragraph box. Inline caret and selection drawn from the text engine layout. ⌥/⌘ word and line navigation, ↩ newline, ⌘↩ or Esc commits, a click outside commits. One history step per session (coalesce). A new layer is named after its text; an empty one is deleted.
Free Transform transform_tool.rs ⌘T. Corners scale proportionally (⇧ frees them); edges scale one axis; ⌥ scales about the reference point; ⌘-corner distorts; dragging outside rotates (⇧ snaps to 15°); dragging inside moves. Preview = document without the moving pixels + a textured 24×24 mesh. ↩ or a double-click commits via edit.transform {rect, quad}.
Layer masks panels.rs Clicking the mask thumbnail targets the mask (corner-bracket frame; the tab reads "Layer, Layer Mask/8"). Brush, eraser (paints background colour), gradient and bucket then send "target": "mask". Adjustment and fill layers target their mask automatically.
Levels / Curves tone.rs Histogram of the image below the adjustment. Curves: click to add a point, drag out to delete. Every change is a coalesced layer.setAdjustment, so one drag = one undo step and the canvas updates at full resolution on the GPU.

Where the font lacks a symbol (e.g. ∠ ↦ ▔), draw it with the painter or use a Lucide icon; never ship missing-glyph boxes. Check every new panel with the offscreen snapshot tool (docs/development.md).

Menus

menu_catalog.rs holds Photoshop's menu tree (standard command names, order, separators, default shortcuts). Items whose id matches an engine or UI command are live; others render disabled until implemented. Give new commands the catalogue's id (for example image.imageSize) and they light up in the right place automatically.

Automation for visual checks

Use ui.click {x,y}, ui.move, ui.key and ui.type (synthetic input in screen points) to open menus, popups and context menus, then ui.screenshot.