Resizable DocumentWindow AND hide the Maximize Button?

I’ve been fighting with creating a resizable DocumentWindow that does not allow the maximize button to be used. I haven’t found a solution yet, and i’ve tried stepping through all of the DocumentWindow/ResizableWindow/TopLevelWindow code to find where the ComponentPeer::StyleFlags::windowHasMaximiseButton is being set to true when calling the constructor for DocumentWindow like this:

juce::DocumentWindow(contentToDisplay.getName(),
                     Colours::white,
                     0)

This is super easy to verify that it doesn’t work as expected if you create a new GUI project, and set up the MainWindow constructor like this:

class MainWindow    : public DocumentWindow
{
public:
    MainWindow (String name)  : DocumentWindow (name,
                                                Colours::lightgrey,
                                                0) //DocumentWindow::allButtons to '0'
    {
        setUsingNativeTitleBar (true);
        setContentOwned (new MainContentComponent(), true);
        setResizable(true, false); //add this line here
        centreWithSize (getWidth(), getHeight());
        setVisible (true);
    }

For whatever reason (I haven’t found it yet), the call to setResizable(true,...) seems to override the ComponentPeer flag for showing/hiding the MaximizeButton. Also, doing this:

        void maximiseButtonPressed() override
        {
            /*
             since I can't seem to make this button be hidden, I'll just override the behavior so it doesn't do anything.
             */
        }

has no effect whatsoever.

Is there a proper way to make this happen?

a few months ago I was able to get somewhat of a similar behavior with a completely hidden title bar that allowed being resizable via this:

class SeparateWindowModule : public juce::Component, public AsyncUpdater, //etc
{
    SeparateWindowModule()
    {
        //snip...
        addToDesktop(juce::ComponentPeer::windowIsResizable /*| ComponentPeer::windowHasDropShadow */ );
        getPeer()->setConstrainer(&constrainer); //gotta set the constrainer AFTER its on the desktop.  there's no peer until it's on the desktop
        triggerAsyncUpdate();
    }
    void handleAsyncUpdate() override 
    {
        toFront(true); //must be called after the window is visible
    } 
}

However, this approach is just a regular component and doesn’t have a title bar.
Any ideas?!?!

1 Like

ok, digging super deep here. found this in juce_mac_NSViewComponentPeer.mm when the default document window is created:

           #if defined (MAC_OS_X_VERSION_10_7) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7)
            if ((windowStyleFlags & (windowHasMaximiseButton | windowHasTitleBar)) == (windowHasMaximiseButton | windowHasTitleBar))
                [window setCollectionBehavior: NSWindowCollectionBehaviorFullScreenPrimary];

            if ([window respondsToSelector: @selector (setRestorable:)])
                [window setRestorable: NO];
           #endif

that’s the only thing I can find so far that deals with whether or not a window has the Maximize button and a title bar.

https://developer.apple.com/design/human-interface-guidelines/macos/app-architecture/fullscreen-mode/ explains that you can enable/disable the ability in your application, but I haven’t found a place to do that in JUCE other than the DocumentWindow’s constructor, and that only works if you use JUCE’s title bar, not the Native title bar.

edit:
I can’t find this anywhere in the JUCE codebase:
https://developer.apple.com/documentation/appkit/nsview/1483780-enterfullscreenmode?language=objc
Perhaps there is a way to add some juce functionality to allow us to disable full-screen mode for OS X apps? and possible Windows apps, if windows APIs will let you disable full screen mode?

1 Like

I’d really like to know this too. I never realized this was not already an option and since reading this post I’m trying to find a way, because I don’t want some of my projects to be turned full screen… Does really nobody have any insight on how to achieve this?

1 Like

is this a thing that OS X doesn’t allow if you make your window resizable or something? does Windows also not allow this behavior? @ed95 @jules

even a “hmm yeah, that is weird that it does that on OS X” would be fine, that we’re not doing something stupid or forgetting a line of code that’ll make it do what we expect it to do… :slightly_frowning_face:

@daniel would you mind giving the test to replicate the weird behavior in the original post a shot? it’ll take all of 2 minutes. I (and @benediktadams) would love to know the official JUCE position on this unexpected behavior… It seems you gotta have an indie badge or pro badge on your avatar to get answers around here…

Why do you want to disable the maximise button if you’ve made your window explicitly resizable?

@dave96 because on Mac the maximise button will turn the window into fullscreen (not like making the window cover the whole screen, but it will actually remove the titlebar and the buttons and make it fullscreen). I don’t want that, I want the app to be windowed only

Hmm, it’s a duel button. If you hold the “option” key it changes to “maximise” rather than fullscreen.

It sounds like what you actually want is to disable the OS functionality to enable the window to go in to full-screen. If you can turn that off, the native window “full-screen” button will probably only ever work as the “maximise” button.

I’d also ask why you want to disable this though? Uses on macOS typically expect this behaviour to be enabled…

There was a thread on this a few months back when I was having the same problem, and I see now that I never closed the loop with code that worked for me.

In a file ‘osxUtil.mm’ (with the obvious header file next to it), I have:

#include "osxUtil.h"
#import <AppKit/AppKit.h>


#include "../JuceLibraryCode/JuceHeader.h"

void HideFullscreenButton(void* view)
{

	NSView* nsView = (NSView*)view;
	NSWindow* nsWindow = [nsView window];
	NSButton *button = [nsWindow standardWindowButton:NSWindowZoomButton];
	[button setHidden:YES];
	button.alphaValue = 0.0;
	[button setEnabled:NO];
	button.image = nil;
	button.alternateImage = nil;   

}

…and then at the end of my MainWindow ctor this:

#if JUCE_MAC
   ComponentPeer* peer = this->getPeer();
   HideFullscreenButton(peer->getNativeHandle());
#endif

Note that I banged my head against this for a while–placing this code earlier in the ctor had no effect. I’m not a deep enough macOS developer to know why that is. The code that’s posted here is based on code posted in the forum by @Rebbur, who I never thanked for the tip. Thanks!

5 Likes

@bgporter this actually works! Awesome, thank you!

1 Like

In my case: the client was insistent that the application have a fixed size.

Ah yes, if it requires a fixed size this is totally acceptable. However the behaviour mentioned here is to make it resizable but disable the maximise button. This would be odd behaviour to me, going against OS guidelines. Fixed size apps are a bit different.

For my needs, I can split my GUI into a bunch of separate smaller windows, and none of them should be allowed to enter kiosk mode. though you should be able to maximize their size.

Thanks @bgporter for the hail-mary endzone pass with that sample code.

1 Like

Would there be any reason this code might not work anymore? I’m running into a bad access error when executing this line:

I’m pretty new to Obj-C and only somewhat versed in C++ so struggling to figure out the issue on my own. Is it anything to do with nsView window property being readonly? Or perhaps a Catalina issue? That’s all I got… any help is appreciated!

Well I’m still getting this issue but I realise now it doesn’t matter - I was trying to get rid of the fullscreen resize button which didn’t seem to go away no matter the flags. Turns out I’d accidentally left in the setResizable code.

Carry on everyone, nothing to see here…

Do you have the Obj-C code in a .mm file or in a .cpp file? Sounds to me like it would be the latter.

The trick to mixing Obj-C and C++ is having a .h/.mm file pair. The .h has to be C++, the .mm can contain both, meaning you’ll also have to include any Obj-C header in the .mm file.

Turns out I’d accidentally left in the setResizable code.

Turning off resizing is the proper way to get rid of the maximise button. An app that is freely resizable should also be able to handle fullscreen. This thread and the code solution above discuss how to remove the maximise button but retain resize behaviour, which is in most cases the wrong approach, even though there certainly are fringe cases like the one’s discussed here

Very impressive formula thank you for solving this solution.

To hide the Maximize Button:

mainWindow->setTitleBarButtonsRequired(DocumentWindow::TitleBarButtons::closeButton +
                                       DocumentWindow::TitleBarButtons::minimiseButton, false);
mainWindow->setResizable (false, false); // hack to window activation