diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a82ccec25..6a0fa25aa9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [8.2.2] - 2026-04-03 + +### Fixed + +- Fixed Pointless style updates when resizing https://github.com/Textualize/textual/pull/6464 ## [8.2.1] - 2026-03-29 @@ -3402,6 +3407,7 @@ https://textual.textualize.io/blog/2022/11/08/version-040/#version-040 - New handler system for messages that doesn't require inheritance - Improved traceback handling +[8.2.2]: https://github.com/Textualize/textual/compare/v8.2.1...v8.2.2 [8.2.1]: https://github.com/Textualize/textual/compare/v8.2.0...v8.2.1 [8.2.0]: https://github.com/Textualize/textual/compare/v8.1.1...v8.2.0 [8.1.1]: https://github.com/Textualize/textual/compare/v8.1.0...v8.1.1 diff --git a/pyproject.toml b/pyproject.toml index b1df604144..b7d0e89675 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "textual" -version = "8.2.1" +version = "8.2.2" homepage = "https://github.com/Textualize/textual" repository = "https://github.com/Textualize/textual" documentation = "https://textual.textualize.io/" diff --git a/src/textual/app.py b/src/textual/app.py index df9db50376..58ddb65cea 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -1630,6 +1630,8 @@ def size(self) -> Size: width, height = self._driver._size else: width, height = self.console.size + if self._driver is not None: + self._driver._size = (width, height) return Size(width, height) @property diff --git a/src/textual/dom.py b/src/textual/dom.py index 5fcf49e866..f8d878297c 100644 --- a/src/textual/dom.py +++ b/src/textual/dom.py @@ -125,8 +125,9 @@ def __set__(self, obj: DOMNode, classes: str | Iterable[str]) -> None: else: class_names = set(classes) check_identifiers("class name", *class_names) - obj._classes = class_names - obj.update_node_styles() + if obj._classes != class_names: + obj._classes = class_names + obj.update_node_styles() @rich.repr.auto @@ -1795,10 +1796,9 @@ def add_class(self, *class_names: str, update: bool = True) -> Self: Self. """ check_identifiers("class name", *class_names) - old_classes = self._classes.copy() - self._classes.update(class_names) - if old_classes == self._classes: + if self._classes.issuperset(class_names): return self + self._classes.update(class_names) if update: self.update_node_styles() return self @@ -1814,10 +1814,9 @@ def remove_class(self, *class_names: str, update: bool = True) -> Self: Self. """ check_identifiers("class name", *class_names) - old_classes = self._classes.copy() - self._classes.difference_update(class_names) - if old_classes == self._classes: + if self._classes.isdisjoint(class_names): return self + self._classes.difference_update(class_names) if update: self.update_node_styles() return self diff --git a/src/textual/drivers/linux_driver.py b/src/textual/drivers/linux_driver.py index 66f4c4d978..98bf632ff7 100644 --- a/src/textual/drivers/linux_driver.py +++ b/src/textual/drivers/linux_driver.py @@ -241,6 +241,7 @@ def on_terminal_resize(signum, stack) -> None: send_size_event() signal.signal(signal.SIGWINCH, on_terminal_resize) + send_size_event() self.write("\x1b[?1049h") # Alt screen @@ -277,7 +278,7 @@ def on_terminal_resize(signum, stack) -> None: self.flush() self._key_thread = Thread(target=self._run_input_thread, name="textual-input") - send_size_event() + self._key_thread.start() self._request_terminal_sync_mode_support() self._query_in_band_window_resize() diff --git a/src/textual/screen.py b/src/textual/screen.py index 405b066d46..af18fa8dbd 100644 --- a/src/textual/screen.py +++ b/src/textual/screen.py @@ -1528,27 +1528,52 @@ async def _on_resize(self, event: events.Resize) -> None: ) or [] width, height = event.size - if horizontal_breakpoints: - self._set_breakpoints(width, horizontal_breakpoints) - if vertical_breakpoints: - self._set_breakpoints(height, vertical_breakpoints) - def _set_breakpoints( + if horizontal_breakpoints or vertical_breakpoints: + + remove_breakpoint_classes = { + breakpoint for _, breakpoint in horizontal_breakpoints + } | {breakpoint for _, breakpoint in vertical_breakpoints} + remove_breakpoint_classes = self._classes.intersection( + remove_breakpoint_classes + ) + + breakpoint_classes: set[str] = set() + + if horizontal_breakpoints: + breakpoint_classes |= self._get_breakpoint_classes( + width, horizontal_breakpoints + ) + if vertical_breakpoints: + breakpoint_classes |= self._get_breakpoint_classes( + height, vertical_breakpoints + ) + + remove_breakpoint_classes -= breakpoint_classes + classes = self._classes.copy() + if remove_breakpoint_classes: + self._classes.difference_update(remove_breakpoint_classes) + if breakpoint_classes: + self._classes.update(breakpoint_classes) + if self._classes != classes: + self.update_node_styles(animate=False) + + def _get_breakpoint_classes( self, dimension: int, breakpoints: list[tuple[int, str]] - ) -> None: - """Set horizontal or vertical breakpoints. + ) -> set[str]: + """Get breakpoint classes for given dimension. Args: - dimension: Either the width or the height. - breakpoints: A list of breakpoints. + dimension: Size in cells. + breakpoints: Associated breakpoints. + Returns: + A set containing a breakpoint, or an empty set if none apply. """ - class_names = [class_name for _breakpoint, class_name in breakpoints] - self.remove_class(*class_names) for breakpoint, class_name in sorted(breakpoints, reverse=True): if dimension >= breakpoint: - self.add_class(class_name) - return + return {class_name} + return set() def _update_tooltip(self, widget: Widget) -> None: """Update the content of the tooltip."""