Commit Graph

16 Commits

Author SHA1 Message Date
Skyler Lehmkuhl 4f58edf436 P5 follow-ups: double-tap priority, inspector dismiss, breadcrumb path
- Mobile double-tap is now a single tool-independent priority chain: object →
  enter (movie clip or group); empty inside a clip → exit one level; empty at
  root → zoom-to-fit. Desktop keeps its own select-tool double-click (gated).
- Inspector "tap outside to dismiss" now only HIDES the sheet (with a dismissed
  flag reset on selection change) instead of clearing the selection — the earlier
  clear clobbered the selection before menu/omnibutton actions ran, breaking
  Group/Convert/Cut/Copy. Delete also clears focus so it dismisses the inspector.
- Breadcrumb shows the full editing path (Scene 1 › … › current) via a new
  SharedPaneState.editing_clip_path / EditingContext::clip_path(); each ancestor
  crumb jumps to that level (pending_exit_to_depth → EditingContext::exit_to_depth).
- Remove the "Vello Stage" debug overlay; move the breadcrumb up to the top-left.
2026-07-02 01:14:38 -04:00
Skyler Lehmkuhl 01b20165cc P5 mobile gestures + long-press context menus
Gestures (Stage + Timeline):
- Pinch-zoom via zoom_delta() (touch pinch + Ctrl+wheel), unified with the raw-wheel
  path so Ctrl+wheel zooms exactly once; plain wheel zoom + trackpad pan preserved.
- Double-tap empty → zoom-to-fit (Stage: artboard via zoom_to_fit; Timeline: new
  fit_to_project over the full duration).
- Double-tap-drag on empty → transient marquee regardless of the active tool.

Long-press context menu: a shared SharedPaneState.mobile_context_menu that panes
populate with (label, MenuAction) items on secondary_clicked(); the mobile shell
renders one persistent popup (styled like the timeline menu) and dispatches via
pending_menu_actions. Stage offers Cut/Copy/Duplicate/Delete for clip instances and
Cut/Copy/Convert-to-movie-clip/Delete for geometry.

Fixes surfaced along the way:
- Geometry delete now frees selected fills (free_fill) and GCs isolated vertices
  (new VectorGraph::gc_isolated_vertices) — no more orphaned fills / phantom snap
  points; applies to the Delete key too.
- clipboard_delete_selection clears focus so deleting dismisses the mobile inspector.
- Mobile inspector: appears on pointer release (not press) so drags aren't
  interrupted; reflows only when the tapped point is actually behind the sheet; taps
  outside the sheet dismiss it; rounded-corner border no longer detaches.
2026-07-02 00:11:49 -04:00
Skyler Lehmkuhl 4b2802076b Touch-friendly egui widgets on mobile; egui-based inspector header
Add mobile::apply_touch_style(ctx) — applied every frame when mobile is active —
enlarging egui's global spacing/sizing (interact_size, button padding, slider
width/rail, combo width/height, icon sizes, scrollbar, body/button text) so the
standard widgets in panes and dialogs are finger-sized. Desktop is untouched.

Rework the mobile inspector header into a single row using real egui widgets:
title (left, truncating) + Timeline/Nodes jump buttons + ✕ close, laid out with
egui layouts so they inherit the touch sizing and theme visuals instead of being
hand-painted.
2026-07-01 07:28:13 -04:00
Skyler Lehmkuhl 943ff7c15f Unify colors via theme CSS variables; theme egui visuals
Add Theme::var(name, ctx) (CSS custom-property getter) and Theme::apply_to_egui(ctx)
which maps the palette onto egui's global Visuals so standard widgets share the
theme colors. Add a mobile::palette::Palette built from the theme each frame and
replace the duplicated per-file C_* color constants across all mobile modules
(omni, topbar, stack, inspector, transport, intent, shell) with it. Add --scrim
and --accent-* category vars to styles.css. Apply the theme visuals every frame in
update(). Mobile and the main UI now share one CSS-variable-driven palette that
responds to light/dark/user themes.
2026-07-01 07:08:00 -04:00
Skyler Lehmkuhl f13a127c9d Fit modal dialogs to the phone screen on mobile
Add mobile::dialog_width(ctx, desired) — a min()-clamp that's a no-op on wide
desktop screens. Clamp the Export and Export Progress modals and the Sample
Import window to it. Render Preferences as an egui::Modal (dim backdrop, centered,
no title bar) with a screen-fit scroll height when mobile_active(); desktop keeps
its window. Extracted the preferences body into render_body() shared by both paths.
2026-07-01 06:28:04 -04:00
Skyler Lehmkuhl a0ac4f2d35 Add mobile top bar: filename + command palette (⌕) + overflow (⋯)
Reserve a slim top bar in the mobile shell showing the project filename (or
"Lightningbeam" when unsaved) on the left, and ⌕/⋯ on the right. ⋯ opens a
curated overflow sheet (Save/Save As/Open/New/Import/Export/Undo/Redo/
Preferences); ⌕ opens a command palette that searches the whole menu tree
(flattened from MenuItemDef::menu_structure()). Both dispatch MenuActions.
Completes the P4 omnibutton + global menu.
2026-07-01 06:09:26 -04:00
Skyler Lehmkuhl b8419778a5 Add omnibutton FAB (P4); fix Stage playback freeze when timeline hidden
Omnibutton (mobile/omni.rs): a context FAB whose radial holds the active layer's
drawing tools laid out in a staggered inner/outer arc, plus a "+New" petal
(create Vector/Audio/MIDI/Raster/Video layer, Group, Import, and Split/Duplicate
when a clip is selected — dispatched as MenuActions) and a "more" petal that
opens a full-tools grid when the layer has extra tools. Contextual: the tool ring
only appears when the Stage is in the visible window; off-Stage the FAB is a
"+New" create button. Tools/creates use Lucide icons. Pane-internal adds
(nodes/notes/instruments) deferred to P6.

Playback fix: document.current_time (read by the renderer for animation eval and
video-frame decode) was only synced from the audio playback position inside
TimelinePane::render_content. In the mobile shell, panes render on demand, so with
the timeline off-screen current_time went stale and the Stage froze during
playback. Move the sync into the per-frame block in update() so it runs regardless
of which panes are visible.
2026-07-01 05:42:53 -04:00
Skyler Lehmkuhl 08b6aa0139 Compress mobile layer headers to a swatch; conditional inspector reflow
- Add `is_mobile` to SharedPaneState (set in build_frame). On mobile the timeline
  layer-header column collapses to a minimal type-colored swatch (1:2 w:h, ~30px),
  brighter/outlined when active/selected; tapping it selects the layer (handled by
  the existing header-column input path, which now uses the narrower width).
  Desktop headers unchanged.
- Inspector reflow: the sheet overlaps the stack by default, but when the selected
  pane would be covered by the sheet, the stack reflows (renders above the sheet)
  so it stays visible. Restore on dismiss is automatic since reflow only changes
  the render rect; manual divider moves while the sheet is up persist. Adds
  stack::pane_bottom_in and inspector::target_slot.
2026-07-01 01:14:37 -04:00
Skyler Lehmkuhl 87263489c0 Add mobile selection inspector sheet (P3); editable layer properties
- New mobile/inspector.rs: when something is selected/focused, a bottom sheet
  overlays the lower stack (no reflow) above the transport, showing the object's
  properties by reusing InfopanelPane full-bleed. Header shows a title + ✕ to
  deselect; jump-to chips (Timeline, Nodes) reframe the stack to that surface
  with the selection kept. Grab handle resizes the sheet.
- Make the Infopanel Layer section editable (benefits desktop too): Name (text,
  commit on Enter/blur), Opacity and Volume sliders (live preview, single-undo
  on release), and Mute/Solo/Lock toggles. Backs the plan to reduce mobile layer
  headers to a minimal swatch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 22:16:14 -04:00
Skyler Lehmkuhl f2cb516e8a Add mobile new-file intent picker (P2)
When LB_MOBILE_UI is set, the desktop start screen is replaced by a phone
intent picker: a 2x3 grid of icon cards (Draw / Animate / Compose / Record /
Edit video / Blank) taking the top ~2/3, and a scrollable Recent-projects list
in the bottom ~1/3.

Each intent reuses EditorApp::create_new_project_with_focus and then sets the
initial stack window (e.g. Draw -> fullscreen Stage, Compose -> Timeline +
Piano Roll + Keys). Recent rows load the project and enter the editor. Cards are
icon-only (Lucide glyph + label) for legibility on a phone. Adds the intent
icons to the Lucide set.
2026-06-30 22:16:09 -04:00
Skyler Lehmkuhl b25e639a1c Linked-divider preset snapping + animated transitions for the stack
Rework 3-pane divider behavior and animate every layout change:

- All transitions now ease over ~100ms via a LayoutAnim between two full stack
  configs (resize, slide, collapse, fullscreen) instead of jumping; edge
  transitions continue from the drag's progress.
- 3-pane dividers are linked along a single path parameter through the three
  presets ([.5,.25,.25] / even / [.25,.25,.5]) so both dividers move together
  and reach their snaps simultaneously. Over-grow past the extreme preset is
  anchored to the path endpoint so the non-dragged divider stays continuous.
- Growing a pane past ~66% collapses the window to 2 panes (dropping the
  opposite-end pane); the surviving divider snaps to the nearest 2-pane preset
  (1/4, 1/2, 3/4) based on the drop position. Dragging to the slide extreme
  still slides the window.
- 2-pane snaps to 1/4, 1/2, 3/4.
2026-06-30 02:07:31 -04:00
Skyler Lehmkuhl 01fe14f197 Ease divider snap into place over ~100ms
After releasing a divider, the boundary now eases (cubic ease-out, ~100ms) from
where the finger lifted to the snapped fraction instead of jumping. Starting a
new drag cancels an in-flight ease; membership changes still apply instantly.
2026-06-30 01:39:53 -04:00
Skyler Lehmkuhl 3b39e80e40 Add intermediate divider snapping (uneven pane sizes)
Dragging a divider now resizes its two adjacent panes live and, on release,
snaps the boundary to the nearest of 1/4, 1/3, 1/2, 2/3, 3/4 — so one pane can
be made dominant within a 2- or 3-pane window. Dragging far enough to squeeze a
pane past the collapse threshold still triggers the membership transition
(slide/shrink per R1-R6), after which sizing resets to even.

MobileState gains a `weights: [f32;3]` (normalized on use); config_rects is
weight-aware; edge drags keep animating toward the even-split target.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 01:18:13 -04:00
Skyler Lehmkuhl 76e7963f03 Add Lucide icon font for the mobile UI
Bundle Lucide (ISC license) at assets/fonts/lucide.ttf and register it as the
egui font family "lucide" via mobile/icons.rs::install (called in EditorApp::new).
Icons are drawn as text with icons::font(size) + PUA codepoint constants.

Switch the stack headers (grip, maximize/minimize, node⇄instrument), the footer
chevron, and the transport play/pause to Lucide glyphs instead of relying on
egui's limited built-in emoji-icon-font.
2026-06-30 01:02:03 -04:00
Skyler Lehmkuhl 79f063d00c Replace mobile tabbed shell with vertical sliding-window stack
Per design feedback, drop the top-tabs + single-ribbon model. The mobile UI
is now one vertical stack of all panes in a fixed order (Outliner, Asset
Library, Stage, Timeline, Piano Roll, Virtual Piano, Node/Instrument, Script
editor) with a window of 2-3 consecutive panes visible at a time.

Dragging the dividers between panes and the top/bottom screen edges grows,
shrinks, or slides the window via a small state machine (R1-R6 in stack.rs),
with continuous interpolation during the drag and snap-on-release. The
Node/Instrument band toggles between the node editor and the preset browser.
The transport stays pinned as the bottom floor.

Each visible band reuses PaneInstance::render_content full-bleed (surface.rs).
Removes topbar.rs and ribbon.rs; rewrites mod.rs state (window_top,
window_count, show_instruments, drag); adds stack.rs.
2026-06-30 00:12:57 -04:00
Skyler Lehmkuhl 5ee2df5db7 Add mobile UI shell scaffold behind LB_MOBILE_UI
New src/mobile/ module renders a phone-style shell: top surface tabs
(Stage/Time/Nodes/Mixer/Tree), a single full-bleed hero pane reusing the
existing PaneInstance renderers, a resizable timeline ribbon (peek/half/full
snap tiers riding above the floor), and a fixed transport bar wired to the
audio controller (play/pause, timecode, project scrub).

Gated entirely on the LB_MOBILE_UI env var: when set, main() opens a
phone-aspect window and update() routes the central panel through
render_mobile_shell instead of render_layout_node, sharing the same
build_frame context and post-render drain. Desktop is unchanged when unset.

Tabs mirror the pane list; per the wireframe the Toolbar and Infopanel do
not become tabs (they become the omnibutton and inspector in later phases),
and the Nodes surface is a placeholder pending the focus/patch rework.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 22:40:54 -04:00