@@ -557,6 +557,7 @@ def _align_size(self, child: tuple[int, int], parent: tuple[int, int]) -> Offset
557557class Styles (StylesBase ):
558558 node : DOMNode | None = None
559559 _rules : RulesMap = field (default_factory = dict )
560+ _updates : int = 0
560561
561562 important : set [str ] = field (default_factory = set )
562563
@@ -577,6 +578,7 @@ def clear_rule(self, rule: str) -> bool:
577578 Returns:
578579 bool: ``True`` if a rule was cleared, or ``False`` if it was already not set.
579580 """
581+ self ._updates += 1
580582 return self ._rules .pop (rule , None ) is not None
581583
582584 def get_rules (self ) -> RulesMap :
@@ -592,6 +594,7 @@ def set_rule(self, rule: str, value: object | None) -> bool:
592594 Returns:
593595 bool: ``True`` if the rule changed, otherwise ``False``.
594596 """
597+ self ._updates += 1
595598 if value is None :
596599 return self ._rules .pop (rule , None ) is not None
597600 current = self ._rules .get (rule )
@@ -610,6 +613,7 @@ def refresh(self, *, layout: bool = False, children: bool = False) -> None:
610613
611614 def reset (self ) -> None :
612615 """Reset the rules to initial state."""
616+ self ._updates += 1
613617 self ._rules .clear ()
614618
615619 def merge (self , other : Styles ) -> None :
@@ -618,10 +622,11 @@ def merge(self, other: Styles) -> None:
618622 Args:
619623 other (Styles): A Styles object.
620624 """
621-
625+ self . _updates += 1
622626 self ._rules .update (other ._rules )
623627
624628 def merge_rules (self , rules : RulesMap ) -> None :
629+ self ._updates += 1
625630 self ._rules .update (rules )
626631
627632 def extract_rules (
@@ -929,6 +934,18 @@ def __init__(self, node: DOMNode, base: Styles, inline_styles: Styles) -> None:
929934 self ._base_styles = base
930935 self ._inline_styles = inline_styles
931936 self ._animate : BoundAnimator | None = None
937+ self ._updates : int = 0
938+ self ._rich_style : tuple [int , Style ] | None = None
939+ self ._gutter : tuple [int , Spacing ] | None = None
940+
941+ @property
942+ def _cache_key (self ) -> int :
943+ """A key key, that changes when any style is changed.
944+
945+ Returns:
946+ int: An opaque integer.
947+ """
948+ return self ._updates + self ._base_styles ._updates + self ._inline_styles ._updates
932949
933950 @property
934951 def base (self ) -> Styles :
@@ -946,6 +963,21 @@ def rich_style(self) -> Style:
946963 assert self .node is not None
947964 return self .node .rich_style
948965
966+ @property
967+ def gutter (self ) -> Spacing :
968+ """Get space around widget.
969+
970+ Returns:
971+ Spacing: Space around widget content.
972+ """
973+ if self ._gutter is not None :
974+ cache_key , gutter = self ._gutter
975+ if cache_key == self ._updates :
976+ return gutter
977+ gutter = self .padding + self .border .spacing
978+ self ._gutter = (self ._cache_key , gutter )
979+ return gutter
980+
949981 def animate (
950982 self ,
951983 attribute : str ,
@@ -972,6 +1004,7 @@ def animate(
9721004
9731005 """
9741006 if self ._animate is None :
1007+ assert self .node is not None
9751008 self ._animate = self .node .app .animator .bind (self )
9761009 assert self ._animate is not None
9771010 self ._animate (
@@ -1003,16 +1036,19 @@ def merge(self, other: Styles) -> None:
10031036
10041037 def merge_rules (self , rules : RulesMap ) -> None :
10051038 self ._inline_styles .merge_rules (rules )
1039+ self ._updates += 1
10061040
10071041 def reset (self ) -> None :
10081042 """Reset the rules to initial state."""
10091043 self ._inline_styles .reset ()
1044+ self ._updates += 1
10101045
10111046 def has_rule (self , rule : str ) -> bool :
10121047 """Check if a rule has been set."""
10131048 return self ._inline_styles .has_rule (rule ) or self ._base_styles .has_rule (rule )
10141049
10151050 def set_rule (self , rule : str , value : object | None ) -> bool :
1051+ self ._updates += 1
10161052 return self ._inline_styles .set_rule (rule , value )
10171053
10181054 def get_rule (self , rule : str , default : object = None ) -> object :
@@ -1022,6 +1058,7 @@ def get_rule(self, rule: str, default: object = None) -> object:
10221058
10231059 def clear_rule (self , rule_name : str ) -> bool :
10241060 """Clear a rule (from inline)."""
1061+ self ._updates += 1
10251062 return self ._inline_styles .clear_rule (rule_name )
10261063
10271064 def get_rules (self ) -> RulesMap :
0 commit comments