VLayout: minHeight is ignored and Views overlap

Regardez la following code:

(
var window, carousel;
window = Window(bounds:Rect(100,100,450,400)).front;
ScrollView(window, Rect(50,50,150,300)).hasHorizontalScroller_(false).canvas_(View().layout_(carousel = VLayout()));
5.do({
	var tile = View().minHeight_(150).background_(Color.rand);
	carousel.add(tile);
});
StaticText(window, Rect(250,50,150,50)).string_("For reference, the box below is 150 x 150:");
View(window, Rect(250,100, 150,150)).background_(Color.rand);
)

Each View added to the VLayout inside the ScrollView is told to have a minimum height of 150. However, all but the last have a height of about 50, or possibly they all have the correct height but they’re being rendered partially atop one another.

I know there’s something I’m misunderstanding here, but I can’t for the life of me work out what it is.

Two solutions:

a) Add a small wait time after adding each view:

(
var window, carousel;
window = Window(bounds:Rect(100,100,450,400)).front;
ScrollView(window, Rect(50,50,150,300)).hasHorizontalScroller_(false).canvas_(View().layout_(carousel = VLayout()));
{
	5.do({
		var tile = View().minHeight_(150).background_(Color.rand);
		carousel.add(tile);
		0.05.wait;
	});
}.fork(AppClock);
StaticText(window, Rect(250,50,150,50)).string_("For reference, the box below is 150 x 150:");
View(window, Rect(250,100, 150,150)).background_(Color.rand);
)

b) Define all the views before adding them to the layout. No wait time is needed.

(
var window, carousel = 5.collect{ View().minHeight_(150).background_(Color.rand) };
window = Window(bounds:Rect(100,100,450,400)).front;
ScrollView(window, Rect(50,50,150,300)).hasHorizontalScroller_(false).canvas_(View().layout_(VLayout(*carousel)));
StaticText(window, Rect(250,50,150,50)).string_("For reference, the box below is 150 x 150:");
View(window, Rect(250,100, 150,150)).background_(Color.rand);
)

Hope this helps.

Thank you for your speedy reply. I’m quite surprised by it though. Is this a bug in SC?

I don’t think it is a bug but rather a consequence of not having an equivalent of s.sync for GUI elements but I have to admit that I don’t really understand this fully myself. I have found ways of working around this (like the examples given), but every time I end up having to do a manual wait with some arbitrary small wait time it feels awkward to me, so in general I try to come up with solutions which do not require waiting between operations (like example b)). Maybe somebody with more insight than me can explain this better.

FWIW it always felt more idiomatic to me to pass all the views to the layout at the moment of creation (if their specifications are known), rather than adding dynamically. Or perhaps, a loose intuition that adding dynamically is inviting trouble, and not necessary.

I was going to say that maybe the help should demonstrate the use of * argument syntax to pass in an array of views, but then I see that it does already demonstrate this (the first VLayout example follows pretty much exactly Thor’s model).

I did do a ScrollView + layout rendering a dynamically sized list, but the ā€œdynamicā€ part was meant to be done after a wait time (one by one as needed, not in bulk).

Anyway I agree with Thor. Make the views first, then use them in the layout.

hjh

1 Like

Now we are at it, here is another trick I use a lot when rearranging the sequence of views.

(
~views = 5.collect{ View().minHeight_(150).background_(Color.rand) };
~v = Window(bounds:Rect(100,100,450,400)).front;
~sv = ScrollView(~v, Rect(50,50,150,300)).hasHorizontalScroller_(false).canvas_(View().layout_(VLayout(*~views)));
5.do{|i| StaticText(~views[i]).string_(i) };
)

Now let’s say you want to rearrange the order of the views. Instead of destroying the views and creating new ones, you can ā€˜park’ the views on a non-showing layout, then add them back in the order you need. This is not a big deal for something as simple as this, but for more complex views with lot’s of GUI elements, listeners etc. this trick is really handy.

(
var newSeq = [1, 3, 0, 2, 4];
var parked = VLayout();
~views.do{|view| parked.add(view) };
newSeq.do{|n| ~sv.canvas.layout.add(~views[n]) }
)
1 Like

Okay, thanks folks. I had to rewrite my code a bit but I got there.

Thanks Thor for the extra tip!