Skip to content
This repository has been archived by the owner on Dec 19, 2023. It is now read-only.

Commit

Permalink
Final code of version 2.0.0!
Browse files Browse the repository at this point in the history
Some minor issues are known to exist and they'll get fixed before 2.1 is officially released.

Signed-off-by: Yuhang Zhao <[email protected]>
  • Loading branch information
wangwenx190 committed Apr 23, 2022
1 parent 8460995 commit a0a9b8d
Show file tree
Hide file tree
Showing 13 changed files with 282 additions and 114 deletions.
52 changes: 49 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,23 @@

## Screenshots

TODO
### Windows

![Light](./doc/win_light.png)

![Dark](./doc/win_dark.png)

### Linux

![Light](./doc/linux_light.png)

![Dark](./doc/linux_dark.png)

### macOS

![Light](./doc/mac_light.png)

![Dark](./doc/mac_dark.png)

## Roadmap

Expand All @@ -28,9 +44,39 @@ TODO
- [ ] Windows: Maximize button docking feature introduced in Windows 11
- [ ] More feature requests are welcome!

## Feedback
## Build

```bash
git clone https://github.com/wangwenx190/framelesshelper.git
mkdir build
cd build
cmake -DCMAKE_PREFIX_PATH=<YOUR_QT_SDK_DIR_PATH> -DCMAKE_BUILD_TYPE=Release -GNinja ../framelesshelper
cmake --build . --config Release --target all --parallel
```

**Note**: On Linux you need to install the GTK3 and X11 development packages first.

## Use

For Qt Widgets applications: subclass `FramelessWidget` or `FramelessMainWindow`.

For Qt Quick applications: use `FramelessWindow` instead of `Window`.

Please refer to the demo applications to see more detailed usages: [examples](./examples/)

## Platform Notes

### Windows

- If DWM composition is disabled in some very rare cases (only possible on Windows 7), the top-left corner and top-right corner will appear in round shape.

### Linux

- FramelessHelper will force your application to use the _XCB_ platform plugin when running on Wayland.

### macOS

Please write down your feature requests and bug reports in here: <https://github.com/wangwenx190/framelesshelper/issues/104>. Thanks!
- The three system buttons on the title bar can't be made hidden for Qt Widgets applications, for some unknown reason.

## License

Expand Down
Binary file added doc/linux_dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/linux_light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/mac_dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/mac_light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/win_dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/win_light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 4 additions & 3 deletions include/FramelessHelper/Core/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,10 @@ FRAMELESSHELPER_CORE_API void moveWindowToDesktopCenter(
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isWin8OrGreater();
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isWin8Point1OrGreater();
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isWin10OrGreater();
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isWin101607OrGreater();
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isWin101809OrGreater();
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isWin101903OrGreater();
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isWin10_1607OrGreater();
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isWin10_1809OrGreater();
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isWin10_1903OrGreater();
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isWin10_2004OrGreater();
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isWin11OrGreater();
[[nodiscard]] FRAMELESSHELPER_CORE_API bool isDwmCompositionEnabled();
FRAMELESSHELPER_CORE_API void triggerFrameChange(const WId windowId);
Expand Down
8 changes: 4 additions & 4 deletions src/core/framelesshelper_win.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -245,12 +245,12 @@ void FramelessHelperWin::addWindow(const UserSettings &settings, const SystemPar
}
Utils::updateInternalWindowFrameMargins(params.getWindowHandle(), true);
Utils::updateWindowFrameMargins(windowId, false);
if (Utils::isWin101607OrGreater()) {
if (Utils::isWin10_1607OrGreater()) {
const bool dark = Utils::shouldAppsUseDarkMode();
if (!(settings.options & Option::DontTouchWindowFrameBorderColor)) {
Utils::updateWindowFrameBorderColor(windowId, dark);
}
if (Utils::isWin101809OrGreater()) {
if (Utils::isWin10_1809OrGreater()) {
if (settings.options & Option::SyncNativeControlsThemeWithSystem) {
Utils::updateGlobalWin32ControlsTheme(windowId, dark);
}
Expand Down Expand Up @@ -821,7 +821,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
}
bool systemThemeChanged = ((uMsg == WM_THEMECHANGED) || (uMsg == WM_SYSCOLORCHANGE)
|| (uMsg == WM_DWMCOLORIZATIONCOLORCHANGED));
if (Utils::isWin101607OrGreater()) {
if (Utils::isWin10_1607OrGreater()) {
if (uMsg == WM_SETTINGCHANGE) {
if ((wParam == 0) && (QString::fromWCharArray(reinterpret_cast<LPCWSTR>(lParam))
.compare(qThemeSettingChangeEventName, Qt::CaseInsensitive) == 0)) {
Expand All @@ -830,7 +830,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
if (!(data.settings.options & Option::DontTouchWindowFrameBorderColor)) {
Utils::updateWindowFrameBorderColor(windowId, dark);
}
if (Utils::isWin101809OrGreater()) {
if (Utils::isWin10_1809OrGreater()) {
if (data.settings.options & Option::SyncNativeControlsThemeWithSystem) {
Utils::updateGlobalWin32ControlsTheme(windowId, dark);
}
Expand Down
3 changes: 3 additions & 0 deletions src/core/framelesswindowsmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,9 @@ void FramelessWindowsManagerPrivate::initialize()
m_colorizationArea = Utils::getDwmColorizationArea();
m_accentColor = Utils::getDwmColorizationColor();
#endif
#ifdef Q_OS_LINUX
m_accentColor = Utils::getWmThemeColor();
#endif
#ifdef Q_OS_MACOS
m_accentColor = Utils::getControlsAccentColor();
#endif
Expand Down
46 changes: 29 additions & 17 deletions src/core/utils_linux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,21 +34,21 @@ FRAMELESSHELPER_BEGIN_NAMESPACE

using namespace Global;

static constexpr const auto _NET_WM_MOVERESIZE_SIZE_TOPLEFT = 0;
static constexpr const auto _NET_WM_MOVERESIZE_SIZE_TOP = 1;
static constexpr const auto _NET_WM_MOVERESIZE_SIZE_TOPRIGHT = 2;
static constexpr const auto _NET_WM_MOVERESIZE_SIZE_RIGHT = 3;
static constexpr const auto _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT = 4;
static constexpr const auto _NET_WM_MOVERESIZE_SIZE_BOTTOM = 5;
static constexpr const auto _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT = 6;
static constexpr const auto _NET_WM_MOVERESIZE_SIZE_LEFT = 7;
static constexpr const auto _NET_WM_MOVERESIZE_MOVE = 8;

static constexpr const char WM_MOVERESIZE_OPERATION_NAME[] = "_NET_WM_MOVERESIZE";

static constexpr const char GTK_THEME_NAME_ENV_VAR[] = "GTK_THEME";
static constexpr const char GTK_THEME_NAME_PROP[] = "gtk-theme-name";
static constexpr const char GTK_THEME_PREFER_DARK_PROP[] = "gtk-application-prefer-dark-theme";
[[maybe_unused]] static constexpr const auto _NET_WM_MOVERESIZE_SIZE_TOPLEFT = 0;
[[maybe_unused]] static constexpr const auto _NET_WM_MOVERESIZE_SIZE_TOP = 1;
[[maybe_unused]] static constexpr const auto _NET_WM_MOVERESIZE_SIZE_TOPRIGHT = 2;
[[maybe_unused]] static constexpr const auto _NET_WM_MOVERESIZE_SIZE_RIGHT = 3;
[[maybe_unused]] static constexpr const auto _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT = 4;
[[maybe_unused]] static constexpr const auto _NET_WM_MOVERESIZE_SIZE_BOTTOM = 5;
[[maybe_unused]] static constexpr const auto _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT = 6;
[[maybe_unused]] static constexpr const auto _NET_WM_MOVERESIZE_SIZE_LEFT = 7;
[[maybe_unused]] static constexpr const auto _NET_WM_MOVERESIZE_MOVE = 8;

[[maybe_unused]] static constexpr const char WM_MOVERESIZE_OPERATION_NAME[] = "_NET_WM_MOVERESIZE";

[[maybe_unused]] static constexpr const char GTK_THEME_NAME_ENV_VAR[] = "GTK_THEME";
[[maybe_unused]] static constexpr const char GTK_THEME_NAME_PROP[] = "gtk-theme-name";
[[maybe_unused]] static constexpr const char GTK_THEME_PREFER_DARK_PROP[] = "gtk-application-prefer-dark-theme";
FRAMELESSHELPER_STRING_CONSTANT2(GTK_THEME_DARK_REGEX, "[:-]dark")

template<typename T>
Expand Down Expand Up @@ -80,7 +80,8 @@ template<typename T>
return result;
}

[[nodiscard]] static inline int qtEdgesToWmMoveOrResizeOperation(const Qt::Edges edges)
[[maybe_unused]] [[nodiscard]] static inline int
qtEdgesToWmMoveOrResizeOperation(const Qt::Edges edges)
{
if (edges == Qt::Edges{}) {
return -1;
Expand Down Expand Up @@ -112,7 +113,8 @@ template<typename T>
return -1;
}

static inline void doStartSystemMoveResize(const WId windowId, const QPoint &globalPos, const int edges)
[[maybe_unused]] static inline void
doStartSystemMoveResize(const WId windowId, const QPoint &globalPos, const int edges)
{
Q_ASSERT(windowId);
Q_ASSERT(edges >= 0);
Expand Down Expand Up @@ -161,11 +163,16 @@ void Utils::startSystemMove(QWindow *window, const QPoint &globalPos)
if (!window) {
return;
}
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
Q_UNUSED(globalPos);
window->startSystemMove();
#else
// Qt always gives us logical coordinates, however, the native APIs
// are expecting device coordinates.
const qreal dpr = window->devicePixelRatio();
const QPoint globalPos2 = QPointF(QPointF(globalPos) * dpr).toPoint();
doStartSystemMoveResize(window->winId(), globalPos2, _NET_WM_MOVERESIZE_MOVE);
#endif
}

void Utils::startSystemResize(QWindow *window, const Qt::Edges edges, const QPoint &globalPos)
Expand All @@ -177,6 +184,10 @@ void Utils::startSystemResize(QWindow *window, const Qt::Edges edges, const QPoi
if (edges == Qt::Edges{}) {
return;
}
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
Q_UNUSED(globalPos);
window->startSystemResize(edges);
#else
const int section = qtEdgesToWmMoveOrResizeOperation(edges);
if (section < 0) {
return;
Expand All @@ -186,6 +197,7 @@ void Utils::startSystemResize(QWindow *window, const Qt::Edges edges, const QPoi
const qreal dpr = window->devicePixelRatio();
const QPoint globalPos2 = QPointF(QPointF(globalPos) * dpr).toPoint();
doStartSystemMoveResize(window->winId(), globalPos2, section);
#endif
}

bool Utils::isTitleBarColorized()
Expand Down
58 changes: 32 additions & 26 deletions src/core/utils_mac.mm
Original file line number Diff line number Diff line change
Expand Up @@ -141,29 +141,6 @@ void setSystemTitleBarVisible(const bool visible)
return [nsview window];
}

static inline void mac_windowStartNativeDrag(const WId windowId, const QPoint &globalPos)
{
Q_ASSERT(windowId);
if (!windowId) {
return;
}
const NSWindow * const nswindow = mac_getNSWindow(windowId);
Q_ASSERT(nswindow);
if (!nswindow) {
return;
}
const CGEventRef clickDown = CGEventCreateMouseEvent(
NULL, kCGEventLeftMouseDown, CGPointMake(globalPos.x(), globalPos.y()), kCGMouseButtonLeft);
NSEvent * const nsevent = [NSEvent eventWithCGEvent:clickDown];
Q_ASSERT(nsevent);
if (!nsevent) {
CFRelease(clickDown);
return;
}
[nswindow performWindowDragWithEvent:nsevent];
CFRelease(clickDown);
}

SystemTheme Utils::getSystemTheme()
{
// ### TODO: how to detect high contrast mode on macOS?
Expand Down Expand Up @@ -199,14 +176,43 @@ static inline void mac_windowStartNativeDrag(const WId windowId, const QPoint &g
if (!window) {
return;
}
mac_windowStartNativeDrag(window->winId(), globalPos);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
Q_UNUSED(globalPos);
window->startSystemMove();
#else
const NSWindow * const nswindow = mac_getNSWindow(window->winId());
Q_ASSERT(nswindow);
if (!nswindow) {
return;
}
const CGEventRef clickDown = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDown,
CGPointMake(globalPos.x(), globalPos.y()), kCGMouseButtonLeft);
NSEvent * const nsevent = [NSEvent eventWithCGEvent:clickDown];
Q_ASSERT(nsevent);
if (!nsevent) {
CFRelease(clickDown);
return;
}
[nswindow performWindowDragWithEvent:nsevent];
CFRelease(clickDown);
#endif
}

void Utils::startSystemResize(QWindow *window, const Qt::Edges edges, const QPoint &globalPos)
{
Q_UNUSED(window);
Q_UNUSED(edges);
Q_ASSERT(window);
if (!window) {
return;
}
if (edges == Qt::Edges{}) {
return;
}
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
Q_UNUSED(globalPos);
window->startSystemResize(edges);
#else
Q_UNUSED(globalPos);
#endif
}

QColor Utils::getControlsAccentColor()
Expand Down
Loading

0 comments on commit a0a9b8d

Please sign in to comment.