layout: add Flexes and Rigids

I often run into situations where I only need Rigids or Flexes with same weight in Flex layouts.

Most of the time I need to draw widgets that are located in a slice or a map.

Having to make the slice of FlexChilds is easy but cumbersome, what about having dedicated types in the layout packaged that do it?

Something along the lines of https://git.sr.ht/~pierrec/giox/commit/2c5ee028295c046e95c45a7f776b0fb6177759ad

Assigned to
3 months ago
a month ago
No labels applied.

~eliasnaur 3 months ago*

Not sure. Does it really save that much code and come up often enough?

What I've been thinking is that Flex/Stack.Layout taking a slice of children is often too rigid (heh). I sometimes want to specify children on the fly, like your slice or map backed data.

~pierrec 3 months ago

Yes, I guess the core issue is not being able to specify Flex children in terms of an index. Do we need it in the core library? I would tend to say yes given the number of times I have wished for it. But YMMV.

If so, what about something like this:

type FlexByIndex struct {

type FlexElement func(layout.Context, int) layout.FlexChild

func (f *FlexByIndex) Layout(gtx layout.Context, num int, children FlexElement) layout.Dimensions {

~eliasnaur 3 months ago

I'm thinking more in the direction of adding children incrementally. Flex and Stack used to work like this, but I changed it because you could easily interleave separate Flex or Stack by accident, leading to subtle layout problems.

However, this may not be a problem anymore, given the functional layout.Widget with value layout.Contexts and explicit layout.Dimensions return values.


func (f *Flex) Flex(gtx Context, weight float32, w Widget)
func (f *Flex) Rigid(gtx Context, w Widget)

and then Flex.Layout would append the given list of widgets to widgets already specified with Flex and Rigid. Or you could leave them empty:

var f Flex
f.Flex(gtx, 1, ...)
f.Rigid(gtx, ...)
return f.Layout(gtx)

Perhaps we'd even remove FlexChild altogether if the incremental API turns out good enough.


~pierrec 3 months ago

This looks interesting, I did not know that it was how it used to work. I would make the new methods variadic:

func (f *Flex) Flex(gtx Context, weight float32, w ...Widget)
func (f *Flex) Rigid(gtx Context, w ...Widget)

However, we lose the alloc free property of Flex as it now needs to buffer the children, right?

~eliasnaur 3 months ago

On Sat Mar 13, 2021 at 10:18 CET, ~pierrec wrote:

This looks interesting, I did not know that it was how it used to work. I would make the new methods variadic:

func (f *Flex) Flex(gtx 
Context, weight float32, w ...Widget)
func (f *Flex) Rigid(gtx Context,
 w ...Widget)

Sounds good.

However, we lose the alloc free property of Flex as it now needs to buffer the children, right?

For dynamic Flexes, probably yes, but that's no different from today where you allocate a []layout.FlexChild for your slice or map data backend.

We can preserve the garbage-free property of static layouts by keeping the support for the variadic ...[]FlexChild argument to Flex.Layout.


~stalker_loki a month ago

This is why I wrapped all of the widgets in chained methods. It's just WidgetType().Color("red").CornerRadius(2).Fn(gtx)

For the case of Flex it's: Flex().Rigid().Flexed(0.5,).Fn(gtx)

Then when you are embedding them into each other, you want to break the line after each dot operator if there is more than two (or even sometimes for two).

  Flexed(0.5,  <---
  ).   <---

As you can see, if you click and drag along the gutter of most editors, covering the area of those two arrows, you can easily switch it. Goland has a nice 'flip' function that works with , and most math operators, but unfortunately not the dot operator. But you can see how easily that could be done and how pleasant it would make rearranging the structure of a complex layout.

About the only downside is that practically doubles the indentation, two for each level of depth, which then forces me to cut the inner parts out into logical groups in functions, but you can go pretty deep if you aren't dividing the screen a lot horizontally, before having a mess.

I find in some, maybe a lot of cases, that I am using Flexed.Rigid blocks to counteract the widget's inbuilt expanding tendency, and one of the widgets that does this is the Label. As such, I removed that feature and use Flex or Direction to implement center/left/right (or all 8 directions and centre).

My overall feeling is that there is a lot of overlap between the several different layout widgets, that there probably is a way to collapse and simplify them. One, that reading this issue brought up was to change my methods to variadic, and then within the structure outlined above, instead of only one widget, you can put one, or many, and best of all, without me breaking all of my old work before I revise it to take advantage of this.

You could probably make a widget-and-parameter struct, and by using struct member labels, omit parameters when not needed, but also have the option of then having a list of 'cells' that you can specify things, like a weight value (1=equal) and a stretch-or-truncate option to define when a widget should either change to a smaller form (ellipsise, fade off) or disappear if it has insufficient space and isn't salient. I suppose that this one would suit especially a table layout notion, as one of the parameters could be row/colspan. On one hand you have implicit typed parentheses blocks, with the requirement of a label to omit fields, on the other, you have the option of the (final) parameter of a function being variadic.

~stalker_loki a month ago*

I just realised that I could probably roll the whole flexed/rigid chained parameters on Flex so you can just straight say 'Rigid(' and voila a flex is created in the function if it hasn't already been. I mean, it's just two versions that start from either package root or another type (I embed several things, theme, window, other state structure relevant to the interface)

By the way, there is no reason I can see why dynamically generated flexes would create garbage, since they are not reconfigured. Go just mostly doesn't let you tinker with mutability, but if a slice is never deallocated or resliced it's not going to generate garbage and even then the garbage is only the metadata.

Like, the scroller, it can be out of view and not painting but won't be deallocated and instantly comes up with its last state when the switcher brings it into the view. Think of how many options that is when you combine two words in it, RigidEnd(...layout.Widget).RigidStart(...layout.Widget).RigidW(...layout.Widget).RigidSE(...layout.Widget)... Put the options into a couple of slices and all of those variants can be generated automatically with iteration, I think you could at least collapse Flex and Direction into one unified scheme this way.

Register here or Log in to comment, or comment via email.