diff options
| author | Adam Malczewski <[email protected]> | 2026-06-14 00:13:18 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-14 00:13:18 +0900 |
| commit | f7df36c8f656652e8245d781b35cb3252f17bad2 (patch) | |
| tree | d61d7f857c910070f1bc38359bad0b354b08774b /packages | |
| parent | c4939d736ff55fa48cff31a26333f4922430c3d8 (diff) | |
| download | unbox-f7df36c8f656652e8245d781b35cb3252f17bad2.tar.gz unbox-f7df36c8f656652e8245d781b35cb3252f17bad2.zip | |
ext-stage-dock: full-height left rail — 288px wide, gradient, centered cards
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.
Diffstat (limited to 'packages')
| -rw-r--r-- | packages/ext-stage-dock/src/dock_layout.hpp | 89 | ||||
| -rw-r--r-- | packages/ext-stage-dock/src/extension.cpp | 121 | ||||
| -rw-r--r-- | packages/ext-stage-dock/tests/test_policy.cpp | 114 |
3 files changed, 69 insertions, 255 deletions
diff --git a/packages/ext-stage-dock/src/dock_layout.hpp b/packages/ext-stage-dock/src/dock_layout.hpp index b02d819..d36dba7 100644 --- a/packages/ext-stage-dock/src/dock_layout.hpp +++ b/packages/ext-stage-dock/src/dock_layout.hpp @@ -2,16 +2,17 @@ #include <algorithm> -// Pure decision core 2 — DOCK LAYOUT: the geometry mapping a reveal fraction + -// slot count to on-screen rects for the stage dock. No wlroots / GL / RMLUi — -// plain ints in, plain rects out, doctest-covered in tests/test_policy.cpp. +// Pure decision core 2 — DOCK LAYOUT: the geometry of the stage-dock FRAME. +// No wlroots / GL / RMLUi — plain ints in, plain rects out, doctest-covered in +// tests/test_policy.cpp. // // Fork B division of labour: the RML/RCSS inside the dock document does the -// in-dock slot FLOW (wrapping, styling); this core owns the dock FRAME (its -// on-screen rect as the reveal slides it in from the left), the reveal OFFSET, -// and the SCROLL/CAPACITY math (how many slots fit, total content height, and a -// per-slot rect for callers that want compositor-side placement). All glue (c2/ -// d1) consumes these; this file calls nothing back. +// in-dock slot FLOW (the full-height rail's flex centering, scrolling, card +// styling); this core owns ONLY the dock FRAME — its on-screen rect as the +// reveal slides it in from the left. (The card-stack capacity / content-height / +// per-slot-rect math the earlier hug-the-cards sizing used now lives in the +// RCSS, so those helpers were removed when the dock became a full-height rail.) +// All glue consumes this; this file calls nothing back. // // Single wl_event_loop thread throughout. @@ -29,24 +30,20 @@ struct Box { }; // Static metrics of the dock on a given output. `output_w`/`output_h` are the -// output's pixel size. `dock_width` is the revealed dock's on-screen width. -// `slot_height` is one preview slot's height; `gap` the vertical space between -// slots; `pad` the inner margin at the top (and conceptually all edges) of the -// dock content. All in px. +// output's pixel size. `dock_width` is the revealed dock's on-screen width. All +// in px. struct DockMetrics { int output_w = 0; int output_h = 0; int dock_width = 320; - int slot_height = 96; - int gap = 8; - int pad = 8; }; // The dock's on-screen rect at reveal fraction f in [0,1]. The dock slides // horizontally: at f=0 it is fully hidden off the left (x == -dock_width); at // f=1 it is fully revealed (x == 0). x is monotonic non-decreasing in f. y/h -// cover the full output height; w is always dock_width (the dock keeps its -// width and translates — it does not grow). f is clamped to [0,1]. +// cover the FULL output height (the dock is a full-height left rail); w is +// always dock_width (the dock keeps its width and translates — it does not +// grow). f is clamped to [0,1]. [[nodiscard]] inline auto dock_box(const DockMetrics& m, double fraction) -> Box { const double f = std::clamp(fraction, 0.0, 1.0); // x goes from -dock_width (f=0) to 0 (f=1): x = -dock_width * (1 - f). @@ -54,62 +51,4 @@ struct DockMetrics { return Box{.x = x, .y = 0, .w = m.dock_width, .h = m.output_h}; } -// One slot's full stride: its height plus the gap below it. -[[nodiscard]] inline auto slot_stride(const DockMetrics& m) -> int { - return m.slot_height + m.gap; -} - -// How many slots fit in the revealed dock WITHOUT scrolling. The usable height -// is the output height minus the top+bottom pad; each slot occupies -// slot_height and slots are separated by `gap` (no gap after the last). Returns -// 0 if nothing fits. Independent of `count` — this is pure capacity. -[[nodiscard]] inline auto visible_slots(const DockMetrics& m) -> int { - const int usable = m.output_h - 2 * m.pad; - if (usable < m.slot_height || m.slot_height <= 0) { - return 0; - } - // usable >= slot_height + k*(slot_height+gap) => k = (usable - slot_height) / stride - const int stride = slot_stride(m); - if (stride <= 0) { - return 1; // degenerate gap+height; at least the one that fit above - } - return 1 + (usable - m.slot_height) / stride; -} - -// The total content height needed to stack `count` slots: top pad + count slots -// with `gap` between them + bottom pad (no trailing gap). 0 slots -> 0 (an empty -// dock has no content). Used to clamp the scroll range. -[[nodiscard]] inline auto content_height(const DockMetrics& m, int count) -> int { - if (count <= 0) { - return 0; - } - return 2 * m.pad + count * m.slot_height + (count - 1) * m.gap; -} - -// The POSITIVE surface height that hugs `count` cards: content_height(count) -// clamped to a strictly-positive minimum. The ui substrate REJECTS a surface -// with non-positive geometry (create_surface/set_size return nullptr + log an -// error), so the empty dock (count 0 -> content_height 0) must still be created -// / resized at a positive size and merely hidden (set_visible(false)), never at -// height 0. Returns max(content_height(count), 1): >= 1 for every count >= 0, -// equal to content_height once there is at least one card. (Width never hits 0 -// in practice — dock_width is a fixed positive constant — but callers should -// likewise guard it; this helper covers the height, which is the count-driven -// dimension.) -[[nodiscard]] inline auto surface_height(const DockMetrics& m, int count) -> int { - return std::max(1, content_height(m, count)); -} - -// The on-screen rect of slot `i` (0-based) within the dock content, given the -// current vertical `scroll` offset (px scrolled DOWN; 0 = top). The slot's -// content-space top is pad + i*(slot_height+gap); subtracting `scroll` yields -// its screen y. x is the inner pad; width is dock_width minus pad on both sides -// (clamped to >= 0). A negative or off-screen y is returned as-is (the caller / -// RCSS clips); this core does not cull. -[[nodiscard]] inline auto slot_box(const DockMetrics& m, int i, int scroll) -> Box { - const int content_y = m.pad + i * slot_stride(m); - const int w = std::max(0, m.dock_width - 2 * m.pad); - return Box{.x = m.pad, .y = content_y - scroll, .w = w, .h = m.slot_height}; -} - } // namespace unbox::ext_stage_dock::layout diff --git a/packages/ext-stage-dock/src/extension.cpp b/packages/ext-stage-dock/src/extension.cpp index b859a04..08e3800 100644 --- a/packages/ext-stage-dock/src/extension.cpp +++ b/packages/ext-stage-dock/src/extension.cpp @@ -9,6 +9,7 @@ #include <unbox/kernel/ui.hpp> #include <unbox/kernel/wlr.hpp> +#include <algorithm> #include <cstddef> #include <memory> #include <stdexcept> @@ -52,36 +53,12 @@ using Toplevel = ext_xdg_shell::Toplevel; constexpr std::uint32_t kMinimizeKeysym = XKB_KEY_m; // 0x06d constexpr std::uint32_t kMinimizeMods = WLR_MODIFIER_LOGO; // Super/LOGO -// The dock width (revealed) in px — matches the dock_layout default. For c2 the -// dock sits fully revealed (no reveal animation); d1 animates dock_box(f). -constexpr int kDockWidth = 240; - -// Card-stack metrics, in px, that MIRROR the kDockRml RCSS so the surface rect -// can be sized (via dock_layout::content_height) to hug the rendered card stack -// rather than the full output height. dp == px (substrate dp-ratio is 1.0), so -// these are the RCSS dp values: -// kCardHeight — one div.slot's OUTER (border-box) height. The card IS the -// preview now (a full-bleed `div.thumb` child carries the image() decorator -// and the rounded `overflow:hidden` slot clips it; the title is OVERLAID -// absolute at the bottom — neither child adds to the box height). The card is -// a FIXED EXPLICIT box: `div.slot { width: 224dp; height: 140dp; }`. Both -// dimensions MUST be explicit: the slot's only children are out-of-flow -// (absolute), so an auto/`width:100%` box has no in-flow content and -// COLLAPSED (decorators paint inside the box but contribute NO layout size) — -// only a ~10dp sliver of the rounded edge rendered. The explicit 224dp × -// 140dp box fixes that. 224 = the dock inner width (240 dock_width - 2*8dp -// body padding); 140 ≈ a 16:10 landscape card at 224 wide (224*10/16=140), a -// sane thumbnail aspect. A fixed box keeps the surface-hugging math -// deterministic; the thumb decorator's `cover center center` fit fills the -// box (centered) for any source aspect without distortion (cropping overflow). -// kCardGap — the inter-card vertical space (div.slot margin-bottom: 8dp). -// kStripPad — the strip's inner top/bottom margin (body.dock padding: 8dp). -// content_height(count) = 2*kStripPad + count*kCardHeight + (count-1)*kCardGap. -// MUST stay in lockstep with the RCSS height/margin/padding AND with -// tests/test_policy.cpp's expected hug heights. -constexpr int kCardHeight = 140; -constexpr int kCardGap = 8; -constexpr int kStripPad = 8; +// The dock width (revealed) in px. The rail is kDockWidth wide x the full output +// height tall (dock_box). d1 animates the reveal via the body translateX in RCSS +// (not by resizing the surface). 288 = the original 240 widened ~20% so the +// horizontal gradient (dark left -> transparent right) extends farther right; +// the cards stay 224dp (RCSS) left-aligned, so the extra width is to their right. +constexpr int kDockWidth = 288; // A minimized window's dock entry: the live Toplevel* borrow (valid until its // unmapped event), the frozen Preview (owns the imported texture; null when the @@ -300,13 +277,10 @@ private: // -> the body slides back out; we DEFER set_visible(false) until the // slide-out finishes (on_dock_settled, fired by RmlUi's transitionend // through the existing event binding) so the close animation is seen. - // The surface rect tracks the slot count: whenever there is at least one - // slot we set_size the height to content_height(count) so the rect hugs the - // card stack (brief §3 — minimize the input-capturing area). We do NOT - // shrink to 0 the instant the dock empties: that would collapse the surface - // mid slide-OUT and the close animation would not be seen. Instead we keep - // the last non-empty height through the slide and shrink to 0 in - // on_dock_settled (with set_visible(false)). The width is fixed (kDockWidth). + // The surface is a fixed full-height rail; its size never changes with the + // card count (the RCSS scrolls/centers the cards within it). Only visibility + // toggles: shown when there is >= 1 slot, hidden (after the slide-out) when + // empty so the rail is not always eating the left strip. // // No-op on the visual when the surface is null (no-GL backend); the model is // still tracked, and slot_count()/the c2 invariants are unchanged. @@ -316,13 +290,6 @@ private: } dock_surface_->dirty("slots"); - // Resize the rect to hug the cards while non-empty (growing OR shrinking - // by one of several). When empty, defer the collapse to on_dock_settled. - if (!slots_.empty()) { - const int w = layout::dock_box(dock_metrics(), 1.0).w; - dock_surface_->set_size(w, surface_height_for(slots_.size())); - } - const bool want_open = !slots_.empty(); if (want_open == open_) { return; // reveal state unchanged (e.g. minimize a 2nd window) @@ -351,31 +318,34 @@ private: // again-open dock: we re-check open_. void on_dock_settled() { if (dock_surface_ != nullptr && closing_ && !open_) { + // Slide-out finished and the dock is empty: hide the full-height rail + // so it stops compositing AND stops capturing input over the left + // strip. The surface keeps its full height (no resize) for the next + // reveal; only visibility toggled. dock_surface_->set_visible(false); - // Collapse the rect now that the slide-out is done and the surface is - // hidden: a hidden empty dock captures NO input. Deferred to here (not - // refresh_slots) so the card stack stays sized through the slide. The - // height is the positive 1px placeholder (count==0), never 0. - const int w = layout::dock_box(dock_metrics(), 1.0).w; - dock_surface_->set_size(w, surface_height_for(slots_.size())); // count==0 -> 1px } closing_ = false; } // Create the dock UiSurface (overlay, left edge) and register all data - // bindings BEFORE the first frame. The surface rect HUGS the card stack: its - // width is the dock_box width, its height is content_height(slot count) — - // NOT the full output height — because the substrate consumes pointer/touch - // over the whole rect regardless of visual transparency, so a transparent - // full-height strip would still eat clicks over the empty area (brief §3). - // Height tracks the slot count via set_size in refresh_slots(); at create - // the dock is empty so the height is a POSITIVE 1px placeholder (the - // substrate rejects non-positive geometry — see surface_height_for) and the - // surface is hidden (spec.visible=false) until the first slot. Null surface - // (no-GL backend) is fine — we just skip it and the model is still tracked. + // bindings BEFORE the first frame. The surface is a FULL-HEIGHT left RAIL: + // kDockWidth (240) wide x the full OUTPUT HEIGHT tall, at the output's + // top-left, REGARDLESS of card count (the RCSS owns the in-rail flow: + // flex-column centering + overflow-y scroll). It is hidden (spec.visible = + // false) until the first slot, so the rail only appears when there are + // minimized windows (it does not always eat the left strip). set_size never + // changes the height afterwards — only visibility toggles. + // + // ACCEPTED CAVEAT (deferred): while shown, the full-height surface captures + // pointer/touch across the whole 240px left strip — windows under it there + // are not clickable. Width is kept minimal (240). The real fix is the + // deferred input-transparent UiSurfaceSpec flag (report change-req). + // + // Null surface (no-GL backend) is fine — we just skip it and the model is + // still tracked. void create_dock_surface() { const layout::DockMetrics m = dock_metrics(); - const layout::Box frame = layout::dock_box(m, 1.0); // fully revealed (c2) + const layout::Box frame = layout::dock_box(m, 1.0); // full-height rail kernel::UiSurfaceSpec spec; // External asset (RELATIVE to the asset root the orchestrator wires) so @@ -385,7 +355,10 @@ private: spec.x = frame.x; spec.y = frame.y; spec.width = frame.w; - spec.height = surface_height_for(slots_.size()); // hug the card stack + // Full output height. Guard >= 1: the substrate rejects non-positive + // geometry, and on a backend with no output yet frame.h could be 0 (the + // dock is hidden until a slot exists anyway). + spec.height = std::max(1, frame.h); spec.layer = kernel::SceneLayer::overlay; spec.visible = false; // shown when slot count > 0 @@ -426,9 +399,8 @@ private: // Dock metrics from the first output's size (queried via output_layout). On // a backend with no output yet, falls back to 0x0 (the dock is hidden until - // a slot exists anyway). The dock keeps its fixed width; its HEIGHT now hugs - // the card stack (content_height), not the full output (input caveat, §3). - // The card-stack dims (slot_height/gap/pad) mirror the kDockRml RCSS. + // a slot exists anyway). dock_box() turns these into the full-height rail + // rect (kDockWidth wide x output height tall, at the output top-left). [[nodiscard]] auto dock_metrics() const -> layout::DockMetrics { int ow = 0; int oh = 0; @@ -449,28 +421,9 @@ private: m.output_w = ow; m.output_h = oh; m.dock_width = kDockWidth; - // Card-stack dims mirror the kDockRml RCSS so content_height() yields the - // surface height that hugs the rendered cards (see kCardHeight et al.). - m.slot_height = kCardHeight; - m.gap = kCardGap; - m.pad = kStripPad; return m; } - // The POSITIVE surface HEIGHT that hugs `count` cards: the strip's content - // height (2*pad + count*card + (count-1)*gap) per dock_layout, CLAMPED to a - // strictly-positive minimum (>= 1). The substrate REJECTS non-positive - // geometry (create_surface/set_size return nullptr + log - // "surface needs positive geometry"), so the EMPTY dock (count 0 -> - // content_height 0) must be created/resized at a positive size and merely - // hidden (set_visible(false)), never at height 0. The substrate consumes - // input over the whole rect, so sizing height to the card stack (not the - // full output) leaves the rest of the screen interactive (brief §3 caveat); - // the empty-dock 1px placeholder is hidden, so it captures nothing. - [[nodiscard]] auto surface_height_for(std::size_t count) const -> int { - return layout::surface_height(dock_metrics(), static_cast<int>(count)); - } - const kernel::Manifest manifest_{ .id = "stage-dock", .tier = kernel::Tier::standard, diff --git a/packages/ext-stage-dock/tests/test_policy.cpp b/packages/ext-stage-dock/tests/test_policy.cpp index ed80daa..b07739b 100644 --- a/packages/ext-stage-dock/tests/test_policy.cpp +++ b/packages/ext-stage-dock/tests/test_policy.cpp @@ -133,9 +133,7 @@ TEST_CASE("an inactive (non-edge) recognizer is inert") { // ============================================================================ static auto metrics() -> lay::DockMetrics { - return lay::DockMetrics{ - .output_w = 1920, .output_h = 1080, .dock_width = 300, - .slot_height = 100, .gap = 10, .pad = 20}; + return lay::DockMetrics{.output_w = 1920, .output_h = 1080, .dock_width = 300}; } TEST_CASE("dock_box: f=0 fully off-screen left, f=1 flush at x==0") { @@ -165,98 +163,22 @@ TEST_CASE("dock_box: x is monotonic non-decreasing in f and clamps outside [0,1] CHECK(lay::dock_box(m, 2.0).x == 0); // clamped to f=1 } -TEST_CASE("visible_slots: 0/1/many capacity") { - // usable = 1080 - 40 = 1040; stride = 110; 1 + (1040-100)/110 = 1 + 8 = 9. - CHECK(lay::visible_slots(metrics()) == 9); - - // Exactly one slot fits. - lay::DockMetrics one{.output_w = 0, .output_h = 140, .dock_width = 300, - .slot_height = 100, .gap = 10, .pad = 20}; - CHECK(lay::visible_slots(one) == 1); // usable 100 == slot_height - - // Nothing fits (usable < slot_height). - lay::DockMetrics none{.output_w = 0, .output_h = 100, .dock_width = 300, - .slot_height = 100, .gap = 10, .pad = 20}; - CHECK(lay::visible_slots(none) == 0); // usable 60 < 100 -} - -TEST_CASE("content_height: 0/1/many slots") { - auto m = metrics(); // pad 20, slot 100, gap 10 - CHECK(lay::content_height(m, 0) == 0); - CHECK(lay::content_height(m, 1) == 2 * 20 + 100); // 140, no gap - CHECK(lay::content_height(m, 3) == 2 * 20 + 3 * 100 + 2 * 10); // 360 -} - -// The glue (src/extension.cpp) sizes the dock SURFACE rect to HUG the card stack -// via content_height(card-stack metrics, slot count) instead of the full output -// height — so the transparent strip captures input only over the cards (brief -// §3: the substrate consumes input over the whole rect regardless of visual -// transparency). These are the exact px values the surface height takes, with -// the card-stack metrics that mirror the kDockRml RCSS (kCardHeight=140 — the -// card IS the preview now, a fixed 16:10-ish box with the title OVERLAID (the -// title no longer adds to the box height); kCardGap=8 inter-card margin; -// kStripPad=8 body padding). Keep in lockstep with src/extension.cpp's -// kCard*/kStripPad constants + dock_metrics(). -TEST_CASE("dock surface height hugs the card stack (content_height with RCSS card metrics)") { - // Mirror src/extension.cpp: kCardHeight=140, kCardGap=8, kStripPad=8. - lay::DockMetrics card{.output_w = 1920, .output_h = 1080, .dock_width = 240, - .slot_height = 140, .gap = 8, .pad = 8}; - // Empty dock -> 0 content (but the SURFACE is clamped positive, below). - CHECK(lay::content_height(card, 0) == 0); - // One card -> 2*pad + card (no trailing gap). - CHECK(lay::content_height(card, 1) == 2 * 8 + 140); // 156 - // Two cards -> +gap between them. - CHECK(lay::content_height(card, 2) == 2 * 8 + 2 * 140 + 1 * 8); // 304 - // Many cards grow linearly and stay FAR under the full output height, so the - // surface never spans the whole left edge (the hug-the-cards property). - CHECK(lay::content_height(card, 4) == 2 * 8 + 4 * 140 + 3 * 8); // 600 - CHECK(lay::content_height(card, 4) < card.output_h); // < 1080 -} - -// REGRESSION GUARD (0-geometry boot bug): the ui substrate REJECTS a surface -// with non-positive geometry ("surface needs positive geometry") and returns -// nullptr, so the EMPTY dock must be created/resized at a POSITIVE height, not 0. -// surface_height() — the helper the glue's surface_height_for() delegates to — -// clamps content_height to >= 1, so create_surface/set_size are never called -// with height 0. Cover EVERY count the glue can produce, especially the empty -// case the headless test cannot distinguish from the substrate-null path. -TEST_CASE("surface_height is ALWAYS positive (empty-dock 0-geometry guard)") { - lay::DockMetrics card{.output_w = 1920, .output_h = 1080, .dock_width = 240, - .slot_height = 140, .gap = 8, .pad = 8}; - // The empty dock: content_height is 0, but the surface height is clamped to 1. - CHECK(lay::content_height(card, 0) == 0); - CHECK(lay::surface_height(card, 0) == 1); // positive placeholder (hidden) - // Once there is at least one card, surface_height == content_height (>0). - CHECK(lay::surface_height(card, 1) == lay::content_height(card, 1)); - CHECK(lay::surface_height(card, 4) == lay::content_height(card, 4)); - // Never non-positive for any plausible count (incl. a negative/degenerate). - for (int n = -2; n <= 20; ++n) { - CHECK(lay::surface_height(card, n) >= 1); +// FULL-HEIGHT RAIL: the dock surface is kDockWidth wide x the FULL OUTPUT HEIGHT +// tall, REGARDLESS of card count (the RCSS owns the in-rail flex centering + +// overflow scroll; the C++ no longer sizes the surface to the card stack). The +// revealed frame the glue feeds to UiSurfaceSpec/set_position is dock_box(m, 1.0) +// at x==0, full output height — this is what the glue's create_dock_surface uses +// for spec.width/height. (The earlier hug-the-cards content_height/surface_height +// helpers were removed with the rail change; the RCSS scrolls the overflow.) +TEST_CASE("dock_box: revealed rail is dock_width x full output height, count-independent") { + // Various output heights -> the rail's h always equals output_h (never the + // card-stack content height); w always dock_width; revealed x == 0. + for (int oh : {600, 1080, 1440}) { + lay::DockMetrics m{.output_w = 1920, .output_h = oh, .dock_width = 288}; + auto rail = lay::dock_box(m, 1.0); + CHECK(rail.x == 0); + CHECK(rail.y == 0); + CHECK(rail.w == 288); + CHECK(rail.h == oh); // FULL output height, independent of any card count } - // Even with degenerate (zeroed) metrics — defensive: still >= 1, never 0. - lay::DockMetrics zero{}; - CHECK(lay::surface_height(zero, 0) >= 1); - CHECK(lay::surface_height(zero, 3) >= 1); -} - -TEST_CASE("slot_box: vertical stacking by stride, inset width") { - auto m = metrics(); // pad 20, slot 100, gap 10, dock_width 300 - auto s0 = lay::slot_box(m, 0, 0); - CHECK(s0.x == 20); // inner pad - CHECK(s0.y == 20); // pad + 0*stride - CHECK(s0.w == 300 - 2 * 20); // dock_width minus pad both sides = 260 - CHECK(s0.h == 100); - - auto s1 = lay::slot_box(m, 1, 0); - CHECK(s1.y == 20 + 110); // pad + 1*stride(110) = 130 - auto s2 = lay::slot_box(m, 2, 0); - CHECK(s2.y == 20 + 220); // 240 -} - -TEST_CASE("slot_box: scroll offset shifts slots up") { - auto m = metrics(); - auto s2_unscrolled = lay::slot_box(m, 2, 0); - auto s2_scrolled = lay::slot_box(m, 2, 150); - CHECK(s2_scrolled.y == s2_unscrolled.y - 150); - CHECK(s2_scrolled.x == s2_unscrolled.x); // scroll is vertical only } |
