Cross-Platform GUI Toolkit Trainwreck (2016)
blog.johnnovak.net
blog.johnnovak.net
1) I want to write a GUI code once
2) I want to ship something that works on Windows / Linux / MacOS
2.1) I want to ship something that looks the same on Windows / Linux / MacOS
3) I want to ship a binary that works on Windows, a binary that works on Linux, a binary that works
3.1) I want to ship a small and efficient binary
4) I want to ship something that looks like a Windows app on Windows, a Linux app on Linux, a MacOS app on MacOS, etc...
(By definition, "I want to ship something that looks the same everywhere and looks like an app of my host" is meaningless, right ?)
The "holy grail" seems to be:
5) "I want to write a GUI code once that generates small efficient binaries that looks exactly like an app of the host OS, and if possible looks the same everywhere, and let me go back to writing my business logic rather than agonize over drawing a button."
It's seems from the debate that nothing obvious fulfills 4 and 5.
Then it comes to which requirement you're ready to drop.
If you're ready to drop requirement 2) , I suspect you're doing MacOS specific, go for it ;)
If you're ready to drop requirement 1) , I suspect your managers / salespersons disagree.
I suspect your manager / salespersons do not care about requirement 3.1), but it's debatable. Use Qt/Electron, and ship something.
I suspect your manager / salespersons do not care about requirement 4), and I suspect they're esthetics, which can not be defended.
I gave up waiting for someone to make 5, and don't have the resources / skill / time to do it myself. And maybe we should stop caring and watch the sky instead.
I hope someone is able to get to 1 + 2.1 + 3.1 someday.
I'll use that.
An example of a small GUI app:
https://github.com/vlang/v/tree/master/examples/users_gui
The app is about 100 KB on all platforms with zero dependencies and uses native toolkits (WinAPI, Cocoa).
i.e. you don't actually write comparable code. Instead, you just lambast how slow the compiler is.
Same idea as Electron but it wraps the OS webview instead of Chromium (Cocoa/Webkit, gtk-webkit2, MSHTML, and soon EdgeHTML). The minimal test app is 255kb in size, the static lib is just 48kb (MacOS).
https://github.com/zserge/webview
And I get, "but Safari/IE/whatever are still different platforms!" I'd argue that the edge cases between browser rendering of HTML + CSS + JS is much better to deal with than the architectural and language differences between the native GUI APIs on each platform. As long as you're not pulling in an obscene amount of JS dependencies, webview is very sane.
Totally unrelated to your point, but I'm curious as to what this word means in this context.
https://github.com/flutter/flutter/wiki/Desktop-shells
they say -
"Work is ongoing to extend Flutter to support desktop as a target environment, allowing developers to create macOS, Windows, and Linux applications with Flutter"
We're working on a framework that address 1, 2, 2.1, 3, and 3.1 - Revery [0] - a React-like framework that compiles to native code, built with ReasonML [1]. I believe that the React pure-functional model of UI-as-a-function-of-state is such a powerful paradigm, and not leveraged on native today - ReasonML is a perfect fit for that.
Bullet point 4 is a non-goal for Revery, but our sister project, Brisk [2] is built on platform widgets.
I'd personally also add a couple of dev experience bullet points or ideas to the "holy grail":
6) Fast / instant compilation time
7) Hot-reload (see changes to the app instantly)
We're not there yet, but we hope we could also get to the point of including 6/7 - essentially bringing some of the great aspects of React/Redux to desktop application development, with native code.
- [0] Revery: https://github.com/revery-ui/revery
- [1] ReasonML: https://reasonml.github.io/
- [2] Brisk: https://github.com/briskml/brisk
Why?
I want my macOS apps to use Mac-native UI elements, and Windows apps to use Windows-native UI elements. (There is no one native Linux UI, so we can leave that one aside...)
Branding teams are in my experience very picky about lots of stuff. For example, my employer's brand design team recently released a rule that when our logo is included alongside others logos in a list of companies involved in a partnership, it may not be included in a vertical list, only a horizontal list.
Using the standard background colour for the platform on your app when it's not one of the brand design team approved colours? Also out.
etc. etc.
It's a common concern to avoid confusing users who switch OS often (eg when demoing on someone else's machine.)
The fact that it contradicts the other requirement ("but it does not look like the other apps") is lost on no one.
But I suspect lots of software companies would think that describes them, when in reality there's probably fewer than 10 products that meet that bar.
See VSCode, Discord, Spotify...
It's easier to get a single code-base that 'just works' across platforms if you have an abstraction that is truly cross-platform. One of the major value propositions of developing with Electron is this ability to develop cross-platform 'with confidence' - I can build an app on Windows and be 99% sure it's going to look function the same on OSX / Linux.
> I want my macOS apps to use Mac-native UI elements, and Windows apps to use Windows-native UI elements
That's a fine goal but it means you end up needing more platform-specific code. It's a trade-off, and there isn't a one-size-fits-all solution.
To take Discord as an example, I don't know that people actually can't live without the UI slavishly copied for every platform; I think people rather want light and dark themes on all platforms, message formatting to look basically the same, and for everything to be found in roughly the same place.
This idea that users expect apps to look the same is, I feel sure, something someone said and everybody has gone along with. In my experience, people want things to behave consistently — so if Discord had a native Windows client and a native macOS client, people would be more interested in whether or not they could still find the little microphone and headphone buttons at the lower-left of the screen, sod if the icons and colours are identical.
Most of the users of the FOSS software I'm developing also switch platforms depending on the project they're in, but they want the software to be the same everywhere for instance.
As annoying as it is to switch form cmd+C to ctrl+C when I go from Mac to Windows, it's even worse when a Mac app uses ctrl+C.
Windows support is implemented via https://github.com/Microsoft/react-native-windows and targets W10, Xbox, and Windows Mixed Reality.
MacOS support is experimental, but mostly working. Linux currently needs an Electron wrapper. Theoretically you could port one of the react-native-desktop projects to use GTK or Qt.
Then of course you have native iOS and Android components with Facebook's ReactNative and web support via React.
ReactXP wraps all of these projects allowing you to write React components and TypeScript code that is shared among every platform. It's the same workflow as Electron, even less so, with native UI.
> 2.1) I want to ship something that looks the same on Windows / Linux / MacOS
> 3.1) I want to ship a small and efficient binary
well, with Qt I write my code once, which looks the same on windows / linux / osx / $nicheos and the result is fairly performant - I've got tables with tens of thousands of elements which update at rates greater than 100hz and the UI does not bat an eye. I'm not even using the GPU-based scene graph (QtQuick) but instead uses the CPU-based raster engine. Here's the software I'm developing : https://github.com/OSSIA/score/
So what is missing ? For what it is, the binary is relatively small (and I also link against LLVM and a few other large libraries, and am not even doing global static linking + LTO...).
But sure, qt seems to hit 1,2,3 , although 3.1 will fuel flamme wars that we dont realky care about as long as you can ship code. Good for you !
6) Using my preferred programming language.
In this advanced cases you will need to create custom widgets for each platform, hope that all platform have the advanced datagrid widget(if not you will have to create it) etc.
For simple apps your solution works, and you could also make a CLI app too.
When I've looked at libraries, I always consider how you'd implement the Visual Studio interface with them.
If you look at a mature GUI library advanced widget you will see how many events and thing supports and how many bugs were reported because there are many corner cases that need handling.
I worked a lot with Flex4 I loved the fact that the widgets were optimized, you could have a list or tables with 1 million items and it would not affect performance. In Web I see the pattern where you use a lot of pages, you show like 12 results and you have the user hit Next and Next when you could have fitter a lot or even all the results on a large page, especially if is all text
I guess since he excludes GTK2 at the beginning for size concerns, wxWidgets and Qt would fall into the same category.
well, he can keep joking and I can keep shipping Qt apps and everyone's happy
Instead, I took the (longer) time to hand-tune Qt source and qconfig.h. My cross-platform desktop builds are under 2MB in a single executable, which appears to be something the OP wanted in the first place.
But, now that I've thought about it for a few minutes, it does sound pretty straight forward. Unless Qt makes it difficult for some reason?
It's a Stack Overflow world, baby, we just live in it.
> what cross-platform libraries are available for Nim!
Things might have changed over the past couple years, but, at least at the time, there weren't any (usable) Nim bindings for either.
There is no (grammatical) article in its title. You seem to be assuming the article is implicitly "the", which would imply that it's intended for a more-or-less universal audience. It could just as easily be "a", which would then mean that it's more of a personal rant about the author's specific situation. I'd argue that the leading two sentences in the italicized portion of the article imply that it's the latter.
For the sake of throwing my own $0.02 in, I also have a historical habit of blithely ignoring wxWidgets and QT for my hobby projects. Their being written in C++ bit is a bit of a deal-breaker for me. The quagmire of interacting with C++ code from a language that isn't itself C++ in a cross-platform way results in me shaving my fill of yaks during working hours; I have little taste for doing even more of it during time that's supposed to be reserved for fun.
And qt python bindings seems to be very poorly documented, same case with Java bindings.
the only true "cross-platform" alternative is electron, which is a bit bloated and slow, but vscode is built on it, along with many others and worked fine. I personally prefer electron these days.
If you have to deal with all that baggage, then you might as well go build the UI layer native.
https://wiki.qt.io/Language_Bindings
5.1 Qt for Python (PyQt)
5.2 Qt for Ring (RingQt)
5.3 Qt for Rust (Rust-Qt)
5.4 Qt Quick for Rust (qml-rust)
5.5 Qt Quick for Rust (qmlrs)
5.6 Qt for Crystal (qt5.cr)
5.7 Qt for Go (qt)
5.8 Qt for C#/Mono/.Net (QtSharp)
5.9 Qt for C#/Mono/.Net (Qml.Net)
5.10 Qt for D (QtE5)
5.11 Qt for Haskell (qtHaskell)
5.12 Qtah
5.13 Qt for Julia (QML.jl)
5.14 Qt Quick for Haskell (HsQML)
5.15 Qt Quick for OCaml (lablqml)
5.16 Qt Quick for Node.js (Brig)
5.17 QML bindings for Nelson language
And the only churn was a change about five years ago from a less pythonic API (using wrapped Qt types for most things) to a more pythonic one (using Python's native types where it makes sense). And that wasn't even that bad. Other than that it has no more churn than Qt itself does.
With it, you have the full power of .NET Core and QML. There really isn't any limits.
Yes, time travel would solve most software development problems.
[1]: https://github.com/nim-lang/Nim/issues/6043 [2]: https://github.com/Araq/wxnim
Given how everyone jumps on Electron these days, I don’t think thats a particular concern for anyone who just wants something to “simply be” cross-platform.
Here it is in use on raspberry pi https://medium.com/@decrocksam/building-wpe-webkit-for-raspb...
A long video on it: https://m.youtube.com/watch?v=klfE6m1oCkg
A short video on it: https://m.youtube.com/watch?v=wVSwkj9McCU
It says: "WPE is the reference WebKit port for embedded and low-consumption computer devices. It has been designed from the ground-up with performance, small footprint, accelerated content rendering, and simplicity of deployment in mind, bringing the excellence of the WebKit engine to countless platforms and target devices."
I'd be interested to know how to use it.
"Just use Java or Electron"
I'm sorry, I just can't reconcile the logic here.
Why can't I write a desktop application that just asks the OS for the users preferred browser and provides it with HTML/CSS and UI interaction callbacks / events? Render it in a native looking window.
We shouldn't need Electron at all! It's like we all have this fantastic rail transport network but insist on riding in our own trains.
Plus, it's not overkill when you get the browser for free.
Really, that's the crux of the problem is that there are different paradigms that are not just a little bit different but fundamentally different. This is especially annoying on my Chromebook running Android apps -- they very often expect you to be using a touch-based system without a keyboard. The way this works with a docked Chromebook and a mouse is the mouse is treated as a VERY precise finger touch.
Additionally, on Android and other touch-based systems you're usually not using a Window Manager so, for example, using an Android mail client on my Chromebook, I can't start the reply in a new window so that I can read the message in one window while simultaneously replying... and I especially can't have a bunch of unfinished replies while reading other emails to gather information for the reply.
Creating a meta-layer that can represent the fundamentally different modes from the same data is VERY difficult. HTML and CSS do a terrible job as well unless you model your HTML in a very specific way, which cannot accommodate all GUI semantics.
I find this argument a bit silly, because there are few applications that actually need said cutting edge features and even if you’re one of the handful there’s always polyfills, but maybe I’m missing something.
You can:
- webview (I mentioned it elsewhere) https://github.com/zserge/webview - sciter https://www.google.com/search?client=firefox-b-1-d&q=sciter - ultralight https://ultralig.ht/
Granted it's not "user's preferred browser" but it fits the bill. They all have issues, largely solved by Electron packaging the browser engine with the binary, and even that isn't perfect.
Yep, he did that, as stated here from TFA:
> The solution was to redraw only when needed: as a quick hack I introduced a global boolean doRedraw and set it to true only when an input event was received or the internal application state had been changed (e.g. the framebuffer had been updated). Then the drawing would only happen when doRedraw was set to true. Surely this could be done in a nicer way, but the general concept would be the same.
His issue was this...
> The second issue with text rendering was a harder nut to crack.
> I really don’t want to do a Donald E. Knuth here and spend too much time on a problem that has already been solved on the OS graphics library level in a perfectly satisfactory manner. I just want to call drawText() and be done with it!
How does Nuklear handle text rendering, layouts, fonts, etc?
nk_layout_row_dynamic(ctx, (float) row_height, 3); // a row of 3 buttons if (nk_button_label(ctx, "Previous")) ... if (nk_button_label(ctx, "Next")) ...
Here is small app I made with Nuklear: https://www.youtube.com/watch?v=MycIYcutlMA
If you need HTML5 canvas-style drawing: I propose building an (abstract) SVG document and render with Skia. When part of the SVG document changes, that part will be redrawn by Skia, just like an interactive SVG in your browser. Disclaimer: I haven't tested this idea.
Andy Brice of successfulsoftware.net has a long-time product, Perfect Table Plan (computes seating plan for weddings, given various inputs) that used Qt (and C++), I've read, on his blog. PTP has been there for years now. Seems to be doing well, based on what he says about it. Don't know how good the Windows vs. Mac versions are, but at least they are there. His newer product HyperPlan may also be on the same stack.
https://www.perfecttableplan.com/html/about_us.html
No connection, just have followed the blog for long.
Sadly it's FreePascal, but it really does look like the less bullshit platform to make cross-platform desktop applications.
I’m sorry folks, 198MB menu bar apps is what we’re going to have unless a cross platform GUI effort equivalent to that of Chromium comes about.
For the love of God, check it out.
While those aren't great numbers, I personally think it's tolerable for most usages.
Versions for Linux and MacOS X are ready for download. The version for Windows will follow soon - I will speed up development if someone claps in his hands.
Its essentially a UI abstraction that delegates certain things "backends"
site: http://haxeui.org/ github: https://github.com/haxeui/haxeui-core discourse: https://community.haxeui.org/
It uses the Haxe language (haxe.org), here is a very small/simple/dumb sample of it "in action": https://www.youtube.com/watch?v=cijUTbMKMHI
It can use native components (wxWidgets, android, html5) but you can fairly easily extend it to use other "backends" (like Qt for example - i havent written that backend yet, but its something ill almost certainly do)... It can also handle drawing the components in a variety of methods (so called "composite backends").
Im currently aiming to get it out of alpha asap, and most work goes into "new-component-method" branches (these will become master shortly). Documentation is pretty slim at the moment, but something im actively working on, two examples that i just wrote up today in fact are:
https://github.com/haxeui/haxeui-guides/blob/master/custom-c...
and
https://github.com/haxeui/haxeui-guides/blob/master/modules....
Let me know if you have any questions! Ian Harrigan
Heres some other links / screens that may be useful:
https://twitter.com/IanHarrigan1982/status/11113481164338503...
https://twitter.com/IanHarrigan1982/status/10905353905980416...
I'd check it out before rolling my own :)
But if you do some forms and basic UI controls, wxWindows does the job egregiously. Looks the way it should be on all platforms.
It's funny an article from 2016 mentions issues about text rendering, because certainly we've been regressing in this area. I can spot QML and Electron apps by the broken text rendering alone. FF60+ with quantum has incorrect subpixel hinting and does not correctly hint at all sometimes.
Many of the mentioned apps int the article are "broken" in my eyes: I don't have perfect vision, and I do expect apps to follow the system text scaling (and they don't). For a "broken by design" example, see Darktable, which is awesome, and should do this by default, but the authors hard-coded a theme where literally every widget is styled for looks and not for function.
Nobody speaks about how the widgets should feel and interact, but that's exactly what feels off about "GTK" on windows or macos. QML, Electron and partly GTK3 brings that feeling to all platforms. My biggest letdown is QML, as many QT developers see it as the future so it has a larger adoption than it should have.
It breaks just about any rule in the book: poor behavior on any platform, noticeably slower, style not consistent with the platform, broken scrolling, broken text editing, broken text rendering...
Word choice? "Egregiously" means "Conspicuously bad or offensive." [0] The context suggests you intended to say "adequate for the purpose," for which "competently" is the correct term.[1]
it has always looked terrible to me when comparing to other desktop apps. All the fonts look more blurry.
It's fixed in upstream Electron but until a fixed version is integrated into VSCode I have to live with (months of) broken font rendering.
It even makes me want to use Xcode somewhat. For all its flaws, its font rendering is just the system (working) one, and scrolling in Xcode is actually smooth 60fps instead of the laggy mess that is VSCode.
"so developers can use hipster technologies like HTML, CSS and JavaScript"
"the vast armies of newskool web developers who grew up on JavaScript and the DOM"
...especially when the author doesn't really appear to know what he's talking about in that space:
"And when things don’t quite work as expected, you can’t do much about it—short of maybe just switching to a different framework as a last attempt."
"and long-term maintenance will be a nightmare."
"web development technologies—which technically don’t provide any advantages over more traditional approaches"
There are legitimate criticisms of Electron as a choice for desktop apps, but that doesn't invalidate an entire domain's worth of developers.
What does slow down Web UIs is the DOM (whose rendering engine in Chromium is written in C++). The DOM is the most advanced layout engine ever created. It does an unbelievable amount of work to make interfaces resizable-by-default and adaptable-by-default, and yet is enormously flexible, allowing one to build virtually any box-based interface imaginable. This isn't simply so newcomers have an easier time. It frees application code to focus on the control logic and the structural side of the view, instead of messing with things like pixels and manual sizing, which makes application code more maintainable and reusable. I can place a piece of DOM in wildly different contexts, with wildly different amounts and types of content, and for the most part it will just respond as expected. This significantly reduces code complexity, which improves long-term maintainability and avoids bugs.
This of course comes with a cost. All of those implicit layout adjustments have overhead. The ability for your code to handle content you never thought it would encounter requires CPU time. That cost isn't worth it for certain applications. But it isn't a waste, or a sign of the moral decline of "kids these days". It's a conscious tradeoff that any good developer can weigh and consider for what it is.
(Edit: I realize this complaint is quite old, but from the age where hundreds-of-MB main memory were mainstream we have seen very little actual improvement in GUI fancyness, yet a massive explosion in CPU and memory use, and often even heavily deteriorated responsiveness. That doesn't hold going from the "couple of megs" to "hundreds of megs" age.)
Right now my Palemoon browser uses less than 240Mb. It is possible, just don't do stupid things (like keeping 4385972Z38947 tabs open instead of using bookmarks) or cut out a little the eye-candy and the the "geek status symbol" stuff.
Less importantly, my computer's resources are also not infinite. Yes, maybe your Electron app does something I want, but guess what, there are numerous other things I also want to do, and it is a bit silly that an IRC replacement now takes half of my computing power and manages to be less responsive than the stuff I used 20 years ago, in the computers of 20 years ago.
- Longer start-up times (ridiculous things in 2019: a) SSD prices, b) the fact that it takes longer for an Electron todo app to load off a high-speed SSD than it took for basically the same application to load off my old Amiga's floppy drive)
- Fewer applications that you can run at the same time, higher chance of disk thrashing when resuming from suspend (bloated frameworks still have to make it to RAM, which yes, is also cheap and abundant, but load up a few VMs and see how abundant it seems then)
- Additional bandwidth used for delivering application updates
- Higher battery consumption if you use antivirus software and the like, because all those bits still need to be looked at.
- Sometimes it's just security theater, but not always -- a complex framework still has an attack surface even if your application doesn't use all of it.
These things save way more than a few cents' worth of disk space.
> Our programming culture's priorities are so far skewed from whats' actually important.
Indeed, that's why there are so many electron based bloatware being developed.
If you develop enterprise applications (which many Electron applications are, in fact :-) ), you really have to talk about antivirus time. It's not something you can wish away. And it's not something that you should ascribe to customers being irrational, either. If malware compromises your users' data and it turns out you're not running antivirus software on your computers you're gonna pay way more than you'll ever save by using a big fancy framework.
Sure, it's a bad idea (in strictly technical terms) but you can't just go to your customers with a straight face and ask them to stop running antivirus software because it's 2019.
> Put a monetary or human-time value on it and compare to the cost of the application, or the development time, or a single crashing bug
You say it as if these were independent, but they're not. Debugging performance issues (the critical kind, that make customers yell at you and utter words like "money" and "back") in Electron applications is a mini-project in and of itself. Complex frameworks (I'm not picking on Electron in particular) are cool to quickly develop against, but not cool to troubleshoot, and once you start going off the beaten path, it's not as fun.
Of course, sometimes it's a justified choice, I'm not in the "Real Programmers don't use Electron" camp. But the non-technical aspect of this engineering choice is slippery, as non-technical aspects always are. Sometimes it's the right choice in terms of organization expertise and whatnot, but sometimes it just appeases executive impatience, at the expense of customer experience.
You can do a lot of debugging and performance tuning in the amount of time it takes to write a custom non-framework GUI. But you're right that it's much less fun for the programmer, which I suspect is the real motivation here.
In enterprise applications you totally can just wish AV away by turning it off. It's useless at best and mostly serves to just gum up the works and slow things down without providing tangible benefit. There are much better ways to handle security in an org.
Not only is there a huge chunk of people who are using low-to-midrange laptops ranging anywhere between old and ancient, but manufacturers are still selling brand new machines with CPU power and RAM roughly on par with the average laptop from 2007 (C2D class CPU, 2-4GB RAM, 5400RPM HD). Those users aren’t going away any time soon, and by casting resource consumption aside as a total non-issue, developers are also casting these users aside.
Storage space is not the only consideration. Storage speed may also be limited.
On the other hand if you make a VST plugin or a chat application THEN startup time of 0.1s instead of 1s makes a huge difference.
So I think size/startup time really is important, but I fail to see why he chooses these huge applications as examples (because for them it doesn't matter). I suppose he chose them as they were "state of the art" in terms of complexity etc - but perhaps he should look for smaller aps (such as JUCE plugins) to see the state of the art in terms of size/load time.