Daylight bar chrome + embedded launcher, v0.7.5 pins #5

Merged
Breadway merged 41 commits from feature/theme-spotlight into main 2026-08-31 18:14:19 +08:00
Showing only changes of commit 74f280eaed - Show all commits

View file

@ -277,6 +277,13 @@ struct Geom {
struct TrailInner { struct TrailInner {
tick: Option<gtk4::TickCallbackId>, tick: Option<gtk4::TickCallbackId>,
geom: Geom, geom: Geom,
/// Last width/height measured from a button that was actually allocated.
/// A row rebuild (switching to an empty workspace makes Hyprland create
/// and destroy it) can leave every button unallocated for a frame, and
/// without a remembered size the trail had nothing safe to animate from
/// and fell back to an instant `place()` — which is what made a switch
/// snap instead of move.
natural: Option<(f64, f64)>,
} }
/// Overlay + Fixed pill sitting *behind* the workspace buttons. The /// Overlay + Fixed pill sitting *behind* the workspace buttons. The
@ -317,6 +324,7 @@ impl WorkspaceTrail {
let inner = Rc::new(RefCell::new(TrailInner { let inner = Rc::new(RefCell::new(TrailInner {
tick: None, tick: None,
natural: None,
geom: Geom { geom: Geom {
x: 0.0, x: 0.0,
y: 0.0, y: 0.0,
@ -418,6 +426,7 @@ impl WorkspaceTrail {
} else { } else {
None None
}; };
let cached = st.natural;
drop(st); drop(st);
// Width must always come from a button that is CURRENTLY IN THE ROW. // Width must always come from a button that is CURRENTLY IN THE ROW.
@ -447,19 +456,24 @@ impl WorkspaceTrail {
// Taking position from the live geometry and width from the source // Taking position from the live geometry and width from the source
// button's natural size keeps the motion continuous while making width // button's natural size keeps the motion continuous while making width
// a pure function of which button we started from. // a pure function of which button we started from.
// `natural` is None only if BOTH buttons are detached, in which case if let Some(n) = natural {
// there is no sane width to animate from and the caller falls back to self.inner.borrow_mut().natural = Some((n.w, n.h));
// an instant `place()`. There is deliberately no path that returns the }
// live width. // Only x comes from the live geometry. That is what makes an
match (live, natural) { // interrupted switch continue from where the pill currently is instead
(Some(live), Some(natural)) => Some(Geom { // of jumping back. y/w/h always come from a real button: taking y from
// a mid-animation or post-rebuild geometry is what made the pill sit
// low, and taking w from it is what let the width compound.
let size = natural.map(|n| (n.w, n.h)).or(cached);
match (live, natural, size) {
(Some(live), _, Some((w, h))) => Some(Geom {
x: live.x, x: live.x,
y: live.y, y: natural.map(|n| n.y).unwrap_or(live.y),
w: natural.w, w,
h: natural.h, h,
}), }),
(None, natural) => natural, (None, Some(natural), _) => Some(natural),
(Some(_), None) => None, _ => None,
} }
} }
} }