From fa5ac0dd6887fcda601d1f86e05f6eeedd612c86 Mon Sep 17 00:00:00 2001 From: Will McGugan Date: Fri, 18 Nov 2022 13:59:58 +0000 Subject: [PATCH] simplified cache_key --- CHANGELOG.md | 1 + src/textual/css/styles.py | 39 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f95e4d10..f6cb6dc7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Watchers are now called immediately when setting the attribute if they are synchronous. https://github.com/Textualize/textual/pull/1145 - Widget.call_later has been renamed to Widget.call_after_refresh. - Button variant values are now checked at runtime. https://github.com/Textualize/textual/issues/1189 +- Added caching of some properties in Styles object ### Fixed diff --git a/src/textual/css/styles.py b/src/textual/css/styles.py index 6b4dc1d7e..006f12910 100644 --- a/src/textual/css/styles.py +++ b/src/textual/css/styles.py @@ -557,6 +557,7 @@ class StylesBase(ABC): class Styles(StylesBase): node: DOMNode | None = None _rules: RulesMap = field(default_factory=dict) + _updates: int = 0 important: set[str] = field(default_factory=set) @@ -577,6 +578,7 @@ class Styles(StylesBase): Returns: bool: ``True`` if a rule was cleared, or ``False`` if it was already not set. """ + self._updates += 1 return self._rules.pop(rule, None) is not None def get_rules(self) -> RulesMap: @@ -592,6 +594,7 @@ class Styles(StylesBase): Returns: bool: ``True`` if the rule changed, otherwise ``False``. """ + self._updates += 1 if value is None: return self._rules.pop(rule, None) is not None current = self._rules.get(rule) @@ -610,6 +613,7 @@ class Styles(StylesBase): def reset(self) -> None: """Reset the rules to initial state.""" + self._updates += 1 self._rules.clear() def merge(self, other: Styles) -> None: @@ -618,10 +622,11 @@ class Styles(StylesBase): Args: other (Styles): A Styles object. """ - + self._updates += 1 self._rules.update(other._rules) def merge_rules(self, rules: RulesMap) -> None: + self._updates += 1 self._rules.update(rules) def extract_rules( @@ -929,6 +934,18 @@ class RenderStyles(StylesBase): self._base_styles = base self._inline_styles = inline_styles self._animate: BoundAnimator | None = None + self._updates: int = 0 + self._rich_style: tuple[int, Style] | None = None + self._gutter: tuple[int, Spacing] | None = None + + @property + def _cache_key(self) -> int: + """A key key, that changes when any style is changed. + + Returns: + int: An opaque integer. + """ + return self._updates + self._base_styles._updates + self._inline_styles._updates @property def base(self) -> Styles: @@ -946,6 +963,21 @@ class RenderStyles(StylesBase): assert self.node is not None return self.node.rich_style + @property + def gutter(self) -> Spacing: + """Get space around widget. + + Returns: + Spacing: Space around widget content. + """ + if self._gutter is not None: + cache_key, gutter = self._gutter + if cache_key == self._updates: + return gutter + gutter = self.padding + self.border.spacing + self._gutter = (self._cache_key, gutter) + return gutter + def animate( self, attribute: str, @@ -972,6 +1004,7 @@ class RenderStyles(StylesBase): """ if self._animate is None: + assert self.node is not None self._animate = self.node.app.animator.bind(self) assert self._animate is not None self._animate( @@ -1003,16 +1036,19 @@ class RenderStyles(StylesBase): def merge_rules(self, rules: RulesMap) -> None: self._inline_styles.merge_rules(rules) + self._updates += 1 def reset(self) -> None: """Reset the rules to initial state.""" self._inline_styles.reset() + self._updates += 1 def has_rule(self, rule: str) -> bool: """Check if a rule has been set.""" return self._inline_styles.has_rule(rule) or self._base_styles.has_rule(rule) def set_rule(self, rule: str, value: object | None) -> bool: + self._updates += 1 return self._inline_styles.set_rule(rule, value) def get_rule(self, rule: str, default: object = None) -> object: @@ -1022,6 +1058,7 @@ class RenderStyles(StylesBase): def clear_rule(self, rule_name: str) -> bool: """Clear a rule (from inline).""" + self._updates += 1 return self._inline_styles.clear_rule(rule_name) def get_rules(self) -> RulesMap: