LibWeb: Prevent cursor movement when collapsing text selection
Previously if you had a selection and you pressed the left/right arrow key it would collapse the selection *and* perform the movement. This is not how most text editors work and felt unnatural.
This commit is contained in:
parent
dc381c4ba7
commit
2e930e83ce
3 changed files with 120 additions and 10 deletions
|
|
@ -1034,7 +1034,12 @@ void FormAssociatedTextControlElement::increment_cursor_position_offset(Collapse
|
|||
auto const text_node = form_associated_element_to_text_node();
|
||||
if (!text_node)
|
||||
return;
|
||||
if (auto offset = text_node->grapheme_segmenter().next_boundary(m_selection_end); offset.has_value()) {
|
||||
// If there is a selection range, collapse to the end (max) of that range without moving forward
|
||||
if (collapse == CollapseSelection::Yes && (m_selection_start != m_selection_end)) {
|
||||
collapse_selection_to_offset(max(m_selection_start, m_selection_end));
|
||||
}
|
||||
// Otherwise, move forward if possible
|
||||
else if (auto offset = text_node->grapheme_segmenter().next_boundary(m_selection_end); offset.has_value()) {
|
||||
if (collapse == CollapseSelection::Yes) {
|
||||
collapse_selection_to_offset(*offset);
|
||||
} else {
|
||||
|
|
@ -1049,7 +1054,12 @@ void FormAssociatedTextControlElement::decrement_cursor_position_offset(Collapse
|
|||
auto const text_node = form_associated_element_to_text_node();
|
||||
if (!text_node)
|
||||
return;
|
||||
if (auto offset = text_node->grapheme_segmenter().previous_boundary(m_selection_end); offset.has_value()) {
|
||||
// If there is a selection range, collapse to the start (min) of that range without moving backward
|
||||
if (collapse == CollapseSelection::Yes && (m_selection_start != m_selection_end)) {
|
||||
collapse_selection_to_offset(min(m_selection_start, m_selection_end));
|
||||
}
|
||||
// Otherwise, move backward if possible
|
||||
else if (auto offset = text_node->grapheme_segmenter().previous_boundary(m_selection_end); offset.has_value()) {
|
||||
if (collapse == CollapseSelection::Yes) {
|
||||
collapse_selection_to_offset(*offset);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -597,15 +597,21 @@ void Selection::move_offset_to_next_character(bool collapse_selection)
|
|||
if (!text_node)
|
||||
return;
|
||||
|
||||
if (auto offset = text_node->grapheme_segmenter().next_boundary(focus_offset()); offset.has_value()) {
|
||||
// If there is a selection range, collapse to the end (max) of that range without moving forward
|
||||
if (collapse_selection && !is_collapsed()) {
|
||||
MUST(collapse(text_node, max(anchor_offset(), focus_offset())));
|
||||
m_document->reset_cursor_blink_cycle();
|
||||
}
|
||||
// Otherwise, move forward if possible
|
||||
else if (auto offset = text_node->grapheme_segmenter().next_boundary(focus_offset()); offset.has_value()) {
|
||||
if (collapse_selection) {
|
||||
MUST(collapse(text_node, *offset));
|
||||
m_document->reset_cursor_blink_cycle();
|
||||
} else {
|
||||
MUST(set_base_and_extent(*text_node, anchor_offset(), *text_node, *offset));
|
||||
}
|
||||
scroll_focus_into_view();
|
||||
}
|
||||
scroll_focus_into_view();
|
||||
}
|
||||
|
||||
void Selection::move_offset_to_previous_character(bool collapse_selection)
|
||||
|
|
@ -614,15 +620,21 @@ void Selection::move_offset_to_previous_character(bool collapse_selection)
|
|||
if (!text_node)
|
||||
return;
|
||||
|
||||
if (auto offset = text_node->grapheme_segmenter().previous_boundary(focus_offset()); offset.has_value()) {
|
||||
// If there is a selection range, collapse to the start (min) of that range without moving backward
|
||||
if (collapse_selection && !is_collapsed()) {
|
||||
MUST(collapse(text_node, min(anchor_offset(), focus_offset())));
|
||||
m_document->reset_cursor_blink_cycle();
|
||||
}
|
||||
// Otherwise, move backward if possible
|
||||
else if (auto offset = text_node->grapheme_segmenter().previous_boundary(focus_offset()); offset.has_value()) {
|
||||
if (collapse_selection) {
|
||||
MUST(collapse(text_node, *offset));
|
||||
m_document->reset_cursor_blink_cycle();
|
||||
} else {
|
||||
MUST(set_base_and_extent(*text_node, anchor_offset(), *text_node, *offset));
|
||||
}
|
||||
scroll_focus_into_view();
|
||||
}
|
||||
scroll_focus_into_view();
|
||||
}
|
||||
|
||||
void Selection::move_offset_to_next_word(bool collapse_selection)
|
||||
|
|
@ -637,13 +649,13 @@ void Selection::move_offset_to_next_word(bool collapse_selection)
|
|||
break;
|
||||
|
||||
if (auto offset = text_node->word_segmenter().next_boundary(focus_offset); offset.has_value()) {
|
||||
auto word = text_node->data().substring_view(focus_offset, *offset - focus_offset);
|
||||
if (collapse_selection) {
|
||||
MUST(collapse(text_node, *offset));
|
||||
m_document->reset_cursor_blink_cycle();
|
||||
} else {
|
||||
MUST(set_base_and_extent(*text_node, anchor_offset(), *text_node, *offset));
|
||||
}
|
||||
auto word = text_node->data().substring_view(focus_offset, *offset - focus_offset);
|
||||
if (Unicode::Segmenter::should_continue_beyond_word(word))
|
||||
continue;
|
||||
}
|
||||
|
|
@ -661,13 +673,13 @@ void Selection::move_offset_to_previous_word(bool collapse_selection)
|
|||
while (true) {
|
||||
auto focus_offset = this->focus_offset();
|
||||
if (auto offset = text_node->word_segmenter().previous_boundary(focus_offset); offset.has_value()) {
|
||||
auto word = text_node->data().substring_view(*offset, focus_offset - *offset);
|
||||
if (collapse_selection) {
|
||||
MUST(collapse(text_node, *offset));
|
||||
m_document->reset_cursor_blink_cycle();
|
||||
} else {
|
||||
MUST(set_base_and_extent(*text_node, anchor_offset(), *text_node, *offset));
|
||||
}
|
||||
auto word = text_node->data().substring_view(*offset, focus_offset - *offset);
|
||||
if (Unicode::Segmenter::should_continue_beyond_word(word))
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,16 +24,104 @@
|
|||
return;
|
||||
}
|
||||
|
||||
selection.modify("extend", "backward", "word");
|
||||
if (selection.toString() !== "Well ") {
|
||||
println("FAIL: selection is not what we expected after extending by word");
|
||||
return;
|
||||
}
|
||||
|
||||
// backward by character on a non-collapsed selection
|
||||
selection.modify("move", "backward", "character");
|
||||
if (
|
||||
selection.anchorNode !== a.firstChild || selection.anchorOffset !== 9 ||
|
||||
selection.focusNode !== a.firstChild || selection.focusOffset !== 9 ||
|
||||
selection.anchorNode !== a.firstChild || selection.anchorOffset !== 0 ||
|
||||
selection.focusNode !== a.firstChild || selection.focusOffset !== 0 ||
|
||||
!selection.isCollapsed
|
||||
) {
|
||||
println("FAIL: selection is not what we expected after moving backward by character on non-collapsed selection");
|
||||
return;
|
||||
}
|
||||
|
||||
// forward by character on a non-collapsed selection
|
||||
selection.setBaseAndExtent(a.firstChild, 0, a.firstChild, 10);
|
||||
selection.modify("move", "forward", "character");
|
||||
if (
|
||||
selection.anchorNode !== a.firstChild || selection.anchorOffset !== 10 ||
|
||||
selection.focusNode !== a.firstChild || selection.focusOffset !== 10 ||
|
||||
!selection.isCollapsed
|
||||
) {
|
||||
println("FAIL: selection is not what we expected after moving forward by character on non-collapsed selection");
|
||||
return;
|
||||
}
|
||||
|
||||
// backward by word on a non-collapsed selection
|
||||
selection.setBaseAndExtent(a.firstChild, 8, a.firstChild, 15);
|
||||
selection.modify("move", "backward", "word");
|
||||
if (
|
||||
selection.anchorNode !== a.firstChild || selection.anchorOffset !== 11 ||
|
||||
selection.focusNode !== a.firstChild || selection.focusOffset !== 11 ||
|
||||
!selection.isCollapsed
|
||||
) {
|
||||
println("FAIL: selection is not what we expected after moving backward by word on non-collapsed selection");
|
||||
return;
|
||||
}
|
||||
|
||||
// forward by word on a non-collapsed selection
|
||||
selection.setBaseAndExtent(a.firstChild, 0, a.firstChild, 10);
|
||||
selection.modify("move", "forward", "word");
|
||||
if (
|
||||
selection.anchorNode !== a.firstChild || selection.anchorOffset !== 18 ||
|
||||
selection.focusNode !== a.firstChild || selection.focusOffset !== 18 ||
|
||||
!selection.isCollapsed
|
||||
) {
|
||||
println("FAIL: selection is not what we expected after moving forward by word on non-collapsed selection");
|
||||
return;
|
||||
}
|
||||
|
||||
// backward by character on a collapsed selection
|
||||
selection.modify("move", "backward", "character");
|
||||
if (
|
||||
selection.anchorNode !== a.firstChild || selection.anchorOffset !== 17 ||
|
||||
selection.focusNode !== a.firstChild || selection.focusOffset !== 17 ||
|
||||
!selection.isCollapsed
|
||||
) {
|
||||
println("FAIL: selection is not what we expected after moving backward by character");
|
||||
return;
|
||||
}
|
||||
|
||||
// forward by character on a collapsed selection
|
||||
selection.modify("move", "forward", "character");
|
||||
if (
|
||||
selection.anchorNode !== a.firstChild || selection.anchorOffset !== 18 ||
|
||||
selection.focusNode !== a.firstChild || selection.focusOffset !== 18 ||
|
||||
!selection.isCollapsed
|
||||
) {
|
||||
println("FAIL: selection is not what we expected after moving forward by character");
|
||||
return;
|
||||
}
|
||||
|
||||
// backward by word (twice) on a collapsed selection
|
||||
selection.modify("move", "backward", "word");
|
||||
selection.modify("move", "backward", "word");
|
||||
if (
|
||||
selection.anchorNode !== a.firstChild || selection.anchorOffset !== 5 ||
|
||||
selection.focusNode !== a.firstChild || selection.focusOffset !== 5 ||
|
||||
!selection.isCollapsed
|
||||
) {
|
||||
println("FAIL: selection is not what we expected after moving backward by word");
|
||||
return;
|
||||
}
|
||||
|
||||
// forward by word on a collapsed selection
|
||||
selection.modify("move", "forward", "word");
|
||||
if (
|
||||
selection.anchorNode !== a.firstChild || selection.anchorOffset !== 10 ||
|
||||
selection.focusNode !== a.firstChild || selection.focusOffset !== 10 ||
|
||||
!selection.isCollapsed
|
||||
) {
|
||||
println("FAIL: selection is not what we expected after moving forward by word");
|
||||
return;
|
||||
}
|
||||
|
||||
println("PASS");
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
Loading…
Reference in a new issue