Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -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/"
Expand Down
2 changes: 2 additions & 0 deletions src/textual/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 7 additions & 8 deletions src/textual/dom.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
3 changes: 2 additions & 1 deletion src/textual/drivers/linux_driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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()
Expand Down
51 changes: 38 additions & 13 deletions src/textual/screen.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand Down
Loading