notification daemon
git clone https://git.lucas.co/cce-notifier.git
feat: word-wrap the notification body inside the card
A long body ran straight off the right edge of the card, past the plate's
rounded corner, and was cut off by the surface edge — more visible now
that several cards sit on screen at once.
The body is now boxed text: it wraps into the card's text column, which
is whatever is left between the text origin (the thumbnail pushes it
right) and the card's right padding. `box_height` bounds it — the engine
lays boxed text out at a 1.4 line height, so the column admits three
11px lines and shapes the rest away rather than spilling onto the card
below. A card is a fixed height and cannot grow to fit, so a very long
body is truncated by construction; wrapping just makes the first three
lines readable instead of one clipped one.
The single-line labels above it (app name, summary) gain the same column
as clip bounds, so they stop at the padding instead of running to the
plate's edge.
Verified in a headless shadow: a short body is unchanged, a long one
wraps to three lines, an overlong one stops at three without touching
the next card, a body beside a thumbnail breaks earlier in its narrower
column, and an unbreakable 60-char path breaks at the wrap width instead
of overflowing.
src/main.rs | 37 ++++++++++++++++++++++++++++++++++---
1 file changed, 34 insertions(+), 3 deletions(-)
diff --git a/src/main.rs b/src/main.rs
index 8c02353..930496d 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -31,6 +31,13 @@ const NOTIF_HEIGHT: u32 = MAX_VISIBLE as u32 * (CARD_H + CARD_GAP) - CARD_GAP;
const THUMB_MAX_W: f32 = 100.0;
const THUMB_MAX_H: f32 = 76.0;
+// The text column's right and bottom padding, and where the body starts. The
+// body gets whatever is left of the card below `BODY_TOP`.
+const CARD_PAD: f32 = 14.0;
+const CARD_PAD_B: f32 = 6.0;
+const BODY_TOP: f32 = 46.0;
+const BODY_SIZE: f32 = 11.0;
+
#[derive(Debug, Clone)]
enum UserEvent {
NewNotification {
@@ -221,9 +228,33 @@ fn draw_card(
// from the engine's bundled-only database.
let family = cce_ui::layout::statusbar_font_parsed().0;
let font = Some(family);
- pc.text_with(¬ification.app_name, text_x, top + 12.0, 10.0, srgb_u8(cce_ui::colors::TEXT_DIM), font.clone(), None);
- pc.text_with(¬ification.summary, text_x, top + 28.0, 13.0, srgb_u8(cce_ui::colors::TEXT_HEADER), font.clone(), None);
- pc.text_with(¬ification.body, text_x, top + 48.0, 11.0, srgb_u8(cce_ui::colors::TEXT_FG), font, None);
+ // The text column: from `text_x` (which the thumbnail may have pushed right)
+ // to the card's right padding. Every label is bounded by it, so nothing runs
+ // out over the plate's edge and rounded corner.
+ let text_w = NOTIF_WIDTH as f32 - text_x - CARD_PAD;
+ let column = |t: f32, b: f32| Some([text_x, top + t, text_x + text_w, top + b]);
+ pc.text_with(¬ification.app_name, text_x, top + 12.0, 10.0, srgb_u8(cce_ui::colors::TEXT_DIM), font.clone(), column(8.0, BODY_TOP));
+ pc.text_with(¬ification.summary, text_x, top + 28.0, 13.0, srgb_u8(cce_ui::colors::TEXT_HEADER), font.clone(), column(24.0, BODY_TOP));
+ // The body word-wraps within that column instead of running off the card.
+ // `box_height` is what bounds it: the engine lays boxed text out at a 1.4
+ // line height, so this admits three 11px lines (46.2 of 48) and shapes away
+ // the rest — a card is a fixed height and cannot grow to fit.
+ pc.text_boxed(
+ ¬ification.body,
+ text_x,
+ top + BODY_TOP,
+ BODY_SIZE,
+ srgb_u8(cce_ui::colors::TEXT_FG),
+ font,
+ column(BODY_TOP, card_h - CARD_PAD_B),
+ cce_ui::scene::paint::TextAttrs::default(),
+ cce_ui::scene::paint::TextLayout {
+ wrap_width: Some(text_w),
+ box_height: card_h - BODY_TOP - CARD_PAD_B,
+ align_h: cce_ui::scene::paint::AlignH::Left,
+ align_v: cce_ui::scene::paint::AlignV::Top,
+ },
+ );
}
// ── Application ───────────────────────────────────────────────────────────