Commit graph

19 commits

Author SHA1 Message Date
Breadway
3eada87c7d notifications: click-through-except-buttons input region + push-down entrance
surface::click_through set a completely empty input region on the toast
so every pointer event passed through -- including to the toast's own
action buttons and inline-reply entry. Replace it with
surface::set_hit_region(window, widgets), which sets the input region to
the union of the given widgets' rectangles instead of empty; everywhere
else on the surface stays click-through exactly as before.

popup.rs recomputes this via refresh_hit_region every time the card set
could have changed (shown, dismissed, expired -- routed through the one
dismiss() function) and keeps recomputing every frame for HIT_TRACK_MS
afterward, since a card's own entrance animation or the stack's
push-down reflow can still be moving a button on the frame the change
happens. collect_interactive walks the real widget tree for
GtkButton/GtkEntry rather than tracking a flat list, so it can't drift
out of sync with make_card's structure. build_window's connect_map
handles the first-map race the same way surface::click_through used to.

Also: spring_in_card grows a newly shown card's height from 0 to its
natural size via bread_theme::anim::spring_to (same technique as
main.rs's animate_drawer_height), so the existing stack gets pushed
down smoothly instead of jumping.

KeyboardMode::None is unchanged on the toast; history.rs's OnDemand
mode is untouched.

Still to do: the actual dismiss button in make_card (this commit wires
the mechanism that will hit-test it, but no card has one yet).
2026-08-31 15:45:25 +08:00
Breadway
110ad2c6f9 notifications: re-apply toast click-through after the surface::apply migration
feature/theme-spotlight branched before main's 297207a ('notifications:
stop toast popups from stealing focus or blocking clicks') and the
notification popup was rewritten in this branch to use
surface::apply() for its layer-shell setup, which sets only
anchor/margin/width/layer — no input region — and left the window on
KeyboardMode::OnDemand. Merging this branch would have silently
reintroduced the original bug with no merge conflict to flag it.

Re-apply the fix on top of surface::apply(): add surface::click_through(),
an opt-in helper that sets an empty layer-shell input region on map so
every pointer event passes through to whatever's underneath, and call
it from the toast's create_window(). history.rs's window (the genuinely
interactive notification-history view) is untouched and correctly
keeps OnDemand and normal hit-testing.

Also switch the toast itself from KeyboardMode::OnDemand to
KeyboardMode::None. The toast's card layout does build action buttons
and, when a notification carries an inline-reply hint, a GtkEntry —
but with an empty input region nothing on the toast is ever clickable
or focusable regardless of keyboard mode, so OnDemand only offered a
focus capability with no way to trigger it. Those controls remain
reachable from the history window, which is opened deliberately and
keeps real hit-testing.
2026-08-31 15:45:25 +08:00
Breadway
1d30818510 surface: pin satellite window width instead of just requesting it
surface::apply()'s SurfaceWidth::Px handling only called
set_default_width(), which is advisory — a wide child (an unwrapped
app-name label, or a long summary/body with nothing narrower than its
natural width to wrap against) overrides it, so breadbar-notif (320px)
and breadbar-osd (180px) could render wider than their theme's
configured width instead of wrapping. This is the exact 'wide child
overrides set_default_width' trap main.rs's capsule Width::Px handling
already learned and documented; apply() predates that fix and never
got it.

Add the same set_size_request(px, -1) pin used there. history.rs's
360px override of the shared breadbar-notif namespace has to override
both calls now, not just set_default_width, since the pin from
apply() would otherwise win over a bare default-width override.
2026-08-31 15:45:25 +08:00
Breadway
bbb1a6c8d2 Drive bar/window geometry and CSS tokens from the shell theme manifest
breadbar no longer hardcodes BAR_HEIGHT/BAR_MARGIN_TOP/BAR_MARGIN_SIDES/
CHIP_HEIGHT/ICON_PX or the root window's layer-shell setup: it loads
bread_theme:🐚:ShellTheme once (theme::shell_theme(), cached in a
thread-local) and reads bar.window (anchors, margin, exclusive zone,
keyboard mode - now set explicitly instead of relying on the library
default) plus tokens.{icon_px,chip_height} from it everywhere those used
to be literals.

The four satellite surfaces (breadbar-osd, breadbar-notif, breadbar-panel,
breadbar-dismiss) now get their anchor/margin/width/layer from the
manifest's [surfaces.*] table via a small new surface::apply() helper,
narrowly scoped to the three anchor shapes those surfaces actually use.
The pre-existing 8px gap between the panel's top margin and the dismiss
scrim's is preserved exactly (and now commented) rather than "fixed".

theme.rs's load_css() keeps its ~250 lines of hand-written breadbar CSS
(notifications, wifi popover, control panel, media widget) but now reads
its five radius/pad locals and the two easing curves (spring vs
spring_settle - hover/settle transitions were previously miscategorized
as the overshoot curve in the constant audit; the actual code already
used the settle curve there, confirmed against src) from theme tokens
instead of hardcoding them.

Also wires bread_theme:🐚:watch() so editing the active theme's
theme.toml/extra.css hot-reloads CSS tokens without a restart, same as
a pywal palette change already does. Window-spec values still need a
restart per the plan (read once at window-construction time).

Verified pixel-identical: captured all 10 breadbar --screenshot views
(bar, control-panel, connectivity-wifi/bluetooth, media-popover,
notification/-critical, osd-volume/-brightness, wifi-add-dialog) via
bread-capture's isolated headless-Sway harness against both the
pre-change and post-change binaries; every view diffs byte-identical
at the decoded-pixel level.
2026-08-31 15:45:25 +08:00
Breadway
297207a7aa notifications: stop toast popups from stealing focus or blocking clicks
All checks were successful
dev release / build (push) Successful in 1m42s
Toasts never grab keyboard focus and pass every pointer event through
to whatever's underneath, via an empty layer-shell input region.
2026-08-24 13:11:20 +08:00
Breadway
1806c6f912 Bind each bar to its output palette and finish the island chrome
Some checks failed
dev release / build (push) Failing after 3m37s
One layer-shell window per Hyprland output now loads that output's
bread-theme palette. Notifications, history, and OSD follow the
monitor they appear on. Pin bread-theme to v0.7.4.
2026-08-16 13:23:31 +08:00
Breadway
110f0cd3df Add notification actions and inline reply
Some checks failed
check / check (push) Failing after 10s
dev release / build (push) Successful in 1m0s
Parse FDO action pairs onto popup buttons, emit ActionInvoked on
click (default action on the body), and show an inline-reply field
when senders request it. Closing still emits NotificationClosed.
History persist is unchanged.
2026-08-16 00:26:06 +08:00
Breadway
4fddad510f Persist notification history to XDG state
All checks were successful
dev release / build (push) Successful in 1m32s
2026-08-15 23:11:28 +08:00
Breadway
465088dc55 Add notification history; pin ecosystem crates to v0.7.2
All checks were successful
dev release / build (push) Successful in 1m25s
Keep a bounded in-memory history (last 50) and a layer-shell window
listing app, summary, truncated body, and time. Toggle with
`breadbar --history` or D-Bus `dev.breadway.Bar.ToggleHistory`.

Pin bread-theme, bread-utils, and bread-screenshots to tag v0.7.2.
bread-shared stays on bread v0.8.0-rc.1.
2026-08-15 22:54:04 +08:00
Breadway
566aeeed8b breadbar: capture every view, not just bar/control-panel
breadbar is "a bar + the notification daemon + the OSD" — the screenshot
mode only covered the bar and its control-panel popover, missing seven
more distinct surfaces: the WiFi/Bluetooth connectivity popover (both
tabs), the media-controls popover, the standalone notification window
(both normal and critical urgency), the standalone OSD window (volume
and brightness), and the wifi add-network dialog. All ten views are now
--screenshot targets.

Two real refactors needed to make the standalone notification/OSD
windows screenshot-able at all, not just bigger match arms:

- Both windows are built deep inside an async task (`run_osd`/
  `popup::run`), only reachable after the real event loop starts — no
  window handle ever existed for a caller to hook `connect_map` on before
  that. Window construction is now synchronous in `osd::spawn`/
  `notifications::spawn`, handed to the async loop as a parameter instead
  of created inside it.

- Screenshot mode seeds each with one fixed sample event (SampleKind) via
  the same channel the real pactl/backlight/D-Bus sources feed, instead
  of waiting for real hardware/dbus activity. For notifications
  specifically this also means skipping the real
  org.freedesktop.Notifications D-Bus registration entirely in screenshot
  mode — claiming that well-known name would just race the real breadbar
  (if running) for it, for no benefit, since nothing external needs to
  reach a screenshot-only instance.

show_add_network_dialog gained an `on_build` hook (called before
`.present()`, the only point `connect_map` can still catch the map) so
screenshot mode can capture it without changing its one real call site's
behavior — and its `anchor` parameter widened from `&Button` to
`&impl IsA<Widget>` since the screenshot path anchors off a
`ToggleButton`, not a `Button`.
2026-07-29 17:17:17 +08:00
Breadway
552d771e15 fix: honor x-canonical-private-synchronous hint for notification replacement
notify-send-based senders (e.g. breadcrumbs) fire a new process per
notification, so replaces_id is always 0 — they instead tag related
notifications with the x-canonical-private-synchronous hint and expect
the daemon to replace whatever's currently showing from that app. We
weren't honoring it, so a persistent (Expire::Never) critical
notification like breadcrumbs' "no Wi-Fi adapter" during a suspend
transition could never be superseded by a later notification, leaving
it stuck until breadbar was restarted. Fixes #2.
2026-07-23 10:12:52 +08:00
Breadway
2cd2fbc7f6 Merge branch 'fix/audit-findings' into dev
# Conflicts:
#	assets/icons-needed.txt
#	src/main.rs
#	src/notifications/mod.rs
#	src/notifications/popup.rs
2026-07-17 14:39:23 +08:00
Breadway
86432d718a Design refresh: new icon assets, bluetooth popover, notification urgency styling, RAM/power draw in control panel
Adds real SVG icon assets replacing the icons-needed.txt checklist,
a bluetooth popover module, per-notification urgency (CSS-styled),
and app-name/summary dedup in notification cards. Rounds out the
control panel's stats section with RAM and power draw alongside the
existing CPU/GPU/net rows.
2026-07-17 14:36:33 +08:00
Breadway
1f2d58d97f breadbar: reconnect Hyprland event stream, fix notification spec violations, point lock button at breadlock
- src/bar/workspaces.rs: the Hyprland EventStream loop exited permanently
  on the first Err/end-of-stream (Hyprland restart/reload, IPC hiccup),
  freezing every workspace button for the bar's remaining life. Now wrapped
  in a reconnect loop with capped exponential backoff, re-syncing workspace
  state on every reconnect
- src/notifications/mod.rs + popup.rs: three spec deviations fixed —
  expire_timeout=0 now means never-expire instead of being coerced to 5s
  (and a critical-urgency notification with no explicit timeout also
  persists by default); NotificationClosed is now emitted with the correct
  reason code whenever a notification actually goes away (expiry or an
  explicit CloseNotification call); replaces_id no longer races its
  auto-dismiss timer against the replacement's, via a per-id generation
  counter checked before a stale timer is allowed to dismiss anything
- src/main.rs + README.md + assets/icons-needed.txt: lock button (and its
  docs) now invoke breadlock instead of hyprlock, the thing breadlock was
  built to replace
- src/notifications/mod.rs: added 4 unit tests for the new expire_timeout/
  urgency mapping (pulled into a pure compute_expire() for testability) —
  this crate had zero test coverage before
2026-07-17 08:40:32 +08:00
Breadway
44a8d95887 feat: add system tray (StatusNotifierWatcher / SNI)
Some checks failed
release / build (push) Failing after 3s
Implements org.kde.StatusNotifierWatcher as a D-Bus service so apps
like Nextcloud can register their tray icons. Icons are rendered from
SNI ARGB pixmaps (falling back to icon-name theme lookup), click calls
Activate(0,0), and NameOwnerChanged cleans up ghost icons when an app
exits. Styling follows the Bread Design System (4px tertiary radius,
xs/sm spacing, opacity transitions).

Also fixes a latent infinite-loop risk in osd.rs (.flatten → .map_while)
and syncs the notifications server version string to CARGO_PKG_VERSION.
2026-06-11 22:31:25 +08:00
Breadway
4a3604f78a Fixes 2026-05-19 12:31:26 +08:00
Breadway
633467b6f2 Initial commit 2026-05-17 13:13:37 +08:00
Breadway
3100ee0591 step 8-10: notification daemon (zbus), popup window, SIGHUP theme reload 2026-05-17 08:50:08 +08:00
Breadway
1bbe9a2929 scaffold: Cargo.toml, module stubs, layer-shell window (step 1+2) 2026-05-17 00:38:19 +08:00