Skip to content

Scaffold layer impl#4382

Draft
johnzhou721 wants to merge 19 commits into
beeware:mainfrom
johnzhou721:scaffold-layer
Draft

Scaffold layer impl#4382
johnzhou721 wants to merge 19 commits into
beeware:mainfrom
johnzhou721:scaffold-layer

Conversation

@johnzhou721
Copy link
Copy Markdown
Contributor

@johnzhou721 johnzhou721 commented May 10, 2026

Refs #4298.

This is a work-in-progress implementation. A broad checklist that I try to go off of:

  • Note: Toolbars are going to be handled by scaffold logic on all platforms, even though for users it'll be just window toolbar, because when Sidebar and Option scaffolds are added, toolbars need to be managed by the scaffold implementation, so ti's better to be symmetrical.
  • Main menus can be handled in scaffolds if appropriate; have a registry of scaffolds, and pass down changes in create_menu onto all scaffolds. But this will be handled later.
  • Clearly separate container and window logic on platforms where this isn't done yet
    • Android
    • WinForms
  • Track scaffold of each widget
  • Add scaffold object in core, change backend API contract to set_scaffold
  • Have scaffolds manage content setting
  • Have scaffolds manage toolbar creation storage, but base scaffolds do not expose toolbar attribute
  • Implement Dummy
    • Implement dummy by mocking toolbar creation and content setting; migrate content from window.py
    • Interact with scaffold on frame layout code in dummy
  • Tests for core:
    • Verify setting scaffold works
    • Verify correct semantics for content and _scaffold
    • Verify correct content being hooked onto window
  • Code review on Jun 1st
  • Adapt each backend:
    • macOS
    • iOS
      • Move UINavigationController to scaffold
    • GTK
    • Qt
    • Android
      • Potential TODOs in restructuring for Material 3?
    • Web
    • Textual
  • Change frame layout code to interact with scaffold.
  • Testbed tests:
    • Verify directly setting scaffold works.
    • Update probes
    • Make sure toolbar creation is correctly coordinated:
      • Assignment of new scaffold
    • Nothing more; only existing tests need to be updated as they test toolbar behaviors

PR Checklist:

  • I will abide by the BeeWare Code of Conduct
  • I have read and have followed the CONTRIBUTING.md file
  • This PR was generated or assisted using an AI tool

Assisted-by: GItHub Copilot; Google Gemini

@johnzhou721
Copy link
Copy Markdown
Contributor Author

@freakboy3742 As I've aced my last final exam in school, I've began to work on this more. I'm mostly done with core now but have a question about tracking app and window on Widget.

app and window are directly assignable by the user on Widget... so it's really awkward, and there's a lot of duplciation of keeping track of both when app can just be derived from the window, esp. since set_app on widget is empty on all backends anyways. Is there any particular reason why we're not simplifying app to just be a getter-only for window.app?

The reason I'm talking about this is that I'm thinking of taking a more simplified approach to getting the scaffold — while it is possible to handle it in a similar manner as window and app tracking, i.e. assign scaffold to widget, I think just having a scaffold getter that is self.window.scaffold may be worthwhile. But that does introduce some assymmetry. as app and window are settable and documentd in the docstrings as such.

@freakboy3742
Copy link
Copy Markdown
Member

@freakboy3742 As I've aced my last final exam in school, I've began to work on this more. I'm mostly done with core now but have a question about tracking app and window on Widget.

app and window are directly assignable by the user on Widget... so it's really awkward, and there's a lot of duplciation of keeping track of both when app can just be derived from the window, esp. since set_app on widget is empty on all backends anyways. Is there any particular reason why we're not simplifying app to just be a getter-only for window.app?

The main reason I can think of is that a widget can exist without necessarily being part of a window layout. I don't recall if there was a historical reason why a widget needed to be assigned to an app explicitly so that some "before the widget gets assigned to the window" handling could be done - but it's entirely possible that reason no longer exists (or never existed and we were over-complicating things).

The reason I'm talking about this is that I'm thinking of taking a more simplified approach to getting the scaffold — while it is possible to handle it in a similar manner as window and app tracking, i.e. assign scaffold to widget, I think just having a scaffold getter that is self.window.scaffold may be worthwhile. But that does introduce some assymmetry. as app and window are settable and documentd in the docstrings as such.

app and window need to be settable on widgets in the sense that there are parts of Toga that need to set them - but there's no expectation that a user would ever set them in practice. The setters for those are effectively "private", but there's no real way to declare a public getter and a private setter (other than not making the setter an actual setter).

Proxying scaffold retrieval through the window makes some sense - my only concern is whether that will remain accurate in future in more complicated situations where a second-level scaffold is allowed (e.g., the situations where tabs in an OptionScaffold themselves have scaffolds). However, that might be worth deferring to the future when we actually have that problem - as long as a widget can get it's scaffold, we're not saying anything about how it determines that value, so if it needs to change in future, it can.

@johnzhou721
Copy link
Copy Markdown
Contributor Author

Proxying scaffold retrieval through the window makes some sense - my only concern is whether that will remain accurate in future in more complicated situations where a second-level scaffold is allowed (e.g., the situations where tabs in an OptionScaffold themselves have scaffolds). However, that might be worth deferring to the future when we actually have that problem - as long as a widget can get it's scaffold, we're not saying anything about how it determines that value, so if it needs to change in future, it can.

Well... SidebarScaffold in OptionScaffold having a side pane is effectively broken in older iOS version we need to support—as discussed in #4299... But that may as well be a separate issue.

But since I already suggested getting App from Window, we can take this a step further: track scaffold, get window from scaffold, and get app from window... this is going to make the codebase a lot easier to understand and will alleviate any nesting concerns. I'll try to implement that as part of this PR on my next cycle.

app and window need to be settable on widgets in the sense that there are parts of Toga that need to set them - but there's no expectation that a user would ever set them in practice. The setters for those are effectively "private", but there's no real way to declare a public getter and a private setter (other than not making the setter an actual setter).

The set behavior seems publically documented -- bug in docs?

image

@freakboy3742
Copy link
Copy Markdown
Member

app and window need to be settable on widgets in the sense that there are parts of Toga that need to set them - but there's no expectation that a user would ever set them in practice. The setters for those are effectively "private", but there's no real way to declare a public getter and a private setter (other than not making the setter an actual setter).

The set behavior seems publically documented -- bug in docs?

Not so much a bug as something that is difficult to document. It's traversing the gap between "things that need to exist and be documented for internal use", and "things that the end user actually needs to care about". It's not wrong - that is how the setter works. But as an end user, you shouldn't ever need to care. Can we clean this up? Almost certainly.

@johnzhou721
Copy link
Copy Markdown
Contributor Author

After thinking about this more: I don't think I'm going to try to refactor window and app in this PR, and I'm just going to track scaffold like we track Window and App previously. I'm making this decision because there's a widget registry for each window... so it's going to be hard to move things around (one can of course make a registry for each scaffold but doing all the bookkeeping and restructuring of getting widgets in each object is going to scope creep.)

TL;DR I'm going to track scaffold for each widget the same way app and window are tracked, to leave the door open for future nesting if needed and avoid scope creep.

@johnzhou721
Copy link
Copy Markdown
Contributor Author

johnzhou721 commented May 31, 2026

@freakboy3742 I'm done with core, dummy, and their tests; dependabot PRs are blocking CI, but I've manually tested with macOS 3.12 and 3.13 on my mac and they all work well.

I would appreciate a partial review of those parts.

@johnzhou721
Copy link
Copy Markdown
Contributor Author

johnzhou721 commented Jun 1, 2026

Note: After the latest commit this is still [1] ready for a partial review of core, dummy and their tests; I merely used scaffold to consistently interact with the layout code.

[1] — my last message sent prematurely; I've edited it to indicate this was ready for a partial review, but turns out I missed a change.
[2] EDIT: Android and Winforms code are just refactoring container out of the window logic

@johnzhou721
Copy link
Copy Markdown
Contributor Author

Feel free to give the partial review suggested above, but note that I will not start backend work until #4275 is finished, because both this and #4275 touches iOS window and container logic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants