textual icon indicating copy to clipboard operation
textual copied to clipboard

Fix Cursor Drag on Programmatic Edit

Open jbdyn opened this issue 9 months ago • 0 comments

Please review the following checklist.

  • [x] Docstrings on all new or modified functions / classes
  • [x] Updated documentation
  • [x] Updated CHANGELOG.md (where appropriate)

Hey folks! :wave:

Thank you all for your amazing work. :hearts:

I am building a collaborative text editor by combining Y-CRDTs via ypy/pycrdt with the TextArea widget. You did an awesome job on this and @darrenburns I enjoyed reading your blog post about it. :sparkles: The process of putting the pieces together was pretty smooth.

The only issue I faced until now was that my cursor gets dragged away by inserts after my cursor position:

cursor-drag

With this PR, I added the conditions to only apply the row offset when the insert happens before the cursor location:

no-cursor-drag

A new test comes with it.

:snake: This is the Python script I used for testing.
import anyio

from textual.app import App
from textual.widgets import TextArea, Label


TEXT = [
    "I must not fear.\n",
    "Fear is the mind-killer.\n",
    "Fear is the little-death that brings total obliteration.\n",
    "I will face my fear.\n",
    "\n",
]


class TestApp(App):
   def __init__(self):
       super().__init__()
       self.textarea = TextArea()
       self.textarea.show_line_numbers = True
       self.textarea.cursor_blink = False
       self.label = Label()

   def compose(self):
       yield self.textarea
       yield self.label


async def auto_insert(textarea, label):
    try:
        i = 0
        while True:
            textarea.insert(TEXT[i % 5], (i, 0))
            label.update(f"{i=}, {textarea.cursor_location=}")
            await anyio.sleep(1)
            i += 1
    except anyio.get_cancelled_exc_class():
        return


async def main():
    app = TestApp()
    textarea = app.textarea
    label = app.label
    async with anyio.create_task_group() as tg:
        tg.start_soon(auto_insert, textarea, label)
        await app.run_async()


if __name__ == "__main__":
    try:
        anyio.run(main)
    except KeyboardInterrupt:
        print("exiting gracefully")

What are your thoughts?

jbdyn avatar May 09 '24 00:05 jbdyn