Skip to content

Commit

Permalink
Implement search result highlighting
Browse files Browse the repository at this point in the history
Signed-off-by: Alex Saveau <[email protected]>
  • Loading branch information
SUPERCILEX committed Aug 22, 2024
1 parent 7123033 commit 57a78d7
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 54 deletions.
6 changes: 6 additions & 0 deletions client-sdk/api.golden
Original file line number Diff line number Diff line change
Expand Up @@ -793,9 +793,15 @@ pub enum clipboard_history_client_sdk::ui_actor::UiEntryCache
pub clipboard_history_client_sdk::ui_actor::UiEntryCache::Binary
pub clipboard_history_client_sdk::ui_actor::UiEntryCache::Binary::mime_type: alloc::boxed::Box<str>
pub clipboard_history_client_sdk::ui_actor::UiEntryCache::Error(clipboard_history_core::Error)
pub clipboard_history_client_sdk::ui_actor::UiEntryCache::HighlightedText
pub clipboard_history_client_sdk::ui_actor::UiEntryCache::HighlightedText::end: usize
pub clipboard_history_client_sdk::ui_actor::UiEntryCache::HighlightedText::one_liner: alloc::boxed::Box<str>
pub clipboard_history_client_sdk::ui_actor::UiEntryCache::HighlightedText::start: usize
pub clipboard_history_client_sdk::ui_actor::UiEntryCache::Image
pub clipboard_history_client_sdk::ui_actor::UiEntryCache::Text
pub clipboard_history_client_sdk::ui_actor::UiEntryCache::Text::one_liner: alloc::boxed::Box<str>
impl clipboard_history_client_sdk::ui_actor::UiEntryCache
pub const fn clipboard_history_client_sdk::ui_actor::UiEntryCache::is_text(&self) -> bool
impl core::fmt::Debug for clipboard_history_client_sdk::ui_actor::UiEntryCache
pub fn clipboard_history_client_sdk::ui_actor::UiEntryCache::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
impl core::marker::Freeze for clipboard_history_client_sdk::ui_actor::UiEntryCache
Expand Down
76 changes: 65 additions & 11 deletions client-sdk/src/ui_actor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,31 @@ pub struct UiEntry {

#[derive(Debug)]
pub enum UiEntryCache {
Text { one_liner: Box<str> },
Text {
one_liner: Box<str>,
},
HighlightedText {
one_liner: Box<str>,
start: usize,
end: usize,
},
Image,
Binary { mime_type: Box<str> },
Binary {
mime_type: Box<str>,
},
Error(CoreError),
}

impl UiEntryCache {
#[must_use]
pub const fn is_text(&self) -> bool {
match self {
Self::Text { .. } | Self::HighlightedText { .. } => true,
Self::Image | Self::Binary { .. } | Self::Error(_) => false,
}
}
}

#[derive(Debug)]
pub struct DetailedEntry {
pub mime_type: Box<str>,
Expand Down Expand Up @@ -352,7 +371,7 @@ fn handle_command<'a, Server: AsFd, PasteServer: AsFd, E>(
fn ui_entry(
entry: Entry,
reader: &mut EntryReader,
highlight: Option<(usize, usize)>,
mut highlight: Option<(usize, usize)>,
) -> Result<UiEntry, CoreError> {
let loaded = entry.to_slice(reader)?;
let mime_type = &*loaded.mime_type()?;
Expand All @@ -363,7 +382,7 @@ fn ui_entry(
});
}

let prefix_free = if let Some((start, _)) = highlight {
let prefix_free = if let Some((start, end)) = &mut highlight {
let mut l = &loaded[start.saturating_sub(24)..];
for &b in l.iter().take(3) {
// https://github.com/rust-lang/rust/blob/33422e72c8a66bdb5ee21246a948a1a02ca91674/library/core/src/num/mod.rs#L1090
Expand All @@ -374,6 +393,11 @@ fn ui_entry(
}
l = &l[1..];
}

let diff = loaded.len() - l.len();
*start -= diff;
*end -= diff;

l
} else {
&loaded
Expand All @@ -399,10 +423,22 @@ fn ui_entry(

if prefix_free.len() != loaded.len() {
one_liner.push('…');
if let Some((start, end)) = &mut highlight {
*start += '…'.len_utf8();
*end += '…'.len_utf8();
}
}
let mut prev_char_is_whitespace = false;
for c in s.chars() {
if (prev_char_is_whitespace || one_liner.is_empty()) && c.is_whitespace() {
if let Some((start, end)) = &mut highlight {
if one_liner.len() < *start {
*start -= c.len_utf8();
}
if one_liner.len() < *end {
*end -= c.len_utf8();
}
}
continue;
}

Expand All @@ -412,11 +448,22 @@ fn ui_entry(
if suffix_free.len() != prefix_free.len() {
one_liner.push('…');
}
if let Some((_, end)) = &mut highlight {
*end = min(*end, one_liner.len());
}

UiEntry {
entry,
cache: UiEntryCache::Text {
one_liner: one_liner.into(),
cache: if let Some((start, end)) = highlight {
UiEntryCache::HighlightedText {
one_liner: one_liner.into(),
start,
end,
}
} else {
UiEntryCache::Text {
one_liner: one_liner.into(),
}
},
}
},
Expand Down Expand Up @@ -563,12 +610,19 @@ fn do_search<E>(
unsafe { database.get(id) }?
};

Ok(
ui_entry(entry, reader, Some((start, end))).unwrap_or_else(|e| UiEntry {
cache: UiEntryCache::Error(e),
entry,
}),
Ok(ui_entry(
entry,
reader,
if start == end {
None
} else {
Some((start, end))
},
)
.unwrap_or_else(|e| UiEntry {
cache: UiEntryCache::Error(e),
entry,
}))
})
.collect();
*search_result_buf = results;
Expand Down
107 changes: 66 additions & 41 deletions egui/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ use std::{
use eframe::{
egui,
egui::{
text::LayoutJob, Align, CentralPanel, Event, FontId, FontTweak, Image, InputState, Key,
Label, Layout, Modifiers, PopupCloseBehavior, Pos2, Response, RichText, ScrollArea, Sense,
text::{LayoutJob, LayoutSection},
Align, CentralPanel, Event, FontId, FontTweak, Image, InputState, Key, Label, Layout,
Modifiers, PopupCloseBehavior, Pos2, Response, RichText, ScrollArea, Sense, Stroke,
TextEdit, TextFormat, TopBottomPanel, Ui, Vec2, ViewportBuilder, ViewportCommand, Widget,
},
epaint::FontFamily,
Expand Down Expand Up @@ -584,24 +585,11 @@ fn entry_ui(
max_popup_height: f32,
index: usize,
) {
let response = match &entry.cache {
UiEntryCache::Text { one_liner } => {
let mut job = LayoutJob::single_section(
one_liner.to_string(),
TextFormat {
font_id: FontId::new(16., entry_text_font.clone()),
color: ui.visuals().text_color(),
..Default::default()
},
);
job.wrap = egui::text::TextWrapping {
max_rows: 1,
break_anywhere: true,
..Default::default()
};
macro_rules! response {
($w:expr) => {
row_ui(
ui,
Label::new(job).selectable(false),
$w,
state,
requests,
refresh,
Expand All @@ -611,34 +599,71 @@ fn entry_ui(
max_popup_height,
index,
)
};
}
let response = match &entry.cache {
UiEntryCache::Text { one_liner } | UiEntryCache::HighlightedText { one_liner, .. } => {
let job = LayoutJob {
text: one_liner.to_string(),
break_on_newline: false,
wrap: egui::text::TextWrapping {
max_rows: 1,
break_anywhere: true,
..Default::default()
},
sections: {
let format = TextFormat {
font_id: FontId::new(16., entry_text_font.clone()),
color: ui.visuals().text_color(),
..Default::default()
};
if let UiEntryCache::HighlightedText {
one_liner: _,
start,
end,
} = entry.cache
{
vec![
LayoutSection {
leading_space: 0.0,
byte_range: 0..start,
format: format.clone(),
},
LayoutSection {
leading_space: 0.0,
byte_range: start..end,
format: TextFormat {
underline: Stroke::new(1., ui.visuals().strong_text_color()),
..format.clone()
},
},
LayoutSection {
leading_space: 0.0,
byte_range: end..one_liner.len(),
format,
},
]
} else {
vec![LayoutSection {
leading_space: 0.0,
byte_range: 0..one_liner.len(),
format,
}]
}
},
..LayoutJob::default()
};
response!(Label::new(job).selectable(false))
}
UiEntryCache::Image => row_ui(
ui,
UiEntryCache::Image => response!(
Image::new(format!("ringboard://{}", entry.entry.id()))
.max_height(250.)
.max_width(ui.available_width())
.fit_to_original_size(1.),
state,
requests,
refresh,
entry,
try_scroll,
try_popup,
max_popup_height,
index,
.fit_to_original_size(1.)
),
UiEntryCache::Binary { mime_type } => row_ui(
ui,
UiEntryCache::Binary { mime_type } => response!(
Label::new(format!("Unable to display format of type {mime_type:?}."))
.selectable(false),
state,
requests,
refresh,
entry,
try_scroll,
try_popup,
max_popup_height,
index,
.selectable(false)
),
UiEntryCache::Error(e) => {
show_error(ui, e);
Expand Down Expand Up @@ -717,7 +742,7 @@ fn row_ui(
state.detailed_entry = None;
let _ = requests.send(Command::GetDetails {
id: entry_id,
with_text: matches!(cache, UiEntryCache::Text { .. }),
with_text: cache.is_text(),
});
}

Expand Down
13 changes: 11 additions & 2 deletions tui/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use ratatui::{
},
layout::{Alignment, Constraint, Layout, Rect},
style::{Modifier, Style, Stylize},
text::Line,
text::{Line, Span},
widgets::{
Block, Borders, HighlightSpacing, List, ListState, Padding, Paragraph, StatefulWidget,
Widget, Wrap,
Expand Down Expand Up @@ -374,7 +374,7 @@ fn maybe_get_details(entries: &UiEntries, ui: &mut UiState, requests: &Sender<Co
ui.detail_image_state = None;
let _ = requests.send(Command::GetDetails {
id: entry.id(),
with_text: matches!(cache, UiEntryCache::Text { .. }),
with_text: cache.is_text(),
});
}
}
Expand Down Expand Up @@ -643,6 +643,15 @@ impl Widget for &mut AppWrapper<'_> {

fn ui_entry_line(UiEntry { entry: _, cache }: &UiEntry) -> Line {
match cache {
&UiEntryCache::HighlightedText {
ref one_liner,
start,
end,
} => Line::default().spans([
Span::raw(&one_liner[..start]),
Span::styled(&one_liner[start..end], Modifier::UNDERLINED),
Span::raw(&one_liner[end..]),
]),
UiEntryCache::Text { one_liner } => Line::raw(&**one_liner),
UiEntryCache::Image => Line::raw("Image: open details to view.").italic(),
UiEntryCache::Binary { mime_type } => {
Expand Down

0 comments on commit 57a78d7

Please sign in to comment.