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

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& 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 && 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() &&& 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& 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 &&& onLeft) return TopLeft;
if (onTop &&& onRight) return TopRight;
if (onButtom &&& onLeft) return BottomLeft;
if (onButtom &&& 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 content |
|
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

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.