[Cuis-dev] Rebuilding on events: Experiments in simplifying UI development in Cuis inspired by Mithril.js

Paul D. Fernhout pdfernhout at kurtz-fernhout.com
Sun May 14 10:47:54 PDT 2023


Thanks for the additional background, Hilaire. Not surprising the 
reference you mentioned relates to the web given a lot of experiments 
there over the past decade with new UI libraries (including Mithril and 
Elm).

On legacy complexity and Cuis compared to web UI development, that may 
be debatable in some specific cases -- even as, in general, Cuis is 
simpler and more approachable than the overwhelming complexity of the 
JavaScript ecosystem.

For an example related to Cuis and unexpected complex behavior, I was 
debugging why the background color of the experiment I worked on 
suddenly turned light brown as soon as the window was rebuilt. It 
ultimately had to do with the original LayoutMorphs having a transparent 
background color somehow, but for some reasons the new ones the code 
made had a light brown color. I could not easily figure out why that was 
the case (like where the original LayoutMorph got its color set). I 
spent a lot of time drilling down into the submorphs of the window, past 
layers and layers of LayoutMorphs.

And my conclusion from all that debugging was that the idea of an 
invisible LayoutMorph that Cuis uses is an anti-pattern. :-)

Those extra morphs made it harder to understand what was going on when 
inspecting windows. I'd rather see morphs hold directly onto their 
(visible) submorphs and hold onto a layout object of some sort that 
moves submorphs around as needed. That would make it easier to inspect a 
tree of (visible) morphs. To me, every morph having an optional layout 
object to arrange submorphs also would be simpler conceptually than 
having mysterious invisible-sometimes morphs in between all other 
morphs, because it would be a separation of concerns of layout from 
display. With a replaceable layout object, people could also more easily 
experiment with all sorts of layout options like Swing and HTML have 
(like MiGLayout or FlexBox which I  have found useful).

Here is the latest version of the experimental code which resolves the 
updating issues I mentioned in a previous email to the list (at a 
potential cost of more redrawing) and also has a fix for the background 
color issue mentioned above:
https://github.com/pdfernhout/Cuis-Smalltalk-RedrawWhenClicked/blob/main/KfExperiments-v5.st#L250-L317

That version also adds initial layout support to the builder 
(KfMorphBuilder) -- inspired by MiGLayout from Swing, but very primitive 
comparatively (and also the layout code should move to its own class 
eventually). The highlighted code at the link above is now shorter than 
the previous version because no extra code is needed to add the created 
submorphs to LayoutMorphs as the builder does that for you.

Granted though, you have to understand something about how the layout 
code works, which is an extra complexity. Right now, only "wrap" and 
"newline" are supported as layout options of what is outlined here (and 
not even that well):
https://www.miglayout.com/whitepaper.html

There is still an issue with how the Panel morph gets resized when the 
Kelvin field is added and removed. The show/hide button and input field 
also extend beyond the boundaries of the Panel sometimes, and the half 
of the button morph hanging over the panels' bottom edge sometimes is 
surprising not clickable. There is also another issue with how the other 
fields may have their height adjusted as the Kelvin field is added and 
removed. I can't help but think that layout issue might be easier to 
debug without all the layers of LayoutMorphs in the way -- but that may 
also just be me not understanding the layout approach well enough yet.

As far as efficiency and wasted computing, to get a definitive answer as 
to how big an issue it was, we would need to profile things. I have not 
yet looked into how to do that in Cuis and the CogSpur VM at a low level.

What I saw surprisingly though is that the window I made was responding 
to mouse moved events (not rejecting them), even though no code I added 
had explicitly listened for mouse move events. (This was also leading to 
a flickering issue as the Transcript flashed to the top every time it 
was written to on such events and then submerged again, which may be a 
bug or a feature arguably.) I could not figure out why such events we 
being processed by the window and not rejected -- and still don't know 
for sure. I though maybe at first there were hidden scrollbars listening 
for events? Now I think maybe it may have to do with the way the button 
color highlighting changes after you click and then move the mouse?

If most windows are already processing frequent mouse move events for 
whatever reasons, then there is already some CPU load there. In general, 
it seems to me that Morphic does a lot of querying of most morphs on the 
screen whenever an event happens, because Morphic is trying to determine 
who might want to process and event and does a lot of back-and-forth 
message sending between an event and nested morphs. If Morphic is 
already doing all that, then maybe some extra redrawing on top of all 
that won't make much of a difference?

For Dr. Geo for example, presumably there is a lot of redrawing going on 
when various geometric elements are moved? So, I can wonder for such a 
dynamic application how much more CPU would be needed if some extra UI 
elements were also redrawn? Again, we would need to profile to be sure.

And in a worst case, there are other techniques to reduce excessive 
drawing of complex displays, like by having multiple layers of bitmaps 
composed together. In one application I wrote about twenty-five years 
ago involving a constructivist application in a gardening microworld, I 
had about six layers of bitmaps to get the best UI responsiveness.

I started programming on a KIM-1 with 1K of memory (about a decade after 
the time the Dynabook concept was invented). Cheap computers like a 
Raspberry Pi now have about a million times more speed and memory than 
back then. That legacy still causes me to want to be frugal with CPU and 
memory -- and that is one other reason I like Cuis, that it could in 
theory run frugally on bare metal. But, at this point in my life, in my 
late 50s and with realistically only an all-too-limited number of 
reasonably effective programmer years ahead of me, the thing more 
valuable to me than CPU and memory -- given those changes -- is my time 
spent programming and how effective that time can be. And to me, I feel 
that the rebuild-on-event approach to UI development maximizes my 
productivity in that UI area compared to setting up and debugging 
observer dependencies.

Also, from another direction, generic Smalltalk code generally runs much 
slower than BitBlit code or other compiled plugin code (like presumably 
for the newer vector graphics rendering approach?). So, if (speculating) 
more drawing needed to be done at a low level with the rebuild approach 
but less Smalltalk code needs to run at a high level (because of less 
complexity with dependencies), then in theory there could be a CPU use 
reduction with a system that redraws more but manages dependencies less. 
I don't actually think that is likely here, just that it might be a 
plausible possibility. Again, without profiling it is hard to be sure 
given some uncertainty involving multiple code layers with different 
performance characteristics.

In general though, I agree that overlapping window pose a big challenge 
to the efficiency of the rebuilding approach compared to setting up 
dependencies explicitly. I outlined some possible ways for a 
rebuild-on-events approach to deal more efficiently with multiple 
windows in a previous email. But right now I am thinking it may be good 
enough tradeoff at the start if I just ignore that? Then maybe 
eventually someday we can find some way to transcend the tradeoffs and 
get the best results for both developer ergonomics and CPU efficiency? 
As an experiment, however, such results can't be guaranteed.

And surprisingly, when I looked around, it does seem like there are 
places Cuis already does a more general redraw like when there is a code 
change to a method and all browsers refresh? So that also suggests ad 
hoc solutions to refreshing other windows may be good enough?

Anyway, thanks again for your interest in this topic.

--Paul Fernhout (pdfernhout.net)
"The biggest challenge of the 21st century is the irony of technologies 
of abundance in the hands of those still thinking in terms of scarcity."

On 5/14/23 07:13, Hilaire Fernandes via Cuis-dev wrote:
> Sorry I don't have the reference at hand.  It is an accidental reading. 
> But I remember it is a way of doing in the web and from my understanding 
> to circumvent its inherent complexity in GUI. We don't have this legacy 
> complexity.
> 
> In most WEB GUI you don't have overlapping windows. With overlapping 
> windows, I fear rebuilding at each time may result in wasting a lot of 
> computing. Imagine a model represented visually with several different 
> visible forms (Morphs in fact), these morph embedded in more complex 
> Morphs. Will it required the whole redraw ?


More information about the Cuis-dev mailing list