Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. How to round the corners of a frameless QWidget, when also applying acrylic effects?
Forum Updated to NodeBB v4.3 + New Features

How to round the corners of a frameless QWidget, when also applying acrylic effects?

Scheduled Pinned Locked Moved Unsolved General and Desktop
questionsqwidgetstylecustomplugin
8 Posts 5 Posters 433 Views 3 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • G Offline
    G Offline
    grainyblob
    wrote 22 days ago last edited by
    #1

    Hello everyone! I like to make a dock in Qt for Windows. I have the general idea of how to do it, and I would first like to focus on making the window look pretty. Now I would like to provide the user with an option to change the corner radius of the dock, and also allow for Acrylic or Mica (introduced in W11.) Basically, I want to achieve custom border radii on a frameless window with acrylic.

    Now, I attempted some stuff:

    1. Set stylesheet with border radius property.
    // inside the constructor
    setStyleSheet("border-radius: 32px;");
    
    1. Add a custom mask
    Dock::Dock(QWidget *parent):QWidget(parent)
    {
        // after other methods are called.
        roundZeCorners();
    }
    
    void Dock::roundZeCorners(int radius) {
        QPainterPath path;
        path.addRoundedRect(rect(), radius, radius);
        setMask(QRegion(path.toFillPolygon().toPolygon()));
    }
    
    void Dock::paintEvent(QPaintEvent *event) {
        QPainter painter(this);
        painter.setRenderHint(QPainter::Antialiasing);
        
        QPainterPath path;
        path.addRoundedRect(rect(), border_radius, border_radius);
        
        painter.setClipPath(path);
        painter.fillPath(path, Qt::transparent);
    }
    

    These are the only two solutions I came across to make custom window as described, but neither work. The stylesheet was obvious, because I read that qwidget not support styling border radius, and besides, it is top level window.

    Here is how I apply my window effects:

    #ifdef Q_OS_WIN
    void Dock::applyWindowEffect(uint tint_color) 
    {
        const HINSTANCE hModule = LoadLibraryA("user32.dll");
        if (hModule) {
            struct ACCENTPOLICY
            {
                int nAccentState;
                int nFlags;
                DWORD nColor;
                int nAnimationId;
            };
            struct WINCOMPATTRDATA
            {
                int nAttribute;
                PVOID pData;
                ULONG ulDataSize;
            };
    
            typedef BOOL(WINAPI* pSetWindowCompositionAttribute)(HWND, WINCOMPATTRDATA*);
            const auto SetWindowCompositionAttribute = (pSetWindowCompositionAttribute)GetProcAddress(hModule, "SetWindowCompositionAttribute");
    
            if (SetWindowCompositionAttribute)
            {
                ACCENTPOLICY policy = { 4, 0, tint_color, 0 }; // 4 = ACCENT_ENABLE_ACRYLICBLURBEHIND
                WINCOMPATTRDATA data = { 19, &policy, sizeof(ACCENTPOLICY) }; // 19 = WCA_ACCENT_POLICY
                SetWindowCompositionAttribute((HWND)winId(), &data);
            }
            FreeLibrary(hModule);
        }
    }
    #endif
    

    How would I add the rounded corners? Sorry if this is a stupid post.

    A 1 Reply Last reply 21 days ago
    0
    • G grainyblob
      22 days ago

      Hello everyone! I like to make a dock in Qt for Windows. I have the general idea of how to do it, and I would first like to focus on making the window look pretty. Now I would like to provide the user with an option to change the corner radius of the dock, and also allow for Acrylic or Mica (introduced in W11.) Basically, I want to achieve custom border radii on a frameless window with acrylic.

      Now, I attempted some stuff:

      1. Set stylesheet with border radius property.
      // inside the constructor
      setStyleSheet("border-radius: 32px;");
      
      1. Add a custom mask
      Dock::Dock(QWidget *parent):QWidget(parent)
      {
          // after other methods are called.
          roundZeCorners();
      }
      
      void Dock::roundZeCorners(int radius) {
          QPainterPath path;
          path.addRoundedRect(rect(), radius, radius);
          setMask(QRegion(path.toFillPolygon().toPolygon()));
      }
      
      void Dock::paintEvent(QPaintEvent *event) {
          QPainter painter(this);
          painter.setRenderHint(QPainter::Antialiasing);
          
          QPainterPath path;
          path.addRoundedRect(rect(), border_radius, border_radius);
          
          painter.setClipPath(path);
          painter.fillPath(path, Qt::transparent);
      }
      

      These are the only two solutions I came across to make custom window as described, but neither work. The stylesheet was obvious, because I read that qwidget not support styling border radius, and besides, it is top level window.

      Here is how I apply my window effects:

      #ifdef Q_OS_WIN
      void Dock::applyWindowEffect(uint tint_color) 
      {
          const HINSTANCE hModule = LoadLibraryA("user32.dll");
          if (hModule) {
              struct ACCENTPOLICY
              {
                  int nAccentState;
                  int nFlags;
                  DWORD nColor;
                  int nAnimationId;
              };
              struct WINCOMPATTRDATA
              {
                  int nAttribute;
                  PVOID pData;
                  ULONG ulDataSize;
              };
      
              typedef BOOL(WINAPI* pSetWindowCompositionAttribute)(HWND, WINCOMPATTRDATA*);
              const auto SetWindowCompositionAttribute = (pSetWindowCompositionAttribute)GetProcAddress(hModule, "SetWindowCompositionAttribute");
      
              if (SetWindowCompositionAttribute)
              {
                  ACCENTPOLICY policy = { 4, 0, tint_color, 0 }; // 4 = ACCENT_ENABLE_ACRYLICBLURBEHIND
                  WINCOMPATTRDATA data = { 19, &policy, sizeof(ACCENTPOLICY) }; // 19 = WCA_ACCENT_POLICY
                  SetWindowCompositionAttribute((HWND)winId(), &data);
              }
              FreeLibrary(hModule);
          }
      }
      #endif
      

      How would I add the rounded corners? Sorry if this is a stupid post.

      A Offline
      A Offline
      Axel Spoerl
      Moderators
      wrote 21 days ago last edited by
      #2

      @grainyblob
      Why does addRoundedRect not work? What's the desired and actual outcome?
      Have you tried to draw it directly with QPainter::drawRoundedRect()?
      Maybe also set an explicit brush, just for testing.

      Software Engineer
      The Qt Company, Oslo

      G 1 Reply Last reply 20 days ago
      0
      • V Offline
        V Offline
        Vitalii777
        wrote 21 days ago last edited by
        #3

        Please try to call 'setMask' to round borders in 'resizeEvent' and 'changeEvent -> QEvent::WindowStateChange event' too.

        G 1 Reply Last reply 20 days ago
        0
        • A Axel Spoerl
          21 days ago

          @grainyblob
          Why does addRoundedRect not work? What's the desired and actual outcome?
          Have you tried to draw it directly with QPainter::drawRoundedRect()?
          Maybe also set an explicit brush, just for testing.

          G Offline
          G Offline
          grainyblob
          wrote 20 days ago last edited by grainyblob
          #4

          @Axel-Spoerl
          The desired outcome was to make a acrylic window with a custom border radius. (See attached image). Once I add a roundedRect to the path I set a mask from the path. Now in a normal QWidget, it will round the corners, but my guess is that because of the DWM code I'm using, it's overriding the composition and displaying a rect.


          9850f51e-4adb-47c6-a1de-6d2dadc1d57e-{63D78F5E-4D8E-4E42-91E7-AC709EB7B477}.png
          see above for desired result ↑

          Once again, sorry if my explaination of my issue is bad, I really struggle to explain things most of the time.

          Edit: I forgot to ask, what do you mean by "explicit brush"?

          jsulmJ 1 Reply Last reply 20 days ago
          0
          • G grainyblob
            20 days ago

            @Axel-Spoerl
            The desired outcome was to make a acrylic window with a custom border radius. (See attached image). Once I add a roundedRect to the path I set a mask from the path. Now in a normal QWidget, it will round the corners, but my guess is that because of the DWM code I'm using, it's overriding the composition and displaying a rect.


            9850f51e-4adb-47c6-a1de-6d2dadc1d57e-{63D78F5E-4D8E-4E42-91E7-AC709EB7B477}.png
            see above for desired result ↑

            Once again, sorry if my explaination of my issue is bad, I really struggle to explain things most of the time.

            Edit: I forgot to ask, what do you mean by "explicit brush"?

            jsulmJ Offline
            jsulmJ Offline
            jsulm
            Lifetime Qt Champion
            wrote 20 days ago last edited by
            #5

            @grainyblob said in How to round the corners of a frameless QWidget, when also applying acrylic effects?:

            I forgot to ask, what do you mean by "explicit brush"?

            Your own QBrush instance which you pass to the painter. https://doc.qt.io/qt-6/qbrush.html

            https://forum.qt.io/topic/113070/qt-code-of-conduct

            G 1 Reply Last reply 20 days ago
            0
            • V Vitalii777
              21 days ago

              Please try to call 'setMask' to round borders in 'resizeEvent' and 'changeEvent -> QEvent::WindowStateChange event' too.

              G Offline
              G Offline
              grainyblob
              wrote 20 days ago last edited by
              #6

              @Vitalii777
              have tried to call setMask() in all events, none of them caused any change.

              1 Reply Last reply
              0
              • jsulmJ jsulm
                20 days ago

                @grainyblob said in How to round the corners of a frameless QWidget, when also applying acrylic effects?:

                I forgot to ask, what do you mean by "explicit brush"?

                Your own QBrush instance which you pass to the painter. https://doc.qt.io/qt-6/qbrush.html

                G Offline
                G Offline
                grainyblob
                wrote 20 days ago last edited by
                #7

                @jsulm

                Yes in that case, it works. The corners of the window are rounded as expected. It is only with my applyWindowEffects() method which fails to round corners in this method.

                Alternatively, since it might be a DWM level issue, I'm considering trying to replicate what I do with masking but with native DWM API.

                1 Reply Last reply
                0
                • U Offline
                  U Offline
                  UmarAli
                  wrote 4 days ago last edited by
                  #8

                  You can use my logic of making rounded corners QWidget:
                  #include "RoundedBox.h"
                  #include <QPainter>
                  #include <QPainterPath>
                  #include <QBitmap>
                  #include <QFontMetrics>

                  RoundedBox::RoundedBox(const QString &txt, QWidget *parent)
                  : QWidget(nullptr), isDarkMode(false), text(txt), useAsToolTip(false)
                  {
                  setWindowFlags(Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint);
                  setAttribute(Qt::WA_TranslucentBackground);
                  setAsToolTip(false);
                  }

                  void RoundedBox::enableDarkMode(bool value)
                  {
                  isDarkMode = value;
                  }

                  void RoundedBox::setAsToolTip(bool value) {
                  useAsToolTip = value;
                  if (useAsToolTip) {
                  setWindowFlag(Qt::ToolTip);
                  updateSizeForText();
                  } else {
                  setWindowFlag(Qt::Popup);
                  }
                  update();
                  }

                  void RoundedBox::updateSizeForText() {
                  resize(sizeHint());
                  }

                  QSize RoundedBox::sizeHint() const {
                  QFont font;
                  font.setPointSize(9);
                  font.setFamily("Segoe UI");

                  QFontMetrics fm(font);
                  int MAX_W = 400;
                  QSize s = fm.boundingRect(0, 0, MAX_W, 0, Qt::TextWordWrap, text).size();
                  return QSize(s.width() + 24, s.height() + 12);
                  

                  }

                  void RoundedBox::paintEvent(QPaintEvent *event)
                  {
                  Q_UNUSED(event);

                  // Rounded mask
                  QBitmap bitmap(width(), height());
                  bitmap.fill(Qt::color0);
                  QPainter maskPainter(&bitmap);
                  maskPainter.setRenderHints(QPainter::Antialiasing);
                  QPainterPath maskPath;
                  maskPath.addRoundedRect(rect(), 6, 6);
                  maskPainter.fillPath(maskPath, Qt::color1);
                  setMask(bitmap);
                  
                  // Colors
                  QColor BG = isDarkMode ? QColor("#2D2D2D") : QColor("#FFFFFF");
                  QColor BR = isDarkMode ? QColor("#4D4D4D") : QColor("#BDBDBD");
                  
                  QPainter painter(this);
                  painter.setRenderHints(QPainter::Antialiasing);
                  painter.setBrush(BG);
                  QPen pen(BR);
                  pen.setWidth(1);
                  painter.setPen(pen);
                  
                  QPainterPath path;
                  path.addRoundedRect(rect().adjusted(1.5, 1.5, -1.5, -1.5), 6, 6);
                  painter.drawPath(path);
                  
                  // Text
                  if (useAsToolTip) {
                      QFont font;
                      font.setPointSize(9);
                      font.setFamily("Segoe UI");
                      painter.setFont(font);
                      painter.setPen(isDarkMode ? QColor("#F0F0F0") : QColor("#000000"));
                      QRect text_area(12, 0, width() - 24, height());
                      painter.drawText(text_area, Qt::AlignCenter | Qt::TextWordWrap, text);
                  }
                  

                  }

                  1 Reply Last reply
                  0

                  • Login

                  • Login or register to search.
                  • First post
                    Last post
                  0
                  • Categories
                  • Recent
                  • Tags
                  • Popular
                  • Users
                  • Groups
                  • Search
                  • Get Qt Extensions
                  • Unsolved