mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Updating styles on demand instead of on_idle (#2304)
* Updating styles on demand instead of on_idle * Tidy up update_styles * Fix LRU cache tests * Remove some debugging code * Adding test for pseudoclass style update * Update changelog
This commit is contained in:
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
- Changed signature of Driver. Technically a breaking change, but unlikely to affect anyone.
|
||||
- Breaking change: Timer.start is now private, and returns No
|
||||
- A clicked tab will now be scrolled to the center of its tab container https://github.com/Textualize/textual/pull/2276
|
||||
- Style updates are now done immediately rather than on_idle https://github.com/Textualize/textual/pull/2304
|
||||
- `ButtonVariant` is now exported from `textual.widgets.button` https://github.com/Textualize/textual/issues/2264
|
||||
|
||||
### Added
|
||||
@@ -28,9 +29,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
- Fixed order styles are applied in DataTable - allows combining of renderable styles and component classes https://github.com/Textualize/textual/pull/2272
|
||||
- Fix empty ListView preventing bindings from firing https://github.com/Textualize/textual/pull/2281
|
||||
- Fix `get_component_styles` returning incorrect values on first call when combined with pseudoclasses https://github.com/Textualize/textual/pull/2304
|
||||
- Fixed `active_message_pump.get` sometimes resulting in a `LookupError` https://github.com/Textualize/textual/issues/2301
|
||||
|
||||
|
||||
## [0.19.1] - 2023-04-10
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -72,9 +72,7 @@ class LRUCache(Generic[CacheKey, CacheValue]):
|
||||
return len(self._cache)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"<LRUCache maxsize={self._maxsize} hits={self.hits} misses={self.misses}>"
|
||||
)
|
||||
return f"<LRUCache size={len(self)} maxsize={self._maxsize} hits={self.hits} misses={self.misses}>"
|
||||
|
||||
def grow(self, maxsize: int) -> None:
|
||||
"""Grow the maximum size to at least `maxsize` elements.
|
||||
|
||||
@@ -322,7 +322,6 @@ class App(Generic[ReturnType], DOMNode):
|
||||
self.design = DEFAULT_COLORS
|
||||
|
||||
self.stylesheet = Stylesheet(variables=self.get_css_variables())
|
||||
self._require_stylesheet_update: set[DOMNode] = set()
|
||||
|
||||
css_path = css_path or self.CSS_PATH
|
||||
if css_path is not None:
|
||||
@@ -1229,13 +1228,15 @@ class App(Generic[ReturnType], DOMNode):
|
||||
return self.screen.get_child_by_type(expect_type)
|
||||
|
||||
def update_styles(self, node: DOMNode | None = None) -> None:
|
||||
"""Request update of styles.
|
||||
"""Immediately update the styles of this node and all descendant nodes.
|
||||
|
||||
Should be called whenever CSS classes / pseudo classes change.
|
||||
|
||||
For example, when you hover over a button, the :hover pseudo class
|
||||
will be added, and this method is called to apply the corresponding
|
||||
:hover styles.
|
||||
"""
|
||||
self._require_stylesheet_update.add(self.screen if node is None else node)
|
||||
self.check_idle()
|
||||
descendants = node.walk_children(with_self=True)
|
||||
self.stylesheet.update_nodes(descendants, animate=True)
|
||||
|
||||
def mount(
|
||||
self,
|
||||
@@ -1773,14 +1774,6 @@ class App(Generic[ReturnType], DOMNode):
|
||||
|
||||
def _on_idle(self) -> None:
|
||||
"""Perform actions when there are no messages in the queue."""
|
||||
if self._require_stylesheet_update and not self._batch_count:
|
||||
nodes: set[DOMNode] = {
|
||||
child
|
||||
for node in self._require_stylesheet_update
|
||||
for child in node.walk_children(with_self=True)
|
||||
}
|
||||
self._require_stylesheet_update.clear()
|
||||
self.stylesheet.update_nodes(nodes, animate=True)
|
||||
|
||||
def _register_child(
|
||||
self, parent: DOMNode, child: Widget, before: int | None, after: int | None
|
||||
|
||||
@@ -530,7 +530,6 @@ class Stylesheet:
|
||||
nodes: Nodes to update.
|
||||
animate: Enable CSS animation.
|
||||
"""
|
||||
|
||||
rules_map = self.rules_map
|
||||
apply = self.apply
|
||||
|
||||
|
||||
@@ -252,6 +252,7 @@ class ClientHandler:
|
||||
):
|
||||
await self.incoming_queue.put(message)
|
||||
elif websocket_message.type == WSMsgType.ERROR:
|
||||
self.service.console.print(websocket_message.data)
|
||||
self.service.console.print(
|
||||
DevConsoleNotice("Websocket error occurred", level="error")
|
||||
)
|
||||
|
||||
@@ -269,7 +269,7 @@ class DataTable(ScrollView, Generic[CellType], can_focus=True):
|
||||
background: $primary 10%;
|
||||
}
|
||||
|
||||
DataTable > .datatable--cursor {
|
||||
DataTable > .datatable--cursor {
|
||||
background: $secondary;
|
||||
color: $text;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from textual.app import App
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.widgets import Button
|
||||
|
||||
|
||||
def test_batch_update():
|
||||
@@ -15,3 +16,23 @@ def test_batch_update():
|
||||
assert app._batch_count == 1 # Exiting decrements
|
||||
|
||||
assert app._batch_count == 0 # Back to zero
|
||||
|
||||
|
||||
class MyApp(App):
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Button("Click me!")
|
||||
|
||||
|
||||
async def test_hover_update_styles():
|
||||
app = MyApp()
|
||||
async with app.run_test() as pilot:
|
||||
button = app.query_one(Button)
|
||||
assert button.pseudo_classes == {"enabled"}
|
||||
|
||||
# Take note of the initial background colour
|
||||
initial_background = button.styles.background
|
||||
await pilot.hover(Button)
|
||||
|
||||
# We've hovered, so ensure the pseudoclass is present and background changed
|
||||
assert button.pseudo_classes == {"enabled", "hover"}
|
||||
assert button.styles.background != initial_background
|
||||
|
||||
@@ -8,7 +8,7 @@ from textual._cache import FIFOCache, LRUCache
|
||||
def test_lru_cache():
|
||||
cache = LRUCache(3)
|
||||
|
||||
assert str(cache) == "<LRUCache maxsize=3 hits=0 misses=0>"
|
||||
assert str(cache) == "<LRUCache size=0 maxsize=3 hits=0 misses=0>"
|
||||
|
||||
# insert some values
|
||||
cache["foo"] = 1
|
||||
@@ -65,7 +65,7 @@ def test_lru_cache_hits():
|
||||
assert cache.hits == 3
|
||||
assert cache.misses == 2
|
||||
|
||||
assert str(cache) == "<LRUCache maxsize=4 hits=3 misses=2>"
|
||||
assert str(cache) == "<LRUCache size=1 maxsize=4 hits=3 misses=2>"
|
||||
|
||||
|
||||
def test_lru_cache_get():
|
||||
|
||||
Reference in New Issue
Block a user