Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Xilem architecture prototype #2183

Open
wants to merge 29 commits into
base: master
Choose a base branch
from
Open

Xilem architecture prototype #2183

wants to merge 29 commits into from

Conversation

raphlinus
Copy link
Contributor

I am writing a blog post on the Xilem architecture, and want that to refer to a PR, as that is the most stable identifier for a branch; the PR can be closed and the branch deleted, and still resolve.

I will also continue to explore and develop in this branch. We might rethink that if we start developing more collaboratively.

Also note, I do the same with the idiopath_py and idiopath_async branches. Those are shorter-term explorations, and basically I just want to capture what I did. It's likely the ideas for both will get merged into this branch.

raphlinus and others added 29 commits April 12, 2022 10:14
This is similar to lasagna but with strongly typed elements. Let's see how it goes.
Support for type erasure of views and widgets.
Bring in more widget tuples with a macro.
Event propagation has no mutable access.
This passes the app state down through an Rc using a lens-like construction.

I'm not sure this is the best construction. Perhaps it should be combined with adapt (the callback could take both mutable state references), but that's 3 callbacks.

Also makes state mutable in event propagation, which seems useful.
Also trying out a few more node types in the example.
Fix very small typo
Use xdg-desktop-portal's dbus APIs for open/save dialogs on x11.
This commit was derived from the async experiment, which required adding a waker to the reconciliation context. We will definitely be adding more things to Cx so that should be the type of View methods, but also we'll iterate a while on the core architecture before re-introducing async.

Note, it also bounds the element type for View to Widget, which is less flexible but more ergonomic.
Provide default (unit) action type for View. Add with_id and with_new_id methods to Cx which ensure proper nesting by construction.
Tweak signature of rebuild method to return a boolean indicating whether anything has been changed. This is not used yet, but will be for invalidation.
Let event handlers do different things than return user-specified actions. This may not be used immediately but is important for implementing environment.
The prototype had an extremely skeletal implementation of the widget hierarchy, but with this commit we start building more of a real one. It's based to a large exttent on existing Druid widgets, but some parts may be done differently, in particular an exploration into layout more similar to SwiftUI.
Add some layout methods. Rename Column to VStack. Put in alignment mechanisms.

Much of the core layout protocol is implemented, but it's not exercised very deeply. A lot of view wrappers like "frame" in SwiftUI need to get implemented. I think I want to get the core widget trait more or less stable first (events need work in particular).
Adds axis to SingleAlignment (renamed from OneAlignment).

Incorporates layout offsets into alignment results.
In SwiftUI, HorizontalAlignment.center and VerticalAlignment.center are two different structs, but in Rust it's just as convenient, if not more so, to let the same struct just impl the two different traits.
This brings the event method in line with other widget methods, and also existing Druid.

Also adds mutable access to the event queue to the base context state, so other contexts can add events. This will be useful for LayoutObserver (event generated from layout context).

Actual event propagation is still very under-developed, but it should be possible to build that out without big signature changes.
Implement the "layout observer" concept which is very similar to GeometryReader in SwiftUI.

Also fix some problems in vstack layout computation.
Move relevant bits into core and contexts files, mirroring the structure of existing Druid (which hopefully will make it easier to navigate when adapting existing Druid code to the new architecture).
The widget set really should be structured as a library, and it will no doubt be useful to have multiple examples to exercise various aspects of that. This patch changes from a single main executable to a library with an examples dir.
I've decided on the name "xilem" rather than "idiopath," and this also matches the blog post (now linked from the README, though that is a bit stale).
Import some of the event dispatching logic from Druid. Still fairly minimal, not yet doing real hot/active logic on buttons yet (doing it the same way as Druid would depend on adding lifecycle to the Widget trait).
Add the lifecycle method to the Widget trait, matching existing Druid. I'm not at all convinced it's necessary - in original Druid, one of the main differences is that it doesn't have mutable access to app state, but that concern isn't really relevant. In any case, this minimizes divergence, and can possibly be cleaned up later.
This commit implements the core functionality of button, including hot/active states, drawing of appearance, and reasonable layout.

There are other changes in widget infrastructure to support this functionality.
@jeffparsons
Copy link

Two months on, the peanut gallery wonders what impressions/experiences Druid developers and/or users have had with this prototype. Is the mood optimistic? Have unexpected challenges arisen?

I hope this doesn't come across as "are we there yet" — I'm actually just maddeningly curious about what people think about it! 😅

@jplatte
Copy link
Contributor

jplatte commented Jul 12, 2022

I didn't get the impression that this is in the state where it can be tried out easily, since there was no call for testing or similar.

@dhardy
Copy link
Contributor

dhardy commented Dec 4, 2022

The most innovative aspect of Xilem is event dispatching based on an id path, at each stage providing mutable access to app state.

Nice to see that this idea caught on. You can copy my code for compressed paths if you like, though they may be less useful for Xilem since you have separate Id and Path.

In general I like this idea quite a lot. Some comments:

  • Most elements have to implement both View and Widget
  • What's the relationship between View::event and Widget::event? Aha, the Widget impl leverages Druid's existing machinery for handling events, then triggers a "view event". Is this a temporary relationship or could all event handling code be pushed to the "view" side?
  • Is it desirable that an app always has one "app data"? What if, instead, an app consisted of any number of "modules", each with its own data and view, with each module's view optionally being a window root and optionally being embedded within another view?
  • EventResult::Nop — is that "no-op" or "nope"? 😆

Xilem has inspired me to consider something vaguely similar built on top of KAS, which could go some way to solving its biggest weaknesses (difficulty expressing dynamic layout, and not requiring manual updates of widgets dependant on app state). But if we're honest, the Rust UI space is a lot more crowded today than it was when these projects started.

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.

None yet

7 participants