├── .gitignore ├── FrameLessQtQuick ├── .vscode │ ├── c_cpp_properties.json │ ├── launch.json │ └── settings.json ├── CMakeLists.txt ├── CMakePresets.json ├── MainWindow.cpp ├── MainWindow.hpp ├── Qmls │ └── main.qml ├── Resources │ ├── ApplicationIcon.ico │ ├── ApplicationIcon.png │ ├── Close.png │ ├── CloseDeactivated.png │ ├── CloseHoverOrPressed.png │ ├── Maximize.png │ ├── MaximizeDeactivated.png │ ├── Minimize.png │ ├── MinimizeDeactivated.png │ ├── Restore.png │ ├── RestoreDeactivated.png │ └── Transparent.png ├── main.cpp └── qml.qrc ├── FrameLessQtWidget ├── .vscode │ ├── c_cpp_properties.json │ ├── launch.json │ └── settings.json ├── CMakeLists.txt ├── CMakePresets.json ├── MainWindow.cpp ├── MainWindow.hpp ├── Resources │ ├── ApplicationIcon.ico │ ├── ApplicationIcon.png │ ├── Close.png │ ├── CloseDeactivated.png │ ├── CloseHoverOrPressed.png │ ├── Maximize.png │ ├── MaximizeDeactivated.png │ ├── Minimize.png │ ├── MinimizeDeactivated.png │ ├── Restore.png │ ├── RestoreDeactivated.png │ └── Transparent.png ├── main.cpp └── resources.qrc ├── LICENSE ├── README.md └── resources ├── FrameLessQtQuickShowcase.gif └── FrameLessQtWidgetShowcase.gif /.gitignore: -------------------------------------------------------------------------------- 1 | # Build files are excluded. 2 | */Build/ 3 | */*.user -------------------------------------------------------------------------------- /FrameLessQtQuick/.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Win32", 5 | "includePath": [ 6 | "${workspaceFolder}/**" 7 | ], 8 | "compilerPath": "C:/Program Files/Microsoft Visual Studio/2022/Professional/VC/Tools/MSVC/14.16.27023/bin/HostX64/x64/cl.exe", 9 | "defines": [ 10 | "_DEBUG", 11 | "UNICODE", 12 | "_UNICODE" 13 | ], 14 | "cStandard": "c11", 15 | "cppStandard": "c++17", 16 | "intelliSenseMode": "windows-msvc-x64", 17 | "configurationProvider": "ms-vscode.cmake-tools" 18 | } 19 | ], 20 | "version": 4 21 | } -------------------------------------------------------------------------------- /FrameLessQtQuick/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // IntelliSense를 사용하여 가능한 특성에 대해 알아보세요. 3 | // 기존 특성에 대한 설명을 보려면 가리킵니다. 4 | // 자세한 내용을 보려면 https://go.microsoft.com/fwlink/?linkid=830387을(를) 방문하세요. 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Visual Studio Debug", 9 | "type": "cppvsdbg", 10 | "request": "launch", 11 | "program": "${command:cmake.launchTargetPath}", 12 | "args": [], 13 | "stopAtEntry": false, 14 | "cwd": "${workspaceFolder}", 15 | "environment": [], 16 | "console": "internalConsole" 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /FrameLessQtQuick/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "algorithm": "cpp", 4 | "any": "cpp", 5 | "array": "cpp", 6 | "atomic": "cpp", 7 | "bit": "cpp", 8 | "bitset": "cpp", 9 | "cctype": "cpp", 10 | "cfenv": "cpp", 11 | "charconv": "cpp", 12 | "chrono": "cpp", 13 | "cinttypes": "cpp", 14 | "clocale": "cpp", 15 | "cmath": "cpp", 16 | "codecvt": "cpp", 17 | "compare": "cpp", 18 | "complex": "cpp", 19 | "concepts": "cpp", 20 | "condition_variable": "cpp", 21 | "coroutine": "cpp", 22 | "csetjmp": "cpp", 23 | "csignal": "cpp", 24 | "cstdarg": "cpp", 25 | "cstddef": "cpp", 26 | "cstdint": "cpp", 27 | "cstdio": "cpp", 28 | "cstdlib": "cpp", 29 | "cstring": "cpp", 30 | "ctime": "cpp", 31 | "cwchar": "cpp", 32 | "cwctype": "cpp", 33 | "deque": "cpp", 34 | "exception": "cpp", 35 | "execution": "cpp", 36 | "filesystem": "cpp", 37 | "format": "cpp", 38 | "forward_list": "cpp", 39 | "fstream": "cpp", 40 | "functional": "cpp", 41 | "future": "cpp", 42 | "initializer_list": "cpp", 43 | "iomanip": "cpp", 44 | "ios": "cpp", 45 | "iosfwd": "cpp", 46 | "iostream": "cpp", 47 | "istream": "cpp", 48 | "iterator": "cpp", 49 | "limits": "cpp", 50 | "list": "cpp", 51 | "locale": "cpp", 52 | "map": "cpp", 53 | "memory": "cpp", 54 | "memory_resource": "cpp", 55 | "mutex": "cpp", 56 | "new": "cpp", 57 | "numeric": "cpp", 58 | "optional": "cpp", 59 | "ostream": "cpp", 60 | "queue": "cpp", 61 | "random": "cpp", 62 | "ranges": "cpp", 63 | "ratio": "cpp", 64 | "regex": "cpp", 65 | "scoped_allocator": "cpp", 66 | "set": "cpp", 67 | "shared_mutex": "cpp", 68 | "source_location": "cpp", 69 | "span": "cpp", 70 | "sstream": "cpp", 71 | "stack": "cpp", 72 | "stdexcept": "cpp", 73 | "stop_token": "cpp", 74 | "streambuf": "cpp", 75 | "string": "cpp", 76 | "strstream": "cpp", 77 | "system_error": "cpp", 78 | "thread": "cpp", 79 | "tuple": "cpp", 80 | "type_traits": "cpp", 81 | "typeindex": "cpp", 82 | "typeinfo": "cpp", 83 | "unordered_map": "cpp", 84 | "unordered_set": "cpp", 85 | "utility": "cpp", 86 | "valarray": "cpp", 87 | "variant": "cpp", 88 | "vector": "cpp", 89 | "xfacet": "cpp", 90 | "xhash": "cpp", 91 | "xiosbase": "cpp", 92 | "xlocale": "cpp", 93 | "xlocbuf": "cpp", 94 | "xlocinfo": "cpp", 95 | "xlocmes": "cpp", 96 | "xlocmon": "cpp", 97 | "xlocnum": "cpp", 98 | "xloctime": "cpp", 99 | "xmemory": "cpp", 100 | "xstddef": "cpp", 101 | "xstring": "cpp", 102 | "xtr1common": "cpp", 103 | "xtree": "cpp", 104 | "xutility": "cpp", 105 | "*.ipp": "cpp", 106 | "hash_map": "cpp", 107 | "hash_set": "cpp", 108 | "qqmlapplicationengine": "cpp", 109 | "qabstractnativeeventfilter": "cpp", 110 | "qmetaobject": "cpp", 111 | "qguiapplication": "cpp" 112 | } 113 | } -------------------------------------------------------------------------------- /FrameLessQtQuick/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.23) 2 | 3 | # Project Name 4 | project("FrameLessQtQuick") 5 | 6 | set(CMAKE_AUTOMOC ON) 7 | 8 | set(CMAKE_AUTOUIC ON) 9 | 10 | set(CMAKE_AUTORCC ON) 11 | 12 | # This project uses C++17 13 | set(CMAKE_CXX_STANDARD 17) 14 | 15 | # This path should be set to your directory path where Qt5Config.cmake (or Qt6Config.cmake) is located. 16 | set(QT_DIR "D:\\ProgramFiles\\Qt\\5.15.10\\msvc2022_64\\lib\\cmake\\Qt5") 17 | 18 | # Qt modules 19 | find_package(QT NAMES Qt6 Qt5 REQUIRED Core) 20 | 21 | set(Qt${QT_VERSION_MAJOR}_DIR ${QT_DIR}) 22 | 23 | find_package(Qt${QT_VERSION_MAJOR} REQUIRED Qml Quick QuickControls2) 24 | 25 | file(GLOB SRC_FILES CONFIGURE_DEPENDS ./*.cpp) 26 | 27 | file(GLOB UI_FILES CONFIGURE_DEPENDS ./*.ui) 28 | 29 | file(GLOB QRC_FILES CONFIGURE_DEPENDS ./*.qrc) 30 | 31 | # This project is for Windows OS. (I didn't test it in other OS like mac, linux) 32 | add_executable(${CMAKE_PROJECT_NAME} WIN32 ${SRC_FILES} ${UI_FILES} ${QRC_FILES}) 33 | 34 | target_link_libraries( 35 | ${CMAKE_PROJECT_NAME} 36 | Qt${QT_VERSION_MAJOR}::Core 37 | Qt${QT_VERSION_MAJOR}::Qml 38 | Qt${QT_VERSION_MAJOR}::Quick 39 | Qt${QT_VERSION_MAJOR}::QuickControls2 40 | dwmapi.lib 41 | gdi32.lib 42 | ) 43 | 44 | # For deployment. 45 | add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD 46 | COMMAND windeployqt.exe $,--debug,--release> --qmldir "${CMAKE_SOURCE_DIR}/Qmls" "${CMAKE_BINARY_DIR}/$,Debug,Release>/${CMAKE_PROJECT_NAME}${CMAKE_EXECUTABLE_SUFFIX}" 47 | WORKING_DIRECTORY "${QT_DIR}/../../../bin" 48 | ) 49 | -------------------------------------------------------------------------------- /FrameLessQtQuick/CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "cmakeMinimumRequired": { 4 | "major": 3, 5 | "minor": 23, 6 | "patch": 0 7 | }, 8 | "configurePresets": [ 9 | { 10 | "description": "Windows builds only", 11 | "name": "windows-base", 12 | "hidden": true, 13 | "binaryDir": "${sourceDir}/Build/${presetName}", 14 | "installDir": "${sourceDir}/Installed/${presetName}", 15 | "toolchainFile": "D:/ProgramFiles/vcpkg/scripts/buildsystems/vcpkg.cmake", 16 | "condition": { 17 | "type": "equals", 18 | "lhs": "${hostSystemName}", 19 | "rhs": "Windows" 20 | } 21 | }, 22 | { 23 | "description": "MSVC", 24 | "name": "msvc", 25 | "hidden": true, 26 | "generator": "Visual Studio 17 2022", 27 | "inherits": "windows-base" 28 | }, 29 | { 30 | "description": "MSVC with x64 option", 31 | "name": "msvc-x64", 32 | "displayName": "MSVC x64", 33 | "inherits": "msvc", 34 | "architecture": { 35 | "value": "x64", 36 | "strategy": "set" 37 | }, 38 | "cacheVariables": { 39 | "VCPKG_TARGET_TRIPLET": "x64-windows-static", 40 | "CMAKE_CXX_FLAGS": "/MP /D_UNICODE /DUNICODE /D_CRT_SECURE_NO_WARNINGS /JMC /permissive- /EHsc", 41 | "CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreaded$<$:Debug>DLL" 42 | } 43 | }, 44 | { 45 | "description": "MSVC with x86 option", 46 | "name": "msvc-x86", 47 | "displayName": "MSVC x86", 48 | "inherits": "msvc", 49 | "architecture": { 50 | "value": "Win32", 51 | "strategy": "set" 52 | }, 53 | "cacheVariables": { 54 | "VCPKG_TARGET_TRIPLET": "x86-windows-static", 55 | "CMAKE_CXX_FLAGS": "/DWIN32 /MP /D_UNICODE /DUNICODE /D_CRT_SECURE_NO_WARNINGS /JMC /permissive- /EHsc", 56 | "CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreaded$<$:Debug>DLL" 57 | } 58 | } 59 | ], 60 | "buildPresets": [ 61 | { 62 | "name": "msvc-base-build-settings", 63 | "hidden": true, 64 | "nativeToolOptions": [ 65 | "/maxcpucount", 66 | "/nologo", 67 | "/verbosity:minimal" 68 | ] 69 | }, 70 | { 71 | "name": "msvc-x64-debug-build", 72 | "displayName": "Debug Build", 73 | "inherits": "msvc-base-build-settings", 74 | "configuration": "Debug", 75 | "configurePreset": "msvc-x64" 76 | }, 77 | { 78 | "name": "msvc-x64-release-build", 79 | "displayName": "Release Build", 80 | "inherits": "msvc-base-build-settings", 81 | "configuration": "Release", 82 | "configurePreset": "msvc-x64" 83 | }, 84 | { 85 | "name": "msvc-x86-debug-build", 86 | "displayName": "Debug Build", 87 | "inherits": "msvc-base-build-settings", 88 | "configuration": "Debug", 89 | "configurePreset": "msvc-x86" 90 | }, 91 | { 92 | "name": "msvc-x86-release-build", 93 | "displayName": "Release Build", 94 | "inherits": "msvc-base-build-settings", 95 | "configuration": "Release", 96 | "configurePreset": "msvc-x86" 97 | } 98 | ], 99 | "testPresets": [] 100 | } -------------------------------------------------------------------------------- /FrameLessQtQuick/MainWindow.cpp: -------------------------------------------------------------------------------- 1 | #include "MainWindow.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | MainWindow::MainWindow(QQmlApplicationEngine *engine) 12 | { 13 | if (engine) 14 | initWindow(*engine); 15 | } 16 | 17 | MainWindow::~MainWindow() 18 | { 19 | } 20 | 21 | HWND MainWindow::getHandle() 22 | { 23 | return m_hwnd; 24 | } 25 | 26 | bool MainWindow::initWindow(QQmlApplicationEngine &engine) 27 | { 28 | m_quick_window = qobject_cast(engine.rootObjects().at(0)); 29 | 30 | if (!m_quick_window) 31 | return false; 32 | 33 | m_hwnd = reinterpret_cast(m_quick_window->winId()); 34 | m_resize_border_width = m_quick_window->property("resizeBorderWidth").toInt() * m_quick_window->devicePixelRatio(); 35 | 36 | QObject::connect(m_quick_window, &QQuickWindow::screenChanged, this, &MainWindow::onScreenChanged); 37 | 38 | // Set window shadows. 39 | const MARGINS aero_shadow_on = {1, 1, 1, 1}; 40 | ::DwmExtendFrameIntoClientArea(m_hwnd, &aero_shadow_on); 41 | 42 | // Install event handler 43 | engine.installEventFilter(this); 44 | 45 | // Make cppConnector obj so that we can connect functions for Minimize, Maximize / Restore, Close feature. 46 | engine.rootContext()->setContextProperty("cppConnector", this); 47 | 48 | return true; 49 | } 50 | 51 | // Render again when frame is moved to another monitor. 52 | void MainWindow::onScreenChanged(QScreen *screen) 53 | { 54 | SetWindowPos(m_hwnd, NULL, 0, 0, 0, 0, 55 | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | 56 | SWP_NOOWNERZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE); 57 | } 58 | 59 | bool MainWindow::eventFilter(QObject *obj, QEvent *evt) 60 | { 61 | switch (evt->type()) 62 | { 63 | // case QEvent::Enter: 64 | // break; 65 | // case QEvent::Leave: 66 | // break; 67 | default: 68 | break; 69 | } 70 | 71 | return QObject::eventFilter(obj, evt); 72 | } 73 | 74 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 75 | bool MainWindow::nativeEventFilter(const QByteArray &event_type, void *message, long *result) 76 | #else 77 | bool MainWindow::nativeEventFilter(const QByteArray &event_type, void *message, qintptr *result) 78 | #endif 79 | { 80 | MSG *msg = (MSG *)message; 81 | 82 | switch (msg->message) 83 | { 84 | // Remove the default window frame by hooking the WM_NCCALCSIZE message. 85 | case WM_NCCALCSIZE: { 86 | if (msg->lParam) 87 | { 88 | WINDOWPLACEMENT wp; 89 | GetWindowPlacement(m_hwnd, &wp); 90 | 91 | if (wp.showCmd == SW_MAXIMIZE) 92 | { 93 | NCCALCSIZE_PARAMS *sz = (NCCALCSIZE_PARAMS *)msg->lParam; 94 | sz->rgrc[0].left += 8; 95 | sz->rgrc[0].top += 8; 96 | sz->rgrc[0].right -= 8; 97 | sz->rgrc[0].bottom -= 8; 98 | 99 | // Save window mode state. 100 | m_quick_window->findChild("maximizeButton")->setProperty("checked", true); 101 | } 102 | else if (wp.showCmd == SW_NORMAL) 103 | // Save window mode state. 104 | m_quick_window->findChild("maximizeButton")->setProperty("checked", false); 105 | } 106 | return true; 107 | } 108 | 109 | // Process the mouse when it is on the window border. 110 | case WM_NCHITTEST: { 111 | RECT winrect; 112 | GetWindowRect(msg->hwnd, &winrect); 113 | long x = GET_X_LPARAM(msg->lParam); 114 | long y = GET_Y_LPARAM(msg->lParam); 115 | 116 | if (x >= winrect.left && x < winrect.left + m_resize_border_width && 117 | y < winrect.bottom && y >= winrect.bottom - m_resize_border_width) 118 | { 119 | *result = HTBOTTOMLEFT; 120 | return true; 121 | } 122 | 123 | if (x < winrect.right && x >= winrect.right - m_resize_border_width && 124 | y < winrect.bottom && y >= winrect.bottom - m_resize_border_width) 125 | { 126 | *result = HTBOTTOMRIGHT; 127 | return true; 128 | } 129 | 130 | if (x >= winrect.left && x < winrect.left + m_resize_border_width && 131 | y >= winrect.top && y < winrect.top + m_resize_border_width) 132 | { 133 | *result = HTTOPLEFT; 134 | return true; 135 | } 136 | 137 | if (x < winrect.right && x >= winrect.right - m_resize_border_width && 138 | y >= winrect.top && y < winrect.top + m_resize_border_width) 139 | { 140 | *result = HTTOPRIGHT; 141 | return true; 142 | } 143 | 144 | if (x >= winrect.left && x < winrect.left + m_resize_border_width) 145 | { 146 | *result = HTLEFT; 147 | return true; 148 | } 149 | 150 | if (x < winrect.right && x >= winrect.right - m_resize_border_width) 151 | { 152 | *result = HTRIGHT; 153 | return true; 154 | } 155 | 156 | if (y < winrect.bottom && y >= winrect.bottom - m_resize_border_width) 157 | { 158 | *result = HTBOTTOM; 159 | return true; 160 | } 161 | 162 | if (y >= winrect.top && y < winrect.top + m_resize_border_width) 163 | { 164 | *result = HTTOP; 165 | return true; 166 | } 167 | 168 | *result = HTTRANSPARENT; 169 | break; 170 | } 171 | 172 | // Process window system menu 173 | case WM_SYSCOMMAND: { 174 | if (msg->wParam == VK_SPACE || (msg->wParam == SC_KEYMENU && msg->lParam == VK_SPACE)) 175 | { 176 | HMENU menu = GetSystemMenu(m_hwnd, FALSE); 177 | if (menu) 178 | { 179 | MENUITEMINFO mii; 180 | mii.cbSize = sizeof(MENUITEMINFO); 181 | mii.fMask = MIIM_STATE; 182 | mii.fType = 0; 183 | 184 | mii.fState = MF_ENABLED; 185 | SetMenuItemInfo(menu, SC_RESTORE, FALSE, &mii); 186 | SetMenuItemInfo(menu, SC_SIZE, FALSE, &mii); 187 | SetMenuItemInfo(menu, SC_MOVE, FALSE, &mii); 188 | SetMenuItemInfo(menu, SC_MAXIMIZE, FALSE, &mii); 189 | SetMenuItemInfo(menu, SC_MINIMIZE, FALSE, &mii); 190 | 191 | mii.fState = MF_GRAYED; 192 | 193 | WINDOWPLACEMENT wp; 194 | GetWindowPlacement(m_hwnd, &wp); 195 | 196 | switch (wp.showCmd) 197 | { 198 | case SW_SHOWMAXIMIZED: 199 | SetMenuItemInfo(menu, SC_SIZE, FALSE, &mii); 200 | SetMenuItemInfo(menu, SC_MOVE, FALSE, &mii); 201 | SetMenuItemInfo(menu, SC_MAXIMIZE, FALSE, &mii); 202 | SetMenuDefaultItem(menu, SC_CLOSE, FALSE); 203 | break; 204 | case SW_SHOWMINIMIZED: 205 | SetMenuItemInfo(menu, SC_MINIMIZE, FALSE, &mii); 206 | SetMenuDefaultItem(menu, SC_RESTORE, FALSE); 207 | break; 208 | case SW_SHOWNORMAL: 209 | SetMenuItemInfo(menu, SC_RESTORE, FALSE, &mii); 210 | SetMenuDefaultItem(menu, SC_CLOSE, FALSE); 211 | break; 212 | } 213 | 214 | RECT winrect; 215 | GetWindowRect(m_hwnd, &winrect); 216 | 217 | LPARAM cmd = TrackPopupMenu(menu, (TPM_RIGHTBUTTON | TPM_NONOTIFY | TPM_RETURNCMD), 218 | // When the window is maximized, the pop-up menu activated by "Alt + Space" invades the other monitor. 219 | // To fix this, move the window a bit more to the left. 220 | winrect.left + (wp.showCmd == SW_SHOWMAXIMIZED ? 8 : 0), 221 | winrect.top, NULL, m_hwnd, nullptr); 222 | 223 | if (cmd) 224 | PostMessage(m_hwnd, WM_SYSCOMMAND, cmd, 0); 225 | } 226 | return true; 227 | } 228 | break; 229 | } 230 | default: 231 | break; 232 | } 233 | return false; 234 | } 235 | 236 | // Activated when the user clicks the Minimize button. 237 | void MainWindow::onMinimizeButtonClicked() 238 | { 239 | SendMessage(m_hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0); 240 | } 241 | 242 | // Activated when the user clicks the Maxmimize button. 243 | void MainWindow::onMaximizeButtonClicked() 244 | { 245 | bool checked = m_quick_window->findChild("maximizeButton")->property("checked").toBool(); 246 | SendMessage(m_hwnd, WM_SYSCOMMAND, checked ? SC_MAXIMIZE : SC_RESTORE, 0); 247 | } 248 | 249 | // Activated when the user clicks the Close button. 250 | void MainWindow::onCloseButtonClicked() 251 | { 252 | SendMessage(m_hwnd, WM_CLOSE, 0, 0); 253 | } -------------------------------------------------------------------------------- /FrameLessQtQuick/MainWindow.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HEADER__FILE__MAINWINDOW 2 | #define HEADER__FILE__MAINWINDOW 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class MainWindow : public QObject, public QAbstractNativeEventFilter 10 | { 11 | Q_OBJECT 12 | 13 | QQuickWindow *m_quick_window; 14 | HWND m_hwnd; 15 | int m_resize_border_width; 16 | 17 | public: 18 | MainWindow(QQmlApplicationEngine *engine = nullptr); 19 | ~MainWindow(); 20 | 21 | HWND getHandle(); 22 | bool initWindow(QQmlApplicationEngine &engine); 23 | void onScreenChanged(QScreen *screen); 24 | 25 | bool eventFilter(QObject *obj, QEvent *evt); 26 | 27 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 28 | bool nativeEventFilter(const QByteArray &event_type, void *message, long *result); 29 | #else 30 | bool nativeEventFilter(const QByteArray &event_type, void *message, qintptr *result); 31 | #endif 32 | 33 | // Functions used in main.qml files. 34 | Q_INVOKABLE void onMinimizeButtonClicked(); 35 | Q_INVOKABLE void onMaximizeButtonClicked(); 36 | Q_INVOKABLE void onCloseButtonClicked(); 37 | }; 38 | 39 | #endif // HEADER__FILE__MAINWINDOW 40 | -------------------------------------------------------------------------------- /FrameLessQtQuick/Qmls/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Window 2.15 3 | import QtQuick.Controls 2.15 4 | import QtQuick.Layouts 1.12 5 | 6 | ApplicationWindow { 7 | id: applicationWindow 8 | visible: true 9 | width: 1024 10 | height: 768 11 | minimumWidth: 400 12 | minimumHeight: 300 13 | title: qsTr("FrameLess Window") 14 | flags: Qt.Window 15 | 16 | // Border width for window resizing 17 | // This is used in the WinQuickWindow.cpp file. 18 | property int resizeBorderWidth: 6 19 | 20 | ColumnLayout { 21 | anchors.fill: parent 22 | spacing: 0 23 | 24 | Rectangle { 25 | id: titleBar 26 | color: "#1f0830" 27 | 28 | Layout.fillWidth: true 29 | Layout.preferredHeight: 35 // Title Bar height 30 | Layout.alignment: Qt.AlignTop 31 | 32 | RowLayout { 33 | anchors.fill: parent 34 | spacing: 0 35 | 36 | // You can customize the menu bar as you like. 37 | component TitleBarMenuItem : MenuItem { 38 | id: menuItem 39 | implicitHeight: titleBar.height 40 | contentItem: Text { 41 | text: menuItem.text 42 | font: menuItem.font 43 | color: active ? (menuItem.highlighted ? "#ffffff" : Qt.rgba(0.8, 0.8, 0.8, 1.0)) : Qt.rgba(0.5, 0.5, 0.5, 1.0) 44 | horizontalAlignment: Text.AlignLeft 45 | verticalAlignment: Text.AlignVCenter 46 | elide: Text.ElideRight 47 | } 48 | background: Rectangle { 49 | anchors.fill: parent 50 | opacity: menuItem.highlighted ? 0.7 : 1.0 51 | color: "#170624" 52 | } 53 | } 54 | 55 | MenuBar { 56 | id: titleMenuBar 57 | Layout.alignment: Qt.AlignLeft 58 | 59 | Menu { 60 | title: "File" 61 | Action { 62 | text: "New Text File" 63 | } 64 | Action { 65 | text: "New File" 66 | } 67 | Action { 68 | text: "New Window" 69 | } 70 | MenuSeparator { 71 | contentItem: Rectangle { 72 | implicitHeight: 1 73 | color: "#21be2b" 74 | } 75 | background: Rectangle { 76 | color: "#170624" 77 | } 78 | } 79 | Action { 80 | text: "Open File" 81 | } 82 | Action { 83 | text: "Open Directory" 84 | } 85 | 86 | delegate: TitleBarMenuItem { } 87 | } 88 | Menu { 89 | title: "Edit" 90 | Action { 91 | text: "Undo" 92 | } 93 | Action { 94 | text: "Redo" 95 | } 96 | Action { 97 | text: "Cut" 98 | } 99 | Action { 100 | text: "Copy" 101 | } 102 | Action { 103 | text: "Paste" 104 | } 105 | 106 | delegate: TitleBarMenuItem { } 107 | } 108 | Menu { 109 | title: "Selected Zone" 110 | Action { 111 | text: "Select All" 112 | } 113 | Action { 114 | text: "Expand Select Zone" 115 | } 116 | Action { 117 | text: "Collapse Select Zone" 118 | } 119 | Action { 120 | text: "Add Cursor" 121 | } 122 | Action { 123 | text: "Select Line Mode" 124 | } 125 | 126 | delegate: TitleBarMenuItem { } 127 | } 128 | Menu { 129 | title: "View" 130 | Action { 131 | text: "Command Pallete" 132 | } 133 | Action { 134 | text: "Open View" 135 | } 136 | Action { 137 | text: "Shape" 138 | } 139 | Action { 140 | text: "Edit Layout" 141 | } 142 | 143 | delegate: TitleBarMenuItem { } 144 | } 145 | 146 | delegate: MenuBarItem { 147 | id: menuBarItem 148 | contentItem: Text { 149 | text: menuBarItem.text 150 | font: menuBarItem.font 151 | color: active ? Qt.rgba(0.8, 0.8, 0.8, 1.0) : Qt.rgba(0.5, 0.5, 0.5, 1.0) 152 | horizontalAlignment: Text.AlignLeft 153 | verticalAlignment: Text.AlignVCenter 154 | elide: Text.ElideRight 155 | } 156 | background: Rectangle { 157 | implicitHeight: titleBar.height 158 | color: menuBarItem.highlighted ? Qt.rgba(1.0, 1.0, 1.0, 0.2) : "transparent" 159 | } 160 | } 161 | 162 | background: Rectangle { 163 | implicitHeight: titleBar.height 164 | color: "transparent" 165 | Rectangle { 166 | color: "#21be2b" 167 | width: parent.width 168 | height: 1 169 | anchors.bottom: parent.bottom 170 | } 171 | } 172 | } 173 | 174 | // This is the area where the user can click to move the window. 175 | Item { 176 | Layout.fillHeight: true 177 | Layout.fillWidth: true 178 | 179 | MouseArea { 180 | anchors.fill: parent 181 | 182 | // When you press this area, the window becomes movable. 183 | onPressed: { 184 | applicationWindow.startSystemMove() 185 | } 186 | 187 | // When you double-click this area, the window state changes as well. 188 | onDoubleClicked: { 189 | applicationWindow.visibility = maximizeButton.checked ? Window.Windowed : Window.Maximized 190 | } 191 | } 192 | } 193 | 194 | Button { 195 | id: minimizeButton 196 | Layout.preferredWidth: 46 197 | Layout.fillHeight: true 198 | Layout.alignment: Qt.AlignRight 199 | background: Rectangle { 200 | color: parent.down ? Qt.rgba(1.0, 1.0, 1.0, 0.4) : (parent.hovered ? Qt.rgba(1.0, 1.0, 1.0, 0.2) : 201 | Qt.rgba(1.0, 1.0, 1.0, 0.0)) 202 | } 203 | Image { 204 | source: active ? "qrc:/icon/Minimize.png" : "qrc:/icon/MinimizeDeactivated.png" 205 | anchors.fill: parent 206 | fillMode: Image.PreserveAspectFit 207 | } 208 | 209 | // Trigger onMinimizeButtonClicked function implemented on C++ side. 210 | onClicked: { 211 | cppConnector.onMinimizeButtonClicked() 212 | } 213 | } 214 | 215 | Button { 216 | id: maximizeButton 217 | objectName: "maximizeButton" 218 | Layout.preferredWidth: 46 219 | Layout.fillHeight: true 220 | Layout.alignment: Qt.AlignRight 221 | checkable: true 222 | checked: false 223 | background: Rectangle { 224 | color: parent.down ? Qt.rgba(1.0, 1.0, 1.0, 0.4) : (parent.hovered ? Qt.rgba(1.0, 1.0, 1.0, 0.2) : 225 | Qt.rgba(1.0, 1.0, 1.0, 0.0)) 226 | } 227 | Image { 228 | source: maximizeButton.checked ? (active ? "qrc:/icon/Restore.png" : "qrc:/icon/RestoreDeactivated.png") : 229 | (active ? "qrc:/icon/Maximize.png" : "qrc:/icon/MaximizeDeactivated.png") 230 | anchors.fill: parent 231 | fillMode: Image.PreserveAspectFit 232 | } 233 | 234 | // Trigger onMaximizeButtonClicked function implemented on C++ side. 235 | onClicked: { 236 | cppConnector.onMaximizeButtonClicked() 237 | } 238 | } 239 | 240 | Button { 241 | id: closeButton 242 | Layout.preferredWidth: 46 243 | Layout.fillHeight: true 244 | Layout.alignment: Qt.AlignRight 245 | background: Rectangle { 246 | color: parent.down ? Qt.rgba(0.78, 0.16, 0.184, 0.6) : (parent.hovered ? Qt.rgba(0.86, 0.16, 0.184, 0.9) : 247 | Qt.rgba(1.0, 1.0, 1.0, 0.0)) 248 | } 249 | Image { 250 | source: closeButton.hovered ? "qrc:/icon/CloseHoverOrPressed.png" : 251 | (active ? "qrc:/icon/Close.png" : "qrc:/icon/CloseDeactivated.png") 252 | anchors.fill: parent 253 | fillMode: Image.PreserveAspectFit 254 | } 255 | 256 | // Trigger onMaximizeButtonClicked function implemented on C++ side. 257 | onClicked: { 258 | cppConnector.onCloseButtonClicked() 259 | } 260 | } 261 | } 262 | } 263 | 264 | Rectangle { 265 | id: mainContent 266 | color: "#280a3d" 267 | Layout.fillWidth: true 268 | Layout.fillHeight: true 269 | Layout.alignment: Qt.AlignBottom 270 | 271 | Button { 272 | text: "This is Button!" 273 | anchors.centerIn: parent 274 | } 275 | } 276 | } 277 | 278 | // Move window to center. 279 | Component.onCompleted: { 280 | x = Screen.width / 2 - width / 2 281 | y = Screen.height / 2 - height / 2 282 | } 283 | } 284 | -------------------------------------------------------------------------------- /FrameLessQtQuick/Resources/ApplicationIcon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tongmon/qt-frameless-windows/65f6e51c7b3d30032e0861ac7533d1a89c3354c4/FrameLessQtQuick/Resources/ApplicationIcon.ico -------------------------------------------------------------------------------- /FrameLessQtQuick/Resources/ApplicationIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tongmon/qt-frameless-windows/65f6e51c7b3d30032e0861ac7533d1a89c3354c4/FrameLessQtQuick/Resources/ApplicationIcon.png -------------------------------------------------------------------------------- /FrameLessQtQuick/Resources/Close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tongmon/qt-frameless-windows/65f6e51c7b3d30032e0861ac7533d1a89c3354c4/FrameLessQtQuick/Resources/Close.png -------------------------------------------------------------------------------- /FrameLessQtQuick/Resources/CloseDeactivated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tongmon/qt-frameless-windows/65f6e51c7b3d30032e0861ac7533d1a89c3354c4/FrameLessQtQuick/Resources/CloseDeactivated.png -------------------------------------------------------------------------------- /FrameLessQtQuick/Resources/CloseHoverOrPressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tongmon/qt-frameless-windows/65f6e51c7b3d30032e0861ac7533d1a89c3354c4/FrameLessQtQuick/Resources/CloseHoverOrPressed.png -------------------------------------------------------------------------------- /FrameLessQtQuick/Resources/Maximize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tongmon/qt-frameless-windows/65f6e51c7b3d30032e0861ac7533d1a89c3354c4/FrameLessQtQuick/Resources/Maximize.png -------------------------------------------------------------------------------- /FrameLessQtQuick/Resources/MaximizeDeactivated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tongmon/qt-frameless-windows/65f6e51c7b3d30032e0861ac7533d1a89c3354c4/FrameLessQtQuick/Resources/MaximizeDeactivated.png -------------------------------------------------------------------------------- /FrameLessQtQuick/Resources/Minimize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tongmon/qt-frameless-windows/65f6e51c7b3d30032e0861ac7533d1a89c3354c4/FrameLessQtQuick/Resources/Minimize.png -------------------------------------------------------------------------------- /FrameLessQtQuick/Resources/MinimizeDeactivated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tongmon/qt-frameless-windows/65f6e51c7b3d30032e0861ac7533d1a89c3354c4/FrameLessQtQuick/Resources/MinimizeDeactivated.png -------------------------------------------------------------------------------- /FrameLessQtQuick/Resources/Restore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tongmon/qt-frameless-windows/65f6e51c7b3d30032e0861ac7533d1a89c3354c4/FrameLessQtQuick/Resources/Restore.png -------------------------------------------------------------------------------- /FrameLessQtQuick/Resources/RestoreDeactivated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tongmon/qt-frameless-windows/65f6e51c7b3d30032e0861ac7533d1a89c3354c4/FrameLessQtQuick/Resources/RestoreDeactivated.png -------------------------------------------------------------------------------- /FrameLessQtQuick/Resources/Transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tongmon/qt-frameless-windows/65f6e51c7b3d30032e0861ac7533d1a89c3354c4/FrameLessQtQuick/Resources/Transparent.png -------------------------------------------------------------------------------- /FrameLessQtQuick/main.cpp: -------------------------------------------------------------------------------- 1 | #include "MainWindow.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | int main(int argc, char *argv[]) 11 | { 12 | // Qt::AA_UseSoftwareOpenGL, Qt::AA_UseDesktopOpenGL, Qt::AA_UseOpenGLES 13 | QGuiApplication::setAttribute(Qt::AA_UseOpenGLES); 14 | QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); 15 | 16 | // Render options for Qt Quick 17 | QSurfaceFormat format; 18 | format.setSwapBehavior(QSurfaceFormat::DoubleBuffer); // Double Buffering on 19 | format.setSwapInterval(0); // v-sync off 20 | QSurfaceFormat::setDefaultFormat(format); 21 | 22 | QGuiApplication app(argc, argv); 23 | 24 | //! Native event filter should be installed on MainWindow class at this time. 25 | //! If you install NativeEventFilter later, it is really hard to make window to frameless window. 26 | MainWindow win_quick_window; 27 | app.installNativeEventFilter(&win_quick_window); 28 | 29 | // Set the Icon here. 30 | app.setWindowIcon(QIcon(":/icon/ApplicationIcon.png")); 31 | 32 | QQmlApplicationEngine engine; 33 | 34 | const QUrl url(QStringLiteral("qrc:/qml/main.qml")); 35 | 36 | QObject::connect( 37 | &engine, &QQmlApplicationEngine::objectCreated, 38 | &app, [&](QObject *obj, const QUrl &obj_url) { 39 | // Initialize window here 40 | if ((!obj && url == obj_url) || !win_quick_window.initWindow(engine)) 41 | QCoreApplication::exit(-1); 42 | }, 43 | Qt::QueuedConnection); 44 | engine.load(url); 45 | 46 | return app.exec(); 47 | } 48 | -------------------------------------------------------------------------------- /FrameLessQtQuick/qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | Resources/Close.png 4 | Resources/CloseDeactivated.png 5 | Resources/Maximize.png 6 | Resources/MaximizeDeactivated.png 7 | Resources/Restore.png 8 | Resources/RestoreDeactivated.png 9 | Resources/Minimize.png 10 | Resources/MinimizeDeactivated.png 11 | Resources/Transparent.png 12 | Resources/CloseHoverOrPressed.png 13 | Resources/ApplicationIcon.png 14 | 15 | 16 | 17 | Qmls/main.qml 18 | 19 | 20 | -------------------------------------------------------------------------------- /FrameLessQtWidget/.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Win32", 5 | "includePath": [ 6 | "${workspaceFolder}/**" 7 | ], 8 | "compilerPath": "C:/Program Files/Microsoft Visual Studio/2022/Professional/VC/Tools/MSVC/14.16.27023/bin/HostX64/x64/cl.exe", 9 | "defines": [ 10 | "_DEBUG", 11 | "UNICODE", 12 | "_UNICODE" 13 | ], 14 | "cStandard": "c11", 15 | "cppStandard": "c++17", 16 | "intelliSenseMode": "windows-msvc-x64", 17 | "configurationProvider": "ms-vscode.cmake-tools" 18 | } 19 | ], 20 | "version": 4 21 | } -------------------------------------------------------------------------------- /FrameLessQtWidget/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // IntelliSense를 사용하여 가능한 특성에 대해 알아보세요. 3 | // 기존 특성에 대한 설명을 보려면 가리킵니다. 4 | // 자세한 내용을 보려면 https://go.microsoft.com/fwlink/?linkid=830387을(를) 방문하세요. 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Visual Studio Debug", 9 | "type": "cppvsdbg", 10 | "request": "launch", 11 | "program": "${command:cmake.launchTargetPath}", 12 | "args": [], 13 | "stopAtEntry": false, 14 | "cwd": "${workspaceFolder}", 15 | "environment": [], 16 | "console": "internalConsole" 17 | } 18 | ] 19 | } -------------------------------------------------------------------------------- /FrameLessQtWidget/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "algorithm": "cpp", 4 | "any": "cpp", 5 | "array": "cpp", 6 | "atomic": "cpp", 7 | "bit": "cpp", 8 | "bitset": "cpp", 9 | "cctype": "cpp", 10 | "cfenv": "cpp", 11 | "charconv": "cpp", 12 | "chrono": "cpp", 13 | "cinttypes": "cpp", 14 | "clocale": "cpp", 15 | "cmath": "cpp", 16 | "codecvt": "cpp", 17 | "compare": "cpp", 18 | "complex": "cpp", 19 | "concepts": "cpp", 20 | "condition_variable": "cpp", 21 | "coroutine": "cpp", 22 | "csetjmp": "cpp", 23 | "csignal": "cpp", 24 | "cstdarg": "cpp", 25 | "cstddef": "cpp", 26 | "cstdint": "cpp", 27 | "cstdio": "cpp", 28 | "cstdlib": "cpp", 29 | "cstring": "cpp", 30 | "ctime": "cpp", 31 | "cwchar": "cpp", 32 | "cwctype": "cpp", 33 | "deque": "cpp", 34 | "exception": "cpp", 35 | "execution": "cpp", 36 | "filesystem": "cpp", 37 | "format": "cpp", 38 | "forward_list": "cpp", 39 | "fstream": "cpp", 40 | "functional": "cpp", 41 | "future": "cpp", 42 | "initializer_list": "cpp", 43 | "iomanip": "cpp", 44 | "ios": "cpp", 45 | "iosfwd": "cpp", 46 | "iostream": "cpp", 47 | "istream": "cpp", 48 | "iterator": "cpp", 49 | "limits": "cpp", 50 | "list": "cpp", 51 | "locale": "cpp", 52 | "map": "cpp", 53 | "memory": "cpp", 54 | "memory_resource": "cpp", 55 | "mutex": "cpp", 56 | "new": "cpp", 57 | "numeric": "cpp", 58 | "optional": "cpp", 59 | "ostream": "cpp", 60 | "queue": "cpp", 61 | "random": "cpp", 62 | "ranges": "cpp", 63 | "ratio": "cpp", 64 | "regex": "cpp", 65 | "scoped_allocator": "cpp", 66 | "set": "cpp", 67 | "shared_mutex": "cpp", 68 | "source_location": "cpp", 69 | "span": "cpp", 70 | "sstream": "cpp", 71 | "stack": "cpp", 72 | "stdexcept": "cpp", 73 | "stop_token": "cpp", 74 | "streambuf": "cpp", 75 | "string": "cpp", 76 | "strstream": "cpp", 77 | "system_error": "cpp", 78 | "thread": "cpp", 79 | "tuple": "cpp", 80 | "type_traits": "cpp", 81 | "typeindex": "cpp", 82 | "typeinfo": "cpp", 83 | "unordered_map": "cpp", 84 | "unordered_set": "cpp", 85 | "utility": "cpp", 86 | "valarray": "cpp", 87 | "variant": "cpp", 88 | "vector": "cpp", 89 | "xfacet": "cpp", 90 | "xhash": "cpp", 91 | "xiosbase": "cpp", 92 | "xlocale": "cpp", 93 | "xlocbuf": "cpp", 94 | "xlocinfo": "cpp", 95 | "xlocmes": "cpp", 96 | "xlocmon": "cpp", 97 | "xlocnum": "cpp", 98 | "xloctime": "cpp", 99 | "xmemory": "cpp", 100 | "xstddef": "cpp", 101 | "xstring": "cpp", 102 | "xtr1common": "cpp", 103 | "xtree": "cpp", 104 | "xutility": "cpp", 105 | "*.ipp": "cpp", 106 | "hash_map": "cpp", 107 | "hash_set": "cpp", 108 | "qspaceritem": "cpp", 109 | "qmainwindow": "cpp", 110 | "qdebug": "cpp", 111 | "qapplication": "cpp" 112 | } 113 | } -------------------------------------------------------------------------------- /FrameLessQtWidget/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.23) 2 | 3 | # Project Name 4 | project("FrameLessQtWidget") 5 | 6 | set(CMAKE_AUTOMOC ON) 7 | 8 | set(CMAKE_AUTOUIC ON) 9 | 10 | set(CMAKE_AUTORCC ON) 11 | 12 | # This project uses C++17 13 | set(CMAKE_CXX_STANDARD 17) 14 | 15 | # This path should be set to your directory path where Qt5Config.cmake (or Qt6Config.cmake) is located. 16 | set(QT_DIR "D:\\ProgramFiles\\Qt\\5.15.10\\msvc2022_64\\lib\\cmake\\Qt5") 17 | 18 | # Qt modules 19 | find_package(QT NAMES Qt6 Qt5 REQUIRED Core) 20 | 21 | set(Qt${QT_VERSION_MAJOR}_DIR ${QT_DIR}) 22 | 23 | # If you are using Qt6, set the directory path where Qt6CoreToolsConfig.cmake or Qt6GuiToolsConfig.cmake is located. 24 | if(${QT_VERSION_MAJOR} EQUAL 6) 25 | set(Qt6CoreTools_DIR "${QT_DIR}\\..\\Qt6CoreTools") 26 | set(Qt6GuiTools_DIR "${QT_DIR}\\..\\Qt6GuiTools") 27 | endif() 28 | 29 | find_package(Qt${QT_VERSION_MAJOR} REQUIRED Widgets) 30 | 31 | file(GLOB SRC_FILES CONFIGURE_DEPENDS ./*.cpp) 32 | 33 | file(GLOB UI_FILES CONFIGURE_DEPENDS ./*.ui) 34 | 35 | file(GLOB QRC_FILES CONFIGURE_DEPENDS ./*.qrc) 36 | 37 | # This project is for Windows OS. (I didn't test it in other OS like mac, linux) 38 | add_executable(${CMAKE_PROJECT_NAME} WIN32 ${SRC_FILES} ${UI_FILES} ${QRC_FILES}) 39 | 40 | target_link_libraries( 41 | ${CMAKE_PROJECT_NAME} 42 | Qt${QT_VERSION_MAJOR}::Core 43 | Qt${QT_VERSION_MAJOR}::Widgets 44 | dwmapi.lib 45 | gdi32.lib 46 | ) 47 | 48 | # For deployment. 49 | add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD 50 | COMMAND windeployqt.exe $,--debug,--release> "${CMAKE_BINARY_DIR}/$,Debug,Release>/${CMAKE_PROJECT_NAME}${CMAKE_EXECUTABLE_SUFFIX}" 51 | WORKING_DIRECTORY "${QT_DIR}/../../../bin" 52 | ) -------------------------------------------------------------------------------- /FrameLessQtWidget/CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "cmakeMinimumRequired": { 4 | "major": 3, 5 | "minor": 23, 6 | "patch": 0 7 | }, 8 | "configurePresets": [ 9 | { 10 | "description": "Windows builds only", 11 | "name": "windows-base", 12 | "hidden": true, 13 | "binaryDir": "${sourceDir}/Build/${presetName}", 14 | "installDir": "${sourceDir}/Installed/${presetName}", 15 | "toolchainFile": "D:/ProgramFiles/vcpkg/scripts/buildsystems/vcpkg.cmake", 16 | "condition": { 17 | "type": "equals", 18 | "lhs": "${hostSystemName}", 19 | "rhs": "Windows" 20 | } 21 | }, 22 | { 23 | "description": "MSVC", 24 | "name": "msvc", 25 | "hidden": true, 26 | "generator": "Visual Studio 17 2022", 27 | "inherits": "windows-base" 28 | }, 29 | { 30 | "description": "MSVC with x64 option", 31 | "name": "msvc-x64", 32 | "displayName": "MSVC x64", 33 | "inherits": "msvc", 34 | "architecture": { 35 | "value": "x64", 36 | "strategy": "set" 37 | }, 38 | "cacheVariables": { 39 | "VCPKG_TARGET_TRIPLET": "x64-windows-static", 40 | "CMAKE_CXX_FLAGS": "/MP /D_UNICODE /DUNICODE /D_CRT_SECURE_NO_WARNINGS /JMC /permissive- /EHsc", 41 | "CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreaded$<$:Debug>DLL" 42 | } 43 | }, 44 | { 45 | "description": "MSVC with x86 option", 46 | "name": "msvc-x86", 47 | "displayName": "MSVC x86", 48 | "inherits": "msvc", 49 | "architecture": { 50 | "value": "Win32", 51 | "strategy": "set" 52 | }, 53 | "cacheVariables": { 54 | "VCPKG_TARGET_TRIPLET": "x86-windows-static", 55 | "CMAKE_CXX_FLAGS": "/DWIN32 /MP /D_UNICODE /DUNICODE /D_CRT_SECURE_NO_WARNINGS /JMC /permissive- /EHsc", 56 | "CMAKE_MSVC_RUNTIME_LIBRARY": "MultiThreaded$<$:Debug>DLL" 57 | } 58 | } 59 | ], 60 | "buildPresets": [ 61 | { 62 | "name": "msvc-base-build-settings", 63 | "hidden": true, 64 | "nativeToolOptions": [ 65 | "/maxcpucount", 66 | "/nologo", 67 | "/verbosity:minimal" 68 | ] 69 | }, 70 | { 71 | "name": "msvc-x64-debug-build", 72 | "displayName": "Debug Build", 73 | "inherits": "msvc-base-build-settings", 74 | "configuration": "Debug", 75 | "configurePreset": "msvc-x64" 76 | }, 77 | { 78 | "name": "msvc-x64-release-build", 79 | "displayName": "Release Build", 80 | "inherits": "msvc-base-build-settings", 81 | "configuration": "Release", 82 | "configurePreset": "msvc-x64" 83 | }, 84 | { 85 | "name": "msvc-x86-debug-build", 86 | "displayName": "Debug Build", 87 | "inherits": "msvc-base-build-settings", 88 | "configuration": "Debug", 89 | "configurePreset": "msvc-x86" 90 | }, 91 | { 92 | "name": "msvc-x86-release-build", 93 | "displayName": "Release Build", 94 | "inherits": "msvc-base-build-settings", 95 | "configuration": "Release", 96 | "configurePreset": "msvc-x86" 97 | } 98 | ], 99 | "testPresets": [] 100 | } -------------------------------------------------------------------------------- /FrameLessQtWidget/MainWindow.cpp: -------------------------------------------------------------------------------- 1 | #include "MainWindow.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | MainWindow::MainWindow(QWidget *parent) 14 | : QMainWindow(parent), m_minimize_btn{nullptr}, m_maximize_btn{nullptr}, m_close_btn{nullptr}, m_resize_border_width{6} 15 | { 16 | m_hwnd = reinterpret_cast(winId()); 17 | 18 | // Set window shadows. 19 | const MARGINS aero_shadow_on = {1, 1, 1, 1}; 20 | ::DwmExtendFrameIntoClientArea(m_hwnd, &aero_shadow_on); 21 | 22 | QObject::connect(windowHandle(), &QWindow::screenChanged, this, &MainWindow::onScreenChanged); 23 | 24 | // Add widget. (Initialize central widget) 25 | QWidget *entire_widget = new QWidget(this); 26 | entire_widget->setContentsMargins(0, 0, 0, 0); 27 | setCentralWidget(entire_widget); 28 | 29 | // Layout for entire widgets. 30 | QVBoxLayout *entire_layout = new QVBoxLayout(this); 31 | entire_widget->setLayout(entire_layout); 32 | entire_layout->setContentsMargins(0, 0, 0, 0); 33 | entire_layout->setSpacing(0); 34 | 35 | // Initialize title bar widget 36 | m_titlebar_widget = new QWidget(this); 37 | entire_layout->addWidget(m_titlebar_widget); 38 | m_titlebar_widget->setFixedHeight(35); // Default title bar height is 35 39 | m_titlebar_widget->setContentsMargins(0, 0, 0, 0); 40 | m_titlebar_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); 41 | 42 | // Layout for title bar 43 | QHBoxLayout *titlebar_layout = new QHBoxLayout(this); 44 | m_titlebar_widget->setLayout(titlebar_layout); 45 | titlebar_layout->setContentsMargins(0, 0, 0, 0); 46 | titlebar_layout->setSpacing(0); 47 | 48 | QWidget *custom_titlebar_widget = new QWidget(this); 49 | titlebar_layout->addWidget(custom_titlebar_widget); 50 | custom_titlebar_widget->setContentsMargins(0, 0, 0, 0); 51 | custom_titlebar_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); 52 | 53 | // Minimize button setup. 54 | m_minimize_btn = new QPushButton(this); 55 | titlebar_layout->addWidget(m_minimize_btn); 56 | m_minimize_btn->setFixedWidth(46); 57 | m_minimize_btn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); 58 | m_minimize_btn->setFocusPolicy(Qt::FocusPolicy::NoFocus); 59 | m_minimize_btn->setStyleSheet(R"( 60 | QPushButton { 61 | border-image: url(:/icon/Minimize.png); 62 | background-color: rgba(255, 255, 255, 0%); 63 | background-repeat: no-repeat; 64 | } 65 | 66 | QPushButton:hover { 67 | background-color: rgba(255, 255, 255, 20%); 68 | } 69 | 70 | QPushButton:pressed { 71 | background-color: rgba(255, 255, 255, 40%); 72 | } 73 | 74 | QPushButton:!active { 75 | border-image: url(:/icon/MinimizeDeactivated.png); 76 | } 77 | )"); 78 | 79 | // Maximize button setup. 80 | m_maximize_btn = new QPushButton(this); 81 | titlebar_layout->addWidget(m_maximize_btn); 82 | m_maximize_btn->setFixedWidth(46); 83 | m_maximize_btn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); 84 | m_maximize_btn->setCheckable(true); 85 | m_maximize_btn->setFocusPolicy(Qt::FocusPolicy::NoFocus); 86 | m_maximize_btn->setStyleSheet(R"( 87 | QPushButton { 88 | border-image: url(:/icon/Maximize.png); 89 | background-color: rgba(255, 255, 255, 0%); 90 | background-repeat: no-repeat; 91 | } 92 | QPushButton:hover { 93 | background-color: rgba(255, 255, 255, 20%); 94 | } 95 | QPushButton:pressed { 96 | background-color: rgba(255, 255, 255, 40%); 97 | } 98 | QPushButton:checked { 99 | border-image: url(:/icon/Restore.png); 100 | background-color: rgba(255, 255, 255, 0%); 101 | background-repeat: no-repeat; 102 | } 103 | QPushButton:checked:hover { 104 | background-color: rgba(255, 255, 255, 20%); 105 | } 106 | QPushButton:checked:pressed { 107 | background-color: rgba(255, 255, 255, 40%); 108 | } 109 | QPushButton:!active { 110 | border-image: url(:/icon/MaximizeDeactivated.png); 111 | } 112 | QPushButton:checked:!active { 113 | border-image: url(:/icon/RestoreDeactivated.png); 114 | } 115 | )"); 116 | 117 | // Close button setup. 118 | m_close_btn = new QPushButton(this); 119 | titlebar_layout->addWidget(m_close_btn); 120 | m_close_btn->setFixedWidth(46); 121 | m_close_btn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); 122 | m_close_btn->setFocusPolicy(Qt::FocusPolicy::NoFocus); 123 | m_close_btn->setStyleSheet(R"( 124 | QPushButton { 125 | border-image: url(:/icon/Close.png); 126 | background-color: rgba(255, 255, 255, 0%); 127 | background-repeat: no-repeat; 128 | } 129 | QPushButton:hover { 130 | border-image: url(:/icon/CloseHoverOrPressed.png); 131 | background-color: rgba(220, 41, 47, 90%); 132 | } 133 | QPushButton:pressed { 134 | border-image: url(:/icon/CloseHoverOrPressed.png); 135 | background-color: rgba(200, 41, 47, 60%); 136 | } 137 | QPushButton:!active { 138 | border-image: url(:/icon/CloseDeactivated.png); 139 | } 140 | QPushButton:hover:!active { 141 | border-image: url(:/icon/CloseHoverOrPressed.png); 142 | } 143 | )"); 144 | 145 | // Layout for title bar customization. 146 | m_custom_titlebar_layout = new QHBoxLayout(custom_titlebar_widget); 147 | custom_titlebar_widget->setLayout(m_custom_titlebar_layout); 148 | m_custom_titlebar_layout->setContentsMargins(0, 0, 0, 0); 149 | m_custom_titlebar_layout->setSpacing(0); 150 | m_custom_titlebar_layout->setAlignment(Qt::AlignLeft); 151 | 152 | QObject::connect(m_minimize_btn, &QPushButton::clicked, this, &MainWindow::onMinimizeButtonClicked); 153 | QObject::connect(m_maximize_btn, &QPushButton::clicked, this, &MainWindow::onMaximizeButtonClicked); 154 | QObject::connect(m_close_btn, &QPushButton::clicked, this, &MainWindow::onCloseButtonClicked); 155 | 156 | entire_layout->setAlignment(titlebar_layout, Qt::AlignTop); 157 | 158 | m_content_widget = new QWidget(this); 159 | entire_layout->addWidget(m_content_widget); 160 | m_content_widget->setContentsMargins(0, 0, 0, 0); 161 | m_content_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); 162 | 163 | // Set default title bar palette. 164 | auto pal = m_titlebar_widget->palette(); 165 | pal.setColor(QPalette::Window, QColor(30, 34, 39)); 166 | m_titlebar_widget->setAutoFillBackground(true); 167 | m_titlebar_widget->setPalette(pal); 168 | 169 | // Set default content widget palette. 170 | pal = m_content_widget->palette(); 171 | pal.setColor(QPalette::Window, QColor(35, 39, 46)); 172 | m_content_widget->setAutoFillBackground(true); 173 | m_content_widget->setPalette(pal); 174 | } 175 | 176 | MainWindow::~MainWindow() 177 | { 178 | } 179 | 180 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 181 | bool MainWindow::nativeEvent(const QByteArray &event_type, void *message, long *result) 182 | #else 183 | bool MainWindow::nativeEvent(const QByteArray &event_type, void *message, qintptr *result) 184 | #endif 185 | { 186 | MSG *msg = (MSG *)message; 187 | 188 | switch (msg->message) 189 | { 190 | // Remove the default window frame by hooking the WM_NCCALCSIZE message. 191 | case WM_NCCALCSIZE: { 192 | if (msg->lParam) 193 | { 194 | WINDOWPLACEMENT wp; 195 | GetWindowPlacement(m_hwnd, &wp); 196 | 197 | if (wp.showCmd == SW_MAXIMIZE) 198 | { 199 | NCCALCSIZE_PARAMS *sz = (NCCALCSIZE_PARAMS *)msg->lParam; 200 | sz->rgrc[0].left += 8; 201 | sz->rgrc[0].top += 8; 202 | sz->rgrc[0].right -= 8; 203 | sz->rgrc[0].bottom -= 8; 204 | } 205 | } 206 | return true; 207 | } 208 | 209 | // Process the mouse when it is on the window border. 210 | case WM_NCHITTEST: { 211 | RECT winrect; 212 | GetWindowRect(msg->hwnd, &winrect); 213 | long x = GET_X_LPARAM(msg->lParam); 214 | long y = GET_Y_LPARAM(msg->lParam); 215 | long local_x = x - winrect.left; 216 | long local_y = y - winrect.top; 217 | 218 | if (x >= winrect.left && x < winrect.left + m_resize_border_width && 219 | y < winrect.bottom && y >= winrect.bottom - m_resize_border_width) 220 | { 221 | *result = HTBOTTOMLEFT; 222 | return true; 223 | } 224 | 225 | if (x < winrect.right && x >= winrect.right - m_resize_border_width && 226 | y < winrect.bottom && y >= winrect.bottom - m_resize_border_width) 227 | { 228 | *result = HTBOTTOMRIGHT; 229 | return true; 230 | } 231 | 232 | if (x >= winrect.left && x < winrect.left + m_resize_border_width && 233 | y >= winrect.top && y < winrect.top + m_resize_border_width) 234 | { 235 | *result = HTTOPLEFT; 236 | return true; 237 | } 238 | 239 | if (x < winrect.right && x >= winrect.right - m_resize_border_width && 240 | y >= winrect.top && y < winrect.top + m_resize_border_width) 241 | { 242 | *result = HTTOPRIGHT; 243 | return true; 244 | } 245 | 246 | if (x >= winrect.left && x < winrect.left + m_resize_border_width) 247 | { 248 | *result = HTLEFT; 249 | return true; 250 | } 251 | 252 | if (x < winrect.right && x >= winrect.right - m_resize_border_width) 253 | { 254 | *result = HTRIGHT; 255 | return true; 256 | } 257 | 258 | if (y < winrect.bottom && y >= winrect.bottom - m_resize_border_width) 259 | { 260 | *result = HTBOTTOM; 261 | return true; 262 | } 263 | 264 | if (y >= winrect.top && y < winrect.top + m_resize_border_width) 265 | { 266 | *result = HTTOP; 267 | return true; 268 | } 269 | 270 | // Check the area where the user can click to move the window. 271 | if (determineNonClickableWidgetUnderMouse(m_custom_titlebar_layout, local_x, local_y)) 272 | { 273 | *result = HTCAPTION; 274 | return true; 275 | } 276 | 277 | *result = HTTRANSPARENT; 278 | break; 279 | } 280 | case WM_SIZE: { 281 | if (m_maximize_btn) 282 | { 283 | WINDOWPLACEMENT wp; 284 | GetWindowPlacement(m_hwnd, &wp); 285 | m_maximize_btn->setChecked(wp.showCmd == SW_MAXIMIZE ? true : false); 286 | } 287 | break; 288 | } 289 | default: 290 | break; 291 | } 292 | 293 | return false; 294 | } 295 | 296 | // This is used to change the `active` state of widgets in custom title bar. 297 | bool MainWindow::event(QEvent *evt) 298 | { 299 | switch (evt->type()) 300 | { 301 | case QEvent::WindowActivate: { 302 | #if QT_VERSION > QT_VERSION_CHECK(5, 0, 0) 303 | m_close_btn->setStyleSheet(m_close_btn->styleSheet()); 304 | m_minimize_btn->setStyleSheet(m_minimize_btn->styleSheet()); 305 | m_maximize_btn->setStyleSheet(m_maximize_btn->styleSheet()); 306 | #endif 307 | propagateActiveStateInCustomTitlebar(m_custom_titlebar_layout, true); 308 | break; 309 | } 310 | 311 | case QEvent::WindowDeactivate: { 312 | #if QT_VERSION > QT_VERSION_CHECK(5, 0, 0) 313 | m_close_btn->setStyleSheet(m_close_btn->styleSheet()); 314 | m_minimize_btn->setStyleSheet(m_minimize_btn->styleSheet()); 315 | m_maximize_btn->setStyleSheet(m_maximize_btn->styleSheet()); 316 | #endif 317 | propagateActiveStateInCustomTitlebar(m_custom_titlebar_layout, false); 318 | break; 319 | } 320 | 321 | default: 322 | break; 323 | } 324 | 325 | return QMainWindow::event(evt); 326 | } 327 | 328 | // Determine whether the current mouse coordinate is on the non-clickable widget or not using a recursive method. 329 | bool MainWindow::determineNonClickableWidgetUnderMouse(QLayout *layout, int x, int y) 330 | { 331 | if (!layout->count() && layout->geometry().contains(x, y)) 332 | return true; 333 | 334 | for (size_t i = 0; i < layout->count(); i++) 335 | { 336 | auto item = layout->itemAt(i)->widget(); 337 | if (item) 338 | { 339 | if (item->geometry().contains(x, y)) 340 | return !item->property("clickable widget").toBool(); 341 | } 342 | else 343 | { 344 | auto child_layout = layout->itemAt(i)->layout(); 345 | if (child_layout && child_layout->geometry().contains(x, y)) 346 | return determineNonClickableWidgetUnderMouse(child_layout, x, y); 347 | } 348 | } 349 | return false; 350 | } 351 | 352 | // Set `active' state using recursive method. 353 | void MainWindow::propagateActiveStateInCustomTitlebar(QLayout *layout, bool active_state) 354 | { 355 | for (size_t i = 0; i < layout->count(); i++) 356 | { 357 | auto item = layout->itemAt(i)->widget(); 358 | if (item) 359 | { 360 | item->setProperty("active", active_state); 361 | item->setStyleSheet(item->styleSheet()); 362 | } 363 | else 364 | { 365 | auto child_layout = layout->itemAt(i)->layout(); 366 | if (child_layout) 367 | propagateActiveStateInCustomTitlebar(child_layout, active_state); 368 | } 369 | } 370 | } 371 | 372 | QWidget &MainWindow::getContentWidget() 373 | { 374 | return *m_content_widget; 375 | } 376 | 377 | QWidget &MainWindow::getTitlebarWidget() 378 | { 379 | return *m_titlebar_widget; 380 | } 381 | 382 | QHBoxLayout &MainWindow::getCustomTitlebarLayout() 383 | { 384 | return *m_custom_titlebar_layout; 385 | } 386 | 387 | void MainWindow::setResizeBorderWidth(const int &resize_border_width) 388 | { 389 | m_resize_border_width = resize_border_width; 390 | } 391 | 392 | void MainWindow::setTitlebarHeight(const int &titlebar_height) 393 | { 394 | m_titlebar_widget->setFixedHeight(titlebar_height); 395 | } 396 | 397 | // Render again when frame is moved to another monitor. 398 | void MainWindow::onScreenChanged(QScreen *screen) 399 | { 400 | SetWindowPos(m_hwnd, NULL, 0, 0, 0, 0, 401 | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | 402 | SWP_NOOWNERZORDER | SWP_FRAMECHANGED | SWP_NOACTIVATE); 403 | } 404 | 405 | void MainWindow::onMinimizeButtonClicked() 406 | { 407 | SendMessage(m_hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0); 408 | } 409 | 410 | void MainWindow::onMaximizeButtonClicked() 411 | { 412 | SendMessage(m_hwnd, WM_SYSCOMMAND, m_maximize_btn->isChecked() ? SC_MAXIMIZE : SC_RESTORE, 0); 413 | 414 | // Remove the hover state from the maximize button. 415 | m_maximize_btn->setAttribute(Qt::WA_UnderMouse, false); 416 | } 417 | 418 | void MainWindow::onCloseButtonClicked() 419 | { 420 | SendMessage(m_hwnd, WM_CLOSE, 0, 0); 421 | } 422 | -------------------------------------------------------------------------------- /FrameLessQtWidget/MainWindow.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HEADER__FILE__MAINWINDOW 2 | #define HEADER__FILE__MAINWINDOW 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | class MainWindow : public QMainWindow 16 | { 17 | Q_OBJECT 18 | 19 | HWND m_hwnd; 20 | int m_resize_border_width; 21 | 22 | QPushButton *m_minimize_btn; 23 | QPushButton *m_maximize_btn; 24 | QPushButton *m_close_btn; 25 | 26 | QWidget *m_content_widget; 27 | QWidget *m_titlebar_widget; 28 | QHBoxLayout *m_custom_titlebar_layout; 29 | 30 | public: 31 | explicit MainWindow(QWidget *parent = nullptr); 32 | ~MainWindow(); 33 | void setResizeBorderWidth(const int &resize_border_width); 34 | void setTitlebarHeight(const int &titlebar_height); 35 | QWidget &getContentWidget(); 36 | QWidget &getTitlebarWidget(); 37 | QHBoxLayout &getCustomTitlebarLayout(); 38 | 39 | private: 40 | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 41 | bool nativeEvent(const QByteArray &event_type, void *message, long *result); 42 | #else 43 | bool nativeEvent(const QByteArray &event_type, void *message, qintptr *result); 44 | #endif 45 | bool event(QEvent *evt); 46 | bool determineNonClickableWidgetUnderMouse(QLayout *layout, int x, int y); 47 | void propagateActiveStateInCustomTitlebar(QLayout *layout, bool active_state); 48 | void onScreenChanged(QScreen *screen); 49 | void onMinimizeButtonClicked(); 50 | void onMaximizeButtonClicked(); 51 | void onCloseButtonClicked(); 52 | }; 53 | 54 | #endif // HEADER__FILE__MAINWINDOW 55 | -------------------------------------------------------------------------------- /FrameLessQtWidget/Resources/ApplicationIcon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tongmon/qt-frameless-windows/65f6e51c7b3d30032e0861ac7533d1a89c3354c4/FrameLessQtWidget/Resources/ApplicationIcon.ico -------------------------------------------------------------------------------- /FrameLessQtWidget/Resources/ApplicationIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tongmon/qt-frameless-windows/65f6e51c7b3d30032e0861ac7533d1a89c3354c4/FrameLessQtWidget/Resources/ApplicationIcon.png -------------------------------------------------------------------------------- /FrameLessQtWidget/Resources/Close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tongmon/qt-frameless-windows/65f6e51c7b3d30032e0861ac7533d1a89c3354c4/FrameLessQtWidget/Resources/Close.png -------------------------------------------------------------------------------- /FrameLessQtWidget/Resources/CloseDeactivated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tongmon/qt-frameless-windows/65f6e51c7b3d30032e0861ac7533d1a89c3354c4/FrameLessQtWidget/Resources/CloseDeactivated.png -------------------------------------------------------------------------------- /FrameLessQtWidget/Resources/CloseHoverOrPressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tongmon/qt-frameless-windows/65f6e51c7b3d30032e0861ac7533d1a89c3354c4/FrameLessQtWidget/Resources/CloseHoverOrPressed.png -------------------------------------------------------------------------------- /FrameLessQtWidget/Resources/Maximize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tongmon/qt-frameless-windows/65f6e51c7b3d30032e0861ac7533d1a89c3354c4/FrameLessQtWidget/Resources/Maximize.png -------------------------------------------------------------------------------- /FrameLessQtWidget/Resources/MaximizeDeactivated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tongmon/qt-frameless-windows/65f6e51c7b3d30032e0861ac7533d1a89c3354c4/FrameLessQtWidget/Resources/MaximizeDeactivated.png -------------------------------------------------------------------------------- /FrameLessQtWidget/Resources/Minimize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tongmon/qt-frameless-windows/65f6e51c7b3d30032e0861ac7533d1a89c3354c4/FrameLessQtWidget/Resources/Minimize.png -------------------------------------------------------------------------------- /FrameLessQtWidget/Resources/MinimizeDeactivated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tongmon/qt-frameless-windows/65f6e51c7b3d30032e0861ac7533d1a89c3354c4/FrameLessQtWidget/Resources/MinimizeDeactivated.png -------------------------------------------------------------------------------- /FrameLessQtWidget/Resources/Restore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tongmon/qt-frameless-windows/65f6e51c7b3d30032e0861ac7533d1a89c3354c4/FrameLessQtWidget/Resources/Restore.png -------------------------------------------------------------------------------- /FrameLessQtWidget/Resources/RestoreDeactivated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tongmon/qt-frameless-windows/65f6e51c7b3d30032e0861ac7533d1a89c3354c4/FrameLessQtWidget/Resources/RestoreDeactivated.png -------------------------------------------------------------------------------- /FrameLessQtWidget/Resources/Transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tongmon/qt-frameless-windows/65f6e51c7b3d30032e0861ac7533d1a89c3354c4/FrameLessQtWidget/Resources/Transparent.png -------------------------------------------------------------------------------- /FrameLessQtWidget/main.cpp: -------------------------------------------------------------------------------- 1 | #include "MainWindow.hpp" 2 | #include 3 | #include 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); 8 | QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); 9 | 10 | QApplication app(argc, argv); 11 | 12 | MainWindow w; 13 | 14 | w.setResizeBorderWidth(6); //! You can set the resize border width. 15 | w.setTitlebarHeight(35); //! You also can set the title bar height. 16 | 17 | // Customize menu bar 18 | QMenuBar *menubar = new QMenuBar(&w); 19 | menubar->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); 20 | menubar->setStyleSheet(R"( 21 | QMenuBar { 22 | border-image: url(:/icon/Transparent.png); 23 | background-color: rgba(255, 255, 255, 0%); 24 | background-repeat: no-repeat; 25 | } 26 | QMenuBar::item { 27 | color: rgb(152, 160, 175); 28 | } 29 | QMenuBar::item[active="false"] { 30 | color: rgb(112, 120, 135); 31 | } 32 | QMenuBar::item:selected { 33 | spacing: 3px; 34 | padding: 2px 10px; 35 | background-color: rgb(52, 56, 61); 36 | border-radius: 5px; 37 | } 38 | 39 | QMenu { 40 | color: rgb(152, 160, 175); 41 | background-color: rgb(30, 34, 39); 42 | } 43 | QMenu::item:selected { 44 | background-color: rgb(52, 56, 61); 45 | } 46 | )"); 47 | 48 | // Initialize menu item. 49 | QMenu *menu = menubar->addMenu("File"); 50 | menu->addAction("New Text File"); 51 | menu->addAction("New File"); 52 | menu->addAction("New Window"); 53 | menu->addAction("Open File"); 54 | menu->addAction("Open Directory"); 55 | menu = menubar->addMenu("Edit"); 56 | menu->addAction("Undo"); 57 | menu->addAction("Redo"); 58 | menu->addAction("Cut"); 59 | menu->addAction("Copy"); 60 | menu->addAction("Paste"); 61 | menu = menubar->addMenu("Selected Zone"); 62 | menu->addAction("Select All"); 63 | menu->addAction("Expand Select Zone"); 64 | menu->addAction("Collapse Select Zone"); 65 | menu->addAction("Add Cursor"); 66 | menu->addAction("Select Line Mode"); 67 | menu = menubar->addMenu("View"); 68 | menu->addAction("Command Pallete"); 69 | menu->addAction("Open View"); 70 | menu->addAction("Shape"); 71 | menu->addAction("Edit Layout"); 72 | 73 | // Add widget to title bar. 74 | // "clickable widget" property should be true if you want this widget to work on the title bar. 75 | menubar->setProperty("clickable widget", true); 76 | w.getCustomTitlebarLayout().addWidget(menubar); 77 | 78 | // Add a non-clickable element to make the area moveable for frameless window. 79 | // All you need to do is set the "clickable widget" property to false. 80 | // Then user can move frameless window by clicking area of non_clickable widget. 81 | QWidget *non_clickable = new QWidget(&w); 82 | non_clickable->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); 83 | non_clickable->setProperty("clickable widget", false); 84 | w.getCustomTitlebarLayout().addWidget(non_clickable); 85 | 86 | // Set the Icon here. 87 | w.setWindowIcon(QIcon(":/icon/ApplicationIcon.png")); 88 | 89 | // Set window size. 90 | int window_width = 1024, window_height = 768; 91 | w.setGeometry(QApplication::primaryScreen()->geometry().width() / 2 - window_width / 2, 92 | QApplication::primaryScreen()->geometry().height() / 2 - window_height / 2, 93 | window_width, window_height); 94 | w.setMinimumSize(400, 300); 95 | 96 | //! MainWindow class provides getTitlebarWidget() function. 97 | //! It return title bar widget. 98 | // Set titlebar widget palette. 99 | auto pal = w.getTitlebarWidget().palette(); 100 | pal.setColor(QPalette::Window, QColor(30, 34, 39)); 101 | w.getTitlebarWidget().setAutoFillBackground(true); 102 | w.getTitlebarWidget().setPalette(pal); 103 | 104 | //! MainWindow class provides getContentWidget() function. 105 | //! It return main content widget. 106 | // Set main content widget palette. 107 | pal = w.getContentWidget().palette(); 108 | pal.setColor(QPalette::Window, QColor(35, 39, 46)); 109 | w.getContentWidget().setAutoFillBackground(true); 110 | w.getContentWidget().setPalette(pal); 111 | 112 | // Add layout to main content widget. 113 | QVBoxLayout *main_widget_layout = new QVBoxLayout(&w); 114 | w.getContentWidget().setLayout(main_widget_layout); 115 | 116 | // Add some button. 117 | QPushButton *btn = new QPushButton("This is Button!", &w.getContentWidget()); 118 | btn->setFixedSize(180, 35); 119 | main_widget_layout->addWidget(btn); 120 | main_widget_layout->setAlignment(btn, Qt::AlignHCenter); 121 | 122 | w.show(); 123 | 124 | return app.exec(); 125 | } 126 | -------------------------------------------------------------------------------- /FrameLessQtWidget/resources.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | Resources/Close.png 4 | Resources/CloseDeactivated.png 5 | Resources/Maximize.png 6 | Resources/MaximizeDeactivated.png 7 | Resources/Restore.png 8 | Resources/RestoreDeactivated.png 9 | Resources/Minimize.png 10 | Resources/MinimizeDeactivated.png 11 | Resources/Transparent.png 12 | Resources/CloseHoverOrPressed.png 13 | Resources/ApplicationIcon.png 14 | 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 KyungJoon Lee 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Qt Frameless window on windows OS 2 | 3 | ## About 4 | 5 | This is a template projects that let you know how to make frameless window via Qt on windows OS. 6 | There are two ways to make frameless windows, one uses QtWidget, the other uses QtQuick. 7 |   8 | 9 | ## Showcase of FrameLessQtWidget 10 | ![FrameLessQtWidgetShowcase](/resources/FrameLessQtWidgetShowcase.gif) 11 |   12 | 13 | ## Showcase of FrameLessQtQuick 14 | ![FrameLessQtWidgetShowcase](/resources/FrameLessQtQuickShowcase.gif) 15 |   16 | 17 | ## How to build 18 | 19 | Environment that you may need to install to build this project are listed below. 20 |   21 | 22 | ### CMake 23 | 24 | This is **necessary** to build this project. 25 | I have tested this template project with CMake 3.23. 26 | I also used **CMakePresets.json** file to make this project, but this is not essential. 27 |   28 | 29 | ### Qt 30 | 31 | This project **requires** Qt5 (or higher) to run. 32 | I have tested this template project with Qt 5.15.10 and Qt 6.5.2. 33 |   34 | 35 | ### Visual Studio 2022 36 | 37 | I made this project using MSVC 2022 compiler but other version of MSVC compiler should work too. 38 |   39 | 40 | ### VS Code 41 | 42 | I made this project using VS Code, but this is not **necessary**. 43 | In case you are wondering which VS Code extension I used in this project, I have listed the extension list below. 44 | 45 | * [C/C++ by Microsoft](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) 46 | * [CMake by twxs](https://marketplace.visualstudio.com/items?itemName=twxs.cmake) 47 | * [CMake Tools by Microsoft](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cmake-tools) 48 | 49 |   50 | ### Other Instructions 51 | 52 | You have to modify **CMakePresets.json** file. 53 | ```json 54 | { 55 | "description": "Windows builds only", 56 | "name": "windows-base", 57 | "hidden": true, 58 | "binaryDir": "${sourceDir}/Build/${presetName}", 59 | "installDir": "${sourceDir}/Installed/${presetName}", 60 | "toolchainFile": "D:/ProgramFiles/vcpkg/scripts/buildsystems/vcpkg.cmake", 61 | "condition": { 62 | "type": "equals", 63 | "lhs": "${hostSystemName}", 64 | "rhs": "Windows" 65 | } 66 | } 67 | ``` 68 | If you don't use vcpkg, remove the **toolchainFile** entry. 69 | but if you do, then change this path to your own vcpkg path. 70 |   71 | 72 | You have to change **generator** item too in **CMakePresets.json** file. 73 | ```json 74 | { 75 | "description": "MSVC", 76 | "name": "msvc", 77 | "hidden": true, 78 | "generator": "Visual Studio 17 2022", 79 | "inherits": "windows-base" 80 | } 81 | ``` 82 | If you are already using VS 2022, you are good to go. 83 | But if you're not, change the **generator** you're using. 84 | For example, VS 2019 is **Visual Studio 16 2019** and VS 2017 is **Visual Studio 15 2017**. 85 | You should only use the Visual Studio generator! 86 |   87 | 88 | And you should also change **CMakeLists.txt** file. 89 | ```cmake 90 | # This path should be set to your directory path where Qt5Config.cmake (or Qt6Config.cmake) is located. 91 | set(QT_DIR "D:\\ProgramFiles\\Qt\\5.15.10\\msvc2022_64\\lib\\cmake\\Qt5") 92 | ``` 93 | As the comment above, write your own directory path where Qt5Config.cmake (or Qt6Config.cmake) is located. 94 |   95 | 96 | If you have followed the process above, you are ready to build this template project. 97 | Once you have built the project, you might find the **.exe** file in your own ```Build\msvc-x64\Release(or Debug)``` directory. 98 |   99 | 100 | ## How to Use 101 | 102 | ### FrameLessQtWidget 103 | 104 | You can use frameless window simply like this. 105 | ```c++ 106 | QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); 107 | QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); 108 | 109 | QApplication app(argc, argv); 110 | 111 | MainWindow w; 112 | w.show(); 113 | 114 | return app.exec(); 115 | ``` 116 | It works but you would need more than this. 117 | The MainWindow class offers several additional functions. 118 |   119 | 120 | First, you can initialize the resize border width and title bar height. 121 | ```c++ 122 | MainWindow w; 123 | w.setResizeBorderWidth(6); //! You can set the resize border width. 124 | w.setTitlebarHeight(35); //! You also can set the title bar height. 125 | ``` 126 |   127 | 128 | If you want to change the colors of the custom title bar, write the code like this. 129 | ```c++ 130 | MainWindow w; 131 | 132 | //! MainWindow class provides getTitlebarWidget() function. 133 | //! It return title bar widget. 134 | // Set titlebar widget palette. 135 | auto pal = w.getTitlebarWidget().palette(); 136 | pal.setColor(QPalette::Window, QColor(30, 34, 39)); // Set the colors you want 137 | w.getTitlebarWidget().setAutoFillBackground(true); 138 | w.getTitlebarWidget().setPalette(pal); 139 | ``` 140 |   141 | 142 | If you want to change the colors of the content widget, write the code like this. 143 | ```c++ 144 | MainWindow w; 145 | 146 | //! MainWindow class provides getContentWidget() function. 147 | //! It return main content widget. 148 | // Set main content widget palette. 149 | auto pal = w.getContentWidget().palette(); 150 | pal.setColor(QPalette::Window, QColor(35, 39, 46)); 151 | w.getContentWidget().setAutoFillBackground(true); 152 | w.getContentWidget().setPalette(pal); 153 | ``` 154 |   155 | 156 | If you want to add some widget to the main widget, use ``getContentWidget()`` function like this. 157 | ```c++ 158 | MainWindow w; 159 | 160 | // Add layout to main content widget. 161 | QVBoxLayout *main_widget_layout = new QVBoxLayout(&w); 162 | w.getContentWidget().setLayout(main_widget_layout); 163 | 164 | // Add some button. 165 | QPushButton *btn = new QPushButton("This is Button!", &w.getContentWidget()); 166 | btn->setFixedSize(180, 35); 167 | main_widget_layout->addWidget(btn); 168 | main_widget_layout->setAlignment(btn, Qt::AlignHCenter); 169 | ``` 170 |   171 | 172 | If you want to add menu bar on custom title bar, write the code like this. 173 | ```c++ 174 | MainWindow w; 175 | 176 | // Customize menu bar 177 | QMenuBar *menubar = new QMenuBar(&w); 178 | menubar->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); 179 | 180 | // Initialize menu item. 181 | QMenu *menu = menubar->addMenu("File"); 182 | menu->addAction("New File"); 183 | menu->addAction("Open File"); 184 | menu = menubar->addMenu("Edit"); 185 | menu->addAction("Undo"); 186 | menu->addAction("Redo"); 187 | 188 | // Add widget to title bar. 189 | // "clickable widget" property should be true if you want this widget to work on the title bar. 190 | menubar->setProperty("clickable widget", true); 191 | w.getCustomTitlebarLayout().addWidget(menubar); 192 | ``` 193 | This ```menubar->setProperty("clickable widget", true);``` line determines whether the widget on the custom title bar is clickable or not. 194 | If **clickable widget** property is true, the widget will work but you can't drag that area for moving frameless window. 195 | If **clickable widget** property is false, the widget will not react to your mouse but you can drag that area for moving frameless window. 196 | So if you add a widget to the custom title bar, don't forget to set the **clickable widget** property! 197 |   198 | 199 | ### FrameLessQtQuick 200 | 201 | There are two things you need to follow in **main.cpp** file. 202 | 203 | First, right after declaring QGuiApplication, you need to initialize MainWindow and install native event filter. 204 | ```c++ 205 | QGuiApplication app(argc, argv); 206 | 207 | //! Native event filter should be installed on MainWindow class at this time. 208 | //! If you install NativeEventFilter later, it is really hard to make window to frameless window. 209 | MainWindow win_quick_window; 210 | app.installNativeEventFilter(&win_quick_window); 211 | ``` 212 |   213 | 214 | Second, use the ``initWindow()`` function in the linked lambda function like this. 215 | ```c++ 216 | QQmlApplicationEngine engine; 217 | 218 | const QUrl url(QStringLiteral("qrc:/qml/main.qml")); 219 | 220 | QObject::connect( 221 | &engine, &QQmlApplicationEngine::objectCreated, 222 | &app, [&](QObject *obj, const QUrl &obj_url) { 223 | // Initialize window here 224 | if ((!obj && url == obj_url) || !win_quick_window.initWindow(engine)) 225 | QCoreApplication::exit(-1); 226 | }, 227 | Qt::QueuedConnection); 228 | engine.load(url); 229 | ``` 230 | These two things are all you need to care about in the cpp part. 231 |   232 | 233 | In **main.qml**, ApplicationWindow flags should be **Qt.Window**. 234 | And there should be a resizeBorderWidth property like the one below. 235 | ```qml 236 | // Border width for window resizing 237 | // This is used in the WinQuickWindow.cpp file. 238 | property int resizeBorderWidth: 6 239 | ``` 240 |   241 | 242 | An example of the Minimize button is shown below. 243 | ```qml 244 | Button { 245 | id: minimizeButton 246 | background: Rectangle { 247 | color: parent.down ? Qt.rgba(1.0, 1.0, 1.0, 0.4) : (parent.hovered ? Qt.rgba(1.0, 1.0, 1.0, 0.2) : 248 | Qt.rgba(1.0, 1.0, 1.0, 0.0)) 249 | } 250 | Image { 251 | source: active ? "qrc:/icon/Minimize.png" : "qrc:/icon/MinimizeDeactivated.png" 252 | anchors.fill: parent 253 | fillMode: Image.PreserveAspectFit 254 | } 255 | 256 | // Trigger onMinimizeButtonClicked function implemented on C++ side. 257 | onClicked: { 258 | cppConnector.onMinimizeButtonClicked() 259 | } 260 | } 261 | ``` 262 | ```cppConnector.onMinimizeButtonClicked()``` this can trigger Minimize logic. 263 |   264 | 265 | An example of the Maximize button is shown below. 266 | Maximize button should have **objectName**, **checkable**, **checked** value like below. 267 | ```qml 268 | Button { 269 | id: maximizeButton 270 | objectName: "maximizeButton" 271 | checkable: true 272 | checked: false 273 | background: Rectangle { 274 | color: parent.down ? Qt.rgba(1.0, 1.0, 1.0, 0.4) : (parent.hovered ? Qt.rgba(1.0, 1.0, 1.0, 0.2) : 275 | Qt.rgba(1.0, 1.0, 1.0, 0.0)) 276 | } 277 | Image { 278 | source: maximizeButton.checked ? (active ? "qrc:/icon/Restore.png" : "qrc:/icon/RestoreDeactivated.png") : 279 | (active ? "qrc:/icon/Maximize.png" : "qrc:/icon/MaximizeDeactivated.png") 280 | anchors.fill: parent 281 | fillMode: Image.PreserveAspectFit 282 | } 283 | 284 | // Trigger onMaximizeButtonClicked function implemented on C++ side. 285 | onClicked: { 286 | cppConnector.onMaximizeButtonClicked() 287 | } 288 | } 289 | ``` 290 | ```cppConnector.onMaximizeButtonClicked()``` this can trigger Maximize logic. 291 |   292 | 293 | An example of the Close button is shown below. 294 | ```qml 295 | Button { 296 | id: closeButton 297 | background: Rectangle { 298 | color: parent.down ? Qt.rgba(0.78, 0.16, 0.184, 0.6) : (parent.hovered ? Qt.rgba(0.86, 0.16, 0.184, 0.9) : 299 | Qt.rgba(1.0, 1.0, 1.0, 0.0)) 300 | } 301 | Image { 302 | source: closeButton.hovered ? "qrc:/icon/CloseHoverOrPressed.png" : 303 | (active ? "qrc:/icon/Close.png" : "qrc:/icon/CloseDeactivated.png") 304 | anchors.fill: parent 305 | fillMode: Image.PreserveAspectFit 306 | } 307 | 308 | // Trigger onMaximizeButtonClicked function implemented on C++ side. 309 | onClicked: { 310 | cppConnector.onCloseButtonClicked() 311 | } 312 | } 313 | ``` 314 | ```cppConnector.onCloseButtonClicked()``` this can trigger Close logic. 315 |   316 | 317 | You can make the area for dragging a frameless window like this. 318 | ```qml 319 | MouseArea { 320 | anchors.fill: parent 321 | 322 | // When you press this area, the window becomes movable. 323 | onPressed: { 324 | applicationWindow.startSystemMove() 325 | } 326 | 327 | // When you double-click this area, the window state changes as well. 328 | onDoubleClicked: { 329 | applicationWindow.visibility = maximizeButton.checked ? Window.Windowed : Window.Maximized 330 | } 331 | } 332 | ``` 333 |   334 | 335 | The rest qml is up to you to fill in. 336 | Simply combine what you want! 337 |   338 | 339 | ## Key point of making frameless window 340 | 341 | As you can see from the project code, **WM_NCCALCSIZE**, **WM_NCHITTEST** is the key message you should focus on. 342 |   343 | 344 | Modifying the WM_NCCALCSIZE message process helps you to remove window borders. 345 | ```c++ 346 | case WM_NCCALCSIZE: { 347 | if (msg->lParam) 348 | { 349 | WINDOWPLACEMENT wp; 350 | GetWindowPlacement(m_hwnd, &wp); 351 | 352 | if (wp.showCmd == SW_MAXIMIZE) 353 | { 354 | NCCALCSIZE_PARAMS *sz = (NCCALCSIZE_PARAMS *)msg->lParam; 355 | sz->rgrc[0].left += 8; 356 | sz->rgrc[0].top += 8; 357 | sz->rgrc[0].right -= 8; 358 | sz->rgrc[0].bottom -= 8; 359 | 360 | // Save window mode state. 361 | m_quick_window->findChild("maximumButton")->setProperty("checked", true); 362 | } 363 | else if (wp.showCmd == SW_NORMAL) 364 | // Save window mode state. 365 | m_quick_window->findChild("maximumButton")->setProperty("checked", false); 366 | } 367 | return true; 368 | } 369 | ``` 370 |   371 | 372 | Modifying the WM_NCHITTEST message process helps you change and move frameless window. 373 | ```c++ 374 | case WM_NCHITTEST: { 375 | RECT winrect; 376 | GetWindowRect(msg->hwnd, &winrect); 377 | long x = GET_X_LPARAM(msg->lParam); 378 | long y = GET_Y_LPARAM(msg->lParam); 379 | 380 | if (x >= winrect.left && x < winrect.left + m_resize_border_width && 381 | y < winrect.bottom && y >= winrect.bottom - m_resize_border_width) 382 | { 383 | *result = HTBOTTOMLEFT; 384 | return true; 385 | } 386 | 387 | if (x < winrect.right && x >= winrect.right - m_resize_border_width && 388 | y < winrect.bottom && y >= winrect.bottom - m_resize_border_width) 389 | { 390 | *result = HTBOTTOMRIGHT; 391 | return true; 392 | } 393 | 394 | if (x >= winrect.left && x < winrect.left + m_resize_border_width && 395 | y >= winrect.top && y < winrect.top + m_resize_border_width) 396 | { 397 | *result = HTTOPLEFT; 398 | return true; 399 | } 400 | 401 | if (x < winrect.right && x >= winrect.right - m_resize_border_width && 402 | y >= winrect.top && y < winrect.top + m_resize_border_width) 403 | { 404 | *result = HTTOPRIGHT; 405 | return true; 406 | } 407 | 408 | if (x >= winrect.left && x < winrect.left + m_resize_border_width) 409 | { 410 | *result = HTLEFT; 411 | return true; 412 | } 413 | 414 | if (x < winrect.right && x >= winrect.right - m_resize_border_width) 415 | { 416 | *result = HTRIGHT; 417 | return true; 418 | } 419 | 420 | if (y < winrect.bottom && y >= winrect.bottom - m_resize_border_width) 421 | { 422 | *result = HTBOTTOM; 423 | return true; 424 | } 425 | 426 | if (y >= winrect.top && y < winrect.top + m_resize_border_width) 427 | { 428 | *result = HTTOP; 429 | return true; 430 | } 431 | 432 | *result = HTTRANSPARENT; 433 | break; 434 | } 435 | ``` 436 |   437 | 438 | ## Licence 439 | 440 | This project template is licensed under the [MIT License](https://github.com/tongmon/qt-frameless-windows/blob/main/LICENSE). 441 | -------------------------------------------------------------------------------- /resources/FrameLessQtQuickShowcase.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tongmon/qt-frameless-windows/65f6e51c7b3d30032e0861ac7533d1a89c3354c4/resources/FrameLessQtQuickShowcase.gif -------------------------------------------------------------------------------- /resources/FrameLessQtWidgetShowcase.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tongmon/qt-frameless-windows/65f6e51c7b3d30032e0861ac7533d1a89c3354c4/resources/FrameLessQtWidgetShowcase.gif --------------------------------------------------------------------------------