Custom window border with no gaps?

I think the previous post from LightAndSoundLTD is correct; you’ll need to set the window attribute for the Desktop Window Manager. You don’t need to rewrite any part of JUCE, but you will need to add some Windows-specific code to your app to set the DWMWA_WINDOW_CORNER_PREFERENCE window attribute. Docs here:

https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwm_window_corner_preference

Setting that attribute gave me a window with rounded corners and a drop shadow.

If you don’t want the thin dark border around the window, set the DWMWA_BORDER_COLOR attribute:

I think this cleaner; I don’t have to make the window transparent and I only had to override a single look-and-feel method. You can also have a native title bar:

I still had to call setDropShadowEnabled(false) in my main window constructor to get this to work properly. As far as I can tell, setDropShadowEnabled does not enable or disable the OS shadow; instead, it sets a flag that tells the peer to create a DropShadower that paints the shadow (see LookAndFeel::createDropShadowerForComponent).

Code is below; check out parentHierachyChanged().

Matt

    class MainWindow : public juce::DocumentWindow, public juce::LookAndFeel_V4
    {
    public:
        MainWindow(juce::String name)
            : DocumentWindow(name, juce::Colours::yellow, DocumentWindow::allButtons)
        {
            setUsingNativeTitleBar(false);
            setDropShadowEnabled(false);

            setContentNonOwned(&mainComponent, false);

            setResizable(true, false);
            centreWithSize(500, 500);

            setVisible(true);

            getCurrentColourScheme().setUIColour(juce::LookAndFeel_V4::ColourScheme::widgetBackground, juce::Colours::yellow);
            Component::setColour(juce::DocumentWindow::ColourIds::textColourId, juce::Colours::black);

            setLookAndFeel(this);
        }

        ~MainWindow()
        {
            setLookAndFeel(nullptr);
        }

#if JUCE_WINDOWS
        static uint32_t colourToColorRef(juce::Colour colour)
        {
            uint32_t colorRef = colour.getBlue();
            colorRef |= (colorRef << 8) | colour.getGreen();
            return (colorRef << 8) | colour.getRed();
        }

        void parentHierarchyChanged() override
        {
            if (auto peer = getPeer(); peer != nullptr && juce::SystemStats::getOperatingSystemType() >= juce::SystemStats::Windows11)
            {
                auto windowHandle = (HWND)peer->getNativeHandle();
                auto value = DWMWCP_ROUND;
                auto hr = DwmSetWindowAttribute(windowHandle, DWMWA_WINDOW_CORNER_PREFERENCE, &value, sizeof(value));
                jassert(SUCCEEDED(hr));

                COLORREF transparentBorderColor = colourToColorRef(juce::Colours::yellow);
                hr = DwmSetWindowAttribute(windowHandle, DWMWA_BORDER_COLOR, &transparentBorderColor, sizeof(transparentBorderColor));
            }
        }
#endif

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

        void drawResizableFrame(juce::Graphics& g, int w, int h, const juce::BorderSize<int>&) override
        {
        }

    private:
        class MainComponent : public juce::Component
        {
        public:
            MainComponent() = default;
            ~MainComponent() override = default;

            void paint(juce::Graphics& g) override
            {
                g.fillAll(juce::Colours::yellow);
            }

        private:
            JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MainComponent)
        } mainComponent;

        JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MainWindow)
    };