current go.uik design

429 views
Skip to first unread message

John Asmuth

unread,
Apr 17, 2012, 1:26:10 PM4/17/12
to go-...@googlegroups.com
This is a reply I made in the golang-nuts thread. I felt it might be good to preserve it here.
 
Don't worry - I am difficult to offend.
What I've created so far *is* focusing on the user experience. It's addressing the very real problem of a lot of standard callback-based GUIs - when the callback blocks. Or when the .draw() call blocks. These things can make a UI appear sluggish and cause a lot of irritation for the user. Of the eclipse users here, who hasn't had an issue with the auto-complete spending 20 seconds trying to figure out what things you might want to type while *you* know *exactly* what you want to type? That's what happens when you let a blocking function bring down your whole UI.
What I've done so far with go.uik is trying to solve that problem. If a component blocks on an event, or on a draw call, it only messes that component up. Not its parent, not its siblings. It does this by having all the inter-component communication work via channels. As a result, the code looks extremely unfamiliar, especially if you're used to things that look like the java swing API. But take a bit and get past the unfamiliarity - unfamiliar is not the same as bad.
I'll try to create an ascii diagram of what I'm talking about. Bear with 
             _____________
-----E1----->|           |
-----E2----->|           |
-----E3----->|   Block   |
-----Draw--->|           |
<-Compositor-|___________|
Block is a basic unit of screen space. It is, essentially, a goroutine that selects on its input channels and sends images on the compositor to be drawn. The event types are things like button presses, key typing, etc. They arrive from the parent one at a time, in order. Draw events come in and specify a portion of the block that needs to be redrawn. The block should respond to requests on the Draw channel, but it doesn't have to. It can also send images back up to be composited whenever it likes, unsolicited.
It should be noted that the Block has a dynamically grown queue that can accept arbitrarily many events, so sending an event to the block doesn't ever have to wait for the block to receive that event.
Also, in the near future, the Draw channel will be "stacked". That's a term I'm just now coining to mean that sending to the channel will never block, and receiving from the channel will get a single Draw request representing the union of all unreceived draw requests.
 _____________
 |           |---Events to Block 1--->
 |           |---Events to Block 2--->
 |Foundation |---Events to Block 3--->
 |           |---Draw---------------->
 |___________|<----------Composite----
Foundation is a type of Block (type Foundation struct { Block; /*...*/ }) that can do two main things.
1) It takes the events it gets on its input channels (remember, it has everything that a Block has) and forwards them to whichever of its children it thinks are appropriate. For mouse events, whichever child contains the cursor. For keyboard stuff, whichever child currently has keyboard focus. It also forwards draw requests to its children, if the child overlaps the area that needs to be redrawn.
2) It composites. It has a channel coming in that provides images that it draws over its internal buffer. This internal buffer is then sent to the foundation's parent's compositor. There is a special type of Foundation that will draw the image onto a window.

Having a system that is channel based where there is no need to wait for the different components to do things has the potential to make the UI seem much more responsive, even with poor programming for some widgets. 

Alex

unread,
May 13, 2012, 12:19:19 PM5/13/12
to go-...@googlegroups.com
I ran across a blog post a couple months back that I think could prove useful for uik's design. The link is at the end of my post. It concerns managing complexity in graphical programs, and the ideas in it came about while trying to figure out how best to program GUIs in a purely functional manner. It's written with examples in Haskell using the functional reactive programming (FRP) paradigm, but I still it's very applicable to traditional toolkits. A short summary is:

GUI elements have bidirectional data flow by default (unidirectional elements are the exception rather than the rule)
To avoid complicated feedback loops, follow these three principles:
(1) Only the program may manipulate the output display, never the user.
(2) User input is presented in the form of events.
(3) GUI elements generate events only in response to user input, never in response to program output.

I think these are quite reasonable rules, and as far as I can tell, uik doesn't stray too far from them. Existing toolkits don't follow these rules, and the maintainability of programs using those toolkits suffers as a result.

The post: http://apfelmus.nfshost.com/blog/2012/03/29-frp-three-principles-bidirectional-gui.html

tones111

unread,
Jun 26, 2012, 11:05:01 PM6/26/12
to go-...@googlegroups.com
Thanks for getting the ball rolling on a UI library centered around the interesting concepts go provides.  While I'm still trying to get up to speed on go.uik I have a couple questions on the design.  Perhaps some of my confusion is due to the new terminology and trying to figure out how that maps to more traditional names.  

I think the most straightforward way to approach object hierarchy would be to have types that implement the composite design pattern (https://en.wikipedia.org/wiki/Composite_pattern).  I think you've got this with Foundation mapping to "composite" and Block to "leaf", right?  Any chance those names might change to something more descriptive (like Container and widget, respectively)? What's the difference between a Block and a Widget?  Isn't a widget conceptually just a concrete implementation of a Block?

I tried to make a simple example using the flow layout but couldn't ever get it to display properly.  Would it be possible to add basic examples for each layout because it appears the interfaces to use them differ?  Currently both examples use a GridEngine which seems overly complex for the type of simple applications I'd like to write to get a feel for things.  The flow layout doesn't work as a layouter and I wasn't able to figure out the right magic to get a few buttons added and displayed.

The widget tutorial does a fair job of describing the how things work internally for the widget, but it skims over the design of how a user of the widget receives events.  Any chance you might be able to elaborate on this portion of the design?  One concept I like from Qt is how the library allows connecting signals and slots together (here the output and input channels between blocks).  While I _really_ don't like the lack of compile time checking in Qt I think this is an area where a go implementation could shine.  Since each widget is running in its own go routine you  wouldn't need to spawn a separate go routine to recieve an event just to then manipulate some other widget.  I think that could clean up a lot of the current event handling boilerplate.

John Asmuth

unread,
Jun 27, 2012, 7:17:53 PM6/27/12
to go-...@googlegroups.com


On Tuesday, June 26, 2012 11:05:01 PM UTC-4, tones111 wrote:
Thanks for getting the ball rolling on a UI library centered around the interesting concepts go provides.  While I'm still trying to get up to speed on go.uik I have a couple questions on the design.  Perhaps some of my confusion is due to the new terminology and trying to figure out how that maps to more traditional names.  

I think the most straightforward way to approach object hierarchy would be to have types that implement the composite design pattern (https://en.wikipedia.org/wiki/Composite_pattern).  I think you've got this with Foundation mapping to "composite" and Block to "leaf", right?  Any chance those names might change to something more descriptive (like Container and widget, respectively)? What's the difference between a Block and a Widget?  Isn't a widget conceptually just a concrete implementation of a Block?

Foundation and Block are analogous to what are often called Container and Component. I am purposefully ignoring a lot of existing design and terminology because I am trying to do something different.

With go.uik, different Blocks are separated much more strongly than different components will be in other UI kits. And a Foundation doesn't really contain Blocks, but supports them. I felt like the metaphor invoked by Foundations and Blocks matched more closely to how I thought of the design than Container and Component/Widget. But, in the end, it's just a metaphor - you can think of it however you like.
 
I tried to make a simple example using the flow layout but couldn't ever get it to display properly.  Would it be possible to add basic examples for each layout because it appears the interfaces to use them differ?  Currently both examples use a GridEngine which seems overly complex for the type of simple applications I'd like to write to get a feel for things.  The flow layout doesn't work as a layouter and I wasn't able to figure out the right magic to get a few buttons added and displayed.

Rather than Flow (which I really need to delete), try layouts.HBox() and layouts.VBox(). They're functions that set up grids in a simple and common way.

But...yes. Things are extremely rough around the edges right now. And through the middle too, to be honest. Lots of work needs to be done with robustness, completeness, and documentation.
 

The widget tutorial does a fair job of describing the how things work internally for the widget, but it skims over the design of how a user of the widget receives events.  Any chance you might be able to elaborate on this portion of the design?

At the moment you can make a subscription that monitors all user events. You need to send a uik.Subscription to your block's .Subscribe channel. I have started to move this kind user-facing of interface from channel-based to function-based methods. That way it actually shows up in the documentaiton...
 
 One concept I like from Qt is how the library allows connecting signals and slots together (here the output and input channels between blocks).  While I _really_ don't like the lack of compile time checking in Qt I think this is an area where a go implementation could shine.  Since each widget is running in its own go routine you  wouldn't need to spawn a separate go routine to recieve an event just to then manipulate some other widget.  I think that could clean up a lot of the current event handling boilerplate.

Some helper function that created a certain kind of subsciption, and send another message to another block when that subscription fired, would probably be pretty useful :) potential future work.

On a side note, at the moment I'm trying to focus on writing my dissertation. Once that's done and out of the way I hope to devote more time to go.uik.
Reply all
Reply to author
Forward
0 new messages