notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

commit c4084a1fb5d03a97c220fd01097791492671a20f
parent e4658df8473dcf914ccfb65bffb27274ef3210d8
Author: William Casarin <jb55@jb55.com>
Date:   Tue, 22 Apr 2025 10:48:11 -0700

ui: add note truncation

Truncate notes by default. We still need a show more button though

Diffstat:
Mcrates/notedeck_columns/src/timeline/route.rs | 30++++++++++++++++++------------
Mcrates/notedeck_ui/src/note/contents.rs | 37+++++++++++++++++++++++++++++++------
Mcrates/notedeck_ui/src/note/mod.rs | 5+++++
Mcrates/notedeck_ui/src/note/options.rs | 10+++++++++-
4 files changed, 63 insertions(+), 19 deletions(-)

diff --git a/crates/notedeck_columns/src/timeline/route.rs b/crates/notedeck_columns/src/timeline/route.rs @@ -75,18 +75,24 @@ pub fn render_timeline_route( } } - TimelineKind::Thread(id) => ui::ThreadView::new( - timeline_cache, - unknown_ids, - id.selected_or_root(), - note_options, - &accounts.mutefun(), - note_context, - &accounts.get_selected_account().map(|a| (&a.key).into()), - ) - .id_source(egui::Id::new(("threadscroll", col))) - .ui(ui) - .map(Into::into), + TimelineKind::Thread(id) => { + // don't truncate thread notes for now, since they are + // default truncated everywher eelse + note_options.set_truncate(false); + + ui::ThreadView::new( + timeline_cache, + unknown_ids, + id.selected_or_root(), + note_options, + &accounts.mutefun(), + note_context, + &accounts.get_selected_account().map(|a| (&a.key).into()), + ) + .id_source(egui::Id::new(("threadscroll", col))) + .ui(ui) + .map(Into::into) + } } } diff --git a/crates/notedeck_ui/src/note/contents.rs b/crates/notedeck_ui/src/note/contents.rs @@ -119,6 +119,10 @@ pub fn render_note_contents( let hide_media = options.has_hide_media(); let link_color = ui.visuals().hyperlink_color; + // The current length of the rendered blocks. Used in trucation logic + let mut current_len: usize = 0; + let truncate_len = 280; + if !options.has_is_preview() { // need this for the rect to take the full width of the column let _ = ui.allocate_at_least(egui::vec2(ui.available_width(), 0.0), egui::Sense::click()); @@ -210,18 +214,39 @@ pub fn render_note_contents( } BlockType::Text => { - if options.has_scramble_text() { - ui.add( - egui::Label::new(rot13(block.as_str())) - .wrap() - .selectable(selectable), + // truncate logic + let mut truncate = false; + let block_str = if options.has_truncate() + && (current_len + block.as_str().len() > truncate_len) + { + truncate = true; + // The current block goes over the truncate length, + // we'll need to truncate this block + let block_str = block.as_str(); + let closest = notedeck::abbrev::floor_char_boundary( + block_str, + truncate_len - current_len, ); + &(block_str[..closest].to_string() + "…") } else { + let block_str = block.as_str(); + current_len += block_str.len(); + block_str + }; + + if options.has_scramble_text() { ui.add( - egui::Label::new(block.as_str()) + egui::Label::new(rot13(block_str)) .wrap() .selectable(selectable), ); + } else { + ui.add(egui::Label::new(block_str).wrap().selectable(selectable)); + } + + // don't render any more blocks + if truncate { + break; } } diff --git a/crates/notedeck_ui/src/note/mod.rs b/crates/notedeck_ui/src/note/mod.rs @@ -114,6 +114,11 @@ impl<'a, 'd> NoteView<'a, 'd> { self } + pub fn truncate(mut self, enable: bool) -> Self { + self.options_mut().set_truncate(enable); + self + } + pub fn small_pfp(mut self, enable: bool) -> Self { self.options_mut().set_small_pfp(enable); self diff --git a/crates/notedeck_ui/src/note/options.rs b/crates/notedeck_ui/src/note/options.rs @@ -21,12 +21,19 @@ bitflags! { /// Whether the current note is a preview const is_preview = 0b0000010000000000; + + /// Is the content truncated? If the length is over a certain size it + /// will end with a ... and a "Show more" button. + const truncate = 0b0000100000000000; } } impl Default for NoteOptions { fn default() -> NoteOptions { - NoteOptions::options_button | NoteOptions::note_previews | NoteOptions::actionbar + NoteOptions::options_button + | NoteOptions::note_previews + | NoteOptions::actionbar + | NoteOptions::truncate } } @@ -60,6 +67,7 @@ impl NoteOptions { create_bit_methods!(set_hide_media, has_hide_media, hide_media); create_bit_methods!(set_scramble_text, has_scramble_text, scramble_text); create_bit_methods!(set_is_preview, has_is_preview, is_preview); + create_bit_methods!(set_truncate, has_truncate, truncate); pub fn new(is_universe_timeline: bool) -> Self { let mut options = NoteOptions::default();