mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-10-06 10:07:17 +03:00
LibWeb: Add Document helpers to move its cursor to word boundaries
This implementation is based on the same feature I added to Serenity's TextEditor: https://github.com/SerenityOS/serenity/pull/17477
This commit is contained in:
parent
eece7697fd
commit
ecf2cc600b
Notes:
github-actions[bot]
2024-09-06 05:44:05 +00:00
Author: https://github.com/trflynn89 Commit: https://github.com/LadybirdBrowser/ladybird/commit/ecf2cc600be Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/1292
@ -130,6 +130,8 @@ WebIDL::ExceptionOr<void> CharacterData::replace_data(size_t offset, size_t coun
|
||||
|
||||
if (m_grapheme_segmenter)
|
||||
m_grapheme_segmenter->set_segmented_text(m_data);
|
||||
if (m_word_segmenter)
|
||||
m_word_segmenter->set_segmented_text(m_data);
|
||||
|
||||
return {};
|
||||
}
|
||||
@ -165,4 +167,14 @@ Unicode::Segmenter& CharacterData::grapheme_segmenter()
|
||||
return *m_grapheme_segmenter;
|
||||
}
|
||||
|
||||
Unicode::Segmenter& CharacterData::word_segmenter()
|
||||
{
|
||||
if (!m_word_segmenter) {
|
||||
m_word_segmenter = Unicode::Segmenter::create(Unicode::SegmenterGranularity::Word);
|
||||
m_word_segmenter->set_segmented_text(m_data);
|
||||
}
|
||||
|
||||
return *m_word_segmenter;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ public:
|
||||
WebIDL::ExceptionOr<void> replace_data(size_t offset_in_utf16_code_units, size_t count_in_utf16_code_units, String const&);
|
||||
|
||||
Unicode::Segmenter& grapheme_segmenter();
|
||||
Unicode::Segmenter& word_segmenter();
|
||||
|
||||
protected:
|
||||
CharacterData(Document&, NodeType, String const&);
|
||||
@ -51,6 +52,7 @@ private:
|
||||
String m_data;
|
||||
|
||||
OwnPtr<Unicode::Segmenter> m_grapheme_segmenter;
|
||||
OwnPtr<Unicode::Segmenter> m_word_segmenter;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -5423,6 +5423,24 @@ bool Document::decrement_cursor_position_offset()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Document::increment_cursor_position_to_next_word()
|
||||
{
|
||||
if (!m_cursor_position->increment_offset_to_next_word())
|
||||
return false;
|
||||
|
||||
reset_cursor_blink_cycle();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Document::decrement_cursor_position_to_previous_word()
|
||||
{
|
||||
if (!m_cursor_position->decrement_offset_to_previous_word())
|
||||
return false;
|
||||
|
||||
reset_cursor_blink_cycle();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Document::user_did_edit_document_text(Badge<EditEventHandler>)
|
||||
{
|
||||
reset_cursor_blink_cycle();
|
||||
|
@ -693,6 +693,8 @@ public:
|
||||
void set_cursor_position(JS::NonnullGCPtr<DOM::Position>);
|
||||
bool increment_cursor_position_offset();
|
||||
bool decrement_cursor_position_offset();
|
||||
bool increment_cursor_position_to_next_word();
|
||||
bool decrement_cursor_position_to_previous_word();
|
||||
|
||||
bool cursor_blink_state() const { return m_cursor_blink_state; }
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
#include <AK/Utf8View.h>
|
||||
#include <LibUnicode/CharacterTypes.h>
|
||||
#include <LibUnicode/Segmenter.h>
|
||||
#include <LibWeb/DOM/Node.h>
|
||||
#include <LibWeb/DOM/Position.h>
|
||||
@ -66,6 +67,60 @@ bool Position::decrement_offset()
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool should_continue_beyond_word(Utf8View const& word)
|
||||
{
|
||||
for (auto code_point : word) {
|
||||
if (!Unicode::code_point_has_punctuation_general_category(code_point) && !Unicode::code_point_has_separator_general_category(code_point))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Position::increment_offset_to_next_word()
|
||||
{
|
||||
if (!is<DOM::Text>(*m_node) || offset_is_at_end_of_node())
|
||||
return false;
|
||||
|
||||
auto& node = static_cast<DOM::Text&>(*m_node);
|
||||
|
||||
while (true) {
|
||||
if (auto offset = node.word_segmenter().next_boundary(m_offset); offset.has_value()) {
|
||||
auto word = node.data().code_points().substring_view(m_offset, *offset - m_offset);
|
||||
m_offset = *offset;
|
||||
|
||||
if (should_continue_beyond_word(word))
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Position::decrement_offset_to_previous_word()
|
||||
{
|
||||
if (!is<DOM::Text>(*m_node) || m_offset == 0)
|
||||
return false;
|
||||
|
||||
auto& node = static_cast<DOM::Text&>(*m_node);
|
||||
|
||||
while (true) {
|
||||
if (auto offset = node.word_segmenter().previous_boundary(m_offset); offset.has_value()) {
|
||||
auto word = node.data().code_points().substring_view(*offset, m_offset - *offset);
|
||||
m_offset = *offset;
|
||||
|
||||
if (should_continue_beyond_word(word))
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Position::offset_is_at_end_of_node() const
|
||||
{
|
||||
if (!is<DOM::Text>(*m_node))
|
||||
|
@ -36,6 +36,9 @@ public:
|
||||
bool increment_offset();
|
||||
bool decrement_offset();
|
||||
|
||||
bool increment_offset_to_next_word();
|
||||
bool decrement_offset_to_previous_word();
|
||||
|
||||
bool equals(JS::NonnullGCPtr<Position> other) const
|
||||
{
|
||||
return m_node.ptr() == other->m_node.ptr() && m_offset == other->m_offset;
|
||||
|
Loading…
Reference in New Issue
Block a user