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
2 changed files with 26 additions and 5 deletions
Showing only changes of commit 1b523eab63 - Show all commits

View file

@ -369,7 +369,7 @@ impl WorkspaceTrail {
pub fn stretch(&self, from: Option<&gtk4::Button>, to: &gtk4::Button) { pub fn stretch(&self, from: Option<&gtk4::Button>, to: &gtk4::Button) {
self.cancel(); self.cancel();
let Some(from_g) = self.from_geom(from) else { let Some(from_g) = self.from_geom(from, to) else {
self.place(to); self.place(to);
return; return;
}; };
@ -411,7 +411,7 @@ impl WorkspaceTrail {
self.inner.borrow_mut().tick = Some(id); self.inner.borrow_mut().tick = Some(id);
} }
fn from_geom(&self, from: Option<&gtk4::Button>) -> Option<Geom> { fn from_geom(&self, from: Option<&gtk4::Button>, to: &gtk4::Button) -> Option<Geom> {
let st = self.inner.borrow(); let st = self.inner.borrow();
let live = if self.pill.is_visible() && st.geom.w > 0.5 { let live = if self.pill.is_visible() && st.geom.w > 0.5 {
Some(st.geom) Some(st.geom)
@ -420,7 +420,17 @@ impl WorkspaceTrail {
}; };
drop(st); drop(st);
let natural = from.and_then(|b| button_geom(b, &self.host).map(inset_pill)); // Width must always come from a button that is CURRENTLY IN THE ROW.
// Switching to an empty workspace makes Hyprland create and destroy it,
// which rebuilds the button row mid-animation and can leave `from`
// detached — `button_geom` then returns None. Falling back to the live
// geometry there handed the wide mid-stretch span straight back in,
// which is exactly the accumulation this function exists to prevent
// (reproduced by spamming between workspace 1 and an empty 6). The
// destination button is always live, so it is the correct fallback.
let natural = from
.and_then(|b| button_geom(b, &self.host).map(inset_pill))
.or_else(|| button_geom(to, &self.host).map(inset_pill));
// Continuity without accumulation. // Continuity without accumulation.
// //
@ -437,6 +447,10 @@ 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
// there is no sane width to animate from and the caller falls back to
// an instant `place()`. There is deliberately no path that returns the
// live width.
match (live, natural) { match (live, natural) {
(Some(live), Some(natural)) => Some(Geom { (Some(live), Some(natural)) => Some(Geom {
x: live.x, x: live.x,
@ -444,8 +458,8 @@ impl WorkspaceTrail {
w: natural.w, w: natural.w,
h: natural.h, h: natural.h,
}), }),
(Some(live), None) if live.w <= MAX_CHIP_W => Some(live), (None, natural) => natural,
(_, natural) => natural, (Some(_), None) => None,
} }
} }
} }

View file

@ -1915,6 +1915,13 @@ impl SimpleComponent for App {
// below is a silent no-op. // below is a silent no-op.
self.launcher_entry.set_can_focus(true); self.launcher_entry.set_can_focus(true);
self.launcher_entry.grab_focus(); self.launcher_entry.grab_focus();
// ...and then actually OPEN it. Focus alone no longer
// opens the capsule: `connect_enter` used to call
// `open_fn()`, which is precisely what made the capsule
// open itself during window construction, so that path
// was deliberately removed. Without this call the
// keybind focuses the entry and leaves the drawer shut.
(self.launcher_open_fn)();
} }
} }
} }