Resizing GUI with fixed ratios / square

It’s a given that SC’s GUI is clunky and old fashioned. I’ve been working with the GUI and layouts and have found a challenge. I have a button in an HLayout that I want to be resizable with fixed proportions. More generally, I want to have a grid-type UI that is rigid but resizable.

(
w=Window().bounds_(Rect(500,500, 400, 80)).layout_(HLayout(
	[StaticText().string_("bugs"), stretch:1],
	[nil, stretch:5],
	[Button().states_([["remove the bugs", Color.black, Color.grey]]).maxSize_(200,200).minSize_(120,120), stretch:1]
)).front
)

Setting a min and maxsize doesn’t help with maintaining the size ratio. I want the button to be the largest square possible that fits within the button’s own constraints and the flowlayout’s as well. Has anyone tried to do this before and/or have success?

Another context where I want to preserve aspect ratios but still have scalability is with SoundFileView or 2DSlider.

Thanks,
Hamish

Well, to snipe my own question, I dug up more and I think the answer here for what I’m looking for is a solid no. This is a qt problem, not sc, and the solution is to get into the weeds and implement custom event handlers and layout classes that actually handle the scaling.

Turns out drawing a square is not easy

What about this approach:

(
b = Button().states_([["remove the bugs", Color.black, Color.grey]]);
w = View(nil, 128@128).minHeight_(128).layout_( VLayout(HLayout(StaticText().string_("bugs"), b), nil)).front.onResize_{ 
	var d = max(b.bounds.height, b.bounds.width);
	b.resizeTo(d, d);
}
)

It is kinda ugly when you resize but at least you get the the desired behavior, or at least some of it (I think). If your intention is to gradually cover the Static Text with the button I am not really sure how to do this. I am using View instead of Window as .onResize only works on views. Also, I find Views to be more flexible than Windows even though I must admit that the differences between View and Window confuses me at times.

A Window has a container View (the parent of any child views it holds) which you can get with the methods w.view and w.asView. So your code could work with a Window like this:

(
b = Button().states_([["remove the bugs", Color.black, Color.grey]]);
w = Window().bounds_(Rect(500,500, 400, 200)).front;
w.view.layout_( VLayout(HLayout(StaticText().string_("bugs"), b), nil))
.minHeight_(128).onResize_{ 
	var d = max(b.bounds.height, b.bounds.width);
	b.resizeTo(d, d);
}
)

Best,
Paul

This is essentially the right approach. It requires the LineLayout to have already resized the button, so the full solution would involve creating a new layout management class. Maybe I’ll give that a shot. It’s weird to me that qt doesn’t support fixed-aspect scaling.

Thanks!
Hamish

It seems that the best way to get this behavior in SC gui is a lot simpler. If you make a basic view and set a background image with the tile mode 11, it will maintain the aspect ratio of the image. This is the easiest and least awful looking way to work with tiles.

QT does support fixed-aspect-ratio scaling via the heightForWidth property - it’s just not currently exposed to SuperCollider. It’s pretty simple to add this, I think it just wasn’t done originally and it hasn’t come up much.

If anyone wants a tiny C++ project, adding this would require adding something like a fixedAspectRatio property to QcDefaultWidget, stored as a std::optional<int>, and then overriding heightForWidth to return this value / hasHeightForWidth to return true/false if the value as been set. Might take some reading through the QT docs to make sure there aren’t any subtleties to this, and I’m not sure we have an example of a numeric value that can be possibly-nil in the codebase, but other that this would be a very short change.