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

[MERGED] Implement experimental WinUIBackend #75

Closed
wants to merge 7 commits into from

Conversation

LorenzoFerri
Copy link
Contributor

This pull request aims to implement a backend that relies on the native Windows Runtime. It uses the bindings provided by thebrowsercompany in order to use modern WinUI components.

As explained in this example a requirement to use this library on Windows is to use the Swift SDK provided at: thebrowsercompany/swift-build

The WinUIBackend manages to run most of the examples, but some things are still not working:

  • The updatePicker method is half implemented. The picker works only if it does not change the options at runtime as I failed to understand how to get the current items of the picker.
  • The foregroundColor property does not work. The code should be correct if I understood the Windows documentation but it does nothing.
  • The oneOfContainer is currently not animated, there is a navigate method that should handle animation but I fail to understand how to pass something to it.
  • Spacer do not work properly. Windows layout does not have something that correspond to the Flex system on the web, so while a stack of items it's pretty easy if you want these child to grow you have to do some trickery with a Grid, and I wasn't able to position items properly inside of it.
  • For the same reason as the Spacer also the SplitView and Table fails to render properly.
  • I have no idea on how to implement the getInheritedOrientation as I didn't find anything related on Windows API.

I also have doubt about the way I implemented createLayoutTransparentStack but I think it's working?

Apart from these problems all the controls element seems to be working properly so here some screenshots from my computer (dark theme, purple accent):

Counter Example
Random Number Generator Example
Greetings Generator Example
Windowing Example
Controls Example

Let me know if you have any suggestions.

@stackotter
Copy link
Owner

This looks amazing! 🎉 It's very exciting how native it looks!

I haven't reviewed the code changes yet but just the screenshots alone look great.

  • I have no idea on how to implement the getInheritedOrientation as I didn't find anything related on Windows API.

This also isn't part of the Gtk API, it's added by us (kind of a janky way to fix inheriting orientation from parent containers).

As explained in this example a requirement to use this library on Windows is to use the Swift SDK provided at: thebrowsercompany/swift-build

Interesting, any idea why the regular SwiftPM build system doesn't work? Had a quick poke around but couldn't find an explanation

  • The updatePicker method is half implemented. The picker works only if it does not change the options at runtime as I failed to understand how to get the current items of the picker.

Ah yes, that was incredibly annoying in Gtk too lol. Which may indicate that I need to rethink that part of the API to possibly pass the previous options as well as the current options, or something along those lines? Now that I think about it, similar changes could probably be made to avoid the need for getInheritedOrientation (by passing the expected orientation to the backend instead of getting the backend to inspect the current state of the UI).

Copy link
Owner

@stackotter stackotter left a comment

Choose a reason for hiding this comment

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

Just got a few formatting/simple changes, but for the most part it looks great! The GitHub Actions errors should go away once you fix the fileViewExampleDependencies related issue

Package.swift Outdated Show resolved Hide resolved
Package.swift Outdated Show resolved Hide resolved
Package.swift Outdated Show resolved Hide resolved
Package.swift Outdated Show resolved Hide resolved
Package.swift Outdated Show resolved Hide resolved
Package.swift Outdated Show resolved Hide resolved
Sources/WinUIBackend/WinUIBackend.swift Show resolved Hide resolved
Sources/WinUIBackend/WinUIBackend.swift Outdated Show resolved Hide resolved
@LorenzoFerri
Copy link
Contributor Author

Interesting, any idea why the regular SwiftPM build system doesn't work? Had a quick poke around but couldn't find an explanation

No idea, the only exaplanation that I found is this:

This project may require Swift toolchain builds which are more recent than the latest released version. For best results, find the release tag used by the GitHub Actions build workflow and download the corresponding toolchain build from swift-build releases.

I know that they are actively contributing to Swift so maybe they need some experimental feature to make the bindings work?

Ah yes, that was incredibly annoying in Gtk too lol. Which may indicate that I need to rethink that part of the API to possibly pass the previous options as well as the current options, or something along those lines? Now that I think about it, similar changes could probably be made to avoid the need for getInheritedOrientation (by passing the expected orientation to the backend instead of getting the backend to inspect the current state of the UI).

Yes I think passing the previous options would make it easier, great idea.

@LorenzoFerri
Copy link
Contributor Author

Please wait a bit more to merge as I just found out how to make Grid work, so I'm gonna fix some of the problem that I mentioned earlier

@LorenzoFerri
Copy link
Contributor Author

image
image
Ok also those are working now, if now I can figure out how to fix the Spacer and the foregroundColor I would say it would be in a usable state. About the animation during navigation I found out I'm not the only one with this problem: thebrowsercompany/windows-samples#14

@stackotter
Copy link
Owner

Interesting, any idea why the regular SwiftPM build system doesn't work? Had a quick poke around but couldn't find an explanation

No idea, the only exaplanation that I found is this:

This project may require Swift toolchain builds which are more recent than the latest released version. For best results, find the release tag used by the GitHub Actions build workflow and download the corresponding toolchain build from swift-build releases.

I know that they are actively contributing to Swift so maybe they need some experimental feature to make the bindings work?

Ah yes, that was incredibly annoying in Gtk too lol. Which may indicate that I need to rethink that part of the API to possibly pass the previous options as well as the current options, or something along those lines? Now that I think about it, similar changes could probably be made to avoid the need for getInheritedOrientation (by passing the expected orientation to the backend instead of getting the backend to inspect the current state of the UI).

Yes I think passing the previous options would make it easier, great idea.

Ah ok, sounds like it's just cause they'll often use features that haven't landed in main yet, makes sense. Probably mostly works with regular toolchains except for when they start using a new feature or something along those lines, I'll have to look into that.

Ok also those are working now, if now I can figure out how to fix the Spacer and the foregroundColor I would say it would be in a usable state. About the animation during navigation I found out I'm not the only one with this problem: thebrowsercompany/windows-samples#14

Sounds good, just let me know when you're ready to merge.

@LorenzoFerri
Copy link
Contributor Author

Ok so I gave another try implementig the foregroundColor but I still didn't find a solution.
In Windows a Style object is a group of setters of some properties:

let style = Style(.init(name: "TextBlock", kind: .primitive))
style.setters.append(Setter(TextBlock.foregroundProperty, "Red"))

I can now apply this to a text block with:

widget.style = style

Great now the text is red!
The problem is that I can apply this style only to TextBlock, so if I try to apply it directly to the styleContainer I get a runtime error.

In order to handle shared style Windows uses the concept of Resources. You can attach resources to a widget, but even if a style resource is added, those setters are not automatically executed. Because of this there is no form of cascading style like in the web.

To apply the foregroundColor I would need to know the desired style in updateTextView and apply it from there.
I tried a solution where I would keep track of each widget parent and inside updateTextView I would traverse up to the first styleContainer with a TextBlock resource style set. This worked for the first render, but when I tried in the RandomNumberGeneratorExample to change color, because the execution of the widgets start from the inside out, the result was one render behind.

Do you have any suggestion?

@stackotter
Copy link
Owner

To apply the foregroundColor I would need to know the desired style in updateTextView and apply it from there.
I tried a solution where I would keep track of each widget parent and inside updateTextView I would traverse up to the first styleContainer with a TextBlock resource style set. This worked for the first render, but when I tried in the RandomNumberGeneratorExample to change color, because the execution of the widgets start from the inside out, the result was one render behind.

Ah very interesting, I've had a few similar issues with frameworks having opposite architectures (e.g. LVGL requires making parents before children, so I ended up making a weird lazy creation system with closures and stuff, it was kinda cool, but a bit dodgy probably). It wouldn't be great, but is it possible to traverse back down from the style container to update all of its children?

@LorenzoFerri
Copy link
Contributor Author

Hi, sorry but I didn't find time last week to work on this, but I just pushed a few fixes.
As you suggested traversing down from the style container to all the childrens works. It's not really efficent but at least it works, I just needed to make a small fix for nested foreground properties like this:

VStack {
    Text("I should be yellow")
        .foregroundColor(.yellow)
    Text("I should be red")
}
.foregroundColor(.red)

Since by drilling down the update .red would overwrite .yellow

I also fixed the Spacer, now it expands both horizontaly and vertically depending if it's inside a VStack or HStack, however I still ignore the values expandHorizontally and expandVertically passed to updateSpacer, I just assumed it always expand.

@stackotter
Copy link
Owner

stackotter commented Mar 26, 2024

I'm trying to test this on my Windows laptop and I can't get swift-cwinrt to build successfully. My error is expected ';' at end of declaration list on line 45 of Sources/CWinRT/include/RestrictedErrorInfo.h. Is this something that you've run into in the past? I tried playing around with -Xcc -std=... and -Xcxx -std=... cause that helped me yesterday when I ran into a similar issue building cpp on Linux, but it didn't seem to change anything this time. I'll open an issue on the swift-cwinrt repo if it's not just an issue caused by me not knowing the ins and outs of development on Windows.

Also, for the Windows build action we can probably do something similar to what swift-winrt's GitHub action does here: https://github.com/thebrowsercompany/swift-winrt/blob/main/.github/actions/windows-build/action.yml

@stackotter
Copy link
Owner

Don't worry about those issues that I was running into, managed to get things sorted out eventually.

This is so cool! Can't wait to flesh out the AppKit backend too and essentially have write-once native-everywhere. The windowing seems a little broken but it's broken in many ways on existing platforms anyway so don't worry about that. Happy to merge once Windows CI is passing (and I can help out with sorting out the CI if needed) 👍

stackotter
stackotter previously approved these changes Apr 7, 2024
Copy link
Owner

@stackotter stackotter left a comment

Choose a reason for hiding this comment

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

I'm gonna merge this now since Windows CI is broken due to the xz backdoor anyway (which caused the xz repo to be disabled by GitHub which in turn broke vcpkg install for anything that depends on xz).

@stackotter
Copy link
Owner

I had to do some weird merge conflict resolution stuff so I merged through the command line and it didn't merge the PR 😭 it mustn't have recognised the commits cause they were rebased. Sorry about that, the commits are still authored by you though.

@stackotter stackotter closed this Apr 8, 2024
@stackotter stackotter changed the title Implement experimental WinUIBackend [MERGED] Implement experimental WinUIBackend Apr 8, 2024
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