bar: entrance animation on first map
ANIMATION WORK #3. Liquid Motion only: the island's layer-shell top margin springs (anim::spring_to) from just above its resting position up to the theme's configured margin.top over 420ms on first map, plus an opacity-only fade (the new bar-entrance CSS class / bar-in keyframe). Deliberately animates the surface's own layer-shell margin in Rust rather than a CSS margin on an inner widget: that would touch box-model geometry, and WorkspaceTrail::place()'s single-shot initial sample is a documented previous crash site for exactly that kind of still-moving-on-first-paint bug (see the ws-in/row-in guard's own comment further down). A layer-shell margin change repositions the whole surface without touching any widget's own measured size, so it's invisible to compute_bounds(host) by construction. glass-workbench (flush edge-to-edge bar, no floating margin to slide from) gets nothing. spotlight gets the opacity fade only -- its capsule already has its own width/drawer motion in flight during a real search, so a margin spring felt like piling onto that rather than complementing it.
This commit is contained in:
parent
c0a013c8ed
commit
9761b85112
2 changed files with 63 additions and 0 deletions
52
src/main.rs
52
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"`
|
||||
|
|
|
|||
11
src/theme.rs
11
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}; }}\
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue