LibWeb: Implement up/down arrow navigation for EditingHostManager

This commit is contained in:
Timothy Flynn 2025-08-20 15:17:03 -04:00 committed by Tim Flynn
parent f529a4d98e
commit 2674bd428e
5 changed files with 119 additions and 4 deletions

View File

@ -161,14 +161,14 @@ void EditingHostManager::decrement_cursor_position_to_previous_word(CollapseSele
void EditingHostManager::increment_cursor_position_to_next_line(CollapseSelection collapse)
{
(void)collapse;
// FIXME: Implement this method
if (auto selection = m_document->get_selection())
selection->move_offset_to_next_line(collapse == CollapseSelection::Yes);
}
void EditingHostManager::decrement_cursor_position_to_previous_line(CollapseSelection collapse)
{
(void)collapse;
// FIXME: Implement this method
if (auto selection = m_document->get_selection())
selection->move_offset_to_previous_line(collapse == CollapseSelection::Yes);
}
void EditingHostManager::handle_delete(DeleteDirection direction)

View File

@ -13,6 +13,7 @@
#include <LibWeb/DOM/Position.h>
#include <LibWeb/DOM/Range.h>
#include <LibWeb/DOM/Text.h>
#include <LibWeb/GraphemeEdgeTracker.h>
#include <LibWeb/Selection/Selection.h>
namespace Web::Selection {
@ -650,4 +651,40 @@ void Selection::move_offset_to_previous_word(bool collapse_selection)
}
}
void Selection::move_offset_to_next_line(bool collapse_selection)
{
auto* text_node = as_if<DOM::Text>(anchor_node().ptr());
if (!text_node)
return;
auto new_offset = compute_cursor_position_on_next_line(*text_node, focus_offset());
if (!new_offset.has_value())
return;
if (collapse_selection) {
MUST(collapse(text_node, *new_offset));
m_document->reset_cursor_blink_cycle();
} else {
MUST(set_base_and_extent(*text_node, anchor_offset(), *text_node, *new_offset));
}
}
void Selection::move_offset_to_previous_line(bool collapse_selection)
{
auto* text_node = as_if<DOM::Text>(anchor_node().ptr());
if (!text_node)
return;
auto new_offset = compute_cursor_position_on_previous_line(*text_node, focus_offset());
if (!new_offset.has_value())
return;
if (collapse_selection) {
MUST(collapse(text_node, *new_offset));
m_document->reset_cursor_blink_cycle();
} else {
MUST(set_base_and_extent(*text_node, anchor_offset(), *text_node, *new_offset));
}
}
}

View File

@ -68,6 +68,8 @@ public:
void move_offset_to_previous_character(bool collapse_selection);
void move_offset_to_next_word(bool collapse_selection);
void move_offset_to_previous_word(bool collapse_selection);
void move_offset_to_next_line(bool collapse_selection);
void move_offset_to_previous_line(bool collapse_selection);
private:
Selection(GC::Ref<JS::Realm>, GC::Ref<DOM::Document>);

View File

@ -0,0 +1,19 @@
Home: position=0 character="h"
Right: position=1 character="e"
Right: position=2 character="l"
Right: position=3 character="l"
Right: position=4 character="o"
Right: position=5 character=" "
Down: position=28 character="👩🏼‍❤️‍👨🏻"
Left: position=27 character=" "
Up: position=2 character="l"
Right: position=3 character="l"
Right: position=4 character="o"
Right: position=5 character=" "
Right: position=6 character="👩🏼‍❤️‍👨🏻"
Down: position=40 character=" "
Up: position=6 character="👩🏼‍❤️‍👨🏻"
Down: position=40 character=" "
Left: position=28 character="👩🏼‍❤️‍👨🏻"
Left: position=27 character=" "
Up: position=2 character="l"

View File

@ -0,0 +1,57 @@
<!DOCTYPE html>
<script src="../include.js"></script>
<p id="text" contenteditable style="width: 120px">hello 👩🏼‍❤️‍👨🏻 there
my 👩🏼‍❤️‍👨🏻 friends!</p>
<script>
test(() => {
// We need to ensure layout has occurred for arrow navigation to have a layout node to interact with.
document.body.offsetWidth;
const segmenter = new Intl.Segmenter("en", { granularity: "grapheme" });
const content = text.innerText.trim();
const cursorLocation = () => {
return window.getSelection().getRangeAt(0).startOffset;
};
const graphemeAtLocation = (cursor) => {
const segments = segmenter.segment(content.substring(cursor));
return Array.from(segments)[0].segment;
};
const moveCursor = direction => {
internals.sendKey(text, direction);
const cursor = cursorLocation();
const character = graphemeAtLocation(cursor);
println(`${direction}: position=${cursor} character="${character}"`);
};
moveCursor("Home");
moveCursor("Right");
moveCursor("Right");
moveCursor("Right");
moveCursor("Right");
moveCursor("Right");
moveCursor("Down");
moveCursor("Left");
moveCursor("Up");
moveCursor("Right");
moveCursor("Right");
moveCursor("Right");
moveCursor("Right");
moveCursor("Down");
moveCursor("Up");
moveCursor("Down");
moveCursor("Left");
moveCursor("Left");
moveCursor("Up");
});
</script>