Something between AlwaysOnTop and normal windows


#1

Hi

After searching the forum I see that this question has been raised before over the years, but with no definitive answer/solution.

http://www.juce.com/forum/topic/componentaddtodesktop-and-setalwaysontop

http://www.juce.com/forum/topic/child-window-behaviour-desktop-windows-possible

 

As I see it as a pretty important/basic function I would like to pick this up again.

So:

I have an application with multiple windows, the main window and some floating windows that could be editors, toolboxes, etc.

Like in this picture:

Same as most adobe apps, visual studio,etc

Now, if you simply create these windows, naturally, if you click on the main window the other windows will go behind the main window. You can set the child windows to setAlwaysOnTop() but then they stay on top of all other application windows.

The behavior you want is that the child window stays on top of the main window at all time, but that the child window and the main window can go behind other apps.

So the Z of the child would be the main apps Z+1 (sort of)

 

I have managed to achieve this by not setting the windows to AlwaysOnTop, but rather bringing them to front by overriding focusGained() and activeWindowStatusChanged() of the main window and looping the child windows. This works on windows but not on osx.

In addition on osx it creates an issue with the native menu bar; if you click on a editor window the menu gets disabled, and cant be used before you focus the main window.

 

I’m thinking that many of you have had to fix this in your apps? Like with plugin editors in hosts. It would be super annoying if an editor kept hiding behind a maximized app-window.

 

Any ideas surrounding this issue would be most welcome. If I find a solution I would be sure to post it back here.


#2

I think this is what you're looking for?

http://www.juce.com/forum/topic/tip-make-window-float-above-application-not-system


#3

Ahh! yes that looks like exactly what I need. Thank you

I can´t get it to compile though. I´m not good with the whole osx/objectiveC thing.

I created a .mm file, added a namespace added the code, but it complains that NSView and NSWindow are undefined.

I´m guessing I need to include/import something? but what..

Care to point me in the right direction?


#4

This is what I have for mine.  There's some extra stuff included for for other things I'm doing in my file.  For this I think you just need the cocoa import.  The defines might be necessary as well.

 

#define MemoryBlock DumbBlock
#define Point CarbonDummyPointName
#define Component CarbonDummyCompName

#import <Cocoa/Cocoa.h>
#import <IOKit/pwr_mgt/IOPMLib.h>
#import <ScriptingBridge/ScriptingBridge.h>
#include "iTunes.h"

#undef Point
#undef Component
#undef MemoryBlock

#5

On windows, is there a way to do this.

When I set the plugin window alwaysOnTop( true ) it works except when the plugin itself opens a dialog. The plugin's dialog is never in front. This is not a problem for a normal plugin window.

In the demo host code, setAlwaysOnTop( true ), open Kontakt player, load an instrument, make a change to the instrument like moving the volumn knob, and then close the instrument, not the player. A save, save as, etc. dialog appears behind all other windows and you can not bring it to front even by clicking on it it the task bar. At this stage the host is frozen. The only way out is to click on the x close icon on the top of the task bar preview window, and you can never close the instrument in the player because of this.

 


#6

I have managed to achieve this on windows by not setting the windows to AlwaysOnTop, but rather bringing them to front by overriding focusGained() and activeWindowStatusChanged() of the main window and looping the child windows. This works on windows but not on osx.

This has some issues with focus of texteditors etc. it is not a good solution

On osx, to make windows that stay on top but get hidden when  other apps get focus, I did this :

 

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 setHidesOnDeactivate:YES];

 

Still looking for a way to do this on Windows. I would say that it is a missing feature in Juce


#7

I would be interested on what kind API people would expect from JUCE to provide such functionality. Just adding another window level to JUCE (currently JUCE has "alwaysOnTop" and default window levels) doesn't seem like a sustainable solution to me. Propably something like grouping windows or implementing a hierachy tree for windows (for example creating child windows of a main window) is more sensible. What are people's feelings and opinions on this? Can someone comment on what sort of APIs different OSes/toolkits provide to do this? Maybe somebody already has some code we could use?


#8

On Linux/X11 you have the XSetTransientForHint which allows to put a window as "parent" of another.

I know OSX has some calls to this via Window Ids (Window::setParentWindow?).

 


#9

@fabian@ROLI: Good points I just did not wanted to keep my request simple, but I see that maybe we should think outside the box.

I don´t have a great overview of other frameworks window structure, and if we make it to fancy we will end up with something that does not feel at home on any OS.

The place where I miss this most is for "Tool windows":

These are found in many apps, everyone has seen photoshop.
Tool windows stay on top of the main window, but go behind other apps.

These could be used for the editor of hosted plugins, for tool-pallets etc.

These windows are standard behavior in both osx and windows.

In osx it is like the code I posted above. The Tool windows hide when the app looses focus.

On windows (when I have made this in other development environments) they just go behind other apps.

I'll get back to you with some more research when I get time

 

 

 


#10

I've been looking for a solution to this on OS-X for Reaper extension plugins. Now, Reaper of course isn't a Juce based application, so the solution, if one exists or will become available, should also work by parenting the Juce Component to a NSView...(Or perhaps NSWindow, though I am not sure if that is possible currently to get from Reaper. Cockos might add an API for that though...)

On Windows I was somehow able to cobble together some Juce Component flags/settings that worked as I hoped. But that doesn't work for OS-X, so I had to resort to making the plugin window "always on top", which is royally annoying as the window will then stay visible even when switching away from Reaper.

The so called "tool" windows are indeed what would be needed in Juce...

 

edit : So hmm, interesting, there might be a solution for OS-X already given in the thread. I need to try that!

edit 2 : It does seem to work! 


#11

It's not clear which Reaper extension you're talking about, but we already support their "hasCockosViewAsConfig" extension.


#12

Reaper extension plugins, which are completely different from VST/AU plugins, so Juce has no support for those.


#13

Ok, I don't know about the reaper stuff, I think that might be another topic.

I'm now talking about Tool Window / pallete style windows in Windows OS. (OSX I got sorted)

So the answer is here in the windows docs:

https://goo.gl/NOaOR8

"Owned Windows" When using Spy++ I see that the windows (from other apps) that behave the way I want, have an owner window set.

This is not the same as a child window, where one window is inside the other.

Now how can I set this is Juce ? thats the big question.

I have tried

TopLevelWindow::addToDesktop ( int  windowStyleFlags,void *  nativeWindowToAttachTo = nullptr )

the name "nativeWindowToAttachTo" hints that it has nothing to do with this, but the code in CreateWindowEx seems to pass on a handle from the parameter "parentToAddTo"

So anyone know how I can make a OwnerWindow?


#14

The solution is actually very simple, but reveals some assumptions/shortcuts in juce_win32Windowing.cpp

All you have to do is create your floating panel like this:

(ChildWindow is a TopLevelWindow)

childWindow->setUsingNativeTitleBar(true);

                childWindow->addToDesktop(childWindow->getDesktopWindowStyleFlags(), mainWindow->getWindowHandle());

This makes childwindow “Linked” to the mainWindow.

Now there is one drawback, you HAVE to use native title bars.

This is because the createWindow() function has this piece of code:

   else if (parentToAddTo != 0)

        {

            type |= WS_CHILD;

        }

It assumes that if you pass in a HWND as “parentToAddTo” you want the window to be a child window of the parent. This is not always the case as you can see in the MSDN docs in the post above.

I’m not sure what other assumptions are made, or what would be the best way to add this feature (where and how). But I will give the Juce-Team the basic code that would be needed.

I guess adding a new function complementing addToDesktop would be an idea. [edit: better solution in next post]

The following is not nice clean code to be copy/pasted, but you understand what is needed:

On OSX:

You can create the window as normal but you need to set NSWindow

 window setHidesOnDeactivate:YES

This will enable standard OSX behavoiur for these kinds of windows

On windows

You need to set the parentToAddTo to the handle of main app-window,  when calling CreateWindowEx inside createWindow(), also you must NOT set the flag WS_CHILD. (see the docs)

I'm really hoping you guys could add this in somehow, I'll try to hack my way around it for now.

I truly believe that this is a small but basic feature missing from Juce, that would enable us to make better apps.

Thank you

 


#15

Actually, you could solve this by simply adding a new StyleFlag, “windowIsFloatingPanel” (or something like that) this way you solve it with minor tweaks to existing code

No need for new functions or anything


#16

I take it the lack of this feature in Juce is the reason why Tracktion has those annoying plugin windows that pile up in the task bar and IIRC also tended to stay on top of even when Tracktion was no longer the active application? (Dunno if that has changed in Tracktion 6 though, but in Tracktion 5 it was quite miserable...)

edit : Looks like Tracktion 6 still has this issue. This is NOT a good way to deal with plugin windows...(I haven't seen any other DAW application that does it the way Tracktion does.)

http://i.imgur.com/EtcUILg.gif

If it isn't clear from the screen capture, the issues are :

1) Each plugin GUI creates a new task bar entry in the Windows 7 task bar (With the small previews of the plugin GUIs. Someone might think that is cute and/or useful, I don't. I only want to see the entry for the host main window there.)

2) To get those multiple plugin GUIs to appear to begin with, the plugin GUIs have to be set to "always on top", including on top of other applications. (You can see how the GRM plugin GUI can easily be made to be visible on top of the web browser, when the plugin GUI should just keep within Tracktion.)

I dragged in Tracktion into this only because I assume the plugin window behavior in it is because of Juce. This same behavior can also be seen in the Juce plugin host demo application.

 


#17

Don´t know about traction, but this is the exact behaviour I´m trying to avoid in my own host.

As I said, I previously tried to fixed this by doing some trix, like bringing the plug windows to front when the main app window gets focus, etc. but this is starting to cause problems and crashes in ways I woun´t bother you with.


#18

I have done some more testing on windows (the osx code is working for me).

To wrap up the previous posts:

You need to set the main app window as the "owner" of the floating window (This is a windows thing)

The floating window, can not have the WS_CHILD flag set, but must have WS_OVERLAPPED or WS_POPUP.

This works if the Juce´s NativeTitleBar is enabled. But I´m trying to make a window with the Juce title bar.

What happens then is that the floating panel gets it´s x/y coordinates relative to the parent window, even though it is not inside it.

This messes up the "drag to move" behavior and the position of the drop shadow.

I have no idea why this happens, It´s kind of difficult to figure this out when I don´t really know how the deeper parts of the Juce code work.

Any help on this would be very appreciated, heck we would even pay someone to fix this, so we can back to adding new features and fixing our own bugs :)

 

 

 


#19

The reason for the window positioning issue is this little thing in HWNDComponentPeer:

    Rectangle<int> getBounds() const override
    {
        Rectangle<int> bounds (rectangleFromRECT (getWindowRect (hwnd)));

        if (HWND parentH = GetParent (hwnd))
        {
                    RECT r = getWindowRect(parentH);
                    bounds.translate(-r.left, -r.top);            
        }

        return windowBorder.subtractedFrom (bounds);
    }

When creating the window with WS_POPUP and providing a parent/owner pointer

The new window gets both a parent and an owner window (But it is the same window)

Now, in this floating mode (Owned Window, again this is very normal in windows) the window bounds are relative to the desktop and not to the parent/owner window.

I hacked my way around this by adding a “floating-flag” in HWNDComponentPeer

    Rectangle<int> getBounds() const override

    {
        Rectangle<int> bounds (rectangleFromRECT (getWindowRect (hwnd)));
             
             if (!floatingPanel)
             {
                    if (HWND parentH = GetParent (hwnd))

                    {
                           RECT r = getWindowRect(parentH);
                           bounds.translate(-r.left, -r.top); 
                    }
             }

        return windowBorder.subtractedFrom (bounds);

    }

A better solution would be to not make the window a child window in the first place (Only "Owned") Can't figure out how to that.

And there is probably a better way to create this workaround, I'm all ears...


Some more details, if anyone are interested:
 

If you use the WS_POPUP flag with and provide an owner handle you get a window with out borders and caption bar, if you use WS_OVERLAPPING you get border and a ToolWindow style caption bar.

 

I know this is pretty Windows specific and should probably go in the windows section but I did not know that when starting this thread, sorry


#20

Dont know if anyone cares but I'm still fighting this, and update this post mainly to keep track of my work and keep the topic hot

I previously said:

It assumes that if you pass in a HWND as “parentToAddTo” you want the window to be a child window of the parent. This is not always the case as you can see in the MSDN docs in the post above.

I’m not sure what other assumptions are made, or what would be the best way to add this feature (where and how). But I will give the Juce-Team the basic code that would be needed.

Well I figured out that the code forwards messages to the parent, and in my case this would cause strange behavior,

This happens using the forwardMessageToParent function, so I'll try to work around that one as well.

Still.. There is a big missing/unfinished pieces in a basic section of the Juce code.. Or in win32 to be specific (Dont get me wrong, still love this framework)