From 26cb31354f8c923614d3580eb977a20c35bb47d5 Mon Sep 17 00:00:00 2001 From: Breadway Date: Fri, 31 Jul 2026 07:21:13 +0800 Subject: [PATCH] breadpad: fix reminder alert, snooze popover, and compose popup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reminder alert: - Dismiss now matches Snooze's full-opacity text color instead of 0.6 alpha, which read as a disabled button. - Button row inset brought to 20px to match the header/body zone above it (was 16px, visible as a step across the divider). Snooze popover: - Options are left-aligned (each button's label now sets its own xalign) and span the full row width instead of centering as a ragged-left stack of labels. - Added a hairline divider between options so the list reads as distinct rows even in the popover's static/non-hover state. - Added a "Custom…" option that reveals a free-form time entry, reusing the same rule-based time parsing breadman's dialogs use, instead of only offering the three fixed presets. - Popover now matches the reminder card's flat-bordered elevation (no arrow, 1px border, no shadow) instead of GTK's default arrow+drop-shadow popover chrome. Compose popup: dropped the bare accent-teal checkmark submit button (same colour as the selected type pill, unlabeled) in favor of a "Press Enter to add" hint, since the popup already grabs focus and is keyboard-driven. Also made the entry's CSS padding uniform (14px on all sides, was 12px/16px). --- breadpad-shared/src/theme.rs | 34 ++++++++++-- breadpad/src/main.rs | 100 ++++++++++++++++++++++++++--------- 2 files changed, 107 insertions(+), 27 deletions(-) diff --git a/breadpad-shared/src/theme.rs b/breadpad-shared/src/theme.rs index 295a6dd..6823934 100644 --- a/breadpad-shared/src/theme.rs +++ b/breadpad-shared/src/theme.rs @@ -33,7 +33,7 @@ window { border-radius: 8px; } color: @fg; border: 2px solid @blue; border-radius: 6px; - padding: 12px 16px; + padding: 14px; font-size: 14px; caret-color: @fg; } @@ -183,12 +183,15 @@ window { border-radius: 8px; } color: @fg; } +/* Dismiss and Snooze are both secondary/outline actions and should read at + equal weight - Dismiss used to sit at 0.6 alpha next to Snooze's full + opacity, which made the button that closes the reminder look disabled. */ .reminder-dismiss { background: transparent; border: 1px solid @overlay; border-radius: 8px; padding: 8px 16px; - color: alpha(@fg, 0.6); + color: @fg; } .reminder-dismiss:hover { background: shade(@bg, 1.1); } @@ -203,15 +206,40 @@ window { border-radius: 8px; } .reminder-snooze:hover { background: shade(@bg, 1.1); } +/* Left-aligned (the button's child label sets xalign itself), full-width + row with a hairline divider so the list reads as distinct clickable rows + even before hover - a hover tint alone doesn't show up in a static + reading of the popover's default state. */ .snooze-option { background: transparent; border: none; border-radius: 6px; - padding: 8px 12px; + padding: 10px 12px; color: @fg; + border-bottom: 1px solid alpha(@overlay, 0.15); } .snooze-option:hover { background: shade(@bg, 1.2); } + +.snooze-custom-entry { + background: @bg; + color: @fg; + border: 1px solid @overlay; + border-radius: 6px; + padding: 8px 12px; + margin: 4px; +} + +.snooze-custom-entry:focus-within { border-color: @blue; outline: none; } + +/* Matches the reminder alert card's flat-bordered elevation (1px border, + 8px radius, no shadow) instead of GTK's default arrow+drop-shadow popover + chrome - the two surfaces used to speak two different elevation + languages. */ +popover.snooze-popover > contents { + border: 1px solid @overlay; + box-shadow: none; +} "#); if let Some(extra) = user_css { diff --git a/breadpad/src/main.rs b/breadpad/src/main.rs index 7ba8c87..4949963 100644 --- a/breadpad/src/main.rs +++ b/breadpad/src/main.rs @@ -3,6 +3,7 @@ use breadpad_shared::{ calendar::CalDavClient, classifier::Classifier, config::Config, + parser::parse_rule_based, scheduler::Scheduler, store::Store, types::{Note, NoteType}, @@ -492,14 +493,15 @@ fn build_reminder_window( .orientation(gtk4::Orientation::Horizontal) .build()); - // Button row + // Button row — same inset as the header/body zone above (20px), which + // used to be 16px here, visible as a step across the divider. let btn_row = gtk4::Box::builder() .orientation(gtk4::Orientation::Horizontal) .spacing(8) .margin_top(12) .margin_bottom(12) - .margin_start(16) - .margin_end(16) + .margin_start(20) + .margin_end(20) .build(); let dismiss_btn = gtk4::Button::builder() @@ -507,23 +509,35 @@ fn build_reminder_window( .css_classes(["reminder-dismiss"]) .build(); - // Snooze popover + // Snooze popover. No arrow and a matching flat border (see + // popover.snooze-popover in the shared theme) so it reads as the same + // elevation language as the reminder card, instead of GTK's default + // arrow+drop-shadow chrome next to the card's flat 1px border. let snooze_popover = gtk4::Popover::new(); + snooze_popover.set_has_arrow(false); + snooze_popover.add_css_class("snooze-popover"); let snooze_vbox = gtk4::Box::builder() .orientation(gtk4::Orientation::Vertical) - .spacing(4) - .margin_top(8) - .margin_bottom(8) - .margin_start(8) - .margin_end(8) + .spacing(0) + .margin_top(4) + .margin_bottom(4) .build(); + // Left-aligned row: a bare Button::builder().label() centers its text, + // so each option gets an explicit xalign(0.0) label as its child and + // hexpand(true) so the row fills the popover's full width instead of + // shrinking to the longest label. + let snooze_option_row = |label: &str| { + gtk4::Button::builder() + .child(>k4::Label::builder().label(label).xalign(0.0).build()) + .css_classes(["snooze-option"]) + .hexpand(true) + .build() + }; + for opt in &cfg.settings.snooze_options { let label = humanize_snooze(opt).to_string(); - let btn = gtk4::Button::builder() - .label(&label) - .css_classes(["snooze-option"]) - .build(); + let btn = snooze_option_row(&label); let key = opt.clone(); let note_c = note.clone(); let cfg_c = cfg.clone(); @@ -543,6 +557,47 @@ fn build_reminder_window( }); snooze_vbox.append(&btn); } + + // Custom… — reuses the same free-form time parsing breadman's dialogs + // already use, rather than inventing a separate time-picker widget. + let custom_entry = gtk4::Entry::builder() + .placeholder_text("tomorrow 9am / in 45 minutes") + .css_classes(["snooze-custom-entry"]) + .visible(false) + .build(); + { + let note_c = note.clone(); + let cfg_c = cfg.clone(); + let win_c = window.clone(); + let popover_c = snooze_popover.clone(); + let entry_c = custom_entry.clone(); + custom_entry.connect_activate(move |_| { + let text = entry_c.text().to_string(); + let parsed = parse_rule_based(&text, &cfg_c.reminders.default_morning); + if let Some(until) = parsed.time { + if let Ok(store) = Store::new().map(|s| s.with_calendar_if_enabled(&cfg_c)) { + let mut updated = note_c.as_ref().clone(); + updated.snoozed_until = Some(until); + let _ = store.update_note(&updated); + let _ = Scheduler::schedule(&updated); + } + popover_c.popdown(); + win_c.close(); + } + }); + } + let custom_btn = snooze_option_row("Custom\u{2026}"); + { + let custom_entry_c = custom_entry.clone(); + custom_btn.connect_clicked(move |btn| { + btn.set_visible(false); + custom_entry_c.set_visible(true); + custom_entry_c.grab_focus(); + }); + } + snooze_vbox.append(&custom_btn); + snooze_vbox.append(&custom_entry); + snooze_popover.set_child(Some(&snooze_vbox)); let snooze_btn = gtk4::MenuButton::builder() @@ -735,10 +790,13 @@ fn build_window( } } - // Confirm button - let confirm_btn = gtk4::Button::builder() - .label("✓") - .css_classes(["confirm-button"]) + // No submit button — this popup is keyboard-driven (grabs focus on + // open, Escape closes it) and a bare accent-teal checkmark used to sit + // next to the selected-type pill in the same accent teal, carrying two + // different meanings in one colour. A hint is enough. + let enter_hint = gtk4::Label::builder() + .label("Press Enter to add") + .css_classes(["dim-label"]) .build(); let bottom_row = gtk4::Box::builder() @@ -749,7 +807,7 @@ fn build_window( let spacer = gtk4::Box::builder().hexpand(true).build(); bottom_row.append(&spacer); - bottom_row.append(&confirm_btn); + bottom_row.append(&enter_hint); vbox.append(&entry); vbox.append(&bottom_row); @@ -785,12 +843,6 @@ fn build_window( } }; - // Confirm button click - { - let save = save_and_close.clone(); - confirm_btn.connect_clicked(move |_| save()); - } - // Entry activate (Enter key) { let save = save_and_close.clone();