Skip to content

Commit

Permalink
LibWeb: Update editable node selections with arrow/home/end keys
Browse files Browse the repository at this point in the history
When an editable node is focused and one of the arrow/home/end keys are
pressed while shift is held, we will now create or update the document's
selection. There is a bit of nuance to the behavior here, which matches
how the cursor behaves in other engines.

We will of course want to abstract this in the future to extend any non-
editable node text selections. This also does not implement holding ctrl
to jump by word, rather than grapheme.

(cherry picked from commit 1240aaa294991b66a04a5ac5e7efcfa494d01d78)
  • Loading branch information
trflynn89 authored and nico committed Nov 1, 2024
1 parent 3a3d8b1 commit 596907d
Showing 1 changed file with 73 additions and 34 deletions.
107 changes: 73 additions & 34 deletions Userland/Libraries/LibWeb/Page/EventHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -800,25 +800,35 @@ bool EventHandler::handle_keydown(UIEvents::KeyCode key, u32 modifiers, u32 code

auto& realm = document->realm();

if (auto selection = document->get_selection()) {
auto range = selection->range();
if (range && !range->collapsed() && range->start_container()->is_editable()) {
auto selection = document->get_selection();
auto range = [&]() -> JS::GCPtr<DOM::Range> {
if (selection) {
if (auto range = selection->range(); range && !range->collapsed())
return range;
}
return nullptr;
}();

if (selection && range && range->start_container()->is_editable()) {
auto clear_selection = [&]() {
selection->remove_all_ranges();

// FIXME: This doesn't work for some reason?
document->set_cursor_position(DOM::Position::create(realm, *range->start_container(), range->start_offset()));
};

if (key == UIEvents::KeyCode::Key_Backspace || key == UIEvents::KeyCode::Key_Delete) {
m_edit_event_handler->handle_delete(document, *range);
return true;
}
// FIXME: Text editing shortcut keys (copy/paste etc.) should be handled here.
if (!should_ignore_keydown_event(code_point, modifiers)) {
m_edit_event_handler->handle_delete(document, *range);
m_edit_event_handler->handle_insert(document, JS::NonnullGCPtr { *document->cursor_position() }, code_point);
document->increment_cursor_position_offset();
return true;
}
if (key == UIEvents::KeyCode::Key_Backspace || key == UIEvents::KeyCode::Key_Delete) {
clear_selection();
m_edit_event_handler->handle_delete(document, *range);
return true;
}
// FIXME: Text editing shortcut keys (copy/paste etc.) should be handled here.
if (!should_ignore_keydown_event(code_point, modifiers)) {
clear_selection();
m_edit_event_handler->handle_delete(document, *range);
m_edit_event_handler->handle_insert(document, JS::NonnullGCPtr { *document->cursor_position() }, code_point);
document->increment_cursor_position_offset();
return true;
}
}

Expand All @@ -832,6 +842,8 @@ bool EventHandler::handle_keydown(UIEvents::KeyCode key, u32 modifiers, u32 code
return false;

if (document->cursor_position() && document->cursor_position()->node()->is_editable()) {
auto& node = *document->cursor_position()->node();

if (key == UIEvents::KeyCode::Key_Backspace) {
if (!document->decrement_cursor_position_offset()) {
// FIXME: Move to the previous node and delete the last character there.
Expand All @@ -849,32 +861,58 @@ bool EventHandler::handle_keydown(UIEvents::KeyCode key, u32 modifiers, u32 code
m_edit_event_handler->handle_delete_character_after(document, *document->cursor_position());
return true;
}
if (key == UIEvents::KeyCode::Key_Right) {
if (!document->increment_cursor_position_offset()) {
// FIXME: Move to the next node.
}
return true;
}
if (key == UIEvents::KeyCode::Key_Left) {
if (!document->decrement_cursor_position_offset()) {
// FIXME: Move to the previous node.

if (key == UIEvents::KeyCode::Key_Left || key == UIEvents::KeyCode::Key_Right) {
auto increment_or_decrement_cursor = [&]() {
if (key == UIEvents::KeyCode::Key_Left)
return document->decrement_cursor_position_offset();
return document->increment_cursor_position_offset();
};

if ((modifiers & UIEvents::Mod_Shift) == 0) {
if (selection && range) {
auto cursor_edge = key == UIEvents::KeyCode::Key_Left ? range->start_offset() : range->end_offset();

document->set_cursor_position(DOM::Position::create(document->realm(), node, cursor_edge));
selection->remove_all_ranges();
} else {
increment_or_decrement_cursor();
}
} else {
auto previous_position = document->cursor_position()->offset();
auto should_udpdate_selection = increment_or_decrement_cursor();

if (should_udpdate_selection && selection) {
auto selection_start = range ? selection->anchor_offset() : previous_position;
auto selection_end = document->cursor_position()->offset();

(void)selection->set_base_and_extent(node, selection_start, node, selection_end);
}
}

return true;
}
if (key == UIEvents::KeyCode::Key_Home) {
auto& cursor_position_node = *document->cursor_position()->node();
if (cursor_position_node.is_text())
document->set_cursor_position(DOM::Position::create(realm, cursor_position_node, 0));
return true;
}
if (key == UIEvents::KeyCode::Key_End) {
auto& cursor_position_node = *document->cursor_position()->node();
if (cursor_position_node.is_text()) {
auto& text_node = static_cast<DOM::Text&>(cursor_position_node);
document->set_cursor_position(DOM::Position::create(realm, text_node, (unsigned)text_node.data().bytes().size()));

if (key == UIEvents::KeyCode::Key_Home || key == UIEvents::KeyCode::Key_End) {
auto cursor_edge = key == UIEvents::KeyCode::Key_Home ? 0uz : node.length();

if ((modifiers & UIEvents::Mod_Shift) == 0) {
if (selection && range)
selection->remove_all_ranges();
} else {
auto previous_position = document->cursor_position()->offset();
auto should_udpdate_selection = previous_position != cursor_edge;

if (should_udpdate_selection && selection) {
auto selection_start = range ? selection->anchor_offset() : previous_position;
(void)selection->set_base_and_extent(node, selection_start, node, cursor_edge);
}
}

document->set_cursor_position(DOM::Position::create(realm, node, cursor_edge));
return true;
}

if (key == UIEvents::KeyCode::Key_Return) {
HTML::HTMLInputElement* input_element = nullptr;
if (auto node = document->cursor_position()->node()) {
Expand All @@ -896,6 +934,7 @@ bool EventHandler::handle_keydown(UIEvents::KeyCode key, u32 modifiers, u32 code
return true;
}
}

// FIXME: Text editing shortcut keys (copy/paste etc.) should be handled here.
if (!should_ignore_keydown_event(code_point, modifiers)) {
m_edit_event_handler->handle_insert(document, JS::NonnullGCPtr { *document->cursor_position() }, code_point);
Expand Down

0 comments on commit 596907d

Please sign in to comment.