From d727627edd13b153b6b0ed002281020cf196b483 Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Tue, 26 Sep 2023 16:54:00 +0100 Subject: [PATCH] Fixing IME alignment for Input widget. TextArea remains unfixed. --- src/textual/app.py | 8 ++++++++ src/textual/screen.py | 2 +- src/textual/widgets/_input.py | 12 +++++++++++- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/textual/app.py b/src/textual/app.py index 48fd36188a..8ad739e3e1 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -53,6 +53,7 @@ import rich.repr from rich import terminal_theme from rich.console import Console, RenderableType +from rich.control import Control from rich.protocol import is_renderable from rich.segment import Segment, Segments from rich.traceback import Traceback @@ -417,6 +418,7 @@ def __init__( self._animator = Animator(self) self._animate = self._animator.bind(self) self.mouse_position = Offset(0, 0) + self.cursor_position = Offset(0, 0) self._exception: Exception | None = None """The unhandled exception which is leading to the app shutting down, @@ -2423,7 +2425,11 @@ def _display(self, screen: Screen, renderable: RenderableType | None) -> None: try: try: if isinstance(renderable, CompositorUpdate): + cursor_x, cursor_y = self.cursor_position terminal_sequence = renderable.render_segments(console) + terminal_sequence += Control.move_to( + cursor_x, cursor_y + ).segment.text else: segments = console.render(renderable) terminal_sequence = console._render_buffer(segments) @@ -2433,7 +2439,9 @@ def _display(self, screen: Screen, renderable: RenderableType | None) -> None: self._driver.write(terminal_sequence) finally: self._end_update() + self._driver.flush() + finally: self.post_display_hook() diff --git a/src/textual/screen.py b/src/textual/screen.py index 631dadb4ba..c5404bc05f 100644 --- a/src/textual/screen.py +++ b/src/textual/screen.py @@ -515,7 +515,7 @@ def _reset_focus( chosen = candidate break - # Go with the what was found. + # Go with what was found. self.set_focus(chosen) def _update_focus_styles( diff --git a/src/textual/widgets/_input.py b/src/textual/widgets/_input.py index b92161e504..59c3c09c95 100644 --- a/src/textual/widgets/_input.py +++ b/src/textual/widgets/_input.py @@ -15,7 +15,7 @@ from .._segment_tools import line_crop from ..binding import Binding, BindingType from ..events import Blur, Focus, Mount -from ..geometry import Size +from ..geometry import Offset, Size from ..message import Message from ..reactive import reactive from ..suggester import Suggester, SuggestionReady @@ -254,6 +254,7 @@ def __init__( super().__init__(name=name, id=id, classes=classes, disabled=disabled) if value is not None: self.value = value + self.placeholder = placeholder self.highlighter = highlighter self.password = password @@ -327,6 +328,14 @@ def _watch_cursor_position(self) -> None: else: self.view_position = self.view_position + self.app.cursor_position = self.cursor_screen_offset + + @property + def cursor_screen_offset(self) -> Offset: + """The offset of the cursor of this input in screen-space. (x, y)/(column, row)""" + x, y, _width, _height = self.content_region + return Offset(x + self._cursor_offset - self.view_position, y) + async def _watch_value(self, value: str) -> None: self._suggestion = "" if self.suggester and value: @@ -425,6 +434,7 @@ def _on_focus(self, _: Focus) -> None: self.cursor_position = len(self.value) if self.cursor_blink: self.blink_timer.resume() + self.app.cursor_position = self.cursor_screen_offset async def _on_key(self, event: events.Key) -> None: self._cursor_visible = True