Add possibility to enable native window frame to Ui::Window

This commit is contained in:
Ilya Fedin 2021-07-28 00:07:09 +04:00 committed by John Preston
parent 95cafc5064
commit 7a71876124
12 changed files with 229 additions and 54 deletions

View file

@ -253,6 +253,7 @@ elseif(LINUX)
target_link_libraries(lib_ui
PUBLIC
desktop-app::lib_waylandshells
desktop-app::external_kwayland
)
endif()

View file

@ -24,23 +24,71 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <private/qwaylandwindow_p.h>
#include <private/qwaylandshellsurface_p.h>
#include <connection_thread.h>
#include <registry.h>
Q_DECLARE_METATYPE(QMargins);
using QtWaylandClient::QWaylandIntegration;
using QtWaylandClient::QWaylandWindow;
using namespace KWayland::Client;
namespace Ui {
namespace Platform {
WaylandIntegration::WaylandIntegration() {
struct WaylandIntegration::Private {
std::unique_ptr<ConnectionThread> connection;
Registry registry;
QEventLoop interfacesLoop;
bool interfacesAnnounced = false;
};
WaylandIntegration::WaylandIntegration()
: _private(std::make_unique<Private>()) {
_private->connection = std::unique_ptr<ConnectionThread>{
ConnectionThread::fromApplication(),
};
_private->registry.create(_private->connection.get());
_private->registry.setup();
QObject::connect(
_private->connection.get(),
&ConnectionThread::connectionDied,
&_private->registry,
&Registry::destroy);
QObject::connect(
&_private->registry,
&Registry::interfacesAnnounced,
[=] {
_private->interfacesAnnounced = true;
if (_private->interfacesLoop.isRunning()) {
_private->interfacesLoop.quit();
}
});
}
WaylandIntegration::~WaylandIntegration() = default;
WaylandIntegration *WaylandIntegration::Instance() {
if (!::Platform::IsWayland()) return nullptr;
static WaylandIntegration instance;
return &instance;
}
void WaylandIntegration::waitForInterfaceAnnounce() {
Expects(!_private->interfacesLoop.isRunning());
if (!_private->interfacesAnnounced) {
_private->interfacesLoop.exec();
}
}
bool WaylandIntegration::xdgDecorationSupported() {
return _private->registry.hasInterface(
Registry::Interface::XdgDecorationUnstableV1);
}
bool WaylandIntegration::windowExtentsSupported() {
// initialize shell integration before querying
if (const auto integration = static_cast<QWaylandIntegration*>(

View file

@ -14,15 +14,21 @@ namespace Platform {
class WaylandIntegration {
public:
static WaylandIntegration *Instance();
[[nodiscard]] static WaylandIntegration *Instance();
bool windowExtentsSupported();
void waitForInterfaceAnnounce();
[[nodiscard]] bool xdgDecorationSupported();
[[nodiscard]] bool windowExtentsSupported();
void setWindowExtents(QWindow *window, const QMargins &extents);
void unsetWindowExtents(QWindow *window);
bool showWindowMenu(QWindow *window);
private:
WaylandIntegration();
~WaylandIntegration();
struct Private;
const std::unique_ptr<Private> _private;
};
} // namespace Platform

View file

@ -12,6 +12,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Ui {
namespace Platform {
struct WaylandIntegration::Private {
};
WaylandIntegration::WaylandIntegration() {
}
@ -21,6 +24,13 @@ WaylandIntegration *WaylandIntegration::Instance() {
return &instance;
}
void WaylandIntegration::waitForInterfaceAnnounce() {
}
bool WaylandIntegration::xdgDecorationSupported() {
return false;
}
bool WaylandIntegration::windowExtentsSupported() {
return false;
}

View file

@ -6,6 +6,8 @@
//
#include "ui/platform/linux/ui_window_linux.h"
#include "ui/platform/linux/ui_linux_wayland_integration.h"
namespace Ui {
namespace Platform {
@ -14,5 +16,11 @@ std::unique_ptr<BasicWindowHelper> CreateSpecialWindowHelper(
return nullptr;
}
bool NativeWindowFrameSupported() {
const auto waylandIntegration = WaylandIntegration::Instance();
return !waylandIntegration
|| waylandIntegration->xdgDecorationSupported();
}
} // namespace Platform
} // namespace Ui

View file

@ -412,5 +412,9 @@ std::unique_ptr<BasicWindowHelper> CreateSpecialWindowHelper(
return std::make_unique<WindowHelper>(window);
}
bool NativeWindowFrameSupported() {
return false;
}
} // namespace Platform
} // namespace Ui

View file

@ -44,6 +44,9 @@ void BasicWindowHelper::setTitle(const QString &title) {
void BasicWindowHelper::setTitleStyle(const style::WindowTitle &st) {
}
void BasicWindowHelper::setNativeFrame(bool enabled) {
}
void BasicWindowHelper::setMinimumSize(QSize size) {
_window->setMinimumSize(size);
}
@ -161,17 +164,21 @@ void DefaultWindowHelper::init() {
rpl::combine(
window()->sizeValue(),
_title->heightValue()
) | rpl::start_with_next([=](QSize size, int titleHeight) {
_title->heightValue(),
_title->shownValue()
) | rpl::start_with_next([=](
QSize size,
int titleHeight,
bool titleShown) {
const auto area = resizeArea();
const auto sizeWithoutMargins = size
.shrunkBy({ 0, titleHeight, 0, 0 })
.shrunkBy({ 0, titleShown ? titleHeight : 0, 0, 0 })
.shrunkBy(area);
const auto topLeft = QPoint(
area.left(),
area.top() + titleHeight);
area.top() + (titleShown ? titleHeight : 0));
_body->setGeometry(QRect(topLeft, sizeWithoutMargins));
}, _body->lifetime());
@ -233,7 +240,9 @@ bool DefaultWindowHelper::hasShadow() const {
}
QMargins DefaultWindowHelper::resizeArea() const {
if (window()->isMaximized() || window()->isFullScreen()) {
if (window()->isMaximized()
|| window()->isFullScreen()
|| _nativeFrame) {
return QMargins();
}
@ -310,16 +319,23 @@ void DefaultWindowHelper::setTitleStyle(const style::WindowTitle &st) {
_title->st()->height);
}
void DefaultWindowHelper::setNativeFrame(bool enabled) {
_nativeFrame = enabled;
window()->windowHandle()->setFlag(Qt::FramelessWindowHint, !enabled);
_title->setVisible(!enabled);
updateWindowExtents();
}
void DefaultWindowHelper::setMinimumSize(QSize size) {
const auto sizeWithMargins = size
.grownBy({ 0, _title->height(), 0, 0 })
.grownBy({ 0, _title->isVisible() ? _title->height() : 0, 0, 0 })
.grownBy(resizeArea());
window()->setMinimumSize(sizeWithMargins);
}
void DefaultWindowHelper::setFixedSize(QSize size) {
const auto sizeWithMargins = size
.grownBy({ 0, _title->height(), 0, 0 })
.grownBy({ 0, _title->isVisible() ? _title->height() : 0, 0, 0 })
.grownBy(resizeArea());
window()->setFixedSize(sizeWithMargins);
_title->setResizeEnabled(false);
@ -327,7 +343,7 @@ void DefaultWindowHelper::setFixedSize(QSize size) {
void DefaultWindowHelper::setGeometry(QRect rect) {
window()->setGeometry(rect
.marginsAdded({ 0, _title->height(), 0, 0 })
.marginsAdded({ 0, _title->isVisible() ? _title->height() : 0, 0, 0 })
.marginsAdded(resizeArea()));
}
@ -376,7 +392,7 @@ void DefaultWindowHelper::paintBorders(QPainter &p) {
}
void DefaultWindowHelper::updateWindowExtents() {
if (hasShadow()) {
if (hasShadow() && !_nativeFrame) {
Platform::SetWindowExtents(
window()->windowHandle(),
resizeArea());

View file

@ -30,6 +30,7 @@ public:
[[nodiscard]] virtual not_null<RpWidget*> body();
virtual void setTitle(const QString &title);
virtual void setTitleStyle(const style::WindowTitle &st);
virtual void setNativeFrame(bool enabled);
virtual void setMinimumSize(QSize size);
virtual void setFixedSize(QSize size);
virtual void setStaysOnTop(bool enabled);
@ -67,6 +68,7 @@ public:
not_null<RpWidget*> body() override;
void setTitle(const QString &title) override;
void setTitleStyle(const style::WindowTitle &st) override;
void setNativeFrame(bool enabled) override;
void setMinimumSize(QSize size) override;
void setFixedSize(QSize size) override;
void setGeometry(QRect rect) override;
@ -86,6 +88,7 @@ private:
const not_null<DefaultTitleWidget*> _title;
const not_null<RpWidget*> _body;
bool _extentsSet = false;
bool _nativeFrame = false;
};
@ -100,5 +103,7 @@ private:
return std::make_unique<DefaultWindowHelper>(window);
}
bool NativeWindowFrameSupported();
} // namespace Platform
} // namespace Ui

View file

@ -148,7 +148,7 @@ WindowHelper::WindowHelper(not_null<RpWidget*> window)
, _handle(GetWindowHandle(window))
, _title(Ui::CreateChild<TitleWidget>(window.get()))
, _body(Ui::CreateChild<RpWidget>(window.get()))
, _shadow(window, st::windowShadowFg->c) {
, _shadow(std::in_place, window, st::windowShadowFg->c) {
Expects(_handle != nullptr);
GetNativeFilter()->registerWindow(_handle, this);
@ -172,18 +172,36 @@ void WindowHelper::setTitleStyle(const style::WindowTitle &st) {
_title->setStyle(st);
}
void WindowHelper::setNativeFrame(bool enabled) {
_nativeFrame = enabled;
_title->setVisible(!enabled);
if (enabled) {
_shadow.reset();
} else {
_shadow.emplace(window(), st::windowShadowFg->c);
_shadow->setResizeEnabled(!fixedSize());
}
updateMargins();
fixMaximizedWindow();
}
void WindowHelper::setMinimumSize(QSize size) {
window()->setMinimumSize(size.width(), _title->height() + size.height());
const auto titleHeight = _title->isVisible() ? _title->height() : 0;
window()->setMinimumSize(size.width(), titleHeight + size.height());
}
void WindowHelper::setFixedSize(QSize size) {
window()->setFixedSize(size.width(), _title->height() + size.height());
const auto titleHeight = _title->isVisible() ? _title->height() : 0;
window()->setFixedSize(size.width(), titleHeight + size.height());
_title->setResizeEnabled(false);
_shadow.setResizeEnabled(false);
if (_shadow) {
_shadow->setResizeEnabled(false);
}
}
void WindowHelper::setGeometry(QRect rect) {
window()->setGeometry(rect.marginsAdded({ 0, _title->height(), 0, 0 }));
const auto titleHeight = _title->isVisible() ? _title->height() : 0;
window()->setGeometry(rect.marginsAdded({ 0, titleHeight, 0, 0 }));
}
void WindowHelper::showFullScreen() {
@ -205,19 +223,25 @@ void WindowHelper::showNormal() {
void WindowHelper::init() {
style::PaletteChanged(
) | rpl::start_with_next([=] {
_shadow.setColor(st::windowShadowFg->c);
if (_shadow) {
_shadow->setColor(st::windowShadowFg->c);
}
Ui::ForceFullRepaint(window());
}, window()->lifetime());
rpl::combine(
window()->sizeValue(),
_title->heightValue()
) | rpl::start_with_next([=](QSize size, int titleHeight) {
_title->heightValue(),
_title->shownValue()
) | rpl::start_with_next([=](
QSize size,
int titleHeight,
bool titleShown) {
_body->setGeometry(
0,
titleHeight,
titleShown ? titleHeight : 0,
size.width(),
size.height() - titleHeight);
size.height() - (titleShown ? titleHeight : 0));
}, _body->lifetime());
updateMargins();
@ -256,22 +280,27 @@ bool WindowHelper::handleNativeEvent(
if (LOWORD(wParam) == WA_CLICKACTIVE) {
Ui::MarkInactivePress(window(), true);
}
if (LOWORD(wParam) != WA_INACTIVE) {
_shadow.update(WindowShadow::Change::Activate);
} else {
_shadow.update(WindowShadow::Change::Deactivate);
if (_shadow) {
if (LOWORD(wParam) != WA_INACTIVE) {
_shadow->update(WindowShadow::Change::Activate);
} else {
_shadow->update(WindowShadow::Change::Deactivate);
}
}
window()->update();
} return false;
case WM_NCPAINT: {
if (::Platform::IsWindows8OrGreater()) {
if (::Platform::IsWindows8OrGreater() || _nativeFrame) {
return false;
}
if (result) *result = 0;
} return true;
case WM_NCCALCSIZE: {
if (_nativeFrame) {
return false;
}
WINDOWPLACEMENT wp;
wp.length = sizeof(WINDOWPLACEMENT);
if (GetWindowPlacement(_handle, &wp)
@ -315,6 +344,9 @@ bool WindowHelper::handleNativeEvent(
} return true;
case WM_NCACTIVATE: {
if (_nativeFrame) {
return false;
}
if (IsCompositionEnabled()) {
const auto res = DefWindowProc(_handle, msg, wParam, -1);
if (result) *result = res;
@ -326,16 +358,18 @@ bool WindowHelper::handleNativeEvent(
case WM_WINDOWPOSCHANGING:
case WM_WINDOWPOSCHANGED: {
WINDOWPLACEMENT wp;
wp.length = sizeof(WINDOWPLACEMENT);
if (GetWindowPlacement(_handle, &wp)
&& (wp.showCmd == SW_SHOWMAXIMIZED
|| wp.showCmd == SW_SHOWMINIMIZED)) {
_shadow.update(WindowShadow::Change::Hidden);
} else {
_shadow.update(
WindowShadow::Change::Moved | WindowShadow::Change::Resized,
(WINDOWPOS*)lParam);
if (_shadow) {
WINDOWPLACEMENT wp;
wp.length = sizeof(WINDOWPLACEMENT);
if (GetWindowPlacement(_handle, &wp)
&& (wp.showCmd == SW_SHOWMAXIMIZED
|| wp.showCmd == SW_SHOWMINIMIZED)) {
_shadow->update(WindowShadow::Change::Hidden);
} else {
_shadow->update(
WindowShadow::Change::Moved | WindowShadow::Change::Resized,
(WINDOWPOS*)lParam);
}
}
} return false;
@ -354,30 +388,36 @@ bool WindowHelper::handleNativeEvent(
window()->windowHandle()->windowStateChanged(state);
}
updateMargins();
const auto changes = (wParam == SIZE_MINIMIZED
|| wParam == SIZE_MAXIMIZED)
? WindowShadow::Change::Hidden
: (WindowShadow::Change::Resized
| WindowShadow::Change::Shown);
_shadow.update(changes);
if (_shadow) {
const auto changes = (wParam == SIZE_MINIMIZED
|| wParam == SIZE_MAXIMIZED)
? WindowShadow::Change::Hidden
: (WindowShadow::Change::Resized
| WindowShadow::Change::Shown);
_shadow->update(changes);
}
}
} return false;
case WM_SHOWWINDOW: {
const auto style = GetWindowLongPtr(_handle, GWL_STYLE);
const auto changes = WindowShadow::Change::Resized
| ((wParam && !(style & (WS_MAXIMIZE | WS_MINIMIZE)))
? WindowShadow::Change::Shown
: WindowShadow::Change::Hidden);
_shadow.update(changes);
if (_shadow) {
const auto style = GetWindowLongPtr(_handle, GWL_STYLE);
const auto changes = WindowShadow::Change::Resized
| ((wParam && !(style & (WS_MAXIMIZE | WS_MINIMIZE)))
? WindowShadow::Change::Shown
: WindowShadow::Change::Hidden);
_shadow->update(changes);
}
} return false;
case WM_MOVE: {
_shadow.update(WindowShadow::Change::Moved);
if (_shadow) {
_shadow->update(WindowShadow::Change::Moved);
}
} return false;
case WM_NCHITTEST: {
if (!result) {
if (!result || _nativeFrame) {
return false;
}
@ -389,7 +429,7 @@ bool WindowHelper::handleNativeEvent(
p.y - r.top + _marginsDelta.top());
if (!window()->rect().contains(mapped)) {
*result = HTTRANSPARENT;
} else if (!_title->geometry().contains(mapped)) {
} else if (!_title->isVisible() || !_title->geometry().contains(mapped)) {
*result = HTCLIENT;
} else switch (_title->hitTest(_title->pos() + mapped)) {
case HitTestResult::Client:
@ -409,6 +449,9 @@ bool WindowHelper::handleNativeEvent(
} return true;
case WM_NCRBUTTONUP: {
if (_nativeFrame) {
return false;
}
SendMessage(_handle, WM_SYSCOMMAND, SC_MOUSEMENU, lParam);
} return true;
@ -519,8 +562,11 @@ void WindowHelper::updateMargins() {
_marginsDelta = QMargins();
}
if (_isFullScreen) {
if (_isFullScreen || _nativeFrame) {
margins = QMargins();
if (_nativeFrame) {
_marginsDelta = QMargins();
}
}
if (const auto native = QGuiApplication::platformNativeInterface()) {
native->setWindowProperty(
@ -573,6 +619,25 @@ void WindowHelper::updateSystemMenu(Qt::WindowState state) {
}
}
void WindowHelper::fixMaximizedWindow() {
auto r = RECT();
GetClientRect(_handle, &r);
const auto style = GetWindowLongPtr(_handle, GWL_STYLE);
const auto styleEx = GetWindowLongPtr(_handle, GWL_EXSTYLE);
AdjustWindowRectEx(&r, style, false, styleEx);
if (style & WS_MAXIMIZE) {
auto w = RECT();
GetWindowRect(_handle, &w);
if (const auto hMonitor = MonitorFromRect(&w, MONITOR_DEFAULTTONEAREST)) {
MONITORINFO mi;
mi.cbSize = sizeof(mi);
GetMonitorInfo(hMonitor, &mi);
const auto m = mi.rcWork;
SetWindowPos(_handle, 0, 0, 0, m.right - m.left - _marginsDelta.left() - _marginsDelta.right(), m.bottom - m.top - _marginsDelta.top() - _marginsDelta.bottom(), SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREPOSITION);
}
}
}
not_null<WindowHelper::NativeFilter*> WindowHelper::GetNativeFilter() {
Expects(QCoreApplication::instance() != nullptr);
@ -619,5 +684,9 @@ std::unique_ptr<BasicWindowHelper> CreateSpecialWindowHelper(
return std::make_unique<WindowHelper>(window);
}
bool NativeWindowFrameSupported() {
return true;
}
} // namespace Platform
} // namespace Ui

View file

@ -24,6 +24,7 @@ public:
not_null<RpWidget*> body() override;
void setTitle(const QString &title) override;
void setTitleStyle(const style::WindowTitle &st) override;
void setNativeFrame(bool enabled) override;
void setMinimumSize(QSize size) override;
void setFixedSize(QSize size) override;
void setGeometry(QRect rect) override;
@ -38,6 +39,7 @@ private:
void updateMargins();
void updateSystemMenu();
void updateSystemMenu(Qt::WindowState state);
void fixMaximizedWindow();
[[nodiscard]] bool handleNativeEvent(
UINT msg,
WPARAM wParam,
@ -50,11 +52,12 @@ private:
const HWND _handle = nullptr;
const not_null<TitleWidget*> _title;
const not_null<RpWidget*> _body;
WindowShadow _shadow;
std::optional<WindowShadow> _shadow;
bool _updatingMargins = false;
QMargins _marginsDelta;
HMENU _menu = nullptr;
bool _isFullScreen = false;
bool _nativeFrame = false;
};

View file

@ -36,6 +36,10 @@ void Window::setTitleStyle(const style::WindowTitle &st) {
_helper->setTitleStyle(st);
}
void Window::setNativeFrame(bool enabled) {
_helper->setNativeFrame(enabled);
}
void Window::setMinimumSize(QSize size) {
_helper->setMinimumSize(size);
}

View file

@ -39,6 +39,7 @@ public:
void setTitle(const QString &title);
void setTitleStyle(const style::WindowTitle &st);
void setNativeFrame(bool enabled);
void setMinimumSize(QSize size);
void setFixedSize(QSize size);
void setStaysOnTop(bool enabled);