diff --git a/src/main.rs b/src/main.rs index d185963..cf98d3c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -350,6 +350,58 @@ impl SimpleComponent for App { Exclusive::Px(px) => px, }; root.set_exclusive_zone(exclusive_zone); + // ANIMATION WORK #3: bar entrance on first map, Liquid Motion + // only. The flush glass-workbench bar has no floating margin to + // slide from (it sits edge-to-edge against the screen), so it + // gets nothing; spotlight's capsule already has its own width/ + // drawer motion in flight during a real search, so stacking a + // margin spring underneath that felt like fighting it rather than + // adding to it — it gets a plain opacity fade instead (the + // `bar-entrance` CSS class, added unconditionally by both arms + // below; only liquid-motion also gets the margin spring). + // + // This animates the SURFACE's own layer-shell top margin via + // `anim::spring_to`, not a CSS property, deliberately: `theme.rs` + // has no `margin-top` transition/keyframe reachable from here + // that wouldn't also require moving `root_vbox`'s or + // `center_box`'s own CSS margin, which — unlike an outer + // layer-shell margin, a compositor-level surface placement, not a + // GTK box-model property at all — WOULD perturb every descendant + // widget's own measured size, including the workspace row's. + // `WorkspaceTrail::place()`'s single-shot initial geometry sample + // is a documented previous crash site for exactly that: `ws-in`'s + // margin-top keyframe on a first-build button raced that sample + // and froze the trail pill low (see the `!is_first_build` guard + // and its comment, a few hundred lines below, for the full + // story). A layer-shell margin change repositions the whole + // surface on screen without touching any widget's own box model, + // so `compute_bounds(host)`'s host-local coordinates — the trail's + // whole coordinate system — never see it move at all, by + // construction rather than by luck. + let theme_id = theme::shell_theme().id().to_string(); + if theme_id == "liquid-motion" { + const BAR_ENTRANCE_DROP: i32 = 18; + let target_top = window_spec.margin.top; + let start_top = (target_top - BAR_ENTRANCE_DROP).max(0); + root.set_margin(Edge::Top, start_top); + root.add_css_class("bar-entrance"); + let played = Rc::new(Cell::new(false)); + root.connect_map(move |win| { + // Guard against replaying on a later remap (e.g. a + // satellite hidden/reshown — see `root.set_visible(false)` + // a few lines below for the unbound-satellite case): this + // is a first-paint entrance, not a reveal animation. + if played.replace(true) { + return; + } + let target = win.clone(); + bread_theme::anim::spring_to(win, start_top, target_top, 420.0, move |v| { + target.set_margin(Edge::Top, v); + }); + }); + } else if theme_id == "spotlight" { + root.add_css_class("bar-entrance"); + } // breadbar never called `set_keyboard_mode` before Phase 2 — it // relied on gtk4-layer-shell's own default (`KeyboardMode::None`), // which is exactly what the builtin manifest's `keyboard = "none"` diff --git a/src/theme.rs b/src/theme.rs index 2d2f904..ebf648d 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -243,6 +243,17 @@ fn load_css() -> String { @keyframes row-in {{ from {{ opacity: 0; margin-top: 8px; }} to {{ opacity: 1; margin-top: 0; }} }}\ @keyframes digit-flip {{ from {{ opacity: 0; margin-top: 7px; }} to {{ opacity: 1; margin-top: 0; }} }}\ @keyframes caret-draw {{ from {{ margin-right: 200px; opacity: 0.2; }} to {{ margin-right: 4px; opacity: 1; }} }}\ + /* ANIMATION WORK #3, bar entrance on first map: opacity ONLY —\ + no margin/geometry term — so this can never perturb any\ + descendant's own box-model size (see main.rs's own long\ + comment on this, next to where `bar-entrance` gets added, for\ + why that matters to the workspace trail specifically).\ + Liquid Motion additionally springs the surface's own\ + layer-shell top margin via `anim::spring_to` in Rust, which\ + this keyframe knows nothing about; glass-workbench never adds\ + this class at all. */\ + @keyframes bar-in {{ from {{ opacity: 0; }} }}\ + .bar-entrance {{ animation: bar-in 0.4s {spring_settle} both; }}\ window.breadbar {{ background-color: alpha(@bg, {bg_alpha}); color: @on-bg;\ border-radius: {radius_bar}; {window_border}\ transition: border-radius 0.3s {spring_settle}; }}\