@@ -389,6 +389,8 @@ class Widget(DOMNode):
389
389
"nocolor" : lambda widget : widget .app .no_color ,
390
390
"first-of-type" : lambda widget : widget .first_of_type ,
391
391
"last-of-type" : lambda widget : widget .last_of_type ,
392
+ "first-child" : lambda widget : widget .first_child ,
393
+ "last-child" : lambda widget : widget .last_child ,
392
394
"odd" : lambda widget : widget .is_odd ,
393
395
"even" : lambda widget : widget .is_even ,
394
396
} # type: ignore[assignment]
@@ -500,6 +502,10 @@ def __init__(
500
502
"""Used to cache :first-of-type pseudoclass state."""
501
503
self ._last_of_type : tuple [int , bool ] = (- 1 , False )
502
504
"""Used to cache :last-of-type pseudoclass state."""
505
+ self ._first_child : tuple [int , bool ] = (- 1 , False )
506
+ """Used to cache :first-child pseudoclass state."""
507
+ self ._last_child : tuple [int , bool ] = (- 1 , False )
508
+ """Used to cache :last-child pseudoclass state."""
503
509
self ._odd : tuple [int , bool ] = (- 1 , False )
504
510
"""Used to cache :odd pseudoclass state."""
505
511
self ._last_scroll_time = monotonic ()
@@ -852,6 +858,34 @@ def last_of_type(self) -> bool:
852
858
return self ._last_of_type [1 ]
853
859
return False
854
860
861
+ @property
862
+ def first_child (self ) -> bool :
863
+ """Is this the first widget in its siblings?"""
864
+ parent = self .parent
865
+ if parent is None :
866
+ return True
867
+ # This pseudo class only changes when the parent's nodes._updates changes
868
+ if parent ._nodes ._updates == self ._first_child [0 ]:
869
+ return self ._first_child [1 ]
870
+ for node in parent ._nodes :
871
+ self ._first_child = (parent ._nodes ._updates , node is self )
872
+ return self ._first_child [1 ]
873
+ return False
874
+
875
+ @property
876
+ def last_child (self ) -> bool :
877
+ """Is this the last widget in its siblings?"""
878
+ parent = self .parent
879
+ if parent is None :
880
+ return True
881
+ # This pseudo class only changes when the parent's nodes._updates changes
882
+ if parent ._nodes ._updates == self ._last_child [0 ]:
883
+ return self ._last_child [1 ]
884
+ for node in reversed (parent ._nodes ):
885
+ self ._last_child = (parent ._nodes ._updates , node is self )
886
+ return self ._last_child [1 ]
887
+ return False
888
+
855
889
@property
856
890
def is_odd (self ) -> bool :
857
891
"""Is this widget at an oddly numbered position within its siblings?"""
@@ -1304,7 +1338,7 @@ def update_styles(children: list[DOMNode]) -> None:
1304
1338
"""Update order related CSS"""
1305
1339
if before is not None or after is not None :
1306
1340
# If the new children aren't at the end.
1307
- # we need to update both odd/even and first-of-type/last-of-type
1341
+ # we need to update both odd/even, first-of-type/last-of-type and first-child/last-child
1308
1342
for child in children :
1309
1343
if child ._has_order_style or child ._has_odd_or_even :
1310
1344
child ._update_styles ()
0 commit comments