Implementing a Frameless Window with Custom Title Bar, Rounded Corners, Resizing, and Shadows in Qt C++

Implementing a Frameless Window with Custom Title Bar, Rounded Corners, Resizing, and Shadows in Qt C++When developing interfaces using Qt, frameless window designs are often adopted to enhance visual effects.

Implementing a frameless window is actually quite simple, accomplished with a single line of code.

setWindowFlag(Qt::FramelessWindowHint);

Since the default system title bar is removed, the window loses its native moving and resizing capabilities, which need to be implemented manually through code.

This article aims to use the Qt framework to implement a frameless window with the following core functionalities:

  • Remove the system’s default window borders and title bar;
  • Support moving the entire window by dragging with the mouse;
  • Reserve the interface structure for standard window operations such as minimize, maximize, and close (button functionalities will be implemented in subsequent sections).

This solution is suitable for Qt developers who wish to customize the appearance of their windows and enhance the aesthetic quality of their interfaces, especially beneficial for beginners to understand the basic implementation principles of frameless windows.

Core Technical Points

Technology

Description

setWindowFlags(Qt::FramelessWindowHint)

Remove the system’s default borders and title bar

setWindowFlags(Qt::WindowSystemMenuHint)

Retain the system right-click menu (such as move, resize, etc.) to enhance user experience

Override mousePressEvent

Record the relative position when the mouse is pressed, preparing for dragging

Override mouseMoveEvent

Update the window position in real-time based on mouse movement to achieve dragging effect

Part1Implementing a Frameless Window

1.1 Header File Definition

<span><span>BasicFramelessWindow.h</span></span>

#pragma once
#include <QtWidgets/QWidget>
#include <QMouseEvent>
class BasicFramelessWindow : public QWidget {
    Q_OBJECT
public:
    explicit BasicFramelessWindow(QWidget *parent = nullptr);
    ~BasicFramelessWindow();
protected:
    void mousePressEvent(QMouseEvent* event) override;
    void mouseMoveEvent(QMouseEvent* event) override;
private:
    QPoint dragPosition; // Record the offset of the mouse press relative to the top-left corner of the window
};

Description:

  • Inherits from <span><span>QWidget</span></span>, constructing the basic window;
  • Defines two protected event handling functions to capture mouse actions;
  • Uses <span><span>dragPosition</span></span> to store the offset between the drag starting point and the window coordinates.

1.2 Source File Implementation

<span><span>BasicFramelessWindow.cpp</span></span>

#include "BasicFramelessWindow.h"
BasicFramelessWindow::BasicFramelessWindow(QWidget *parent)
    : QWidget(parent) {
    // Set the window to be frameless and retain the system menu (right-click can bring up system operations)
    setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint);
    // Disable transparent background (ensure background displays normally)
    setAttribute(Qt::WA_TranslucentBackground, false);
    // Set fixed window size
    setFixedSize(600, 400);
    // Set style: white background + gray border (for observation)
    setStyleSheet("background-color: white; border: 1px solid gray;");
}
BasicFramelessWindow::~BasicFramelessWindow() {
    // Destructor (currently no special handling required)
}
// Mouse press event: record the starting position for dragging
void BasicFramelessWindow::mousePressEvent(QMouseEvent *event) {
    if (event->button() == Qt::LeftButton) {
        // Calculate the offset between the mouse click position and the top-left corner of the window
        dragPosition = event->globalPosition().toPoint() - frameGeometry().topLeft();
        event->accept(); // Accept the event to prevent it from being handled by other controls
    }
}
// Mouse move event: execute window dragging
void BasicFramelessWindow::mouseMoveEvent(QMouseEvent *event) {
    if (event->buttons() & Qt::LeftButton) {
        // Move the window based on the offset
        move(event->globalPosition().toPoint() - dragPosition);
        event->accept();
    }
}

Key Logic Analysis:

  • <span><span>event->globalPosition().toPoint(): Get the mouse position in the screen coordinate system;</span></span>
  • <span><span>frameGeometry().topLeft(): Get the top-left corner coordinates of the window on the screen;</span></span>
  • The difference between the two is the “drag anchor point”, ensuring the mouse always “grabs” the same position of the window while moving;
  • <span><span>move(...) directly changes the window position, achieving smooth dragging.</span></span>

After running the program, a 600×400 white frameless window will be displayed.

Although the interface is blank, it already possesses the following capabilities:

  • The window can be moved to any position by clicking and dragging with the left mouse button

  • The window has no system title bar or borders

  • The system right-click menu is retained (can be accessed by right-clicking the taskbar icon to bring up options like “Move” and “Resize”)

Part2Implementing a Custom Title Bar

Implementing a Custom Title Bar: Supporting Dragging, Double-Click Maximization, and Dynamic Button Switching

2.1 Functional Goals

Function

Description

Custom Title Bar

Replace the system’s default title bar, supporting free layout and style customization

Three Button Controls

Minimize, maximize/restore, close, communicating with the main window through signals

Dynamic Icon Switching

When the window is maximized, the “maximize button” automatically changes to the “restore icon”

Dragging Movement

Mouse press on the title bar allows dragging the window (when not maximized)

Double-Click State Switching

Double-clicking the title bar toggles between maximized ↔ normal state

2.2 Title Bar Component Implementation (<span><span>TitleBar</span></span>)

We will encapsulate the title bar as an independent component <span><span>TitleBar</span></span>, inheriting from <span><span>QWidget</span></span> for reuse in different windows.

1. Header File:<span><span>TitleBar.h</span></span>

#pragma once
#include <QWidget>
#include <QPushButton>
#include <QLabel>
#include <QHBoxLayout>
#include <QMouseEvent>
class TitleBar : public QWidget {
    Q_OBJECT
public:
    explicit TitleBar(QWidget* parent = nullptr);
    // Set whether it is currently maximized, for icon switching
    void setMaximized(bool maximized);
signals:
    void signalMinimize();           // Send minimize signal
    void signalMaximizeRestore();    // Send maximize/restore toggle signal
    void signalClose();              // Send close signal
protected:
    void mousePressEvent(QMouseEvent* event) override;
    void mouseMoveEvent(QMouseEvent* event) override;
    void mouseDoubleClickEvent(QMouseEvent* event) override;
private:
    QPushButton* btnMin;           // Minimize button
    QPushButton* btnMaxRestore;    // Maximize/restore button
    QPushButton* btnClose;         // Close button
    QLabel* titleLabel;            // Title label
    QPoint dragPosition;           // Drag offset
    bool isMaximized;              // Whether currently maximized
};

Design Description:

  • Here we define a TitleBar class, inheriting from QWidget.

  • In public, there is a constructor and a setMaximized method to set the icon state;

  • In the signals section, there are three signals corresponding to minimize, maximize/restore, and close actions;

  • In protected, we override mouse press, move, and double-click events;

  • In private, there are the three buttons, title label, drag position record dragPosition, and a flag indicating whether it is maximized isMaximized.

2. Source File:<span><span>TitleBar.cpp</span></span>

#include "TitleBar.h"
#include <QMouseEvent>
#include <QStyle>
#include <QApplication>
TitleBar::TitleBar(QWidget* parent)
    : QWidget(parent) {
    isMaximized = false;
    setFixedHeight(35);  // Title bar height
    setAttribute(Qt::WA_StyledBackground, true); // Enable stylesheet rendering
    // Set overall style
    setStyleSheet(R"(
        TitleBar {
            background-color: rgb(223, 235, 250);
        }
        QPushButton {
            border: none;
            background-color: transparent;
            min-width: 45px;
            min-height: 35px;
        }
        QPushButton:hover {
            background-color: rgb(211, 226, 237);
        }
        QPushButton:pressed {
            background-color: rgba(255, 255, 255, 50);
        }
    )");
    // Title text
    titleLabel = new QLabel("My App");
    titleLabel->setStyleSheet("border:none; background:transparent; font-weight:bold; padding-left:10px;");
    // Create buttons and set icons (ensure resources are added to qrc)
    btnMin = new QPushButton();
    btnMin->setIcon(QIcon(":/new/prefix1/resources/min.png"));
    btnMaxRestore = new QPushButton();
    btnMaxRestore->setIcon(QIcon(":/new/prefix1/resources/max.png"));
    btnClose = new QPushButton();
    btnClose->setIcon(QIcon(":/new/prefix1/resources/close.png"));
    // Layout management
    auto mainLayout = new QHBoxLayout(this);
    mainLayout->addWidget(titleLabel);
    mainLayout->addStretch();
    mainLayout->addWidget(btnMin);
    mainLayout->addWidget(btnMaxRestore);
    mainLayout->addWidget(btnClose);
    mainLayout->setContentsMargins(0, 0, 0, 0);
    mainLayout->setSpacing(0);
    // Signal connections
    connect(btnMin, &QPushButton::clicked, this, &TitleBar::signalMinimize);
    connect(btnMaxRestore, &QPushButton::clicked, this, &TitleBar::signalMaximizeRestore);
    connect(btnClose, &QPushButton::clicked, this, &TitleBar::signalClose);
}

Icon State Switching

void TitleBar::setMaximized(bool maximized) {
    isMaximized = maximized;
    btnMaxRestore->setIcon(
        isMaximized ?
            QIcon(":/new/prefix1/resources/restore.png") :  // Restore icon
            QIcon(":/new/prefix1/resources/max.png")       // Maximize icon
    );
}

Mouse Event Handling

void TitleBar::mousePressEvent(QMouseEvent* event) {
    if (event->button() == Qt::LeftButton) {
        dragPosition = event->globalPosition().toPoint() - parentWidget()->frameGeometry().topLeft();
    }
}
void TitleBar::mouseMoveEvent(QMouseEvent* event) {
    if ((event->buttons() & Qt::LeftButton) && !isMaximized) {
        parentWidget()->move(event->globalPosition().toPoint() - dragPosition);
    }
}

⚠️ Note: Dragging is only allowed innon-maximized state to avoid misoperation.

void TitleBar::mouseDoubleClickEvent(QMouseEvent* event) {
    Q_UNUSED(event);
    emit signalMaximizeRestore(); // Double-click triggers maximize/restore
}

Double-clicking the title bar toggles the window state, conforming to user habits.

2.3 Integrating into the Main Window (<span><span>BasicFramelessWindow</span></span>)

Next, we will embed <span><span>TitleBar</span></span> into the main window and connect signals and slots to achieve complete control logic.

1. Update Header File:<span><span>BasicFramelessWindow.h</span></span>

#pragma once
#include <QWidget>
#include <QMouseEvent>
#include "TitleBar.h"
class BasicFramelessWindow : public QWidget {
    Q_OBJECT
public:
    explicit BasicFramelessWindow(QWidget* parent = nullptr);
    ~BasicFramelessWindow();
protected:
    void mousePressEvent(QMouseEvent* event) override;
    void mouseMoveEvent(QMouseEvent* event) override;
private slots:
    void onMinimize();
    void onMaxRestore();
    void onClose();
private:
    TitleBar* titleBar;
    bool isMaximized;
    QPoint dragPosition;
};

2. Implement Main Window Logic:<span><span>BasicFramelessWindow.cpp</span></span>

#include "BasicFramelessWindow.h"
#include <QVBoxLayout>
BasicFramelessWindow::BasicFramelessWindow(QWidget* parent)
    : QWidget(parent), isMaximized(false) {
    setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint);
    setAttribute(Qt::WA_TranslucentBackground, false);
    setFixedSize(600, 400);
    setStyleSheet("background-color: white; border: 1px solid gray;");
    // Create title bar
    titleBar = new TitleBar(this);
    // Connect signals and slots
    connect(titleBar, &TitleBar::signalMinimize,
        this, &BasicFramelessWindow::onMinimize);
    connect(titleBar, &TitleBar::signalMaximizeRestore, this, &BasicFramelessWindow::onMaxRestore);
    connect(titleBar, &TitleBar::signalClose,           this, &BasicFramelessWindow::onClose);
    // Main layout
    auto mainLayout = new QVBoxLayout(this);
    mainLayout->addWidget(titleBar);
    mainLayout->addStretch();
    mainLayout->setSpacing(0);
    mainLayout->setContentsMargins(1, 1, 1, 1); // Leave margin for borders
}
BasicFramelessWindow::~BasicFramelessWindow() = default;
void BasicFramelessWindow::mousePressEvent(QMouseEvent* event) {
    if (event->button() == Qt::LeftButton) {
        dragPosition = event->globalPosition().toPoint() - frameGeometry().topLeft();
        event->accept();
    }
}
void BasicFramelessWindow::mouseMoveEvent(QMouseEvent* event) {
    if (event->buttons() & Qt::LeftButton) {
        move(event->globalPosition().toPoint() - dragPosition);
        event->accept();
    }
}
// Slot function implementations
void BasicFramelessWindow::onMinimize() {
    showMinimized();
}
void BasicFramelessWindow::onMaxRestore() {
    if (isMaximized) {
        showNormal();
    } else {
        showMaximized();
    }
    isMaximized = !isMaximized;
    titleBar->setMaximized(isMaximized); // Synchronize button icon
}
void BasicFramelessWindow::onClose() {
    close();
}

The signal-slot mechanism achieves loose coupling;

After the window state changes, the title bar icon is updated synchronously;

Supports full functionality for minimizing, maximizing/restoring, and closing.

2.4 Running Effect

Implementing a Frameless Window with Custom Title Bar, Rounded Corners, Resizing, and Shadows in Qt C++

Part3Implementing a Frameless Window

Next, we will implement a key feature: like system windows, resize the window by dragging the edges or corners with the mouse.

This includes:

  • Stretching from the top, bottom, left, and right edges
  • Diagonal scaling from the four corners (top left, top right, bottom left, bottom right)
  • Display the corresponding directional cursor (↔, ↕, ↘, etc.) when hovering the mouse
  • Dynamic adjustment of window size, supporting minimum width and height limits

3.1 Function Description

Function

Description

Capture Mouse Position

Determine if the mouse is located in the edge or corner area of the window

Change Mouse Shape

Display as ↔ (horizontal), ↕ (vertical), ↘ (diagonal) etc. system resize cursors

Respond to Mouse Dragging

Press and drag the mouse to dynamically adjust the window size

Limit Minimum Size

Prevent the window from being resized to invisible or too small

3.2 Code Implementation

The core class design:<span><span>CustomWindow</span></span>

We will encapsulate the window resizing functionality in the <span><span>CustomWindow</span></span> class, inheriting from <span><span>QWidget</span></span> as the base class for all windows requiring frameless resizing functionality.

<span><span>CustomWindow.h</span></span>

#pragma once
#include <QtWidgets/QWidget>
#include <QMouseEvent>
#include <qpoint.h>
class CustomWindow : public QWidget {
    Q_OBJECT
public:
    explicit CustomWindow(QWidget* parent = nullptr);
protected:
    void mousePressEvent(QMouseEvent* event) override;
    void mouseReleaseEvent(QMouseEvent* event) override;
    void mouseMoveEvent(QMouseEvent* event) override;
    void leaveEvent(QEvent* event) override;
private:
    enum ResizeRegion {
        NoEdge = 0,
        Left,
        Right,
        Top,
        Bottom,
        TopLeft,
        TopRight,
        BottomLeft,
        BottomRight
    };
    const int EDGE_MARGIN = 8; // Edge detection range
    ResizeRegion getResizeRegion(const QPoint&amp; pos);
    bool isResizing = false; // Whether resizing is in progress
    ResizeRegion currentRegion = NoEdge;
    QPoint dragStartGlobalPos; // Mouse drag starting point
    QRect originalGeometry;  // Original position of the window during dragging
};

Code Explanation:

  • Here we define a CustomWindow class, inheriting from QWidget.

  • In protected, we override mouse press, release, move, and leave events.

  • In private, we define an enum ResizeRegion, categorizing the edges and corners of the window, from NoEdge (not at the edge) to each direction’s edge and corner.

  • EDGE_MARGIN is set to 8, which is the edge detection range; if the mouse is this close to the edge, it is considered in the edge area.

  • The getResizeRegion method is used to determine which area the mouse position belongs to.

  • The variable isResizing marks whether resizing is in progress, currentRegion records which area the mouse is in, dragStartGlobalPos is the global position when dragging starts, and originalGeometry is the position and size of the window before dragging.

CustomWindow.cpp

#include "CustomWindow.h"
CustomWindow::CustomWindow(QWidget* parent) : QWidget(parent) {
    setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint); // Frameless
    setMouseTracking(true); // Mouse movement triggers mouseMoveEvent
}

In the constructor, we first set the window to be frameless, then setMouseTracking(true), so that mouse movement over the window triggers mouseMoveEvent without needing to press the mouse button, facilitating mouse position detection for changing the cursor.

// Prepare for dragging
void CustomWindow::mousePressEvent(QMouseEvent* event) {
    if (event->button() == Qt::LeftButton &amp;&amp; currentRegion != NoEdge) {
        isResizing = true;
        dragStartGlobalPos = event->globalPosition().toPoint();
        originalGeometry = geometry();
    }
    QWidget::mousePressEvent(event);
}

In the mouse press event, if the left button is pressed and the current mouse is in the edge area (not NoEdge), set isResizing to true, record the global position of the mouse at the start of dragging dragStartGlobalPos, and the position and size of the window at that time originalGeometry.

// End dragging
void CustomWindow::mouseReleaseEvent(QMouseEvent* event) {
    isResizing = false;
    QWidget::mouseReleaseEvent(event);
}

When the mouse is released, set isResizing to false to end resizing.

// Set cursor or drag to resize
void CustomWindow::mouseMoveEvent(QMouseEvent* event) {
    if (isResizing) {
        QPoint delta = event->globalPosition().toPoint() - dragStartGlobalPos;
        QRect newGeom = originalGeometry;
        switch (currentRegion) {
        case Left:
            newGeom.setLeft(originalGeometry.left() + delta.x());
            break;
        case Right:
            newGeom.setRight(originalGeometry.right() + delta.x());
            break;
        case Top:
            newGeom.setTop(originalGeometry.top() + delta.y());
            break;
        case Bottom:
            newGeom.setBottom(originalGeometry.bottom() + delta.y());
            break;
        case TopLeft:
            newGeom.setTopLeft(originalGeometry.topLeft() + delta);
            break;
        case TopRight:
            newGeom.setTopRight(originalGeometry.topRight() + delta);
            break;
        case BottomLeft:
            newGeom.setBottomLeft(originalGeometry.bottomLeft() + delta);
            break;
        case BottomRight:
            newGeom.setBottomRight(originalGeometry.bottomRight() + delta);
            break;
        default:
            break;
        }
        if (newGeom.width() >= minimumWidth() &amp;&& newGeom.height() >= minimumHeight()) {
            setGeometry(newGeom);
        }
    } else {
        // Set mouse cursor shape
        ResizeRegion region = getResizeRegion(event->pos());
        currentRegion = region;
        switch (region) {
        case Left:
        case Right:
            setCursor(Qt::SizeHorCursor);
            break;
        case Top:
        case Bottom:
            setCursor(Qt::SizeVerCursor);
            break;
        case TopLeft:
        case BottomRight:
            setCursor(Qt::SizeFDiagCursor);
            break;
        case TopRight:
        case BottomLeft:
            setCursor(Qt::SizeBDiagCursor);
            break;
        default:
            unsetCursor();
            break;
        }
    }
    QWidget::mouseMoveEvent(event);
}

The mouse move event is divided into two situations: if resizing is in progress (isResizing is true), first calculate the distance the mouse has moved delta — the current global mouse position minus the position at the start of dragging. Then, based on currentRegion, which is the area being resized, adjust newGeom (the new window position and size). For example, if it is the Left area, adjust the left boundary of the window; if it is Right, adjust the right boundary, and so on for the four corners. After adjusting, check if the new width and height are not less than the minimum size, and if so, use setGeometry to set the new window shape.

If not resizing, call getResizeRegion to determine which area the mouse is currently in, and change the cursor shape accordingly. For example, use the horizontal resize cursor ↔ for the left and right edges, the vertical resize cursor ↕ for the top and bottom edges, and the corresponding diagonal cursor for the corners; restore the default cursor if not at the edge.

// Mouse leaves the window, cancel highlight
void CustomWindow::leaveEvent(QEvent* event) {
    if (!isResizing) unsetCursor();
    QWidget::leaveEvent(event);
}

When the mouse leaves the window, if not resizing, restore the default cursor.

// Get edge region
CustomWindow::ResizeRegion CustomWindow::getResizeRegion(const QPoint&amp; pos) {
    bool onLeft = pos.x() <= EDGE_MARGIN;
    bool onRight = pos.x() >= width() - EDGE_MARGIN;
    bool onTop = pos.y() <= EDGE_MARGIN;
    bool onButtom = pos.y() >= height() - EDGE_MARGIN;
    if (onTop &amp;&& onLeft) return TopLeft;
    if (onTop &amp;&& onRight) return TopRight;
    if (onButtom &amp;&& onLeft) return BottomLeft;
    if (onButtom &amp;&& onRight) return BottomRight;
    if (onTop) return Top;
    if (onButtom) return Bottom;
    if (onLeft) return Left;
    if (onRight) return Right;
    return NoEdge;
}

The getResizeRegion method determines which area the mouse position pos belongs to. It first checks if it is at the left, right, top, or bottom edge (based on EDGE_MARGIN), then combines them; for example, if it is at the top and left, it is TopLeft, and so on, finally returning the corresponding region.

3.3 Integrating Code Logic

Since CustomWindow is a separate class, to use its resizing functionality in the previous BasicFramelessWindow, we need to integrate the logic.

First, modify BasicFramelessWindow.h to inherit from CustomWindow, removing the duplicate mouse event handling logic:

#pragma once
#include "TitleBar.h"
#include "CustomWindow.h"
class BasicFramelessWindow : public CustomWindow {
    Q_OBJECT
public:
    explicit BasicFramelessWindow(QWidget *parent = nullptr);
    ~BasicFramelessWindow();
private slots:
    void onMinimize();
    void onMaxRestore();
    void onClose();
private:
    TitleBar* titleBar;
    bool isMaximized;
};

Now we don’t need to handle mouse events ourselves; we can directly use those from CustomWindow.

Next, modify BasicFramelessWindow.cpp to remove the original mouse event handling code and adjust the constructor:

BasicFramelessWindow::BasicFramelessWindow(QWidget *parent)
    : CustomWindow(parent), isMaximized(false) {
    // Constructor logic
}

We also need to change the original setFixedSize() to resize(), as we now need to allow resizing of the window and cannot fix its size.

With this integration, the mouse stretching functionality of the window is fully implemented, allowing resizing by dragging the edges or corners.

Part4Rounded Corners and Shadow Effects

Next, we will implement the “rounded corners and shadow effects” for the frameless window. This will require modifications in the existing program, so we will create a new project to achieve this effect.

4.1 Core Implementation Principles

Technical Points

Implementation Method

Remove system borders

setWindowFlags(Qt::FramelessWindowHint)

Support transparent background

setAttribute(Qt::WA_TranslucentBackground)

Implement rounded corners

Set border-radius style on contentWidget

Implement shadows

Use QGraphicsDropShadowEffect added to the contentwidget

Avoid jagged edges

Use QPainterPath to draw anti-aliased backgrounds (optional enhancement)

4.2 Code Implementation

<span><span>CustomWindow.h</span></span>

#pragma once
#include <QtWidgets/QWidget>
class CustomWindow : public QWidget {
    Q_OBJECT
public:
    explicit CustomWindow(QWidget* parent = nullptr);
    ~CustomWindow();
protected:
    void paintEvent(QPaintEvent* event) override;
private:
    void initUI();                    // Initialize UI
    QWidget* contentWidget;           // Main content area (rounded corners + shadow carrier)
};

Here we define the CustomWindow class, inheriting from QWidget. In public, there are constructors and destructors; in protected, we override the paintEvent event for drawing; in private, there is a contentWidget pointer for the main content area and an initUI method for initializing the interface.

<span><span>CustomWindow.cpp</span></span>

#include "CustomWindow.h"
#include <QGraphicsDropShadowEffect>
#include <QPainter>
#include <QPainterPath>
#include <QVBoxLayout>
#include <QLabel>
CustomWindow::CustomWindow(QWidget* parent)
    : QWidget(parent) {
    // Set frameless window
    setWindowFlags(Qt::FramelessWindowHint | Qt::Window);
    // Enable transparent background (key!)
    setAttribute(Qt::WA_TranslucentBackground);
    // Initial size
    resize(600, 400);
    // Initialize UI
    initUI();
}

In the constructor, we first set the window to be frameless, then setAttribute(Qt::WA_TranslucentBackground) to enable the transparent background, allowing shadows and rounded corners to display correctly.We set the initial size of the window to 600×400, and finally call initUI to initialize the interface.

void CustomWindow::initUI() {
    contentWidget = new QWidget(this);
    contentWidget->setObjectName("contentWidget");
    contentWidget->setStyleSheet("#contentWidget {"
        "   background-color: white;"
        "   border-radius: 10px;"
        "   border: 1px solid #E0E0E0;"
        "}");

In the initUI method, we first create a new contentWidget. We set an object name “contentWidget” for later use in the stylesheet. The stylesheet specifies that the contentWidget’s background is white, with a border radius of 10 pixels and a 1-pixel light gray border, achieving the rounded corner effect.

    // Add shadow
    QGraphicsDropShadowEffect* shadow = new QGraphicsDropShadowEffect(this);
    shadow->setBlurRadius(20);
    shadow->setOffset(0, 0);
    shadow->setColor(QColor(0, 0, 0, 80));
    contentWidget->setGraphicsEffect(shadow);

This part adds the shadow. We create a new QGraphicsDropShadowEffect object, setBlurRadius(20) to specify a blur radius of 20 pixels for a softer appearance; setOffset(0,0) means no offset; setColor specifies the shadow color, using a semi-transparent black. Finally, we apply this shadow effect to contentWidget.

    auto label = new QLabel("Window Shadow and Rounded Corners", contentWidget);
    label->setAlignment(Qt::AlignCenter);
    label->setStyleSheet("font-size: 24px;");
    // Layout
    QVBoxLayout* layout = new QVBoxLayout(contentWidget);
    layout->addWidget(label);
    QVBoxLayout* mainLayout = new QVBoxLayout(this);
    mainLayout->setContentsMargins(10, 10, 10, 10);  // Shadow margins
    mainLayout->addWidget(contentWidget);
}

Here we create a label to display “Window Shadow and Rounded Corners”, set it to center alignment, and font size 24 pixels. Then we use QVBoxLayout to manage the layout of contentWidget, adding the label. We also create a mainLayout for the entire window layout, setting contents margins to 10 pixels to leave space for the shadow, and finally adding contentWidget to it.

void CustomWindow::paintEvent(QPaintEvent* event) {
    // Draw transparent background
    QPainter painter(this);
    painter.fillRect(rect(), Qt::transparent);
}

In paintEvent, we use QPainter to paint the window background transparent, avoiding any unintended background color and ensuring the shadow displays correctly.

CustomWindow::~CustomWindow() {}

The destructor is left empty, as there is nothing special to handle.

Running Effect

Implementing a Frameless Window with Custom Title Bar, Rounded Corners, Resizing, and Shadows in Qt C++

Summary

Through the above practice, we have constructed a fully functional, clearly structured, and visually modern Qt frameless window framework. It not only breaks through the style limitations of traditional Qt windows but also provides a solid foundation for developing professional-grade desktop applications.

This solution is suitable for various scenarios such as login interfaces, main program windows, pop-ups, and settings panels, possessing good engineering value and expansion potential.

Previous Recommendations

C++ Qt Learning Path from Start to Finish! (Desktop Development & Embedded Development)

Writing a Video Player with Qt + FFmpeg (with complete code)

Detailed Explanation of Qt Signal and Slot Mechanism

Click below to follow 【Linux Tutorials】 to get programming learning paths, project tutorials, resume templates, large company interview question PDFs, large company interview experiences, programming communication circles, and more.

Leave a Comment