LibGUI: Fix search highlighting so that it matches the search query

There is a bug in the "search by typing" feature of
AbstractView, where the highlighted text shown in items' title won't
match the search query if the search query contains whitespace.
This commit fixes that bug.

The bug is caused by a bad assumption made in
AbstractView::draw_item_text about the Painter::draw_text API.
Specifically that the function passed to it will be called for each
code point of the string instead of for each drawable glyph.

This is fixed by not relying on looking at the length of the highlight
string, but instead looking at its content.
This commit is contained in:
Adam Harald Jørgensen 2023-08-23 15:36:38 +02:00 committed by Tim Flynn
parent f115e44066
commit 258af88b29

View file

@ -721,31 +721,34 @@ void AbstractView::draw_item_text(Gfx::Painter& painter, ModelIndex const& index
text_color = is_focused() ? palette().selection_text() : palette().inactive_selection_text();
else
text_color = index.data(ModelRole::ForegroundColor).to_color(palette().color(foreground_role()));
if (index == m_highlighted_search_index) {
Utf8View searching_text(m_highlighted_search);
auto searching_length = searching_text.length();
if (searching_length > search_highlighting_offset)
searching_length -= search_highlighting_offset;
else if (search_highlighting_offset > 0)
searching_length = 0;
auto const byte_offset = search_highlighting_offset < m_highlighted_search.length() ? 0 : item_text.length();
auto const byte_length = min(item_text.length() - byte_offset, m_highlighted_search.length() - search_highlighting_offset);
Utf8View const searching_text(item_text.substring_view(byte_offset, byte_length));
// Highlight the text background first
auto background_searching_length = searching_length;
painter.draw_text([&](Gfx::FloatRect const& rect, Utf8CodePointIterator&) {
if (background_searching_length > 0) {
background_searching_length--;
painter.fill_rect(rect.to_type<int>().inflated(0, 2), palette().highlight_searching());
}
},
text_rect, item_text, font, alignment, elision);
auto highlight_rect = text_rect.shrunken(0, text_rect.height() - font.pixel_size_rounded_up() - 2);
highlight_rect.set_width((int)font.width(searching_text));
// If the text is center aligned the highlight rect needs to be shifted to the right so that the two line up
if (alignment == Gfx::TextAlignment::Center)
highlight_rect.translate_by((text_rect.width() - (int)font.width(item_text)) / 2, 0);
painter.fill_rect(highlight_rect, palette().highlight_searching());
// Then draw the text
auto text_searching_length = searching_length;
auto highlight_text_color = palette().highlight_searching_text();
searching_length = searching_text.length();
auto searching_text_it = searching_text.begin();
while (searching_text_it != searching_text.end() && is_ascii_space(*searching_text_it))
++searching_text_it;
auto const highlight_text_color = palette().highlight_searching_text();
painter.draw_text([&](auto const& rect, Utf8CodePointIterator& it) {
if (text_searching_length > 0) {
text_searching_length--;
if (searching_text_it != searching_text.end()) {
do {
++searching_text_it;
} while (searching_text_it != searching_text.end() && is_ascii_space(*searching_text_it));
painter.draw_glyph_or_emoji(rect.location(), it, font, highlight_text_color);
} else {
painter.draw_glyph_or_emoji(rect.location(), it, font, text_color);