1+ from collections import OrderedDict
2+ from collections .abc import Callable
3+
14from travertino .size import at_least
25
6+ from textual .widget import Widget as TextualWidget
37from toga .style .pack import ROW
48from toga .types import Size
59
@@ -74,7 +78,7 @@ def set_bounds(self, x, y, width, height):
7478
7579 # Positions are more complicated. The (x,y) provided as an argument is
7680 # in absolute coordinates. The `content_left` ad `content_right` values
77- # of the layout are relative coordianate . Textual doesn't allow specifying
81+ # of the layout are relative coordinate . Textual doesn't allow specifying
7882 # either absolute *or* relative - we can only specify margin values within
7983 # a row/column box. This means we need to reverse engineer the margins from
8084 # the computed layout.
@@ -158,7 +162,7 @@ def insert_child(self, index, child):
158162 pass
159163
160164 def remove_child (self , child ):
161- self .native .remove ( child .native )
165+ self .native .remove_children ([ child .native ] )
162166
163167 def refresh (self ):
164168 intrinsic = self .interface .intrinsic
@@ -169,3 +173,38 @@ def refresh(self):
169173
170174 intrinsic .width = self .scale_out_horizontal (intrinsic .width )
171175 intrinsic .height = self .scale_out_vertical (intrinsic .height )
176+
177+
178+ class DeferredMount :
179+ """Mount children to a widget once the widget itself is mounted.
180+
181+ Inject this mixin in to the MRO for native Textual widgets that support children.
182+
183+ Textual does not permit mounting widgets on to a widget that is not itself already
184+ mounted. As such, when a child is added to an unmounted widget, a callback is added
185+ that can mount it. This mixin runs those callbacks once the widget is mounted.
186+ """
187+
188+ def __init__ (self , * children : TextualWidget ):
189+ super ().__init__ (* children )
190+ self ._on_mount_callback : OrderedDict [Widget , Callable ] = OrderedDict ()
191+
192+ def mount (self , widget , ** kwargs ):
193+ if self .is_attached :
194+ super ().mount (widget , ** kwargs )
195+ else :
196+ self ._on_mount_callback [widget ] = lambda : self .mount (widget )
197+
198+ def remove_children (self , widgets ):
199+ for widget in widgets :
200+ try :
201+ self ._on_mount_callback .pop (widget )
202+ except KeyError :
203+ super ().remove_children ([widget ])
204+
205+ def on_mount (self ):
206+ """Textual calls this hook when the widget is mounted."""
207+ if self ._on_mount_callback :
208+ for child , callback in self ._on_mount_callback .copy ().items ():
209+ callback ()
210+ self ._on_mount_callback .pop (child )
0 commit comments