notifications: add a dismiss button to the toast card
NOTIFICATION INTERACTION #A: a direct way to dismiss a toast, rather than only auto-expiry or the D-Bus CloseNotification path. A small "x" button floats in the card's top-right corner via a gtk4::Overlay wrapping the existing content box, styled via the new .notification-dismiss CSS class, so it doesn't add an extra header row the approved demo's card layout never has. dismiss_card emits NotificationClosed(id, DISMISSED_BY_USER) -- the same freedesktop reason code (2) invoke_action/submit_reply already use for their own user-initiated dismissals -- via the shared dismiss() helper, which (as of the previous commit) already recomputes the toast's clickable input region on every removal.
This commit is contained in:
parent
3eada87c7d
commit
c0a013c8ed
1 changed files with 44 additions and 1 deletions
|
|
@ -397,7 +397,34 @@ fn make_card(spec: CardSpec<'_>) -> gtk4::Box {
|
|||
content.add_controller(gesture);
|
||||
}
|
||||
|
||||
card.append(&content);
|
||||
// NOTIFICATION INTERACTION #A: a direct dismiss control. Floated in
|
||||
// the card's top-right corner via an Overlay rather than a full extra
|
||||
// header row, so it doesn't add vertical bulk the approved demo's own
|
||||
// card never has (see the `.notification-dismiss` CSS in theme.rs).
|
||||
// `collect_interactive` (this module) picks it up the same way it
|
||||
// picks up the action/reply buttons below, by walking the real widget
|
||||
// tree — it doesn't need to know this button lives one level deeper,
|
||||
// inside the overlay, than they do.
|
||||
let overlay = gtk4::Overlay::new();
|
||||
overlay.set_child(Some(&content));
|
||||
|
||||
let dismiss_btn = gtk4::Button::with_label("×");
|
||||
dismiss_btn.add_css_class("notification-dismiss");
|
||||
dismiss_btn.set_halign(gtk4::Align::End);
|
||||
dismiss_btn.set_valign(gtk4::Align::Start);
|
||||
let dismiss_invoke = Invoke {
|
||||
conn: spec.conn.clone(),
|
||||
cards: spec.cards.clone(),
|
||||
cards_box: spec.cards_box.clone(),
|
||||
window: spec.window.clone(),
|
||||
id: spec.id,
|
||||
};
|
||||
dismiss_btn.connect_clicked(move |_| {
|
||||
dismiss_card(dismiss_invoke.clone());
|
||||
});
|
||||
overlay.add_overlay(&dismiss_btn);
|
||||
|
||||
card.append(&overlay);
|
||||
|
||||
let visible: Vec<&Action> = spec
|
||||
.actions
|
||||
|
|
@ -500,6 +527,22 @@ fn invoke_action(invoke: Invoke, key: &str) {
|
|||
});
|
||||
}
|
||||
|
||||
/// NOTIFICATION INTERACTION #A: the card's own dismiss button. Unlike
|
||||
/// `invoke_action`, this never emits `ActionInvoked` — there's no action
|
||||
/// key here, the user just closed the toast unprompted — only the
|
||||
/// spec-mandated `NotificationClosed(id, reason)`, with
|
||||
/// `close_reason::DISMISSED_BY_USER` (freedesktop value 2, "dismissed by
|
||||
/// the user") so clients are told properly, same reason code
|
||||
/// `invoke_action` above and `submit_reply` below already use for their
|
||||
/// own user-initiated dismissals.
|
||||
fn dismiss_card(invoke: Invoke) {
|
||||
relm4::spawn_local(async move {
|
||||
if dismiss(&invoke.cards_box, &invoke.window, &invoke.cards, invoke.id) {
|
||||
emit_closed(&invoke.conn, invoke.id, close_reason::DISMISSED_BY_USER).await;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn submit_reply(entry: >k4::Entry, invoke: Invoke) {
|
||||
let text = entry.text().to_string();
|
||||
if text.trim().is_empty() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue