Limiting window size

Does cinder have functionality to set a minimum window size?
I tried this quick way (in setup()):

	getWindow()->getSignalResize().connect([&]() {
		ivec2 ws = getWindowSize();
		ivec2 clampedWs = max(ws, ivec2(500, 300));
		if (ws != clampedWs) {
			setWindowSize(clampedWs);
		}
	});

But it creates bugs (e.g. sometimes the window size gets locked to the minimum and can’t be changed anymore). And I don’t think it’s infinite recursion, because the app keeps working and doesn’t crash.

Unfortunately this doesn’t seem to be supported out of the box. If you’re on mac, you just need to call a method on the NSWindow you can retrieve from app::getWindow()->getNative(). You’ll need to compile as objective-c++ so recommend putting this in a separate file with a free function.

NSView * view = (NSView *)getWindow()->getNative();
[[view window] setMinSize:NSMakeSize(600, 300)];

On windows, it’s a little bit less straightforward, and involves hooking the window message loop in order to respond to the WM_GETMINMAXINFO message. This code hasn’t been tested so please take it with a grain of salt, but will hopefully give you a push in the right direction. You’ll need to call InitHooking and DestroyHooking from your app’s setup() and cleanup() methods respectively.

Turns out you’re not allowed to modify the window limits from within a hook, so you have to install a custom wndproc to catch the event. I’ve tested this on windows 10 and it works fine. Just call InitHooking ( getWindow() ) from setup() and you’re good to go.

static WNDPROC kPrevWndProc;
static HWND    kHwnd;

LRESULT APIENTRY HandleWndProc ( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
	if ( hwnd == kHwnd )
	{
		switch ( uMsg )
		{
			case WM_GETMINMAXINFO:
			{
				LPMINMAXINFO limits = (LPMINMAXINFO)lParam;
				limits->ptMinTrackSize.x = 1200;
				limits->ptMinTrackSize.y = 800;

				break;
			}
		}
	}
	
	return CallWindowProc ( kPrevWndProc, hwnd, uMsg, wParam, lParam );
}

void InitHooking ( const app::WindowRef& window )
{
	kHwnd = static_cast<HWND> ( window->getNative() );
	kPrevWndProc = (WNDPROC)( SetWindowLongPtr ( kHwnd, GWLP_WNDPROC, (LONG_PTR)(HandleWndProc) ) );
}
3 Likes

I’ve put together a little gist here that will handle this on windows and osx, so fingers crossed you’re not on linux.

Make sure to compile WindowLimits.cxx as objective-c++ on mac or you’ll get a lot of weird errors. In your app’s setup, you can then do:

void YourApp::setup ( )
{
    static WindowLimits kLimits { app::getWindow(), WindowLimits::Format().minSize ( ivec2 ( 320, 240 ) ).maxSize ( ivec2 ( 640, 480 ) ) };
}

both minSize and maxSize default to WindowLimits::kUnbounded. On mac, the WindowLimits instance can be disposed immediately as it just sends a one-time call to the underlying NSWindow. On windows, however, it needs to persist as the hooks to the underlying HWND are destroyed when it goes out of scope, hence the static in my example, as a quick and dirty way of providing a persistent instance.

3 Likes

I think this would be a nice feature addition to ci::app::Window, which I suppose could be no-ops on the platforms that don’t support something like this.

cheers,
Rich