breadpad: fix reminder alert, snooze popover, and compose popup
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).
This commit is contained in:
parent
de7965edae
commit
26cb31354f
2 changed files with 107 additions and 27 deletions
|
|
@ -33,7 +33,7 @@ window { border-radius: 8px; }
|
||||||
color: @fg;
|
color: @fg;
|
||||||
border: 2px solid @blue;
|
border: 2px solid @blue;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
padding: 12px 16px;
|
padding: 14px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
caret-color: @fg;
|
caret-color: @fg;
|
||||||
}
|
}
|
||||||
|
|
@ -183,12 +183,15 @@ window { border-radius: 8px; }
|
||||||
color: @fg;
|
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 {
|
.reminder-dismiss {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: 1px solid @overlay;
|
border: 1px solid @overlay;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
color: alpha(@fg, 0.6);
|
color: @fg;
|
||||||
}
|
}
|
||||||
|
|
||||||
.reminder-dismiss:hover { background: shade(@bg, 1.1); }
|
.reminder-dismiss:hover { background: shade(@bg, 1.1); }
|
||||||
|
|
@ -203,15 +206,40 @@ window { border-radius: 8px; }
|
||||||
|
|
||||||
.reminder-snooze:hover { background: shade(@bg, 1.1); }
|
.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 {
|
.snooze-option {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
padding: 8px 12px;
|
padding: 10px 12px;
|
||||||
color: @fg;
|
color: @fg;
|
||||||
|
border-bottom: 1px solid alpha(@overlay, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
.snooze-option:hover { background: shade(@bg, 1.2); }
|
.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 {
|
if let Some(extra) = user_css {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ use breadpad_shared::{
|
||||||
calendar::CalDavClient,
|
calendar::CalDavClient,
|
||||||
classifier::Classifier,
|
classifier::Classifier,
|
||||||
config::Config,
|
config::Config,
|
||||||
|
parser::parse_rule_based,
|
||||||
scheduler::Scheduler,
|
scheduler::Scheduler,
|
||||||
store::Store,
|
store::Store,
|
||||||
types::{Note, NoteType},
|
types::{Note, NoteType},
|
||||||
|
|
@ -492,14 +493,15 @@ fn build_reminder_window(
|
||||||
.orientation(gtk4::Orientation::Horizontal)
|
.orientation(gtk4::Orientation::Horizontal)
|
||||||
.build());
|
.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()
|
let btn_row = gtk4::Box::builder()
|
||||||
.orientation(gtk4::Orientation::Horizontal)
|
.orientation(gtk4::Orientation::Horizontal)
|
||||||
.spacing(8)
|
.spacing(8)
|
||||||
.margin_top(12)
|
.margin_top(12)
|
||||||
.margin_bottom(12)
|
.margin_bottom(12)
|
||||||
.margin_start(16)
|
.margin_start(20)
|
||||||
.margin_end(16)
|
.margin_end(20)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let dismiss_btn = gtk4::Button::builder()
|
let dismiss_btn = gtk4::Button::builder()
|
||||||
|
|
@ -507,23 +509,35 @@ fn build_reminder_window(
|
||||||
.css_classes(["reminder-dismiss"])
|
.css_classes(["reminder-dismiss"])
|
||||||
.build();
|
.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();
|
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()
|
let snooze_vbox = gtk4::Box::builder()
|
||||||
.orientation(gtk4::Orientation::Vertical)
|
.orientation(gtk4::Orientation::Vertical)
|
||||||
.spacing(4)
|
.spacing(0)
|
||||||
.margin_top(8)
|
.margin_top(4)
|
||||||
.margin_bottom(8)
|
.margin_bottom(4)
|
||||||
.margin_start(8)
|
|
||||||
.margin_end(8)
|
|
||||||
.build();
|
.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 {
|
for opt in &cfg.settings.snooze_options {
|
||||||
let label = humanize_snooze(opt).to_string();
|
let label = humanize_snooze(opt).to_string();
|
||||||
let btn = gtk4::Button::builder()
|
let btn = snooze_option_row(&label);
|
||||||
.label(&label)
|
|
||||||
.css_classes(["snooze-option"])
|
|
||||||
.build();
|
|
||||||
let key = opt.clone();
|
let key = opt.clone();
|
||||||
let note_c = note.clone();
|
let note_c = note.clone();
|
||||||
let cfg_c = cfg.clone();
|
let cfg_c = cfg.clone();
|
||||||
|
|
@ -543,6 +557,47 @@ fn build_reminder_window(
|
||||||
});
|
});
|
||||||
snooze_vbox.append(&btn);
|
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));
|
snooze_popover.set_child(Some(&snooze_vbox));
|
||||||
|
|
||||||
let snooze_btn = gtk4::MenuButton::builder()
|
let snooze_btn = gtk4::MenuButton::builder()
|
||||||
|
|
@ -735,10 +790,13 @@ fn build_window(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Confirm button
|
// No submit button — this popup is keyboard-driven (grabs focus on
|
||||||
let confirm_btn = gtk4::Button::builder()
|
// open, Escape closes it) and a bare accent-teal checkmark used to sit
|
||||||
.label("✓")
|
// next to the selected-type pill in the same accent teal, carrying two
|
||||||
.css_classes(["confirm-button"])
|
// 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();
|
.build();
|
||||||
|
|
||||||
let bottom_row = gtk4::Box::builder()
|
let bottom_row = gtk4::Box::builder()
|
||||||
|
|
@ -749,7 +807,7 @@ fn build_window(
|
||||||
|
|
||||||
let spacer = gtk4::Box::builder().hexpand(true).build();
|
let spacer = gtk4::Box::builder().hexpand(true).build();
|
||||||
bottom_row.append(&spacer);
|
bottom_row.append(&spacer);
|
||||||
bottom_row.append(&confirm_btn);
|
bottom_row.append(&enter_hint);
|
||||||
|
|
||||||
vbox.append(&entry);
|
vbox.append(&entry);
|
||||||
vbox.append(&bottom_row);
|
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)
|
// Entry activate (Enter key)
|
||||||
{
|
{
|
||||||
let save = save_and_close.clone();
|
let save = save_and_close.clone();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue