Making a floating window that doesn't bring the application to the front?

Hey guys, have an odd request. I’m trying to make a window that floats above all other apps (setAlwaysOnTop works great for this) but that also doesn’t bring the app to the front when clicked or interacted with - from what I can tell that involves using the NSNonactivatingPanelMask flag. I found a chunk of code on the forums (from @justin) that I’ve attempted to hack to do that, but it doesn’t seem to work:

–inside a .mm file:

namespace MacHelpers
{
	void makeWindowFloatingPanel(juce::Component *aComponent)
	{
		jassert(aComponent);
		ComponentPeer *componentPeer=aComponent->getPeer();
		jassert(componentPeer);
		componentPeer->setAlwaysOnTop(true);
		NSView* const peer = (NSView*) (componentPeer->getNativeHandle());
		jassert(peer);
		NSWindow *window=[peer window];
		jassert(window);
		window.styleMask &= ~NSNonactivatingPanelMask;
	}
}

I’m thinking it’s either because the window (I’m just using a DocumentWindow) is an NSWindow, but it’s not (Also?) an NSPanel, or it’s because that flag needs to be set when the window is initially created, and setting it afterwards doesn’t do anything. I might also just be diving down the wrong hole entirely!

An update! SUCCESS!

void makeNewFloatingPanel(juce::Component *aComponent)
	{
		NSRect frame = NSMakeRect(00, 00, 400, 300);
		NSPanel* newWindow  = [[[NSPanel alloc] initWithContentRect:frame
														  styleMask:NSNonactivatingPanelMask | NSWindowStyleMaskUtilityWindow | NSWindowStyleMaskHUDWindow | NSWindowStyleMaskTitled
															backing:NSBackingStoreBuffered
															  defer:NO] autorelease];
		//[newWindow setBackgroundColor:[NSColor blueColor]];
		[newWindow makeKeyAndOrderFront:NSApp];
		aComponent->addToDesktop(0, newWindow.contentView) ;
	}

Calling that from a component (not a window) will add that component as the main content to a 400x300 floating panel.

Now to get it resizing and otherwise acting nicely…

1 Like

OK, I’m stumped. Can’t seem to get the native window to tell the component to resize whenever it’s frame is changed. I’ve spent hours trying to figure it out, but haven’t really made any progress. Anyone have any ideas?

Welp! Solved my own problem (funny how talking about it can help):

this now works:

void makeNewFloatingPanel(juce::Component *aComponent, int width, int height)
{
    
    NSRect frame = makeNSRect(aComponent->getBounds());
    
    NSWindow* newWindow  = [[[NSWindow alloc]
                            initWithContentRect:frame
                            styleMask:NSNonactivatingPanelMask |
                            NSWindowStyleMaskUtilityWindow |
                            NSWindowStyleMaskHUDWindow |
                            NSWindowStyleMaskTitled |
                            NSWindowStyleMaskClosable |
                            //NSWindowStyleMaskMiniaturizable |
                            NSWindowStyleMaskResizable
                            backing:NSBackingStoreBuffered
                            defer:NO] autorelease];
    
    newWindow.contentView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
    [newWindow setDelegate: (id<NSWindowDelegate>) newWindow];
    [newWindow setBackgroundColor:[NSColor blueColor]];
    [newWindow makeKeyAndOrderFront:NSApp];
    [newWindow.contentView setPostsFrameChangedNotifications:YES];
    [newWindow.contentView setAutoresizesSubviews:YES];
    
    aComponent->addToDesktop(juce::ComponentPeer::StyleFlags::windowIsResizable |
                             juce::ComponentPeer::StyleFlags::windowHasTitleBar |
                             juce::ComponentPeer::StyleFlags::windowHasCloseButton
                             , newWindow.contentView) ;
    
    ComponentPeer *componentPeer=aComponent->getPeer();
    NSView* const peer = (NSView*) (componentPeer->getNativeHandle());
    peer.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
}

I had to addtodesktop, then ask the peer for a reference to the view it’s newly-created, then set the resize mask for that view. There’s probably some extra chaff in there that’s unnecessary now, too.

2 Likes