Floating child windows (fork)

Hey everyone

I think this might be of interest to some of you

I’ve been working on floating child windows (windows that float above a designated parent window, but not above everything on the desktop, like with always-on-top windows) on a personal fork of JUCE for the last couple months.

Here’s how everything looks:

fork-demo-macos-tiny

fork-demo-windows

fork-demo-linux

This feature has been requested to death since 2006 (!!!), so I figured I’d share what I’ve been able to do here

Please let me know if you have any questions, I’m happy to help however I can

9 Likes

Nice work. This is one of the major short coming of juce for anybody that is doing applications rather than plugins. Hopefully this can get merged into the official juce at some point.

2 Likes

Thanks for the kind words

And I hope so too. It would be a pretty straightforward merge as well. My work has only touched a handful of files, and in the time since I made my fork, they’ve hardly been modified at all on develop

It’s just a matter of whether my code is up to JUCE’s standards :P

1 Like

Also I saw your post (from 2007!) about setAlwaysOnTop being unimplemented on linux

I gave it a proper implementation while I was doing my work on child windows. Though I think setAlwaysOnTop would probably see less usage in a context where proper floating child windows are available (a lot of the usage of setAlwaysOnTop that I see is just people trying to achieve child window-like behavior)

But if anyone still needs it, it’s in there

1 Like

Nice work, any chance you could produce a patch, maybe some of us could review it for you before you set it up for an official JUCE PR?

1 Like

@original-picture This is amazing, thanks for sharing
@ibisum I’ve attached a code-changes-only patch that will apply to JUCE 8.0.8 on the master branch

There were a few conflicts on develop and I wanted to keep the patch exactly as it is in @original-picture ‘s repo, so master it is

JUCE-72cd769-Floating child windows.patch (86.9 KB)

1 Like

@andyjtb - Thanks for this patch, I will have a poke at this during the week. I hope you get a chance to submit this as a PR to the main devs too, would be good to see this improvement. Will let you know further thoughts after a code review on my end ..

1 Like

Thank you!

maybe some of us could review it for you before you set it up for an official JUCE PR?

I was thinking the same thing. If the code looks good to everyone here, I’ll see about opening a PR

Edit: oops, I meant to reply to @ibisum

Wow, thank you for going and creating a patch!

I’m glad it sounds like this is something people would want to see on master

Also one more thing I remembered

If anyone wants to help with the development of these features, the best thing you can do is build and (manually) test your existing JUCE GUI applications with this fork. I need to make sure that projects built with the fork and master behave identically when the new functionality isn’t being used, and lots of manual testing is the only way to do that!

Great work!
Since I made similar modifications a decade back, it would be great if this could get implemented in Juce officialy.
As far as I recall, some issues I had that I would suggest testing were:

  • Windows and Mac handle these types of child windows differently, I would check on both platforms
  • I seem to recall some issues related to Drag & Drop, might be just the way I did it, but it’s worth a check.

Also, is it possible to “unlik” the parent and the child? You might not always want the position of the parent to change the position of the child ?

1 Like

Hey, thanks for the kind words!

Windows and Mac handle these types of child windows differently

At least in my experience, the only real observable difference is that on macOS, child windows move with their parents, but all the correct windowMovedOrResized calls still get made, so this doesn’t seem to be a issue in practice

I seem to recall some issues related to Drag & Drop

I haven’t tested drag and drop at all, so it definitely could be broken. But none of my modifications touched the drag and drop code, so I don’t think I’ve actually introduced any drag and drop related bugs

Also, is it possible to “unlik” the parent and the child? You might not always want the position of the parent to change the position of the child ?

Yep! Just use ComponentPeer::removeFloatingChildPeer
But also do be aware that parent windows changing the positions of their children is only a thing on macOS (not my decision, that’s just how child windows work on macOS). Parent transformations don’t get applied to children on windows or linux/GNOME

@t0m is there any way this can make it into the official juce? I have been maintaining a fork (not this exact solution) for years. It would be great to have this built into the official version.

2 Likes

Thanks for the great work!

Is this intended to work for standalone applications only and not for plugins?

I keep hitting this assertion:

jassert (! isAttachedToAnotherWindow());       // You tried to add a floating child to this peer when this peer is already attached to another window (using the nativeWindowToAttachTo parameter of Component::addToDesktop)

This makes sense because the main plugin window is being created by the host and eventually addToDesktop() is called with the nativeWindowToAttachTo parameter being a valid pointer to the window created by the host.

That means plugins are always attached to another window so this would not work?
Is this correct or am i overlooking something?

1 Like

hey, thanks for testing my code!

this assert is actually a bug that I forgot to fix!
a few months ago, I was testing the code in a plugin, and I ran into the exact same issue you’re having. I ended up removing this assert locally but I just forgot to push my changes lol
I just pushed the updated version. It should work, but let me know if you run into any issues

also, one other thing you should remember is that Component::getPeer() can (and often does) return null, so be sure to check for that before calling addFloatingChildPeer
this can be kind of annoying, because you might find yourself in a situation where you want to create a child window, but the component you want to use as a parent doesn’t have an associated peer (getPeer() returns null)
there isn’t a great solution for this in the current API. As a workaround, you can check at multiple points to see if getPeer() returns non-null, but this is obviously not ideal
I’m thinking of adding a new API to Component to solve this. Something like Component::addFloatingChildPeerWhenAddedToDesktop, that sort of “queues” a child peer for addition until its parent Component is added to the desktop, but I want to make sure I fully understand the problem before I start cooking up a new API to solve it

if you run into this kind of issue, let me know so I can develop a solution that addresses your use case

Is this intended to work for standalone applications only and not for plugins?

I designed it to work for both standalone applications and plugins. Wanting to create plugins that host other plugins was my original motivation for writing this fork, so your use case is definitely supported

1 Like

Alright thanks!

I’m not running into the issue where Component::getPeer() returns null. But i am running into an issue where the child window is being put behind the window of the plugin host.

I’m trying to workaround this by calling Component::toFront() whenever it loses focus but it seems that does not always work. Are your experiencing these same issues?

1 Like

thanks for attaching that video! It helps a lot
I’m at my day job right now, but I’ll take a closer look at it later this evening. If you could share the code for the demo shown in your video, that would be super helpful

it does look like this is a bug in my code though
if I had to guess, I’d say that the child window probably got destroyed and recreated after you called addFloatingChildPeer (JUCE likes to destroy and recreate peers sometimes), which broke the parent-child relationship between the editor window and the child window
this is kind of just an issue with the API I created. I’m realizing that because peers can get destroyed and recreated at any time, it makes more sense to have the bookkeeping for the parent-child relationship live in Component instead of ComponentPeer

anyway, thanks again for helping me test this. I’ll dig into this more when I’m back home

I just tested this functionality in my own plugin, and I’m not getting the issue you’re having


can you share your code so I can test it?

and if you’re interested, here’s the project shown in the video. The relevant code is in PluginEditor.cpp in the function HostAudioProcessorEditor::create_inner_plugin_editor_
I’ll apologize in advance because the code is very hard to read. The JUCE example project that I adapted this from is very dense and has barely any comments. I need to refactor it into something that’s easier to understand

Thanks, i had a quick look at your example code. The differences i can see is that your child window has a title bar while mine does not and it looks like you are re-adding the floating child peer in the parentHierarchyChanged() callback. Maybe that’s what necessary to keep it in front of the host?

The code for my project can be found here.

hey just wanna let you know that I haven’t forgotten about this!
my day job and person life have been a bit busy lately, but I’m going to get back to this soon and rework a lot of my code so that you don’t have to do a bunch of hacky workarounds

1 Like