Skip to content

Commit 42f0a5e

Browse files
committed
Layout root nodes once per loop
1 parent 55a8912 commit 42f0a5e

File tree

2 files changed

+64
-4
lines changed

2 files changed

+64
-4
lines changed

core/src/toga/widgets/base.py

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
StyleT = TypeVar("StyleT", bound=BaseStyle)
1818

1919

20+
TAB = " "
21+
22+
2023
class Widget(Node):
2124
_MIN_WIDTH = 100
2225
_MIN_HEIGHT = 100
@@ -319,19 +322,57 @@ def enabled(self) -> bool:
319322
def enabled(self, value: bool) -> None:
320323
self._impl.set_enabled(bool(value))
321324

325+
_layouts = 0
326+
_level = 0
327+
322328
def refresh(self) -> None:
329+
name = type(self).__name__
330+
# print(TAB * self._level + f"Refresh requested on {name}")
331+
332+
if not self.window:
333+
# No need to do anything if the widget hasn't been added to a window.
334+
return
335+
323336
self._impl.refresh()
324337

325338
# Refresh the layout
326339
if self._root:
327340
# We're not the root of the node hierarchy;
328341
# defer the refresh call to the root node.
329342
self._root.refresh()
343+
330344
else:
331-
# We can't compute a layout until we have a container
332-
if self._impl.container:
333-
super().refresh(self._impl.container)
334-
self._impl.container.refreshed()
345+
# Uncomment to always compute layout:
346+
347+
# self._refresh_layout()
348+
# return
349+
350+
if self.window._currently_laying_out:
351+
self._refresh_layout()
352+
return
353+
354+
print(TAB * self._level + f"Adding {name} to dirty set")
355+
self.window._dirty_root_widgets.add(self)
356+
357+
if self.window._pending_layout is None:
358+
self.window._pending_layout = self.app.loop.call_soon(
359+
self.window._refresh_layouts
360+
)
361+
362+
def _refresh_layout(self):
363+
# print(self._level)
364+
name = type(self).__name__
365+
366+
Widget._layouts += 1
367+
print(TAB * self._level + f"#{self._layouts}. Laying out {name}")
368+
369+
Widget._level += 1
370+
371+
super().refresh(self._impl.container)
372+
self._impl.container.refreshed()
373+
374+
Widget._level -= 1
375+
# print(TAB * self._level + f"Done laying out {name}\n")
335376

336377
def focus(self) -> None:
337378
"""Give this widget the input focus.

core/src/toga/window.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,10 @@ def __init__(
208208
size=Size(*size),
209209
)
210210

211+
self._pending_layout = None
212+
self._dirty_root_widgets = set()
213+
self._currently_laying_out = False
214+
211215
# Add the window to the app
212216
App.app.windows.add(self)
213217

@@ -369,6 +373,21 @@ def content(self, widget: Widget) -> None:
369373
# Update the geometry of the widget
370374
widget.refresh()
371375

376+
def _refresh_layouts(self):
377+
self._currently_laying_out = True
378+
379+
toga.Widget._level += 1
380+
print("\nLoop(")
381+
382+
while self._dirty_root_widgets:
383+
self._dirty_root_widgets.pop()._refresh_layout()
384+
385+
print(")\n")
386+
toga.Widget._level -= 1
387+
388+
self._currently_laying_out = False
389+
self._pending_layout = None
390+
372391
@property
373392
def widgets(self) -> FilteredWidgetRegistry:
374393
"""The widgets contained in the window.

0 commit comments

Comments
 (0)