summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-13 19:57:45 +0900
committerAdam Malczewski <[email protected]>2026-06-13 19:57:45 +0900
commite605b3614a87936bf7d0873eb41193d52de6d8b1 (patch)
tree1e99fd73827ec7ee62d2c914de3793d67f32fd17
parentc52aa6d9c134090c8d5c86fc85ba84bb32e736f0 (diff)
downloadunbox-e605b3614a87936bf7d0873eb41193d52de6d8b1.tar.gz
unbox-e605b3614a87936bf7d0873eb41193d52de6d8b1.zip
Slice 10 c2/d1 fix: dock previews were blank (RML/RCSS authoring bugs)
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.
-rw-r--r--packages/ext-stage-dock/src/extension.cpp33
1 files changed, 22 insertions, 11 deletions
diff --git a/packages/ext-stage-dock/src/extension.cpp b/packages/ext-stage-dock/src/extension.cpp
index 823da21..82814e2 100644
--- a/packages/ext-stage-dock/src/extension.cpp
+++ b/packages/ext-stage-dock/src/extension.cpp
@@ -73,8 +73,12 @@ struct Slot {
// rounded like the Stage-Manager reference. d1 ADDS the RCSS animation on top of
// c2's static document — without touching the data model (same "slots" list,
// same per-row preview/title/restore bindings). data-model "ui" (the substrate
-// default). {{ row.preview }} is the Preview source_uri(); data-event-click
-// delivers the row index to restore().
+// default). The img src is the Preview source_uri(), bound via
+// `data-attr-src="row.preview"` — RmlUi interpolates {{ }} only in TEXT, so an
+// element attribute must use the data-attr-<attr> attribute-binding form
+// (verified against vendored RmlUi 6.2: data_binding/options samples). The
+// title is TEXT, so {{ row.title }} is correct there. data-event-click delivers
+// the row index to restore().
//
// d1 animation (RCSS, RMLUi 6.2; verified against the vendored source):
//
@@ -95,16 +99,20 @@ struct Slot {
// the element is instanced, which is exactly when dirty("slots") grows the
// list — so a new minimize animates its card in with no extra binding.
//
-// transform-origin keeps the slot scaling toward its own top so the grow reads
-// as "into the dock", within what a left-strip surface can convey (the literal
-// cross-screen flight needs an input-transparent overlay — report change-req).
+// transform-origin keeps the slot scaling toward its own top-left so the grow
+// reads as "into the dock", within what a left-strip surface can convey (the
+// literal cross-screen flight needs an input-transparent overlay — report
+// change-req). RmlUi 6.2's `transform-origin` is an X-then-Y-then-Z shorthand
+// whose X axis takes only {left,center,right} (or length/percent) — so the
+// CSS-style `top left` is a parse error (top is not a valid X keyword). We use
+// the unambiguous percentage form `0% 0%` (= top-left); `left top` also parses.
constexpr const char* kDockRml = R"RML(<rml>
<head>
<style>
body.dock {
background-color: #1c1c1ee6;
padding: 8dp;
- font-family: sans-serif;
+ font-family: Noto Sans;
transform: translateX(-100%);
transition: transform 0.18s cubic-in-out;
}
@@ -123,11 +131,12 @@ body.dock.open {
}
div.slot {
display: block;
+ min-height: 40dp;
margin-bottom: 8dp;
padding: 6dp;
background-color: #2e2e32ff;
border-radius: 10dp;
- transform-origin: top left;
+ transform-origin: 0% 0%;
animation: slot-enter 0.16s cubic-out 1 normal;
}
div.slot img.preview {
@@ -147,7 +156,7 @@ div.slot span.title {
</head>
<body data-model="ui" class="dock" data-class-open="open" data-event-transitionend="dock_settled()">
<div data-for="row : slots" class="slot" data-event-click="restore(it_index)">
-<img class="preview" src="{{ row.preview }}"/>
+<img class="preview" data-attr-src="row.preview"/>
<span class="title">{{ row.title }}</span>
</div>
</body>
@@ -199,13 +208,15 @@ public:
// minimize the focused window. TODO: migrate to a config-driven
// `minimize` action in ext-keybindings + a Service we export (post-d1;
// change-request in the report). We do NOT trigger on Super alone (that
- // is ext-keybindings' tap-launcher).
+ // is ext-keybindings' tap-launcher). When NOTHING is focused there is
+ // nothing to minimize, so we do NOT consume the chord (let the key pass
+ // rather than silently eating it — minor UX, brief §Also).
key_filter_ = host.subscribe(
host.key_filter(), [this](kernel::KeyEvent ev) {
if (ev.pressed && ev.keysym == kMinimizeKeysym &&
- (ev.modifiers & kMinimizeMods) != 0) {
+ (ev.modifiers & kMinimizeMods) != 0 && focused_ != nullptr) {
do_minimize_focused();
- ev.handled = true; // consume
+ ev.handled = true; // consume (only when we actually acted)
}
return ev;
});