Search code examples
qtmenu

Qt menu without shadow?


I copied the question description below from other asked but not answered question, because this is the exactly the same one I wanna ask.

I have a QMenu with a translucent background and rounded edges (border-radius). Unfortunately, Windows 7 draws a drop shadow for this menu, which does not fit to the rounded edges. Its the shadow that would be drawn for normal rectangular menus.

Is there either - a way to completely disable drawing drop shadows for QMenu or - a way to make the shadow fit to the rounded edges ?

Here is a minimalistic example where it occurs:

QPushButton b("press me");
QMenu m;
m.addAction("hello"); m.addAction("world");
m.setWindowFlags(m.windowFlags() | Qt::FramelessWindowHint);
m.setAttribute(Qt::WA_TranslucentBackground);
m.setStyleSheet("background:rgba(255,0,0,50%); border-radius:5px;");
b.setMenu(&m);
b.show();

Right now I have to turn off the menu shadow in Windows Control panel manually to get rid of that shadow. Actually what I want to achieve is a menu like Qt's pie menu or a menu like this one: http://upload.wikimedia.org/wikipedia/commons/8/85/Blender_2.36_Screenshot.jpg I tried the popup widget, but it gets the shadow artifact described above. Could anyone help this out?


Solution

  • On Windows Vista and higher, I wanted a menu with normal window shadow. So I had to do two things:

    1. Remove CS_DROPSHADOW from the menu HWND's WNDCLASS that Qt is adding deep down in the core.
    2. Add shadow using DWM APIs.

    The trick is to capture QEvent::WinIdChange to get the HWND handle to the menu window, and then to use GetClassLong / SetClassLong to remove CS_DROPSHADOW flag. I'm doing this only once (by using a static bool), as theWNDCLASS is always the same for all menus. This might lead into a problem if part of your app wants to show the menu shadows and other does not.

    I have subclassed the QMenu and I'm always using my overriden class when creating menus

    Menu * my_menu = new Menu(tr("&File"));
    mainMenu->addMenu(my_menu);
    

    Here's the whole code, enjoy:

    menu.h

    #ifndef MENU_H
    #define MENU_H
    
    #include <QMenu>
    
    class Menu : public QMenu
    {
        Q_OBJECT
    public:
        explicit Menu(QWidget *parent = 0);
        explicit Menu(const QString & title);
    
    protected:
        virtual bool event(QEvent *event);
    
    signals:
    
    public slots:
    
    };
    
    #endif // MENU_H
    

    menu.cpp

    #include "menu.h"
    
    #pragma comment( lib, "dwmapi.lib" )
    #include "dwmapi.h"
    
    Menu::Menu(QWidget *parent) :
        QMenu(parent)
    {
    
    }
    
    Menu::Menu(const QString &title) :
        QMenu(title)
    {
    
    }
    
    
    
    bool Menu::event(QEvent *event)
    {
        static bool class_amended = false;
        if (event->type() == QEvent::WinIdChange)
        {
            HWND hwnd = reinterpret_cast<HWND>(winId());
            if (class_amended == false)
            {
                class_amended = true;
                DWORD class_style = ::GetClassLong(hwnd, GCL_STYLE);
                class_style &= ~CS_DROPSHADOW;
                ::SetClassLong(hwnd, GCL_STYLE, class_style);
            }
            DWMNCRENDERINGPOLICY val = DWMNCRP_ENABLED;
            ::DwmSetWindowAttribute(hwnd, DWMWA_NCRENDERING_POLICY, &val, sizeof(DWMNCRENDERINGPOLICY));
    
            // This will turn OFF the shadow
            // MARGINS m = {0};
            // This will turn ON the shadow
            MARGINS m = {-1};
            HRESULT hr = ::DwmExtendFrameIntoClientArea(hwnd, &m);
            if( SUCCEEDED(hr) )
            {
                //do more things
            }
        }
        return QWidget::event(event);
    }