Minimizing a Window and GUI management

Hi -
I’m trying to figure something out with the SC GUI, but I’m not sure if it’s possible or not - I’m not even sure what it’s called exactly.

Let’s say I have a button that generates new windows with different backgrounds.

When I “minimize” the window, a thumbnail shows up on my docking toolbar (on an OSX mac).

I’m wondering if there’s a way to have minimized windows “collect” in another SC-designated window, instead of on the desktop.

Thank you.

Excellent question.

So, do you want… a script that you keep in startup.scd, for a general workflow when using GUI’s?

Or, do you require an (expandable) solution for what is currently a more targeted desire.

?

My hope is to have an expandable solution.
For example, if I also had a button that generated windows with gradient backgrounds - it would be nice to have them minimize to a second panel, while the solid backgrounds minimized to the first panel.

I forgot how to do this myself, so I made a quick mock-up of how to re-parent Views to do a “pop-out” / “minimize” behavior. I’m not totally sure what you’re going for, but this might be a starting point.

(
// Map of a view to it's container (a separate window or an "icon")
~iconViews = ();
~expandedViews = ();

~rootView = View(bounds:300@300).layout_(VLayout(
	ToolBar(
		~add = MenuAction("add"),
		~collect = MenuAction("collect")
	),
	View().layout_(
		~iconLayout = VLayout().spacing_(8);
	),
	nil
));

// Function to take a view and pop it out into a separate window
~expandView = {
	|view|
	var windowView;
	
	windowView = View(bounds:200@200).front;
	windowView.layout_(VLayout(
		view
	));
	view.visible = true;
	
	// track new view
	~expandedViews[view] = windowView;

	// remove old view
	~iconViews[view].remove; 
	~iconViews[view] = nil;
};

// Function to take a view, put it inside of an "icon"
// with the same background color, and place it in the main
// window. 
~collectView = {
	|view|
	var iconView;
	
	iconView = View().minSize_(64@64);
	iconView.background = view.background;
	iconView.layout_(HLayout(
		view
	).spacing_(0).margins_(0));

	// When we click an icon, expand its view.
	iconView.mouseUpAction = { ~expandView.(view) };

	~iconLayout.add(iconView);

	// HIDE our view when it's in icon mode but keep it's state. 
	// Commenting this out also works, the view will continue to be 
	// visible when in the main window.
	view.visible = false;
	
	// track new icon
	~iconViews[view] = iconView;

	// remove old expanded window
	~expandedViews[view].remove; 
	~expandedViews[view] = nil;
};

// Make some random knobs in a view.
~add.action = {
	var newView;

	newView = View().background_(Color.rand);
	newView.layout_(VLayout(
		Knob(),
		Knob(),
		Knob(),
	));
	~expandView.(newView);
};

// Find all expanded views and call ~collectView on them.
~collect.action = {
	~expandedViews.keys.do {
		|view|
		view.postln;
		~collectView.(view)
	}
};

~rootView.front;
)
2 Likes

Here’s a version of this I like better, where views minimize to a toolbar button (with a colored icon), and the toolbar buttons that have a drop-down menu that actually show the original UI.

Also, a cute trick is to replace the colored icon on the iconView with a screenshot of the actual view you’re minimizing:

icon_(Image.fromWindow(view).setSize(64, 64, \keepAspectRatio))
Code
(
// Map of a view to it's container (a separate window or an "icon")
~iconViews = ();
~expandedViews = ();

~rootView = View(bounds:300@300).layout_(VLayout(
	ToolBar(
		~add = MenuAction("add"),
		~collect = MenuAction("collect"),
	),
	View().layout_(
		~iconLayout = VLayout().spacing_(8);
	),
	nil
));

// Function to take a view and pop it out into a separate window
~expandView = {
	|view|
	var windowView;
	
	windowView = View(bounds:200@200).front;
	windowView.layout_(VLayout(
		view
	));
	view.visible = true;
	
	// track new view
	~expandedViews[view] = windowView;

	// remove old view
	~iconViews[view].remove; 
	~iconViews[view] = nil;
};

// Function to take a view, put it inside of an "icon"
// with the same background color, and place it in the main
// window. 
~collectView = {
	|view|
	var iconView;
	
	iconView = ToolBar().minSize_(64@64);
	iconView.addAction(
		MenuAction()
		.action_({ ~expandView.(view) })
		.icon_(Image.color(64, 64, view.background))
		.menu_(Menu(view.asMenuAction))
	);
	
	// When we click an icon, expand its view.
	iconView.mouseUpAction = { ~expandView.(view) };

	~iconLayout.add(iconView);

	// HIDE our view when it's in icon mode but keep it's state. 
	// Commenting this out also works, the view will continue to be 
	// visible when in the main window.
	view.visible = false;
	
	// track new icon
	~iconViews[view] = iconView;

	// remove old expanded window
	~expandedViews[view].remove; 
	~expandedViews[view] = nil;
};

// Make some random knobs in a view.
~add.action = {
	var newView;

	newView = View().background_(Color.rand);
	newView.layout_(VLayout(
		Knob(),
		Knob(),
		Knob(),
	));
	~expandView.(newView);
};

// Find all expanded views and call ~collectView on them.
~collect.action = {
	~expandedViews.keys.do {
		|view|
		view.postln;
		~collectView.(view)
	}
};

~rootView.front;
)
2 Likes

Oh, this is very cool! Thank you!

Not to push my luck too much, but is there a straightforward way to collect the icons with GridLayout instead of VLayout?
I’m new to Layout Management in SuperCollider and having a little trouble understanding the documentation.

Definitely - I originally wrote the example with GridLayout, but I switched it because it requires a couple of extra tricky things that made it more convoluted as example code. :slight_smile:

Other than actually switching the VLayout for GridLayout, the only thing you’ll have to change: the add method of GridLayout requires that you specify a row and column, so instead of ~iconLayout.add(iconView), you’ll need ~iconLayout.add(iconView, row, col); - and you’ll need to keep track of these row and column numbers yourself. I think you can probably just use ~iconViews.size to calculate what the next empty row+column combination is.

1 Like