LibWeb: Move textarea Home and End by line

Make unmodified Home and End in textarea use the current line
boundary instead of the whole control. Keep modified Home and End on
the existing whole-control path so Ctrl+Home and Ctrl+End still jump
across the textarea.

Update the textarea keyboard navigation test to cover the fixed line
movement and the preserved modified-key behavior.
This commit is contained in:
Andreas Kling 2026-05-20 10:42:20 +02:00 committed by Andreas Kling
parent cfb1ddbe29
commit 5fedacb7de
5 changed files with 58 additions and 4 deletions

View file

@ -1219,6 +1219,34 @@ void FormAssociatedTextControlElement::move_cursor_to_end(CollapseSelection coll
selection_was_changed(SelectionSource::UI);
}
void FormAssociatedTextControlElement::move_cursor_to_start_of_current_line(CollapseSelection collapse)
{
auto text_node = form_associated_element_to_text_node();
if (!text_node)
return;
auto new_offset = find_line_start(text_node->data().utf16_view(), m_selection_end);
if (collapse == CollapseSelection::Yes) {
collapse_selection_to_offset(new_offset);
} else {
m_selection_end = new_offset;
}
selection_was_changed(SelectionSource::UI);
}
void FormAssociatedTextControlElement::move_cursor_to_end_of_current_line(CollapseSelection collapse)
{
auto text_node = form_associated_element_to_text_node();
if (!text_node)
return;
auto new_offset = find_line_end(text_node->data().utf16_view(), m_selection_end);
if (collapse == CollapseSelection::Yes) {
collapse_selection_to_offset(new_offset);
} else {
m_selection_end = new_offset;
}
selection_was_changed(SelectionSource::UI);
}
void FormAssociatedTextControlElement::increment_cursor_position_offset(CollapseSelection collapse)
{
auto const text_node = form_associated_element_to_text_node();

View file

@ -263,6 +263,8 @@ public:
virtual void set_selection_focus(GC::Ref<DOM::Node>, size_t offset) override;
virtual void move_cursor_to_start(CollapseSelection) override;
virtual void move_cursor_to_end(CollapseSelection) override;
void move_cursor_to_start_of_current_line(CollapseSelection);
void move_cursor_to_end_of_current_line(CollapseSelection);
virtual void increment_cursor_position_offset(CollapseSelection) override;
virtual void decrement_cursor_position_offset(CollapseSelection) override;
virtual void increment_cursor_position_to_next_word(CollapseSelection) override;

View file

@ -832,12 +832,26 @@ EventResult EventHandler::handle_keydown(UIEvents::KeyCode key, u32 modifiers, u
if (key == UIEvents::KeyCode::Key_Home) {
auto collapse = modifiers & UIEvents::Mod_Shift ? InputEventsTarget::CollapseSelection::No : InputEventsTarget::CollapseSelection::Yes;
auto const modifiers_without_shift_or_keypad = modifiers & ~(UIEvents::Mod_Shift | UIEvents::Mod_Keypad);
if (modifiers_without_shift_or_keypad == UIEvents::Mod_None) {
if (auto* textarea_element = as_if<HTML::HTMLTextAreaElement>(document->focused_area().ptr())) {
textarea_element->move_cursor_to_start_of_current_line(collapse);
return EventResult::Handled;
}
}
target->move_cursor_to_start(collapse);
return EventResult::Handled;
}
if (key == UIEvents::KeyCode::Key_End) {
auto collapse = modifiers & UIEvents::Mod_Shift ? InputEventsTarget::CollapseSelection::No : InputEventsTarget::CollapseSelection::Yes;
auto const modifiers_without_shift_or_keypad = modifiers & ~(UIEvents::Mod_Shift | UIEvents::Mod_Keypad);
if (modifiers_without_shift_or_keypad == UIEvents::Mod_None) {
if (auto* textarea_element = as_if<HTML::HTMLTextAreaElement>(document->focused_area().ptr())) {
textarea_element->move_cursor_to_end_of_current_line(collapse);
return EventResult::Handled;
}
}
target->move_cursor_to_end(collapse);
return EventResult::Handled;
}

View file

@ -1,4 +1,6 @@
Home from middle line: start=0 end=0
End from middle line: start=25 end=25
Shift+Home from middle line: start=0 end=12
Shift+End from middle line: start=12 end=25
Home from middle line: start=6 end=6
End from middle line: start=19 end=19
Shift+Home from middle line: start=6 end=12
Shift+End from middle line: start=12 end=19
Ctrl+Home from middle line: start=0 end=0
Ctrl+End from middle line: start=25 end=25

View file

@ -26,5 +26,13 @@ test(() => {
textarea.setSelectionRange(12, 12);
internals.sendKey(textarea, "End", internals.MOD_SHIFT);
reportSelection("Shift+End from middle line");
textarea.setSelectionRange(12, 12);
internals.sendKey(textarea, "Home", internals.MOD_CTRL);
reportSelection("Ctrl+Home from middle line");
textarea.setSelectionRange(12, 12);
internals.sendKey(textarea, "End", internals.MOD_CTRL);
reportSelection("Ctrl+End from middle line");
});
</script>