Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TextEdit multicaret and selection issues #86863

Closed
kitbdev opened this issue Jan 6, 2024 · 0 comments · Fixed by #86978
Closed

TextEdit multicaret and selection issues #86863

kitbdev opened this issue Jan 6, 2024 · 0 comments · Fixed by #86978

Comments

@kitbdev
Copy link
Contributor

kitbdev commented Jan 6, 2024

Tested versions

Godot v4.2, v4.3.dev (179dfdc)

System information

Windows 10.0.22621 - Vulkan (Forward+) - dedicated NVIDIA GeForce RTX 2070 (NVIDIA; 31.0.15.3598) - Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz (12 Threads)

Issue description

Some of these issues are only relevent to CodeEdit, CodeEditor, or ScriptTextEditor, but most stem from TextEdit.

related issues:

related PRs:

I am working on a PR to fix all of these issues.

API #

  • Lots of methods don't redraw.
    • add_caret, remove_caret, set_caret_line, set_caret_col, set_line, remove_text, etc.
    • This is noticeable when caret_blink is off.
  • Passing out of bounds caret_index to various functions doesn't error for indexes at carets.size() or below -1.
    • For insert_text_at_caret, select_word_under_caret, select_word_under_caret, delete_selection, _handle_unicode_input_internal, _backspace_internal, _cut_internal, _copy_internal, _paste_internal, _paste_primary_clipboard_internal, get_word_under_caret, has_selection, get_selected_text.
  • remove_text() doesn't adjust carets. It is mentioned in the documentation, but it is not consistent with all other methods.
  • set_line() doesn't properly adjust carets. selection from is ignored and passing in multiple lines doesn't adjust carets below.
  • Adding multiple lines with insert_line_at doesn't offset carets after the inserted line correctly.
    • When insert_line_at is called with the subsequent line, they are merged together so undo will undo both calls.
  • When the TextEdit is out of the tree, editing multiple carets multiple times does not work as expected and can show errors. This is because caret_index_edit_dirty is only set when in the tree, and after the first edit, getting the caret order won't be up to date.

Backspace #

  • Using backspace when indent_using_spaces is true and there are multiple carets in the last tab of a line, the wrong characters can be deleted. There must be a caret at the first non whitespace column, and another before it within that same tab.

    • Steps:
      1. Set a line to test.
      2. Put a caret before the t, and another one two space to the left of that caret.
      3. Backspace.
      4. The line becomes tst, when it should be test.
    • Screenshot:
      godot te backspace space issue
  • Using backspace at the start of a line with multiple carets on it moves the carets straight up to the previous line if it can.

    • If there is already a caret on the line above they can collapse into one position without being merged.
    • Steps:
      1. Have two lines with text.
      2. Put a caret at the second line column 0, and another on the second line.
      3. Backspace.
      4. The second caret did not move with the text correctly.
  • If a caret is placed one character to the left of another, it shows errors like The main caret should not be removed. or scene\gui\text_edit.cpp:4878 - Index p_caret = 1 is out of bounds (carets.size() = 1).

    • The caret on the left must be placed after the one on the right, so it has a higher index.

Delete #

  • Using ctrl+delete (ui_text_delete_word) with at least 3 carets on one word shows an error: scene\gui\text_edit.cpp:4563 Index p_caret = 2 is out of bounds or The main caret should not be removed..
    • Sometimes it doesn't happen if the word is too small.
    • Gif:
      godot te control delete error

Copy, Cut, Paste #

  • Copying or cutting with no selection doesn't handle line ends properly when pasting externally or to a different TextEdit. The first line content doesn't have a newline at the end.
  • Copying one line with multiple carets on it adds multiple copies of that line to the clipboard, it should only add it once.
  • Pasting a full line after copying with no selection moves the caret to the start of the line, instead of keeping it at the same column.
  • Cutting from multiple lines with no selection doesn't have a new line for the first line, even when pasting into the same TextEdit.
  • Cutting when the caret is on a tab or non-same width text may move the caret around, it doesn't stay at the same visual position.
  • Cutting with multiple carets on one line shows an error like The main caret should not be removed. or scene\gui\text_edit.cpp:4878 - Index p_caret = 1 is out of bounds (carets.size() = 1).
  • Cutting a folded line doesn't unfold it. The line before the one that was cut may be unfolded instead.
  • Cutting should still add text to clipboard even if not editable.
  • Crash when cutting in some cases: Random crashes using Ctrl + X while multiple lines selected #81535

IME (Fixed #87479) #

  • Clicking inside the TextEdit but outside of IME area does not apply the IME text or close it.

    • Right clicking moves the caret and the IME preview text the next time it is updated, but it doesn't clear the preview from the previous line so 'fake' text appears that shows errors when clicked scene\gui\text_edit.cpp:4991 - Index p_column = 3 is out of bounds (text[carets[p_caret].selection.selecting_line].length() + 1 = 1).
  • Clicking outside the TextEdit (on something focusable) closes the IME and does not apply it, however clicking outside the window or using alt+tab closes and applies the IME.

  • It is possible to select text while the IME is still open by clicking and dragging. This can cause multiple errors. scene\gui\text_edit.cpp::4984

    • This happens on mouse move, not mouse down.
    • Gif:
      godot te ime select
  • Cannot use the mouse wheel or drag the minimap when IME is open, but can use scrollbar.

  • Some actions can still be used while IME is open, when using shortcut or the MenuBar. These should all close the IME.

    • Fold all lines can hide the IME text and move the caret.
    • Cut can remove the line.
    • Trim trailing whitespace can shift where the text should be and can cause errors.
    • Search find next will move the caret.
    • Basically, anything that can move the caret or change text should close the IME first.

Mouse selection #

  • Selections can overlap.

    • steps:
      1. Make a small selection in the middle of some text.
      2. Make a new selection with the mouse holding alt that completely contains the first one, without touching it.
    • There are other ways to cause this and merge_overlapping_carets() doesn't catch it.
    • Using overlapping selections can cause lots of errors since selections are assumed to never overlap like this.
  • Shift selecting left/up when selection is left to right appends to the selection instead of selecting to the from position.

    • Doing vice versa or dragging the mouse shows how it should work.
    • This can show errors in some cases. scene\gui\text_edit.cpp:7841 - Condition "p_to_line < p_from_line" is true. Returning: String()
      • Steps:
        • Click in the middle of a line.
        • Shift+click to select the line below.
        • Shift+click to select the line above.
        • Shift+click to select the end of the original line.
    • Gif:
      godot te shift select error
  • Error when clicking in selection if it's not the last caret's selection.

    • Steps:
      • Select a range of text.
      • Add another caret somewhere else with alt+click.
      • Regular click inside the first selection.
      • scene\gui\text_edit.cpp:5189 - Condition "!has_selection(p_caret)" is true. Returning: -1
  • If a selection is made in selection mode word or line, adding a new selection with a new caret (alt+click and drag) will be made in the same mode instead of in pointer mode.

  • When shift selecting and changing selection mode with shift+double click the selection origin can change unexpectedly.

  • While dragging and using shortcuts that change text at the same time (toggling comments, indentation, etc) the selection origin can be moved around.

Selection mode word

  • When double clicking without a word (such as at the end of a line, spaces or symbols), the starting selection word will become the next word highlighted or wrong instead of being none.

    • Gif:
      godot te word select start issue
  • Cannot activate on an empty line.

  • Shift+double click and drag starts drag and drop instead of starting selection mode word.

Selection mode line

  • Doesn't adjust the scroll when going above or below the visible area.
  • Cannot activate on an empty line.
  • When selecting to the last line, the caret should be at end of last line, not the start.
    • Clicking the gutter on the last line doesn't select its content.
  • When selecting upwards, the caret is one line above where it should be, it's disconnected from the selection.
  • Shift+triple click restarts selection instead of extending existing selection and starting selection mode line.

Drag and drop selected text #

  • Starting drag when the last caret is not a selection shows errors. 'scene\gui\text_edit.cpp:5189 - Condition "!has_selection(p_caret)" is true. Returning: -1'.
  • Dragging moves caret 0, and separates it from its selection. If canceled (escape) the caret is not restored.
    • Undoing a drop from an external source also does not restore the caret.
  • Dragging can merge and remove other carets.
  • When dropping selected text onto itself, the caret will be in middle of selected area, not at either end of the selection.
  • Cannot drop text from an external source onto a selection that is not the first caret. It will instead be put at the first selection, or show errors if caret 0 has no selection. scene\gui\text_edit.cpp:5195 - Condition "!has_selection(p_caret)" is true. Returning: -1
  • Changing any text, adding, removing, or moving carets don't cancel drag. This can cause errors and make the dragged text content out of date. scene\gui\text_edit.cpp:5207 - Condition "!has_selection(p_caret)" is true. Returning: -1.
    • Use shortcuts like ui_text_add_caret_below to change carets while dragging text.
  • Dragging text then right clicking opens the context menu and removes the selection but doesn't cancel the drag so releasing left mouse shows errors. scene\gui\text_edit.cpp:5213 - Condition "!has_selection(p_caret)" is true. Returning: -1
  • Drag text over a text edit without focus, then move the mouse off of it. The caret shows and stays at the last valid drop position when it should disappear.
    • This is because drag_caret_force_displayed doesn't get set back to false when the mouse exits.
    • To test this, I created a TextEdit in the Scene and used the Text and Placeholder Text fields in the Inspector.

Toggle comment #

Toggle indent #

Auto indent

  • Only operates on the first selection, ignores others.

Convert Indent

  • Converting from spaces to indent can move carets and selections to the right past where they should by a couple of characters if they are not aligned with the tabs.
    • Gif:
      godot te convert tab offset

Duplicate Selection #

  • Can show index out of bounds error in some cases.
    • Steps:
      • Select from the end of a line to the left (selection size of 1 is fine).
      • Add another caret to the same line.
      • Use Duplicate Selection.
  • Duplicating mixed carets and selections order of operations depends on their left to right order. It should be more consistent, such as all single carets first then all selections (or vise versa).

Duplicate Lines Down #

Add caret above/below #

  • Invalid selection when adding to a hidden line. Selection will be detached from caret. Hidden lines should be ignored.
  • Doesn't adjust the scroll when going above or below the visible area.
  • Selections can be made to overlap when there are multiple selections and one is bigger than the other.
  • Insert caret above when on the first line shows error. scene\gui\text_edit.cpp:5294 - Index p_line = -1 is out of bounds (text.size() = 124).
  • Insert caret below with a caret on the last wrap of the last line adds a caret on the first wrap of that line instead of doing nothing.
  • Selection origin has no last_fit_x, so the origin of the selection works differently than the caret side.
  • Selections don't preserve last_fit_x, so it interacts with tabs and non fixed width text unexpectedly.

Toggle Fold line #

  • Undoing/Redoing an action in an area that was just folded moves the caret to a hidden line and doesn't unfold.

Create Code Region #

  • Shows an error when there are both selections and regular carets. scene\gui\text_edit.cppscene\gui\text_edit.cpp:5189 - Condition "!has_selection(p_caret)" is true. Returning: -1
  • Creating a Code Region and undoing it removes the region but doesn't unfold, so the caret will be put on a hidden line.

Connect Signal #

  • When connecting a signal to an existing function, the selection is not cleared and gets separated from the caret.
  • When indent mode is set to spaces, after connecting a signal, the caret will be put at column 1 instead of after the number of spaces per tab.
  • Undo after creating a function does not fully restore carets.

Go to Line and Go to Function #

  • Go to Line does not deselect, disconnects caret from selection.
  • Members overview puts the line at the top of the visible area, but Go to Function/Line just scrolls until the line is visible.
  • Members overview puts the caret at column 0, but Go to Function/Line doesn't change the caret column.

Reload Scripts #

  • Switching scripts when the first caret does not have a selection but the second caret does shows errors. scene\gui\text_edit.cpp:5195 - Condition "!has_selection(p_caret)" is true. Returning: -1
    • This is because CodeTextEditor::get_navigation_state() checks if there is any selection before trying to get the first carets selection, even if the first caret has no selection.
    • This also happens when opening documentation by control clicking, history back, and history forward
  • Multiple carets aren't handled when using script history back and forward. Only the first one is saved/loaded and no carets are not added or removed.
  • Multiple carets are not saved or restored when reopening a script.
  • Reloading scripts if content has changed doesn't keep multiple carets or selections.

I found a few other issues, but I'll open separate issues for them.

Steps to reproduce

See above for steps and details, let me know if any aren't clear enough.
API issues are reproducible with a TextEdit and using the related methods.
All others should be reproducible in the Script editor, unless otherwise specified.

Minimal reproduction project (MRP)

N/A

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants