mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 00:07:36 +00:00
LibGUI: Add search API to TextEditor with highlighted results
This adds a search API to TextEditor. The API that is similar to "find_text" of TextDocument (which is used internally to do the search). All search results (as well as the current one) are highlighted with a "span collection", which is pretty neat :^)
This commit is contained in:
parent
a12385dc4b
commit
5f2a0f03a6
2 changed files with 75 additions and 1 deletions
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||||
* Copyright (c) 2021, Jakob-Niklas See <git@nwex.de>
|
* Copyright (c) 2021, Jakob-Niklas See <git@nwex.de>
|
||||||
|
* Copyright (c) 2022, the SerenityOS developers.
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -2105,4 +2106,62 @@ void TextEditor::set_text_is_secret(bool text_is_secret)
|
||||||
did_update_selection();
|
did_update_selection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextRange TextEditor::find_text(StringView needle, SearchDirection direction, GUI::TextDocument::SearchShouldWrap should_wrap, bool use_regex, bool match_case)
|
||||||
|
{
|
||||||
|
GUI::TextRange range {};
|
||||||
|
if (direction == SearchDirection::Forward) {
|
||||||
|
range = document().find_next(needle,
|
||||||
|
m_search_result_index.has_value() ? m_search_results[*m_search_result_index].end() : GUI::TextPosition {},
|
||||||
|
should_wrap, use_regex, match_case);
|
||||||
|
} else {
|
||||||
|
range = document().find_previous(needle,
|
||||||
|
m_search_result_index.has_value() ? m_search_results[*m_search_result_index].start() : GUI::TextPosition {},
|
||||||
|
should_wrap, use_regex, match_case);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!range.is_valid()) {
|
||||||
|
reset_search_results();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto all_results = document().find_all(needle, use_regex, match_case);
|
||||||
|
on_search_results(range, all_results);
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextEditor::reset_search_results()
|
||||||
|
{
|
||||||
|
m_search_result_index.clear();
|
||||||
|
m_search_results.clear();
|
||||||
|
document().set_spans(search_results_span_collection_index, {});
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextEditor::on_search_results(GUI::TextRange current, Vector<GUI::TextRange> all_results)
|
||||||
|
{
|
||||||
|
m_search_result_index.clear();
|
||||||
|
m_search_results.clear();
|
||||||
|
|
||||||
|
set_cursor(current.start());
|
||||||
|
if (auto it = all_results.find(current); it->is_valid())
|
||||||
|
m_search_result_index = it.index();
|
||||||
|
m_search_results = move(all_results);
|
||||||
|
|
||||||
|
Vector<GUI::TextDocumentSpan> spans;
|
||||||
|
for (size_t i = 0; i < m_search_results.size(); ++i) {
|
||||||
|
auto& result = m_search_results[i];
|
||||||
|
GUI::TextDocumentSpan span;
|
||||||
|
span.range = result;
|
||||||
|
span.attributes.background_color = palette().hover_highlight();
|
||||||
|
span.attributes.color = Color::from_argb(0xff000000); // So text without spans from a highlighter will have color
|
||||||
|
if (i == m_search_result_index) {
|
||||||
|
span.attributes.bold = true;
|
||||||
|
span.attributes.underline = true;
|
||||||
|
}
|
||||||
|
spans.append(move(span));
|
||||||
|
}
|
||||||
|
document().set_spans(search_results_span_collection_index, move(spans));
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||||
|
* Copyright (c) 2022, the SerenityOS developers.
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -213,6 +214,15 @@ public:
|
||||||
void set_text_is_secret(bool text_is_secret);
|
void set_text_is_secret(bool text_is_secret);
|
||||||
void force_rehighlight();
|
void force_rehighlight();
|
||||||
|
|
||||||
|
enum class SearchDirection {
|
||||||
|
Forward,
|
||||||
|
Backward,
|
||||||
|
};
|
||||||
|
TextRange find_text(StringView needle, SearchDirection, GUI::TextDocument::SearchShouldWrap, bool use_regex, bool match_case);
|
||||||
|
void reset_search_results();
|
||||||
|
Optional<size_t> search_result_index() const { return m_search_result_index; }
|
||||||
|
Vector<TextRange> const& search_results() const { return m_search_results; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit TextEditor(Type = Type::MultiLine);
|
explicit TextEditor(Type = Type::MultiLine);
|
||||||
|
|
||||||
|
@ -260,7 +270,6 @@ private:
|
||||||
// ^Syntax::HighlighterClient
|
// ^Syntax::HighlighterClient
|
||||||
virtual Vector<TextDocumentSpan>& spans() final { return document().spans(); }
|
virtual Vector<TextDocumentSpan>& spans() final { return document().spans(); }
|
||||||
virtual Vector<TextDocumentSpan> const& spans() const final { return document().spans(); }
|
virtual Vector<TextDocumentSpan> const& spans() const final { return document().spans(); }
|
||||||
virtual void highlighter_did_set_spans(Vector<TextDocumentSpan> spans) final { document().set_spans(move(spans)); }
|
|
||||||
virtual void set_span_at_index(size_t index, TextDocumentSpan span) final { document().set_span_at_index(index, move(span)); }
|
virtual void set_span_at_index(size_t index, TextDocumentSpan span) final { document().set_span_at_index(index, move(span)); }
|
||||||
virtual void highlighter_did_request_update() final { update(); }
|
virtual void highlighter_did_request_update() final { update(); }
|
||||||
virtual String highlighter_did_request_text() const final { return text(); }
|
virtual String highlighter_did_request_text() const final { return text(); }
|
||||||
|
@ -337,6 +346,9 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void will_execute(TextDocumentUndoCommand const&) { }
|
virtual void will_execute(TextDocumentUndoCommand const&) { }
|
||||||
|
void on_search_results(GUI::TextRange current, Vector<GUI::TextRange> all_results);
|
||||||
|
|
||||||
|
static constexpr auto search_results_span_collection_index = 1;
|
||||||
|
|
||||||
Type m_type { MultiLine };
|
Type m_type { MultiLine };
|
||||||
Mode m_mode { Editable };
|
Mode m_mode { Editable };
|
||||||
|
@ -408,6 +420,9 @@ private:
|
||||||
RefPtr<Gfx::Bitmap> m_icon;
|
RefPtr<Gfx::Bitmap> m_icon;
|
||||||
|
|
||||||
bool m_text_is_secret { false };
|
bool m_text_is_secret { false };
|
||||||
|
|
||||||
|
Optional<size_t> m_search_result_index;
|
||||||
|
Vector<GUI::TextRange> m_search_results;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue