Draw an emoji in a line as a character, not as a tally
`:reaction` was the only node that reached the Twemoji pack, so anything wanting a colour glyph had to ask for one — and got the pill that comes with it. In a message that is wrong twice over: the pill and border say somebody reacted to the line, and the node answers the pointer and opens a tooltip naming people who were never there. So split the picture from the tally. `:emoji` allocates the square and paints the glyph, and stops: no frame, no count, no `mine`, no hover. `vidya_core::emoji_icon` has drawn exactly that all along; nothing in the tree layer had a way to ask for it. `:size` defaults to the theme's body size rather than the caption size a chip uses, because an emoji in a sentence has to sit level with the words either side of it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
00d32dd parent: a7f6202 modified
crates/jolt-vidya/src/tree.rs +25 -0 | @@ -68,6 +68,9 @@ pub enum Tag { | ||
| 68 | 68 | Image, |
| 69 | 69 | Avatar, |
| 70 | 70 | Reaction, |
| 71 | + /// One emoji, drawn from the pack and nothing else: no pill, no count, no | |
| 72 | + /// pointer. `Reaction` is the same glyph wearing a tally's clothes. | |
| 73 | + Emoji, | |
| 71 | 74 | Status, |
| 72 | 75 | /// A tag this backend has not grown yet, keeping the name it was created |
| 73 | 76 | /// with so a dump answers what the caller actually asked for. |
| @@ -100,6 +103,7 @@ impl Tag { | ||
| 100 | 103 | "image" => Self::Image, |
| 101 | 104 | "avatar" => Self::Avatar, |
| 102 | 105 | "reaction" => Self::Reaction, |
| 106 | + "emoji" => Self::Emoji, | |
| 103 | 107 | "status" => Self::Status, |
| 104 | 108 | other => Self::Unknown(other.to_owned()), |
| 105 | 109 | } |
| @@ -130,6 +134,7 @@ impl Tag { | ||
| 130 | 134 | Self::Image => "image", |
| 131 | 135 | Self::Avatar => "avatar", |
| 132 | 136 | Self::Reaction => "reaction", |
| 137 | + Self::Emoji => "emoji", | |
| 133 | 138 | Self::Status => "status", |
| 134 | 139 | Self::Unknown(name) => name, |
| 135 | 140 | } |
| @@ -1352,6 +1357,26 @@ impl Tree { | ||
| 1352 | 1357 | } |
| 1353 | 1358 | } |
| 1354 | 1359 | |
| 1360 | + Tag::Emoji => { | |
| 1361 | + // A glyph the text font cannot set, drawn from the pack and | |
| 1362 | + // put in the line as if it were a word. `Reaction` draws the | |
| 1363 | + // same picture, but a reaction is a tally: it wears a pill, it | |
| 1364 | + // answers the pointer, and it names the people in it on hover. | |
| 1365 | + // An emoji in a sentence is none of those things — it is a | |
| 1366 | + // character — so this allocates the square and paints, and | |
| 1367 | + // stops there. | |
| 1368 | + let emoji = props.str("emoji").to_owned(); | |
| 1369 | + let emoji = if emoji.is_empty() { | |
| 1370 | + props.label().to_owned() | |
| 1371 | + } else { | |
| 1372 | + emoji | |
| 1373 | + }; | |
| 1374 | + // Body size by default, because the words either side are what | |
| 1375 | + // it has to sit level with. | |
| 1376 | + let size = props.num("size", theme.type_scale.body as f64) as f32; | |
| 1377 | + vidya_core::emoji_icon(ui, theme, &emoji, size); | |
| 1378 | + } | |
| 1379 | + | |
| 1355 | 1380 | Tag::Image => { |
| 1356 | 1381 | // Two sources, one tag: a `src` is a file decoded once and |
| 1357 | 1382 | // cached by its path, a `feed` is live pixels pushed in under |
| @@ -68,6 +68,9 @@ pub enum Tag { | |||
| 68 | Image, | 68 | Image, |
| 69 | Avatar, | 69 | Avatar, |
| 70 | Reaction, | 70 | Reaction, |
| 71 | + /// One emoji, drawn from the pack and nothing else: no pill, no count, no | ||
| 72 | + /// pointer. `Reaction` is the same glyph wearing a tally's clothes. | ||
| 73 | + Emoji, | ||
| 71 | Status, | 74 | Status, |
| 72 | /// A tag this backend has not grown yet, keeping the name it was created | 75 | /// A tag this backend has not grown yet, keeping the name it was created |
| 73 | /// with so a dump answers what the caller actually asked for. | 76 | /// with so a dump answers what the caller actually asked for. |
| @@ -100,6 +103,7 @@ impl Tag { | |||
| 100 | "image" => Self::Image, | 103 | "image" => Self::Image, |
| 101 | "avatar" => Self::Avatar, | 104 | "avatar" => Self::Avatar, |
| 102 | "reaction" => Self::Reaction, | 105 | "reaction" => Self::Reaction, |
| 106 | + "emoji" => Self::Emoji, | ||
| 103 | "status" => Self::Status, | 107 | "status" => Self::Status, |
| 104 | other => Self::Unknown(other.to_owned()), | 108 | other => Self::Unknown(other.to_owned()), |
| 105 | } | 109 | } |
| @@ -130,6 +134,7 @@ impl Tag { | |||
| 130 | Self::Image => "image", | 134 | Self::Image => "image", |
| 131 | Self::Avatar => "avatar", | 135 | Self::Avatar => "avatar", |
| 132 | Self::Reaction => "reaction", | 136 | Self::Reaction => "reaction", |
| 137 | + Self::Emoji => "emoji", | ||
| 133 | Self::Status => "status", | 138 | Self::Status => "status", |
| 134 | Self::Unknown(name) => name, | 139 | Self::Unknown(name) => name, |
| 135 | } | 140 | } |
| @@ -1352,6 +1357,26 @@ impl Tree { | |||
| 1352 | } | 1357 | } |
| 1353 | } | 1358 | } |
| 1354 | 1359 | ||
| 1360 | + Tag::Emoji => { | ||
| 1361 | + // A glyph the text font cannot set, drawn from the pack and | ||
| 1362 | + // put in the line as if it were a word. `Reaction` draws the | ||
| 1363 | + // same picture, but a reaction is a tally: it wears a pill, it | ||
| 1364 | + // answers the pointer, and it names the people in it on hover. | ||
| 1365 | + // An emoji in a sentence is none of those things — it is a | ||
| 1366 | + // character — so this allocates the square and paints, and | ||
| 1367 | + // stops there. | ||
| 1368 | + let emoji = props.str("emoji").to_owned(); | ||
| 1369 | + let emoji = if emoji.is_empty() { | ||
| 1370 | + props.label().to_owned() | ||
| 1371 | + } else { | ||
| 1372 | + emoji | ||
| 1373 | + }; | ||
| 1374 | + // Body size by default, because the words either side are what | ||
| 1375 | + // it has to sit level with. | ||
| 1376 | + let size = props.num("size", theme.type_scale.body as f64) as f32; | ||
| 1377 | + vidya_core::emoji_icon(ui, theme, &emoji, size); | ||
| 1378 | + } | ||
| 1379 | + | ||
| 1355 | Tag::Image => { | 1380 | Tag::Image => { |
| 1356 | // Two sources, one tag: a `src` is a file decoded once and | 1381 | // Two sources, one tag: a `src` is a file decoded once and |
| 1357 | // cached by its path, a `feed` is live pixels pushed in under | 1382 | // cached by its path, a `feed` is live pixels pushed in under |