Hacker News new | ask | show | jobs
by swiftcoder 20 days ago
> Experience has shown us that an immediate mode API is the only sane way to program GUI applications

I wonder how long till they pivot away from this belief. I feel like everyone in UI goes through this phase as some point, but in the end it doesn't scale to truly complicated UI

4 comments

Yeah. Based on my personal experience I think some kind of hybrid of old-school imperative retained and declarative retained, both with granular reactivity is probably the correct balance for "serious" high-utility desktop applications. Declarative approaches are great for smaller components but become a nightmare for anything much more complex than a relatively simple mobile app while imperative requires a lot of extra legwork at the component level, and as I understand (which may be incorrect) immediate mode makes certain types of optimization more difficult.
In my experience, it's the opposite. Immediate mode GUI, or at least a functional and declarative approach, is the only way I've seen it scale well. It's more modular and scale-independent. On the other hand, retained mode, or imperative/OOP approach to state management, becomes complicated and monstrous quickly; it's the dominant style and can be made to work OK, but typically hellish to maintain or develop beyond a certain scale.

Admittedly I'm simplifying too much and conflating paradigms. My preference is something like "functional core, imperative shell" or maybe "immediate-mode core, retained-mode shell" if that makes sense.

Is Tracy complicated enough? Because it's imgui.

https://github.com/wolfpld/tracy

That looks quite simple. Think about something like this, a commercial SystemVerilog simulator (this only shows a fraction of the UI).

https://blog.reds.ch/wp-content/uploads/2018/09/questa13.png

Or something like Visual Studio.

Obviously most GUIs are not nearly that complex so immediate mode can get you quite far. Its biggest limitation is that it makes it hard to do some layouts. Your GUI layout becomes dictated by your data dependencies which is quite awkward.

Absolutely zero difficulty redoing this in a react style renderer. The only complexity is being careful with your data dependencies so as to not needlessly rerender.

Each pane is easily isolated, can share data with a view model scoped properly, etc. Writing it in an imperative toolkit is a "oops I forgot to update my data here" kind of hell. Data binding makes it slightly less worse

> Absolutely zero difficulty redoing this in a react style renderer

I would not classify a react style renderer as "immediate mode". It has aesthetic similarities with IM GUIs, but ultimately there is a fully retained tree under the hood, that gets diffed/mutated on every update

It's the only thing that can scale to complicated UI
What does "scale"even mean in UI context? 10 or 100 controls in app makes difference how exactly? Retained apps redraw when needed, they are idle most of the times. How redrawing every frame helps to scale?
This thread (and post) keeps mistaking immediate mode GUIs with declarative UIs. You can have declarative UIs that aren't immediate: the vast majority of them are, and they are idle most of the time.
Does "immediate mode" mean the UI refreshes at a constant 60fps rate like a video game, redrawing everything all the time?

No.

It means that you build the UI by describing what it should look like now, based on the data / state you own, without referencing any existing "widget" object or trying to manipulate it.

Scale is not about the number of buttons, but the structure of the data.

You have a list of objects, within each objects you have several fields, some of them lists, some of them maps. within some of those sub-items you have other lists and maps, nested arbitrarily.

This would be hell to manage for a retained mode UI. You have to mirror the application data into a widget tree and keep all the elements in sync, all the way down to the arbitrary depths of it.

You'd be writing thousands of lines of code that do nothing but keep your data in sync with widget states. You'd have many one off bugs where one sub field fails to sync in some scenarios. Your only options is to be more defensive: more events, more full-resync. As a result, the codebase is complicated and the application feels slow/heavy, because updating widget states is costly.

In immediate mode, none of that matters. You don't have a parallel widget tree.

> In immediate mode, none of that matters. You don't have a parallel widget tree.

No, instead, you need to have your entire dataset in memory, and potentially rebuild your whole tree on every update. This causes quite a lot of problems for out-of-core datasets - you end up needing to maintain proxies for things like items in scrollable lists that aren't loaded into memory yet.

> you need to have your entire dataset in memory

Are we talking about displaying tables with billions of entries? I'd like you to show me how retained mode gui scales well with that use case.