From 44cb59447bd51001597bb7ee84dc86e570179dec Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 9 Mar 2023 13:54:08 +0000 Subject: [PATCH 1/4] prevent stuck scrollbar --- src/textual/driver.py | 54 +++++++++++++++++++++++----------------- src/textual/scrollbar.py | 10 ++++++-- 2 files changed, 39 insertions(+), 25 deletions(-) diff --git a/src/textual/driver.py b/src/textual/driver.py index afe852bfd6..0e8ac6412c 100644 --- a/src/textual/driver.py +++ b/src/textual/driver.py @@ -27,8 +27,8 @@ def __init__( self._size = size self._loop = asyncio.get_running_loop() self._mouse_down_time = _clock.get_time_no_wait() - self._dragging = False - self._dragging_button = None + self._down_buttons: list[int] = [] + self._last_move_event: events.MouseMove | None = None @property def is_headless(self) -> bool: @@ -44,29 +44,37 @@ def process_event(self, event: events.Event) -> None: """Performs some additional processing of events.""" if isinstance(event, events.MouseDown): self._mouse_down_time = event.time + if event.button: + self._down_buttons.append(event.button) + elif isinstance(event, events.MouseUp): + if event.button: + self._down_buttons.remove(event.button) elif isinstance(event, events.MouseMove): - if event.button and not self._dragging: - self._dragging = True - self._dragging_button = event.button - elif self._dragging and self._dragging_button != event.button: - # Artificially generate a MouseUp event when we stop "dragging" - self.send_event( - MouseUp( - x=event.x, - y=event.y, - delta_x=event.delta_x, - delta_y=event.delta_y, - button=self._dragging_button, - shift=event.shift, - meta=event.meta, - ctrl=event.ctrl, - screen_x=event.screen_x, - screen_y=event.screen_y, - style=event.style, + if ( + self._down_buttons + and not event.button + and self._last_move_event is not None + ): + buttons = list(dict.fromkeys(self._down_buttons).keys()) + self._down_buttons.clear() + move_event = self._last_move_event + for button in buttons: + self.send_event( + MouseUp( + x=move_event.x, + y=move_event.y, + delta_x=0, + delta_y=0, + button=button, + shift=event.shift, + meta=event.meta, + ctrl=event.ctrl, + screen_x=move_event.screen_x, + screen_y=move_event.screen_y, + style=event.style, + ) ) - ) - self._dragging = False - self._dragging_button = None + self._last_move_event = event self.send_event(event) diff --git a/src/textual/scrollbar.py b/src/textual/scrollbar.py index bc2ed6dd7c..5cbcc65d97 100644 --- a/src/textual/scrollbar.py +++ b/src/textual/scrollbar.py @@ -291,6 +291,7 @@ def _render_bar(self, scrollbar_style: Style) -> RenderableType: def _on_hide(self, event: events.Hide) -> None: if self.grabbed: self.release_mouse() + self.grabbed = None def _on_enter(self, event: events.Enter) -> None: self.mouse_over = True @@ -299,10 +300,12 @@ def _on_leave(self, event: events.Leave) -> None: self.mouse_over = False def action_scroll_down(self) -> None: - self.post_message(ScrollDown() if self.vertical else ScrollRight()) + if not self.grabbed: + self.post_message(ScrollDown() if self.vertical else ScrollRight()) def action_scroll_up(self) -> None: - self.post_message(ScrollUp() if self.vertical else ScrollLeft()) + if not self.grabbed: + self.post_message(ScrollUp() if self.vertical else ScrollLeft()) def action_grab(self) -> None: self.capture_mouse() @@ -313,6 +316,8 @@ def action_released(self) -> None: async def _on_mouse_up(self, event: events.MouseUp) -> None: if self.grabbed: self.release_mouse() + self.grabbed = None + print("RELEASED") event.stop() def _on_mouse_capture(self, event: events.MouseCapture) -> None: @@ -324,6 +329,7 @@ def _on_mouse_release(self, event: events.MouseRelease) -> None: event.stop() async def _on_mouse_move(self, event: events.MouseMove) -> None: + print("MOVE", self.grabbed, self.window_size) if self.grabbed and self.window_size: x: float | None = None y: float | None = None From 2ed846c2888fd758da86ed09ad0d0b1869f5edd6 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 9 Mar 2023 13:55:34 +0000 Subject: [PATCH 2/4] update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dcd56a3f07..6ee4642d18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,7 +34,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Fixed bug that prevented pilot from pressing some keys https://github.com/Textualize/textual/issues/1815 - DataTable race condition that caused crash https://github.com/Textualize/textual/pull/1962 -- Fixed scrollbar getting "stuck" to cursor when cursor leaves window during drag https://github.com/Textualize/textual/pull/1968 +- Fixed scrollbar getting "stuck" to cursor when cursor leaves window during drag https://github.com/Textualize/textual/pull/1968 https://github.com/Textualize/textual/pull/2003 - DataTable crash when enter pressed when table is empty https://github.com/Textualize/textual/pull/1973 ## [0.13.0] - 2023-03-02 From e16627587fe0d5d317eef141a3ea085f3905904a Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 9 Mar 2023 13:58:18 +0000 Subject: [PATCH 3/4] remove debug --- src/textual/scrollbar.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/textual/scrollbar.py b/src/textual/scrollbar.py index 5cbcc65d97..8aec32f5bf 100644 --- a/src/textual/scrollbar.py +++ b/src/textual/scrollbar.py @@ -317,7 +317,6 @@ async def _on_mouse_up(self, event: events.MouseUp) -> None: if self.grabbed: self.release_mouse() self.grabbed = None - print("RELEASED") event.stop() def _on_mouse_capture(self, event: events.MouseCapture) -> None: From bc18d2638ee4059dd28e4914153aa62fe0ba8bad Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Thu, 9 Mar 2023 14:04:53 +0000 Subject: [PATCH 4/4] remove debug --- src/textual/scrollbar.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/textual/scrollbar.py b/src/textual/scrollbar.py index 8aec32f5bf..df5516ce9f 100644 --- a/src/textual/scrollbar.py +++ b/src/textual/scrollbar.py @@ -328,7 +328,6 @@ def _on_mouse_release(self, event: events.MouseRelease) -> None: event.stop() async def _on_mouse_move(self, event: events.MouseMove) -> None: - print("MOVE", self.grabbed, self.window_size) if self.grabbed and self.window_size: x: float | None = None y: float | None = None