mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-27 04:37:22 +00:00
LibWeb: Implement up/down arrow navigation in textarea
This commit is contained in:
parent
888cb038e8
commit
23aadc02ca
Notes:
github-actions[bot]
2025-08-15 10:33:20 +00:00
Author: https://github.com/mikiubo
Commit: 23aadc02ca
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4975
Reviewed-by: https://github.com/trflynn89 ✅
6 changed files with 111 additions and 0 deletions
|
@ -140,6 +140,18 @@ void EditingHostManager::decrement_cursor_position_to_previous_word(CollapseSele
|
||||||
selection->move_offset_to_previous_word(collapse == CollapseSelection::Yes);
|
selection->move_offset_to_previous_word(collapse == CollapseSelection::Yes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EditingHostManager::increment_cursor_position_to_next_line(CollapseSelection collapse)
|
||||||
|
{
|
||||||
|
(void)collapse;
|
||||||
|
// FIXME: Implement this method
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditingHostManager::decrement_cursor_position_to_previous_line(CollapseSelection collapse)
|
||||||
|
{
|
||||||
|
(void)collapse;
|
||||||
|
// FIXME: Implement this method
|
||||||
|
}
|
||||||
|
|
||||||
void EditingHostManager::handle_delete(DeleteDirection direction)
|
void EditingHostManager::handle_delete(DeleteDirection direction)
|
||||||
{
|
{
|
||||||
// https://w3c.github.io/editing/docs/execCommand/#additional-requirements
|
// https://w3c.github.io/editing/docs/execCommand/#additional-requirements
|
||||||
|
|
|
@ -35,6 +35,8 @@ public:
|
||||||
virtual void decrement_cursor_position_offset(CollapseSelection) override;
|
virtual void decrement_cursor_position_offset(CollapseSelection) override;
|
||||||
virtual void increment_cursor_position_to_next_word(CollapseSelection) override;
|
virtual void increment_cursor_position_to_next_word(CollapseSelection) override;
|
||||||
virtual void decrement_cursor_position_to_previous_word(CollapseSelection) override;
|
virtual void decrement_cursor_position_to_previous_word(CollapseSelection) override;
|
||||||
|
virtual void increment_cursor_position_to_next_line(CollapseSelection) override;
|
||||||
|
virtual void decrement_cursor_position_to_previous_line(CollapseSelection) override;
|
||||||
|
|
||||||
virtual void visit_edges(Cell::Visitor& visitor) override;
|
virtual void visit_edges(Cell::Visitor& visitor) override;
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,8 @@ public:
|
||||||
virtual void decrement_cursor_position_offset(CollapseSelection) = 0;
|
virtual void decrement_cursor_position_offset(CollapseSelection) = 0;
|
||||||
virtual void increment_cursor_position_to_next_word(CollapseSelection) = 0;
|
virtual void increment_cursor_position_to_next_word(CollapseSelection) = 0;
|
||||||
virtual void decrement_cursor_position_to_previous_word(CollapseSelection) = 0;
|
virtual void decrement_cursor_position_to_previous_word(CollapseSelection) = 0;
|
||||||
|
virtual void increment_cursor_position_to_next_line(CollapseSelection) = 0;
|
||||||
|
virtual void decrement_cursor_position_to_previous_line(CollapseSelection) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1019,6 +1019,89 @@ void FormAssociatedTextControlElement::decrement_cursor_position_to_previous_wor
|
||||||
selection_was_changed();
|
selection_was_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static constexpr size_t find_line_start(Utf16View const& view, size_t offset)
|
||||||
|
{
|
||||||
|
while (offset != 0 && view.code_unit_at(offset - 1) != '\n')
|
||||||
|
--offset;
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr size_t find_line_end(Utf16View const& view, size_t offset)
|
||||||
|
{
|
||||||
|
auto length = view.length_in_code_units();
|
||||||
|
while (offset < length && view.code_unit_at(offset) != '\n')
|
||||||
|
++offset;
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FormAssociatedTextControlElement::increment_cursor_position_to_next_line(CollapseSelection collapse)
|
||||||
|
{
|
||||||
|
auto const text_node = form_associated_element_to_text_node();
|
||||||
|
if (!text_node)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto code_points = text_node->data().utf16_view();
|
||||||
|
auto length = code_points.length_in_code_units();
|
||||||
|
auto current_line_end = find_line_end(code_points, m_selection_end);
|
||||||
|
|
||||||
|
// initialize to handle the case of last line
|
||||||
|
size_t new_offset = current_line_end;
|
||||||
|
|
||||||
|
if (current_line_end < length) {
|
||||||
|
auto next_line_start = current_line_end + 1;
|
||||||
|
auto position_within_line = m_selection_end - find_line_start(code_points, m_selection_end);
|
||||||
|
auto next_line_end = find_line_end(code_points, next_line_start);
|
||||||
|
auto next_line_length = next_line_end - next_line_start;
|
||||||
|
|
||||||
|
new_offset = next_line_start + min(position_within_line, next_line_length);
|
||||||
|
|
||||||
|
if (new_offset > 0 && new_offset < length && AK::UnicodeUtils::is_utf16_low_surrogate(code_points.code_unit_at(new_offset))) {
|
||||||
|
if (AK::UnicodeUtils::is_utf16_high_surrogate(code_points.code_unit_at(new_offset - 1)))
|
||||||
|
--new_offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (collapse == CollapseSelection::Yes) {
|
||||||
|
collapse_selection_to_offset(new_offset);
|
||||||
|
} else {
|
||||||
|
m_selection_end = new_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
selection_was_changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FormAssociatedTextControlElement::decrement_cursor_position_to_previous_line(CollapseSelection collapse)
|
||||||
|
{
|
||||||
|
auto const text_node = form_associated_element_to_text_node();
|
||||||
|
if (!text_node)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto code_points = text_node->data().utf16_view();
|
||||||
|
size_t new_offset = 0;
|
||||||
|
|
||||||
|
if (auto current_line_start = find_line_start(code_points, m_selection_end); current_line_start != 0) {
|
||||||
|
auto position_within_line = m_selection_end - current_line_start;
|
||||||
|
|
||||||
|
auto previous_line_start = find_line_start(code_points, current_line_start - 1);
|
||||||
|
auto previous_line_length = current_line_start - previous_line_start - 1;
|
||||||
|
|
||||||
|
new_offset = previous_line_start + min(position_within_line, previous_line_length);
|
||||||
|
|
||||||
|
if (new_offset > 0 && AK::UnicodeUtils::is_utf16_low_surrogate(code_points.code_unit_at(new_offset))) {
|
||||||
|
if (AK::UnicodeUtils::is_utf16_high_surrogate(code_points.code_unit_at(new_offset - 1)))
|
||||||
|
--new_offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (collapse == CollapseSelection::Yes) {
|
||||||
|
collapse_selection_to_offset(new_offset);
|
||||||
|
} else {
|
||||||
|
m_selection_end = new_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
selection_was_changed();
|
||||||
|
}
|
||||||
|
|
||||||
GC::Ptr<DOM::Position> FormAssociatedTextControlElement::cursor_position() const
|
GC::Ptr<DOM::Position> FormAssociatedTextControlElement::cursor_position() const
|
||||||
{
|
{
|
||||||
auto const node = form_associated_element_to_text_node();
|
auto const node = form_associated_element_to_text_node();
|
||||||
|
|
|
@ -240,6 +240,8 @@ public:
|
||||||
virtual void decrement_cursor_position_offset(CollapseSelection) override;
|
virtual void decrement_cursor_position_offset(CollapseSelection) override;
|
||||||
virtual void increment_cursor_position_to_next_word(CollapseSelection) override;
|
virtual void increment_cursor_position_to_next_word(CollapseSelection) override;
|
||||||
virtual void decrement_cursor_position_to_previous_word(CollapseSelection) override;
|
virtual void decrement_cursor_position_to_previous_word(CollapseSelection) override;
|
||||||
|
virtual void increment_cursor_position_to_next_line(CollapseSelection) override;
|
||||||
|
virtual void decrement_cursor_position_to_previous_line(CollapseSelection) override;
|
||||||
|
|
||||||
GC::Ptr<DOM::Position> cursor_position() const;
|
GC::Ptr<DOM::Position> cursor_position() const;
|
||||||
|
|
||||||
|
|
|
@ -1321,6 +1321,16 @@ EventResult EventHandler::handle_keydown(UIEvents::KeyCode key, u32 modifiers, u
|
||||||
return EventResult::Handled;
|
return EventResult::Handled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (key == UIEvents::KeyCode::Key_Up || key == UIEvents::KeyCode::Key_Down) {
|
||||||
|
auto collapse = modifiers & UIEvents::Mod_Shift ? InputEventsTarget::CollapseSelection::No : InputEventsTarget::CollapseSelection::Yes;
|
||||||
|
if (key == UIEvents::KeyCode::Key_Up) {
|
||||||
|
target->decrement_cursor_position_to_previous_line(collapse);
|
||||||
|
} else {
|
||||||
|
target->increment_cursor_position_to_next_line(collapse);
|
||||||
|
}
|
||||||
|
return EventResult::Handled;
|
||||||
|
}
|
||||||
|
|
||||||
if (key == UIEvents::KeyCode::Key_Home) {
|
if (key == UIEvents::KeyCode::Key_Home) {
|
||||||
auto collapse = modifiers & UIEvents::Mod_Shift ? InputEventsTarget::CollapseSelection::No : InputEventsTarget::CollapseSelection::Yes;
|
auto collapse = modifiers & UIEvents::Mod_Shift ? InputEventsTarget::CollapseSelection::No : InputEventsTarget::CollapseSelection::Yes;
|
||||||
target->move_cursor_to_start(collapse);
|
target->move_cursor_to_start(collapse);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue