summaryrefslogtreecommitdiffhomepage
path: root/packages/ext-stage-dock/src/extension.cpp
AgeCommit message (Collapse)Author
2026-06-14ext-stage-dock: C++-driven interruptible slide animationAdam Malczewski
RmlUi only starts a transition on a class/definition change, never on the inline data-style-transform the dock uses for slide, so keyboard/minimize/restore open-close had stopped animating (snapped). Own the animation in C++ instead: a pure SlideAnimator that every path flows through -- keyboard/minimize/restore play it, a touch drag scrubs it (pause + set position from the finger), and release resumes easing from the current position to the snap target. Duration + easing are read from the #panel RCSS 'transition' via transition_timing(), so they stay hot-reloadable and any named RmlUi tween works. Drives slide per frame via request_frames; the surface now hides when the close animation completes (replaces the old transitionend path). Body=drag-handle / panel=transform split preserved.
2026-06-14ext-stage-dock: interactive touch edge-swipe to open/close the dockAdam Malczewski
Drag from the left edge to open, or drag the open dock back to close; finger-following with a 50%-or-fling snap on release. New gesture::Controller pure core converges both input paths onto one RevealRecognizer: OPEN via the kernel touch bus (dock hidden at down), CLOSE via UiSurface::bind_drag (the visible dock captures the touch). Slide is value-driven (data-style-transform) and eased only when not dragging. Two real-seat fixes found via per-frame logging: - Flicker: RmlUi projects a drag event's coords into the DRAGGED element's transformed frame. We had drag:drag on the same <body> we translate by slide, so the reported x fed back into slide and ping-ponged every frame. Fix: body is a stationary drag handle; an inner .panel carries the transform. - Direction: drag_start now seeds the recognizer from the dock's current fraction (1 + slide/width) instead of a hardcoded value, so a drag opens or closes correctly from any state.
2026-06-14ext-keybindings + ext-stage-dock: config-driven dock-toggle-visible (Super+d)Adam Malczewski
- **ext-stage-dock**: exports a `Service` interface with `toggle_visible()`. The extension inherits from it and registers via `provide_service` in `activate()`. The method slides the dock in/out (using the existing RCSS transition) regardless of slot count; showing an empty dock is valid. - **ext-keybindings**: new `Action::dock_toggle_visible` action, mapped from `"dock-toggle-visible"` in `unbox.toml [[keybind]]`, dispatched to the stage-dock Service. Default binding: `Super+d`. - **Manifest**: ext-keybindings now `depends_on {"xdg-shell", "stage-dock"}`. - **Build**: subdir order swapped so ext-stage-dock builds before ext-keybindings; `ext_stage_dock_dep` is a link-time dep of the ext-keybindings library and transitively exposed via `ext_keybindings_dep`. - **Tests**: glue tests install ext-stage-dock alongside ext-xdg-shell; policy test expects 6 default bindings. All 10/10 green on build + build-asan. Configure in unbox.toml: [[keybind]] keys = "Super+d" action = "dock-toggle-visible"
2026-06-14ext-stage-dock: full-height left rail — 288px wide, gradient, centered cardsAdam Malczewski
Turn the card-hugging dock into a full-height left rail. - C++ (extension.cpp/dock_layout.hpp): the surface is now the full OUTPUT HEIGHT (hug reverted; content_height helpers/tests dropped) and kDockWidth widened 240 -> 288 (~20%). Cards unchanged at 224dp. set_visible still hides the rail when empty, so it only appears when there are minimized windows. - RCSS (dock.rcss): body.dock fills the surface (width:100% height:100%) as a flex COLUMN scroll container with a `horizontal-gradient(#000000ff #00000000)` decorator — fully-opaque black at the left screen edge fading to transparent across the wider rail. Cards centered both axes (align-items:center + a `div.rail` margin:auto wrapper that vertically centers when they fit and collapses gracefully — no flex-center+overflow strand — scrolling when they overflow). Scrollbars hidden (scrollbarvertical/horizontal size 0). - RML (dock.rml): a `div.rail` wrapper around the data-for slot list enables the center-or-scroll pattern. Accepted caveat (quick path): the full-height 288px surface consumes pointer/touch across the left strip while shown; the deferred input-transparent UiSurfaceSpec flag is the real fix. Real-seat verified via live RML/RCSS hot-reload. ext-stage-dock 2/2 green on build + build-asan.
2026-06-13ext-stage-dock + build: externalize the dock document to assets/ ↵Adam Malczewski
(hot-reloadable) - The dock's inline kDockRml C++ string is gone; the document now lives in assets/ext-stage-dock/dock.rml (structure) + dock.rcss (styles), loaded via UiSurfaceSpec::rml_path = "ext-stage-dock/dock.rml". The bind_list*/event setup in activate() is unchanged (the substrate re-applies it across hot-reloads). - Build wiring (top-level meson.build): install_subdir the top-level assets/ tree to <datadir>/unbox/<unit>/, and -DUNBOX_ASSET_DIR_DEFAULT=<prefix>/<datadir>/unbox so an installed unbox finds its assets with no env. Dev runs set UNBOX_ASSET_DIR=<repo>/assets + UNBOX_DEV=1 to read the source tree and arm the hot-reload watcher. Real-seat verified: editing dock.rcss (border-radius 10dp<->70dp) updates the live dock with NO recompile and NO restart. ext-stage-dock 2/2 green on build + build-asan. Design iteration on the dock is now edit-a-file.
2026-06-13ext-stage-dock: thumbnail fully covers the card (fix right-edge placeholder ↵Adam Malczewski
sliver) Real-seat pixel sampling showed a ~2px vertical sliver of the slot placeholder (#2e2e32) on the card's RIGHT edge only (full height; other edges flush). A magenta diagnostic background on the thumb proved the gap was NOT a transparent texture edge (it stayed placeholder-colored, not magenta) — the thumb BOX was ~2px short on the right (RmlUi box rounding anchored top-left). The preview texture import is correct. Fix: the full-bleed thumb now overscans the slot by -2dp on all sides; the rounded overflow:hidden slot clips the overscan, so the image covers the whole rounded card with no placeholder edge on any side and corners stay rounded. RCSS-only. Verified real-seat: image reaches all four edges, zero placeholder sliver. ext-stage-dock 2/2 green on build + build-asan. Also records the transparency/ card-redesign milestone in tasks.md.
2026-06-13ext-stage-dock: card is a rounded thumbnail of the windowAdam Malczewski
Replaces the panel-with-inset-image card with the window preview as the card itself, rounded on all four corners. - div.slot is a rounded overflow:hidden clip container (explicit 224x140dp box, #2e2e32 rounded placeholder for not-yet-rendered previews). - A full-bleed child <div class="thumb"> carries the preview via data-style-decorator="'image( ' + row.preview + ' )'" with `cover center center` (shorthand order verified against vendored RmlUi DecoratorTiled). As a child of the rounded overflow:hidden slot, its image clips to the rounded corners — RmlUi does NOT clip an element's own decorator to its own border-radius, so the decorator must live on the clipped child. - Title overlay disabled (display:none) but kept in the markup + the {{ row.title }} binding/getter stay live, for a later text redesign. - Card height 140 (kCardHeight); surface-hug content heights 156/304/600 updated. - Preserved: d1 slot-enter animation, transparent strip, surface hugs the stack. Real-seat verified: a clean rounded window thumbnail in the dock. ext-stage-dock 2/2 green on build + build-asan.
2026-06-13ext-stage-dock: transparent strip, surface hugs cards, fix re-minimize after ↵Adam Malczewski
empty Builds on the kernel per-pixel-alpha + set_size-resize capabilities. - Transparent strip: body.dock background #1c1c1ee6 -> transparent, so the windows beneath show through everywhere the cards don't cover; cards keep their solid #2e2e32 panel. data-attr-src preview, Noto Sans, and the d1 slot-enter animation are intact. - Surface hugs the card stack: height = surface_height(count) = max(1, 2*pad + count*card + (count-1)*gap) (0->1px hidden, 1->140, 2->272, …), never the full output height, so the transparent area doesn't needlessly capture input. The empty dock is a positive 1px hidden placeholder (the substrate rejects 0 geometry); grows/shrinks via set_size on minimize/restore. - Fix: re-minimize after the dock empties was a no-op. do_restore relied on on_toplevel_focused re-firing, but a restored window was never seat-defocused, so focus() is a seat no-op and the event never fires — leaving focused_ stale, so the next Super+M's focused_ guard rejected it (a new toplevel mapping unstuck it). Fix: set focused_ = tl directly in restore. No kernel change. Tests: new policy cases (surface_height always positive; hug heights 0/140/272/536) and a glue minimize->restore->minimize 1->0->1 cycle with has_focused() probe. ext-stage-dock 2/2 green on build + build-asan (no sanitizer reports). Real-seat verified: transparent strip, dock visible, cards float, re-minimize works. Edits confined to packages/ext-stage-dock/.
2026-06-13Slice 10 c2/d1 fix: dock previews were blank (RML/RCSS authoring bugs)Adam Malczewski
Real-seat (nested) showed the dock compositing but slots blank. Three bugs in the inline dock document, all caught in the live RmlUi log: - <img src="{{ row.preview }}"> never bound — RmlUi interpolates {{ }} only in TEXT, not attributes, so it tried to load a texture literally named "{{ row.preview }}". Fixed with data-attr-src="row.preview". - font-family: sans-serif -> "No font face defined" (substrate only loads Noto Sans). Fixed to the loaded face. - transform-origin: top left -> RCSS parse error. Fixed to RmlUi-valid syntax. Verified real-seat: minimizing two foot windows shows two preview cards in the dock with the actual window snapshots visible; the live log is free of the three [rmlui] errors. Super+M works repeatedly with >1 window ("works once" was the single-window no-op: nothing left to focus/minimize). Suites green build + asan.
2026-06-13Slice 10 d1: RCSS animation — dock slide-in + per-slot settleAdam Malczewski
Layers animation on c2's static pipeline with zero data-model / contract change (c2 glue test passes unchanged). The dock body slides in from the left edge on empty->non-empty via an RCSS transition on transform:translateX driven by a bound `open` bool (data-class-open); slide-out defers set_visible(false) until the slide's transitionend so the close is seen. Each freshly instanced preview card plays a one-shot @keyframes (scale 0.72->1.0 + fade, transform-origin top-left = "scales down into a spot"). Dock stays a 240px left-edge strip (animation is a transform inside the surface, never a resize) so windows stay clickable. Key finding: RmlUi 6.2 dispatches animationend/transitionend as real events, and the substrate's data-event-* path binds a listener for ANY registered event — so data-event-transitionend routes into bind_event with NO kernel change. The ONLY remaining gap for the literal cross-screen window->dock flight is a single input-transparent UiSurfaceSpec flag (a kernel boundary decision, NOT built). Restore stays instant in d1 (animated grow-back is e1's drag-out). 10/10 suites green on build + build-asan (asan clean). Visual feel is real-seat.
2026-06-13Slice 10 c2: stage dock end-to-end (minimize -> preview -> restore)Adam Malczewski
ext-stage-dock activate() now wires the full static pipeline: track toplevels via the ext-xdg-shell Service; Super+M (stopgap keybinding) minimizes the focused window -> snapshot a Preview from its scene_tree(), push a slot, hide() the live node, refocus another window; the dock is one overlay UiSurface rendering the slots via the b2 list bindings (data-for over {preview src, title}); tapping a slot fires bind_list_event -> show()+focus() the window and drop its slot/Preview. Storing the Toplevel* across minimize is safe because hide() keeps it mapped; slots are dropped on on_toplevel_unmapped. Teardown is reverse-declaration-order (subscriptions first, surface before slots), asan-clean. host-bin installs ext-stage-dock (standard, depends_on xdg-shell; hidden until it holds a minimized window). Headless glue test (real in-process xdg client) proves the model + scene-node enable bit true->false->true, slots 0->1->0. RML carries dock/slot classes as d1's RCSS animation hooks. Stopgaps: Super+M (to migrate into an ext-keybindings action + a stage-dock Service post-d1); favicon deferred (needs an XDG icon-theme dep — to surface to the user). 10/10 suites green on build + build-asan. Visual/tap path is real-seat (pending).
2026-06-13Slice 10 b4: ext-stage-dock new unit — skeleton + pure coresAdam Malczewski
New standard extension (id "stage-dock", depends_on xdg-shell). Ships the unit skeleton (factory-only public header, minimal no-op activate, meson + suite) and the two PURE DECISION CORES, doctest-hard with no kernel/wlroots: - src/reveal.hpp: reversible left-edge swipe recognizer (stream -> reveal fraction [0,1]; release -> open/close commit by threshold + fling velocity). - src/dock_layout.hpp: reveal-fraction -> on-screen dock box (slides -dock_width .. 0) + slot capacity / content-height / per-slot rect math. Real wiring (RML doc, snapshot, minimize/restore) lands in c2/d1/e1. Root meson.build gains the subdir; host-bin registration deferred to c2. ext-stage-dock suite green (17 cases / 74 assertions). Edits in packages/ext-stage-dock/ + root meson.build subdir.