Of course, things have gotten a bit postmodern, even on macOS there is no single widget toolkit, but basically a series of choices of what technology to build on. I'm actually not sure that binding other languages to SwiftUI is going to work well at all, but in Objective-C it works fine, just isn't very smooth. On Windows, I've generally found COM to be quite pleasant, and noises coming from Microsoft suggest that story will become even better.
I have never seen this go well in practice :( Qt and Flutter do this and the experience is just…not great. Do you have any ideas on how to do better than them if you're using this approach?
As I said elsewhere, increasingly we're in a world where there isn't a standard set of platform UI widgets. I think that was the case when we had Cocoa on macOS, but on Windows it's a choice between MFC, WinForms, WPF, WinUI, and those are only some of the choices offered by Microsoft itself.
It's a good question, though. It's possible somebody will figure out a good way to abstract over officially supported platform UI toolkits, and if so, that might become a compelling choice. This is one reason why the main point of my blog is that we need to figure out the best approach, rather than just assuming we know what to build.
we sure as hell haven't been using the same WPF apps
Well, there are obviously plenty of people who are fairly happy with Qt and Flutter
e.g. Text, HStack, VStack, ZStack, List, ForEach, Button(action, label)
Even if they don't output the same widgets on all OSes, I think just having a shared convention might be a good first step towards learn-once/write-native-everywhere crossplatform UI.
That would kinda suck for everyone who's used to wpf, qt, swing, gtk+, etc, bootstrap, whatever came before wpf, (winforms? Don't remember) etc etc. "Just copy whatever Apple does" seems like the worst possible solution for a cross platform widget naming convention out of many possible bad solutions.
Naming stuff matters a lot less, (but still sucks) but if you pick UI system and copy all its primatives, you're guaranteed to end up with a bunch of widgets that really, really suck in several native widget sets.
Ultimately, there's not a good way to do cross platform UI. It's always going to either be mediocre on all platforms, or really bad on every platform except one. At best, it will be ok for a while, and then one platform will redefine what "native" looks like and now all of the tradeoffs you've made so that it's mostly ok are just technical debt.
I don't mean to piss on anyone's parade, but cross platform UI is probably the hardest problem in software engineering. There have been a dozen or so teams of people smarter than us who have tried and failed.
If it were my job to make a cross platform UI system for rust, I'd quit and get a job where I wasn't set up to fail. If there was a gun to my head, I'd write a fairly thin wrapper around Qt. The idea being that most people already have 1-3 Qt programs they use regularly. So even though it looks like a Qt application instead of a native one they'd still be familiar with it and it wouldn't look out of place. Then I'd spend most of my time engaged in politics. Install a few developers to developed Qt whose job it is ostensibly to be good Qt developer citizens, but whose actual job is to guide the next major version of Qt (which will break stuff) into breaking as little of my stuff as possible.
The second worst (but still bad) way to make a native cross platform UI is to give up, and make a not UI but close to UI cross platform abstraction layer, and then do the half dozen abstraction layer to native UI things separately. That still sucks more than the arena football league though.
No one's ever really done this, so it definitely sucks more than I think it does. But I walked into that suggestion knowing it sucks, so I don't feel bad. I dunno man. Everything about cross platform UI sucks.
The suggestion wasn't about copying whatever Apple does.
It's that SwiftUI has the most simple syntax (that I've seen.)
ZStack() {
Rectangle()
.foregroundColor(.unicornPuke)
VStack() {
Text("Hello")
Text("World")
}
}
If you can make it nicer and more succinct than that, go for it!But how is that the "worst possible solution?" What OS-specific bias do you see in that?
So maybe the specific constructs for specifying view modifiers and enums could be slightly different for each language, or the ()s might be []s, and maybe we could do away with the {}s.
It would just be a model for describing the UI, that a hypothetical engine backend might take and spit out OS-native widgets. For another OS, you'd plug in a different engine.
At least at first glance it seems to me like if I'm avoiding native OS toolkits, I might as well just target the browser anyway -- or is there something I'm missing?
Personally I'd love to have something like
import SwiftUI // For controls that are common to all OSes
import SwiftUI.Windows // For OS specific featuresIt's hard to imagine that happening. None of these companies have an interest in making it easier to port code away from their platform.
I mean even now Apple has an increased focus on services that must be accessible from everywhere: Music, TV, iCloud Drive. Surely there must be someone there who wishes they had to write most code only once for all OSes without making their app a hog.
I'd settle for just a convention; i.e. let C# remain the primary language on Windows, but introduce SwiftUI-like primitives like Text, HStack, ForEach etc.
The first hit brought me to "Host a custom UWP control in a WPF app using XAML Islands" so there seems to be a distinction (and it implies WPF isn't the star anymore?)
[0] [1] that I could find after a couple searches look way more complex than what the equivalent SwiftUI would be.
<Button x:Name="button" Content="Hello, world!" HorizontalAlignment="Left" Margin = "152,293,0,0" VerticalAlignment="Top"/>
vs. Button(action: play) {
Text("Hello, world!") // Don't need alignment etc. to produce identical output.
}
and ugh [2]:> If you need to embed any Unicode characters into the text, you can use the standard XML syntax. For example, to put the greeting in smart quotes, use: <Label Text="“Hello, XAML!”" />
I suggested emulating SwiftUI's syntax because it seems to be the most succinct.
[0] https://github.com/microsoft/Windows-appsample-photo-lab/blo...
[1] https://docs.microsoft.com/en-us/windows/uwp/get-started/cre...
[2] https://docs.microsoft.com/en-us/xamarin/xamarin-forms/xaml/...
You can also do it in pure .NET code without XML, hence why I specifically mentioned code first.
So to pick on your example,
var btn = new Button
{
Name = "Button",
Content = "Hello, world!",
HorizontalAlignment = HorizontalAlignment.Left,
};
btn.Click += (s, e) =>
{
// handle click event
};
Slightly more verbose, but one can still wrap those calls, or just pick Fabulous, https://github.com/fsprojects/FabulousWinUI is the latest tech, just UWP as usual, still the future of Windows UI, now re-architected not to depend on a specific Windows 10 release, going back all the way to Falls Creator.
I haven't touched C# in ages but that definitely looks odd compared to what I remember of it (like SwiftUI looks a little odd compared to Swift but not by this much.) My immediate thought: why capitalize the property names?
Fabulous is comparatively verbose too but probably more idiomatic with F# (which I have no experience with.)
Windows and OS-X tend to be the more straightforward cases. There is a standard set of UI components which users expect. Things are murkier in the Unix world, with multiple libraries and display technologies.
The alternative of custom painting everything tends to come with more difficult to fix issues however. AWT's successor Swing struggled with things like respecting system user preferences and accessibility. You can find some old Java 1.5 write ups about it: https://web.mit.edu/java_v1.5.0_22/distrib/share/docs/guide/...
https://github.com/kenz-gelsoft/wxRust
As they mention, this is based on a C wrapper round wxWidgets' native C++ API, originally developed to aid in developing a Haskell binding to wxWidgets:
The Rust binding is probably bitrotted for modern Rust, but since it's mostly generated, generating a low-level API using bindgen, which is actively maintained, and then a high-level API using a Python script of its own, it might be fairly easy to resurrect.
bindgen has got pretty good at converting C++ APIs, so it might be worth trying it on wxWidgets' native API, too.
I have no plans to write a GUI application in Rust myself, but i would love for there to be a good option for other people to write native GUI apps in Rust. As many other commenters have noted, cross-platform synthetic GUIs never look or behave quite right.