I would argue it's a "give a mouse a cookie" situation. On the simplest side of things, stuff like Zenity exists for super-simple GUI dialogues and user input but isn't very customizable. You want a window model with a visually-distinct root window and identifiable dialogues and child processes. You also want async management between those windows, atomic data handling for both processes, signal handling for sending data between them, GPU-accelerated animations... what we really end up wanting is pretty much an entire OS.
The best GUI toolkits I've used are either heavily abstracted (tkinter/pygame) or absurdly full-featured (GTK/Qt).
I don’t know if it is that GUIs hate tests or that tests hate UIs but either way it means UI code is hard to test.
The trouble is that a UI element usually calls the framework and gets called by the framework so you wind up needing mocks and such. Or you have something like React which is just batshit crazy (e.g. do I wait until the component renders 7321 times or 7322 times before I can check that it is in the state it is supposed to be in?)
Was just discussing this with a younger colleague this past week. I think the conclusion we came to is that:
a) GUI programming (for the web) is hard because the core technical stack that's made up of browsers, html, js, and css is abhorrent garbage (I'll get to what I'm comparing it to in a sec). It has incomprehensible standards and almost no compliant implementation that can make it all work together -- after decades, untold millions/billions of dollars, and unbelievable quantities of person-hours.
b) the layer right above that (the frameworks and libraries) changes so often, are so buggy and fragile, and reinvent the same stuff over and over and over again, and a billion frameworks that all poorly do the same crap in slightly different ways get released every month -- usually built by bored engineering teams with nothing else to do or people who didn't exist in the "before times".
c) My first job out of college in the "before times" was building Windows desktop software. With no real internet resources, a couple of books and whatever Microsoft called their C++ visual dev environment software back then I managed to build professional GUI software in a couple months that still runs, looks, and works exactly like it did back then and made out company quite a bit of money. Later on I applied basically the same ideas to some Linux desktop software using Qt and everything worked fine and was easy. Visual Basic, Delphi, heck even Microsoft Access demonstrated that it's so easy that even relatively inexpert people can create complex and reasonably attractive GUIs.
Point is, we had the technology, it wasn't hard to use, and it worked pretty much flawlessly and the web is such a broken disaster of a mess that it's almost rewritten the brains of the entire industry to just Stockholm syndrome themselves into accepting that there must be something hard about GUIs.
I use css and I think it’s pretty amazing. It requires understanding and thinking that goes along, not against the technology.
It is hard to abstract and tailwindcss for example is an abstraction which goes against it for the sake of bypassing the deeper understanding. But css is powerful, elegant, and keeps moving in the right direction.
There's a reason every single company wants to just build web apps and electron apps, because it's actually the best and simplest platform for delivering a cross platform UI experience that exists and it's not really that complicated at all and frankly it's unequivocally untrue that if we sampled all of the GUI software built on Visual Basic and Qt in the 90s and 2000s vs the software today that we would find it better.
> There's a reason every single company wants to just build web apps and electron apps, because it's actually the best and simplest platform for delivering a cross platform UI [...]
Maybe, but consider that possibly the reason why many companies go that route is more to do with financial incentives: it provides for the cheapest option (both in terms of resource costs and time), while still retaining complete control over the use of the software (and consequently their revenue generation).
Even ignoring the latter aspect of that, its cheaper to have 1 team build out a single interface than multiple teams each working on platform specific interfaces. Then theres a follow-on reduction in support (and troubleshooting) costs in dealing with platform specific updates, less coordination/communication needed and thus reduced need for management level resources, etc etc. Theres also lower costs in terms of skills the company is hiring.
I'd also extend that to the possibility that the prevalance of tooling such as electron and its kin is a response to a demand for it, rather than arising out of being "best in class".
So best and/or simplest platform... well that depends on whats being measured. From a financial perspective, sure, why not. Other measures though... Im not so sure.
Is Flutter as ubiquitous as html/css/js? Is it similarly cross platform?
I responded to the suggestion that every company was choosing electron (html/css/js) because it was the best and easiest, by pointing out that the reason it might be picked may not have anything to do with simplicity or being the best choice, but rather based on other factors (like financial motivations). Flutter is a different toolkit, and isnt the typical "web page" type building of application UI, and so was outside of the scope of my original response.
I've never used Titanium, but have used similar types of toolkits... all of them, regardless of whether they compile to native apps or not, are (very broadly) generally selected based on the criteria mentioned in my previous comment: why build out different platform native apps when you can largely get by with a web dev(s) building a single app that compiles to the native system? Theres tradeoffs involved, and the typical optimisation in an org is to favour lower cost and/or quicker market time. Theres the "we can do it better later when we have the money to" mentality, except once a system gains enough traction, that perspective changes to "why spend time and budget on changing something thats working". The term "working" though, is subjective... its "working" for the org, but is it truly "working" for the user that is interacting with it?
I too did Windows desktop software with good GUIs using nothing more than MS Visual C++/MFC/ATL and David Kruglinski/Charles Petzold's books (no Internet). There was a learning curve but not too difficult. Later on i moved onto Networking and learnt client/server distributed programming. But when i see what is going on in the Web GUI space i am truly lost. It all seems to be duct-tape and junk. A simple document sharing protocol has been munged to do anything/everything, an ad-hoc language (i.e. JS) has become de-facto GUI language and every kludge under the sun has been employed in the service of event-driven programming just to make something work. This is not Engineering but the very worst of Hacking in the full pejorative sense of the word.
Visual Basic GUIs were suitable for internal apps and tools and indeed easy to develop but that’s about it. There were many shortcomings to this technology and commercial apps werent built in VB. Their simplicity and ease of use are only useful as a contrast to today’s landscape. However, so many things have changed too, desktops not only try to be tablets, the web took the center stage and UI design also has become a confused mishmash of ideas.
Visual Basic 6 (and earlier) GUIs scaled fine for the most part since instead of using raw pixels they used "twips". In fact you can throw a bunch of controls on a VB6 form, compile to an EXE, change the compatibility settings for the executable to let it handle scaling, run it in a high DPI monitor and the controls, text, etc will scale as expected.
AFAIK one place where it fails though is bitmap images - the "picture" container will be scaled but not the bitmap inside it (i think metafile images will be scaled though).
VB was designed at a time when there were monitors not only with different DPIs but some didn't even have square pixels. Windows also had an option for "large fonts" even in Win3.x days which essentially increased the window scaling.
IIRC dialogs defined via resources also use (or can use) the same approach, allowing them to be automatically scaled too.
Of course in practice pretty much every application changed the coordinate mode to pixels and ignored the DPI settings, meaning not only the application didn't scale itself but it also didn't provide information for Windows to do the scaling. This is why more recent versions of Windows lie to applications about the DPI and do bitmap scaling of the window texture in DWM unless they explicitly (via a manifest file) claim they know how to scale themselves (or the user explicitly lets the application scale itself via the executable's compatibility settings).
There's more to responsiveness than scaling. Column based applications don't work on phones and rows of cards are terrible on desktop, just to name two examples.
IIRC Visual Basic 6 was good in many ways but in (at least two) areas, it wasn’t:
* re-laying out the UI controls when the user resized the app window
* nicely handling long-running tasks, with progress updates, allowing user interruption
In both cases the “drag and drop controls, then connect them to each other with a smattering of code” paradigm didn’t work too well.
VB6 was pretty good at re-layout: you could do sticky corners and buttons and controls would move and expand when you maximized the window.
Also since you were writing code behind control events, you could easily bind 2 buttons to the same function: one button visible on normal state, the other visible in maximized state, etc
[context: frontend eng here, with a CS background and lots of experience on the backend]
The reason frontend dev sucks so much, essentially, is humans.
Have you met humans? They are opinionated, dogmatic, and hidebound; their habits and preferences are arbitrary, and, collectively, comprise their horison the way the bars of a cage do a bird's.
A slight user preference for something inconsequential, like, say, a realtime indication of password strength, or an absence of loading delays, means that if your product does not "meet user expectations" for some some incredibly inane value of `$EXPECTATION`, you can reliably expect your competitors to consume not just some, but eventually, all of your sales, and is therefore strongly selected against.
Recalling that you are yourself substrated upon a human, you know exactly what I'm talking about. Be honest. Are all of your preferences rational? What even is 'rational', anyway? I'm not just doing a lazy gesture to pseudo-philosophy here, I'm actively asking you to define practical rationality (not just logic, mind you) in a way that has no reference to common practice, intuition, or habit. Good luck :) (Note: I wasted my twenties in a philosophy PhD programme, so I know enough to advise you that this is a honeytrap that you can literally spend decades unsuccessfully trying to answer. I know I did.)
Anyway, because of user expectations -- expectations like: bespoke widget sets, branding, performance, speed, similarity with native app, cross-browser compat, etc. -- we -- us engineers -- built elaborate tooling back in the day to accomplish all this, for 2008-ish values of 'back in the day'. Some of these, like jQuery, eventually crystallized into new browser APIs, but a lot of them involved various kinds of either stack lock-in, or conceptual lock-in. (React does both.)
Remember how I said humans were creatures of habit? Well, horribly enough, engineers are humans too, and we can't get good (really good) at things until we've put in 10k hours or so. So, if you've put in 10k hours into React, you're going to be at a competitive disadvantage if you move everyone to a saner alternative, such as htmx. This, too, is strongly selected against.
So, there you have it. Frontend development is terrible because it's the part that touches the most humans.
P.S. I note I only answered your question with regard to browser programming; for other GUI situations, things are similar, if not worse. E.g. GTK+, which is used to make some devastatingly effective and elegant Gnome apps these days, is, in my opinion, a Bad API. But good luck trying to reproduce every nuance of Gnome ex-nihilo with FLTK.
And UI programming is hard because of the user you are interfacing with :-) and the fact you are not forcing them to have amazing working memory needed to deal with a CLI.
It's deceptively complex. What looks like buttons and sliders ends up having a lot of interactivity that is underestimated. Then there is the complexity of both the software and hardware stack, from different displays and devices to a rapidly evolving ecosystem of libraries, frameworks, and such. Add to this the complexity of the networking stack (at least for web apps) and it's not hard to see why even UIs with modest functionality become very large and complex.
There is also something to be said about how organization structure their teams to build UIs and APIs, which is to scale productivity by adding and leveraging more people, as oppose to leveraging more powerful code. This is probably more true on the frontend as it is typically not as critical to the business as the backend, so it is not surprising to see UIs constantly break or lack any proper standardization.
GUI programming is hard because some industrialists realized that if they switched the whole world to web based applications they would be able to hoover up information for free and got everyman and his dog to jump onto the everything must be a web application bandwagon.
As for web development frameworks, GUI and otherwise, they are like nuclear fusion, only in this case a new framework is announced every two weeks that is claimed will solve every development problem.
I think causality is probably the other way around, i.e. we keep starting over because the problem is hard. There's a back-and-forth lurch that happens when people discover that yet again, they're caught in a complexity net, have a strong it-shouldn't-have-to-be-this-complicated feeling, and set out to build tools that will make the problem easier in general. Often it's relatively new programmers running through this cycle, who perhaps don't realize (yet) how many times this lurch has already taken place, or how they may be repeating the approach of a previous 'generation'. (That's not a cricitism—it's the cycle of life of the software business, and has been going on since long before most of us showed up.) I do think there's something about UI that lends itself particularly to this phenomenon. Perhaps its statefulness, as someone else said already.
I don't think the problem is too hard. It's just that people don't seem to understand the different models and history of things.
People have this not-invented-here syndrome. It also comes from the fact that they initially think a certain framework is too complex. Only to figure out later that they didn't understand why certain things were done in a specific way. Also, the fame and opportunity that comes with creating a framework cannot be denied.
For example, The whole "MVC" term on the web is plain wrong (web2.0 times). It's a term from actual GUI programmer, bolted on a document presentation format.
These days it's probably faster and easier to distribute an OS such as MenuetOS via web assembly, and simply run a GUI application inside which uses a simple JSON api.
About statefulness... why are we introducing stateful applications which is then patched up with server side rendering when a simple html application would suffice? It makes no sense. It's stupid, costly, and has 0 benefits.
For more complex applications, sure.. but tbh, where are those? Is a simple admin panel or login screen really 10x more difficult to create than VS Code, or for that matter Microsoft Access?
"Engineers" in general like^H^H^Hlove^H^H^Hget off on complexity. That is where the problem lies. It's intellectual porn and toxic as hell.
I always try getting into frontend frameworks but it's just overwhelming. For WebDev there are a trillion frameworks which you need to append with another framework as the each framework doesn't really solve the problem by itself.
I looked into Flutter & Dart as it seems thatr everybody loves it. But then apparently even for Flutter you need another framework for state management. I thought state management is the core issues these frameworks want to solve?
I would've thought game engines have solved the problem of complex state management though. There are games with reactive in-game browsers/websites which work better than most real websites!
So far the easiest for me to grasp and getting started was actually react / nextjs:
- Extremely well documented
- Little boilerplate (especially compared to Flutter - I find that StatefulWidget really ugly)
- Combined with a component library it's incredible easy to get started
Try doing something in Vue, Flutter, or SvelteKit or something... it will probably be significantly easier than what you expect!
The hardness comes from the fact that you're basically writing bookkeeping two way state sync code, and if you don't have a reactive framework you're doing it by hand.
Plus, there are so many GUI libraries, and the easy ones are big. I suspect GUI haters choose low level stuff, then complain about how hard it is.
Or, they might use language without that great of GUI bindings. They might also start out with a specific vision of how it should work, whereas people who find GUIs easy are picking a big popular framework and not really thinking of any ideas outside it's language.
I generally don't even start thinking about architecture or code until after I've already chosen my tools, I want to do designs that fit the tools I'm using, not design something and than choose a language specifically for the design.
Plus, you cannot build a nice GUI on top of a UNIXY backend without lots of work. For some reason I don't understand, programmers seem to think of computers as devices that take input and produce an output, and build stuff that looks like a compiler tool chain.
With GUI, you're not computing an output, you're updating a state based on user input. You generally never want to be in a place where you do things in a sequence, and can't edit previous steps without starting all over.
You also can't always have a logical model for what to include.
Usually GUIs are not general purpose like raw code is. You're not using a blank canvas to build another blank canvas, which is what most devs seem to like, you're building a curated workflow.
You might have "Edit this transaction" buttons and "Export report for the month" buttons. If you're used to thinking in logical layers that build on each other, you might not enjoy essentially recreating some illogical business process in code.
It seems like programmers are really code-philosophers and code is like a meditation for a lot of them, and they seem to get depressed and discouraged working on things that don't have any beauty or logic or reason and are just reflections of some messy illogical real world process full of extra stuff to deal with untrained users.
I'm sure there are many issues with GUIs. My main one is that lots of things can't be (easily) expressed in the API of common languages. For example you set the colour of something to red? Well, that thing renders in some specific way that calls a callback which in some cases will override the background, so setting the background colour will not do anything. You can't express that in objects, there's really no failure here beyond "you didn't consider every possible layer and combination of features". Yet, whenever I try to create some GUI, I run into an issue like that every few minutes.
Devs were so preoccupied with whether they could, they didn't stop to think if they should. Our industry seems to have a problem with FOMO and leaving well enough alone. Most modern web GUI frameworks and modern software in general are engineered around buzzwords not usability. We can't seem to just stick with simple boring things that work and constantly reinvent the wheel because we didn't like the hubcaps.
It’s because you need to bubble state through to all components. Implement a spreadsheet to get a grasp of the initial problem. Then consider that you need to partially update for performance.
I rarely program GUIs but when I do I use React and it works quite well quite easily.
* Parts are fundamentally declarative and parts are fundamentally imperative. This applies both to the abstract problem, and the approach taken in particular toolkits, and the border is not in the same place.
* The state involved is stored in at least 2 places which are distant from each other, and that opens all sorts of logic and timing problems.
* The same GUI framework gets used both for "wait for an event" UIs and "update every frame" UIs.
The core of a GUI framework is nearly always written as asynchronous events and drawn responses, with the developer at that level responsible for the state machine in the middle and invalidation of the GUI in response.
Coordinating large asynchronous systems by hand is challenging. There can be surprising interactions. For example, tapping a button again in the middle of an animation is an example of something where the complexity often leads to the program getting in a bad state or even crashing.
This is one reason that structured concurrency and asynchronicity primitives have been added to many programming languages - to make reasoning about and managing such state machines easier.
Some modern GUI frameworks attempt to simplify this by instead let you declare the UI and its relationship with your data, the interdependencies there, and may have sophisticated means to monitor data changes. This breaks you from having to maintain most GUI state within your program - you respond to user actions by updating your program state, and the GUI responds to those updates.
The biggest issue with GUI programming is always "when to reflow" and "when to relayout".
Take fading in sidebars, for example, which influence how the main content frame is rendered and are different in size on mobiles (e.g. icons, icons with labels, bigger icons, bigger labels or even a more nested structure on desktop, with a burger icon for fade-in and fade-out).
This is almost impossible to implement if you only have horizontal and vertical box layouting at your disposal, which is the most common denominator among frameworks and their renderer concepts (GTK, Springs, QT, etc).
Flexbox and tweenable properties like transparency, rounded corners, margins, paddings, colors and z-indexes are almost a hard requirement these days in terms of UI design. And those features are very hard to implement without a renderer pipeline that can influence both fragment and vertex shaders.
And this is not talking about state management of the UI elements, which is a whole world of problems on its own. React Native's approach, for example, is utterly broken on older mobiles due to how they re-flush the whole state tree on every single frame (which makes old mobiles lag due to them not being able to keep up with the amount of FPS required).
Getting the reflow right with e.g. calendar widgets and calendar popup views which are rendered by native APIs is a nightmare. As there is no unified way to do it across platforms, there eventually will be a lot of redundancies that are implemented, and a whole lot of quirky code behavior.
Plus, there are some wrappers that make seamless transition between desktop and mobile view like MauiKit[2] and Kirigami[3]. I don't think they're perfect or that they are doing it correctly even, but they're onto something with their main idea.
i think the nature of GUI programming hasn't been well enough characterized, and thus the tools we use for building them aren't adequate. it's a bit like trying to do DB programming before transaction, and relational algebra were invented.
Gui has a lot of conceptually complex properties :
- it has both synchronous and asynchronous behaviors ( drag vs trigger an animation or loading data for the next screen )
- it has i/o (the screen)
- it is interactive (a user can click at any point in time, interrupting the process)
- it has transactional properties (you sometimes want a whole set of changes to be performed atomically, with nothing interrupting it).
-it is working both in diff mode ( change just the color of that button) and in whole state change (load a new screen)
GUIs are one giant state machine. Tracking all those states is unwieldy. The best notation I've seen is state charts, as described by Ian Horrock in Constructing the User Interface with Statecharts. Still, it's a ton of work. As a result, everyone I've ever seen just resorts to the 80% solution of coding it up without tracking the states and then playing whack-a-mole trying to make it consistent.
You have to be idle, to wait for user action, which can come at any time. You're also possibly waiting for other computing processes to finish, then you have to deal with the consequences of whatever state you're in based on what the user has done.
Over the years, I have observed the fact that some /many things about GUI is/are just unnecessarily stupid.
See Jgoodies-binding, and more recently elm-lang and htmx for unexamples of this.
btw, rip