Windows Fullscreen mouse Hit-zones misaligned

Windows issue when setting fullScreen on juce::ResizableWindow that has a non native title bar.
Windows bounds seem not to account for the task bar (hidden or not) causing child components hit zones to be misaligned.
Manually setting the bounds of the component using setBounds(Desktop::getInstance().getDisplays.GetDisplaForPoint()->userArea) works correctly and has correct hit zones for child components.

Basically means “fullscreen” is not an option on windows.

Please can you supply the following info:

  • On which version of JUCE do you see the problem? Is it present on the newest develop branch?
  • Which version of Windows are you testing with?
  • Is the taskbar at the bottom of the screen, or has it been moved to a different side?
  • How are you setting the window to fullscreen? Are you calling setFullscreen(), or setKioskModeComponent(), or something else?

On the develop branch, I tried adding the following to the end of the constructor of the MainAppWindow in the DemoRunner project:

setUsingNativeTitlebar (false);
setFullScreen (true);

On opening, the window fills the screen, and the mouse hover/click zones appear to be correct. They continue to work after clicking the ‘maximise’ button twice, to leave and re-enter fullscreen mode. I tested on Windows 11, with the taskbar in both auto-hide and pinned modes.

The issue does sound similar to another one that was already fixed on the develop branch, so please make sure you’re up-to-date and test again. If you’re still seeing problems after updating, please provide the info from my previous post. It would also be helpful if you could check whether you see the same issue in any of the example projects, such as the DemoRunner.

Using setFullScreen(!isFullScreen());
Juce 8.0.6

Sorry, I wish I had time right now to dg deeper. Just thought it was worth reporting.

OK, thanks for reporting. I’m pretty sure this issue has been fixed since 8.0.6, so I’d recommend updating if possible. Please let us know if the problem persists after updating.

upgraded to 8.0.8 and problem went away.

1 Like

Bumping this: I am on 8.0.10, and when using a non native title bar and set fullscreen I don’t have problems with the hitzones however the window will fullscreen below the windows taskbar (which is at the bottom of the screen for me) instead of the usable client area. I am on windows 11. There are no issues when using a native windows titlebar.

Thanks for the report. Please could you provide any relevant details about the failing test-case, especially:

  • Is the issue present on the develop branch as well as 8.0.10?
  • Are you able to provide a minimal test-case that demonstrates the problem?
  • How are you creating the window? Are you using a regular DocumentWindow, or are you modifying any of the desktop flags, setting the opacity/alpha, etc.?
  • Is the taskbar set to auto-hide? I’m assuming not.
  • Is your Windows version up-to-date?
  • Do you have multiple displays connected?
  • What scaling factor is in use by your display(s)?

As a quick test, I tried modifying the blank GUI App template like so:

class MainComponent final : public juce::Component
{
public:
    MainComponent()
    {
        setSize (600, 400);
        addAndMakeVisible (button);
    }

    void paint (juce::Graphics& g)
    {
        g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));

        g.setFont (juce::FontOptions (16.0f));
        g.setColour (juce::Colours::white);
        g.drawText ("Hello World!", getLocalBounds(), juce::Justification::centred, true);

        g.setColour (juce::Colours::cyan);
        g.drawRoundedRectangle (getLocalBounds().reduced (5).toFloat(), 10.0f, 2.0f);
    }

    void resized()
    {
        button.setBounds (50, 50, 150, 30);
    }

    juce::TextButton button { "toggle fullscreen" };

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent)
};

class GuiAppApplication final : public juce::JUCEApplication
{
public:
    GuiAppApplication() = default;

    const juce::String getApplicationName() override       { return JUCE_APPLICATION_NAME_STRING; }
    const juce::String getApplicationVersion() override    { return JUCE_APPLICATION_VERSION_STRING; }
    bool moreThanOneInstanceAllowed() override             { return true; }

    void initialise (const juce::String&) override
    {
        mainWindow.reset (new MainWindow (getApplicationName()));
    }

    void shutdown() override
    {
        mainWindow = nullptr;
    }

    void systemRequestedQuit() override
    {
        quit();
    }

    void anotherInstanceStarted (const juce::String& commandLine) override
    {
        juce::ignoreUnused (commandLine);
    }

    class MainWindow final : public juce::DocumentWindow
    {
    public:
        explicit MainWindow (juce::String name)
            : DocumentWindow (name,
                              juce::Desktop::getInstance().getDefaultLookAndFeel()
                                                          .findColour (backgroundColourId),
                              allButtons)
        {
            setUsingNativeTitleBar (false);
            auto content = juce::rawToUniquePtr (new MainComponent);
            content->button.onClick = [this]
            {
                setFullScreen (! isFullScreen());
            };
            setContentOwned (content.release(), true);

           #if JUCE_IOS || JUCE_ANDROID
            setFullScreen (true);
           #else
            setResizable (true, true);
            centreWithSize (getWidth(), getHeight());
           #endif

            setVisible (true);
        }

        void closeButtonPressed() override
        {
            getInstance()->systemRequestedQuit();
        }

    private:
        JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainWindow)
    };

private:
    std::unique_ptr<MainWindow> mainWindow;
};

START_JUCE_APPLICATION (GuiAppApplication)

We call DocumentWindow::setFullScreen() to toggle the fullscreen mode when the text button is clicked. I see the same result whether I click the “toggle fullscreen” button, or press the maximise button in the titlebar, or double-click the titlebar itself. I also tried calling setFullScreen on the peer instead of directly on the DocumentWindow, and saw the same result in that case too:

If the window were covered by the titlebar, I’d expect the bottom edge of the cyan rectangle to be hidden, but it is visible. Given that I’m unable to reproduce the issue, I’ll need more information in order to continue investigating. Thanks!

Hey there thanks a ton for looking into this!

The difference in my case is that I am passing 0 for the buttons in the constructor of the document window, so that I can use a custom component as a title bar - I’m basically trying to create a title bar a la Chrome, with a tab strip, and min/max/close buttons that I added and manage myself.

When you pass 0, the window will then pass under the taskbar when you maximize it. I’ve managed to implement all other functionality such as resizing, dragging the window, etc. which is needed since this is a custom component, but there doesn’t seem to be a way to keep it from going under the task bar when maximised as far as I can tell.

The goal is really just a custom, non-native titelbar that I can manage myself beyond just updating the paint method in the look and feel. Hope this helps and again thanks for checking.

Thanks, I’m seeing the same behaviour now. I’ll try to work out what’s going wrong.

This seems to be by-design in Windows. If the native window style flags just include WS_POPUP then the system treats a maximize request as a request to fill the entire screen. The main workarounds seem to involve querying the actual usable screen area and resizing the window manually, which seems error-prone.

That workaround has at least one other pitfall: because the system doesn’t know that the window can be maximised, behaviours like double-clicking the caption bar to maximise won’t work immediately and will have to be reimplemented, and then may not behave in quite the same way as the regular system interactions.

I’d suggest trying out an approach like the following. The important parts are that we still pass 0 (no window controls) when constructing the DocumentWindow, but we return the full set of window controls from getDesktopWindowStyleFlags(). This way, the DocumentWindow doesn’t create any JUCE buttons on the titlebar, but the peer is still aware that it can be closed and maximised.

If you use this approach, you should also make sure to override DocumentWindow::findControlAtPoint() to return the appropriate values when the specified point falls within one of your custom window-control buttons or tabs.

When I test out the following code, I see a JUCE window with no titlebar buttons, but all the other behaviours I’d expect are still in place:

  • Clicking the text button switches between maximised/non-maximised mode without overlapping with the taskbar
  • Double-clicking the titlebar has the same effect
  • Aero Snap still works when dragging the caption bar to the side of the screen
  • Double-clicking the top/bottom edges of the window resizes it vertically to fill the height of the screen without overlapping the taskbar
class MainWindow final : public juce::DocumentWindow
{
public:
    explicit MainWindow (juce::String name)
        : DocumentWindow (name,
                          juce::Desktop::getInstance().getDefaultLookAndFeel()
                                                      .findColour (backgroundColourId),
                          0)
    {
        setUsingNativeTitleBar (false);
        auto content = juce::rawToUniquePtr (new MainComponent);
        content->button.onClick = [this]
        {
            setFullScreen (! isFullScreen());
        };
        setContentOwned (content.release(), true);

        setResizable (true, true);
        centreWithSize (getWidth(), getHeight());

        setVisible (true);
    }

    void closeButtonPressed() override
    {
        getInstance()->systemRequestedQuit();
    }

    int getDesktopWindowStyleFlags() const override
    {
        return DocumentWindow::getDesktopWindowStyleFlags()
             | juce::ComponentPeer::windowHasMaximiseButton
             | juce::ComponentPeer::windowHasMinimiseButton
             | juce::ComponentPeer::windowHasCloseButton;
    }

private:
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainWindow)
};

Oh this seems promising! I’ll give this a shot when I’m home and update this message afterwards. Cheers

OK some progress - this is working pretty well, it’s now considering the client area correctly when I maximize which is great, but I don’t get the aero snap functionality for some reason. I’m not sure if this has anything to do with my custom component or not, I’ll need to keep digging, but regardless this is already much more usable than before. Cheers!

EDIT: You need to drag from the titlebar, so if the title bar height is 0, you wont’ get snap functionality. In this image I’m at the top of my screen, if I grab it from the titlebar (I set it to height 10 just to test) then I get snap, if I grab it from my custom component (black) then I won’t get snap. I’ll fool around and see if there’s any tricks around this..

This should be controlled by the findControlAtPoint() function I mentioned. This function should return caption when the argument falls within an area of the window that should be draggable. Note that the caption area is only for moving the window with the mouse cursor, so your tab buttons probably need to continue to return client, and your window control buttons need to return minimise, maximise, or close as appropriate.

Ah I see I missed that comment. Thanks I should have everything I need now, really appreciate you taking the time!

1 Like

Small heads-up that I discovered a minor issue in JUCE’s windowing code while putting together my replies on this topic. I noticed that maximising, minimising, and then restoring a window with a non-native titlebar would leave the window at the wrong size. There’s a fix on the way - I’ll update this thread once it’s public.

2 Likes

If you mean smaller and vertically offset by the height of the titlebar…
YES!! That has been driving me nuts and never get around to investigate it.