Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/2822.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The Textual backend is now compatible with versions of Textual after v0.63.3.
2 changes: 1 addition & 1 deletion textual/src/toga_textual/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def exit(self):
self.native.exit()

def main_loop(self):
self.native.run()
self.loop.run_until_complete(self.native.run_async())

def set_icon(self, icon):
pass
Expand Down
32 changes: 29 additions & 3 deletions textual/src/toga_textual/widgets/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,24 @@ def __init__(self, interface):
self.container = None
self.create()

self._pending_children: list[Widget] = list()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the _pending_children collection needed? Is there any situation where _pending_children will be different to self.interface.children? I can't think of one... the widget is either mounted, in which case all children will be added, or the widget isn't mounted, in which case all children will not be mounted.

Copy link
Member Author

@rmartin16 rmartin16 Sep 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah; that's probably true. I think I was imagining the abstract case where the mounted state could change over time; this would introduce the possibility that the superset of children for the widget is not the same as the widgets that still need to be mounted on it.

Right now, though, it doesn't even work to remove a widget with children and then add that same widget back to the app; when you do this, the children of that widget are lost.

Given all this, I'll just implement the simpler design where you can mount a widget once; therefore, mounting children is only deferred once.


def install(self, parent):
"""Add widget and any pending children to the native DOM for the app.

Textual does not allow widgets to be added to the DOM until their parent is
added. Therefore, when children are added to an unmounted widget, their
mounting is deferred until their parent is mounted.
"""
# Mount widget on to its parent
parent.native.mount(self.native)

# Mount any pending children as native children
for child in self._pending_children:
child.install(parent=self)

self._pending_children.clear()

def get_size(self) -> Size:
return Size(0, 0)

Expand Down Expand Up @@ -74,7 +92,7 @@ def set_bounds(self, x, y, width, height):

# Positions are more complicated. The (x,y) provided as an argument is
# in absolute coordinates. The `content_left` ad `content_right` values
# of the layout are relative coordianate. Textual doesn't allow specifying
# of the layout are relative coordinate. Textual doesn't allow specifying
# either absolute *or* relative - we can only specify margin values within
# a row/column box. This means we need to reverse engineer the margins from
# the computed layout.
Expand Down Expand Up @@ -152,13 +170,21 @@ def set_background_color(self, color):
######################################################################

def add_child(self, child):
self.native.mount(child.native)
# A child can only be added to a widget that is already mounted on the app.
# So, mounting the child is deferred if the parent is not mounted yet.
if self.native.is_attached:
self.native.mount(child.native)
else:
self._pending_children.append(child)

def insert_child(self, index, child):
pass

def remove_child(self, child):
self.native.remove(child.native)
try:
self._pending_children.remove(child)
except ValueError:
self.native.remove_children([child.native])

def refresh(self):
intrinsic = self.interface.intrinsic
Expand Down
4 changes: 2 additions & 2 deletions textual/src/toga_textual/window.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ def close(self):
self.native.dismiss(None)

def set_app(self, app):
app.native.mount(self.native)
app.native.install_screen(self.native, name=self.interface.id)

def show(self):
Expand All @@ -160,8 +161,7 @@ def clear_content(self):

def set_content(self, widget):
self.container.content = widget

self.native.mount(widget.native)
widget.install(parent=self)

######################################################################
# Window size
Expand Down