Allow custom drag area for Ui::Window.

This commit is contained in:
John Preston 2020-08-12 17:33:26 +04:00
parent 8a35ac5d2d
commit 608b25bd32
10 changed files with 177 additions and 79 deletions

View file

@ -84,6 +84,7 @@ PRIVATE
ui/platform/win/ui_window_win.h
ui/platform/win/ui_utility_win.cpp
ui/platform/win/ui_utility_win.h
ui/platform/ui_platform_window.cpp
ui/platform/ui_platform_window.h
ui/platform/ui_platform_utility.h
ui/style/style_core.cpp

View file

@ -9,7 +9,7 @@
namespace Ui {
namespace Platform {
std::unique_ptr<BasicWindowHelper> CreateWindowHelper(
std::unique_ptr<BasicWindowHelper> CreateSpecialWindowHelper(
not_null<RpWidget*> window) {
return nullptr;
}

View file

@ -32,7 +32,6 @@ private:
void init();
void toggleCustomTitle(bool visible);
const not_null<RpWidget*> _window;
const std::unique_ptr<Private> _private;
const not_null<TitleWidget*> _title;
const not_null<RpWidget*> _body;

View file

@ -132,13 +132,13 @@ Fn<void()> WindowHelper::Private::enforceStyleCallback() {
}
void WindowHelper::Private::initOpenGL() {
auto forceOpenGL = std::make_unique<QOpenGLWidget>(_owner->_window);
auto forceOpenGL = std::make_unique<QOpenGLWidget>(_owner->window());
}
void WindowHelper::Private::resolveWeakPointers() {
_owner->_window->createWinId();
_owner->window()->createWinId();
_nativeView = reinterpret_cast<NSView*>(_owner->_window->winId());
_nativeView = reinterpret_cast<NSView*>(_owner->window()->winId());
_nativeWindow = _nativeView ? [_nativeView window] : nullptr;
Ensures(_nativeWindow != nullptr);
@ -189,14 +189,14 @@ void WindowHelper::Private::init() {
}
WindowHelper::WindowHelper(not_null<RpWidget*> window)
: _window(window)
: BasicWindowHelper(window)
, _private(std::make_unique<Private>(this))
, _title(_private->customTitleHeight()
? Ui::CreateChild<TitleWidget>(
_window.get(),
window.get(),
_private->customTitleHeight())
: nullptr)
, _body(Ui::CreateChild<RpWidget>(_window.get())) {
, _body(Ui::CreateChild<RpWidget>(window.get())) {
init();
}
@ -211,7 +211,7 @@ void WindowHelper::setTitle(const QString &title) {
if (_title) {
_title->setText(title);
}
_window->setWindowTitle(
window()->setWindowTitle(
(!_title || _title->isHidden()) ? title : QString());
}
@ -226,29 +226,29 @@ void WindowHelper::toggleCustomTitle(bool visible) {
return;
}
_title->setVisible(visible);
_window->setWindowTitle(visible ? QString() : _title->text());
window()->setWindowTitle(visible ? QString() : _title->text());
}
void WindowHelper::setMinimumSize(QSize size) {
_window->setMinimumSize(
window()->setMinimumSize(
size.width(),
(_title ? _title->height() : 0) + size.height());
}
void WindowHelper::setFixedSize(QSize size) {
_window->setFixedSize(
window()->setFixedSize(
size.width(),
(_title ? _title->height() : 0) + size.height());
}
void WindowHelper::setGeometry(QRect rect) {
_window->setGeometry(
window()->setGeometry(
rect.marginsAdded({ 0, (_title ? _title->height() : 0), 0, 0 }));
}
void WindowHelper::init() {
rpl::combine(
_window->sizeValue(),
window()->sizeValue(),
_title->heightValue(),
_title->shownValue()
) | rpl::start_with_next([=](QSize size, int titleHeight, bool shown) {
@ -263,7 +263,7 @@ void WindowHelper::init() {
}, _body->lifetime());
}
std::unique_ptr<BasicWindowHelper> CreateWindowHelper(
std::unique_ptr<BasicWindowHelper> CreateSpecialWindowHelper(
not_null<RpWidget*> window) {
return std::make_unique<WindowHelper>(window);
}

View file

@ -0,0 +1,77 @@
// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#include "ui/platform/ui_platform_window.h"
#include "ui/rp_widget.h"
#include <QtGui/QWindow>
#include <QtGui/QtEvents>
namespace Ui {
namespace Platform {
BasicWindowHelper::BasicWindowHelper(not_null<RpWidget*> window)
: _window(window) {
}
not_null<RpWidget*> BasicWindowHelper::body() {
return _window;
}
void BasicWindowHelper::setTitle(const QString &title) {
_window->setWindowTitle(title);
}
void BasicWindowHelper::setTitleStyle(const style::WindowTitle &st) {
}
void BasicWindowHelper::setMinimumSize(QSize size) {
_window->setMinimumSize(size);
}
void BasicWindowHelper::setFixedSize(QSize size) {
_window->setFixedSize(size);
}
void BasicWindowHelper::setGeometry(QRect rect) {
_window->setGeometry(rect);
}
void BasicWindowHelper::setBodyTitleArea(Fn<bool(QPoint)> testMethod) {
Expects(!_bodyTitleAreaTestMethod);
if (!testMethod) {
return;
}
_bodyTitleAreaTestMethod = std::move(testMethod);
if (customBodyTitleAreaHandling()) {
return;
}
body()->events() | rpl::start_with_next([=](not_null<QEvent*> e) {
if (e->type() == QEvent::MouseButtonDblClick) {
if (bodyTitleAreaHit(static_cast<QMouseEvent*>(e.get())->pos())) {
const auto state = _window->windowState();
if (state & Qt::WindowMaximized) {
_window->setWindowState(state & ~Qt::WindowMaximized);
} else {
_window->setWindowState(state | Qt::WindowMaximized);
}
}
} else if (e->type() == QEvent::MouseMove) {
const auto mouseEvent = static_cast<QMouseEvent*>(e.get());
if (bodyTitleAreaHit(mouseEvent->pos())
&& (mouseEvent->buttons() & Qt::LeftButton)) {
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) || defined DESKTOP_APP_QT_PATCHED
_window->windowHandle()->startSystemMove();
#endif // Qt >= 5.15 || DESKTOP_APP_QT_PATCHED
}
}
}, body()->lifetime());
}
} // namespace Platform
} // namespace Ui

View file

@ -18,18 +18,45 @@ namespace Platform {
class BasicWindowHelper {
public:
[[nodiscard]] virtual not_null<RpWidget*> body() = 0;
virtual void setTitle(const QString &title) = 0;
virtual void setTitleStyle(const style::WindowTitle &st) = 0;
virtual void setMinimumSize(QSize size) = 0;
virtual void setFixedSize(QSize size) = 0;
virtual void setGeometry(QRect rect) = 0;
explicit BasicWindowHelper(not_null<RpWidget*> window);
[[nodiscard]] virtual not_null<RpWidget*> body();
virtual void setTitle(const QString &title);
virtual void setTitleStyle(const style::WindowTitle &st);
virtual void setMinimumSize(QSize size);
virtual void setFixedSize(QSize size);
virtual void setGeometry(QRect rect);
virtual ~BasicWindowHelper() = default;
void setBodyTitleArea(Fn<bool(QPoint)> testMethod);
protected:
[[nodiscard]] not_null<RpWidget*> window() const {
return _window;
}
[[nodiscard]] bool bodyTitleAreaHit(QPoint point) const {
return _bodyTitleAreaTestMethod && _bodyTitleAreaTestMethod(point);
}
[[nodiscard]] virtual bool customBodyTitleAreaHandling() {
return false;
}
private:
const not_null<RpWidget*> _window;
Fn<bool(QPoint)> _bodyTitleAreaTestMethod;
};
[[nodiscard]] std::unique_ptr<BasicWindowHelper> CreateWindowHelper(
[[nodiscard]] std::unique_ptr<BasicWindowHelper> CreateSpecialWindowHelper(
not_null<RpWidget*> window);
[[nodiscard]] inline std::unique_ptr<BasicWindowHelper> CreateWindowHelper(
not_null<RpWidget*> window) {
if (auto special = CreateSpecialWindowHelper(window)) {
return special;
}
return std::make_unique<BasicWindowHelper>(window);
}
} // namespace Platform
} // namespace Ui

View file

@ -93,11 +93,11 @@ bool WindowHelper::NativeFilter::nativeEventFilter(
}
WindowHelper::WindowHelper(not_null<RpWidget*> window)
: _window(window)
, _handle(GetWindowHandle(_window))
, _title(Ui::CreateChild<TitleWidget>(_window.get()))
, _body(Ui::CreateChild<RpWidget>(_window.get()))
, _shadow(_window, st::windowShadowFg->c) {
: BasicWindowHelper(window)
, _handle(GetWindowHandle(window))
, _title(Ui::CreateChild<TitleWidget>(window.get()))
, _body(Ui::CreateChild<RpWidget>(window.get()))
, _shadow(window, st::windowShadowFg->c) {
Expects(_handle != nullptr);
GetNativeFilter()->registerWindow(_handle, this);
@ -114,7 +114,7 @@ not_null<RpWidget*> WindowHelper::body() {
void WindowHelper::setTitle(const QString &title) {
_title->setText(title);
_window->setWindowTitle(title);
window()->setWindowTitle(title);
}
void WindowHelper::setTitleStyle(const style::WindowTitle &st) {
@ -122,27 +122,27 @@ void WindowHelper::setTitleStyle(const style::WindowTitle &st) {
}
void WindowHelper::setMinimumSize(QSize size) {
_window->setMinimumSize(size.width(), _title->height() + size.height());
window()->setMinimumSize(size.width(), _title->height() + size.height());
}
void WindowHelper::setFixedSize(QSize size) {
_window->setFixedSize(size.width(), _title->height() + size.height());
window()->setFixedSize(size.width(), _title->height() + size.height());
_title->setResizeEnabled(false);
_shadow.setResizeEnabled(false);
}
void WindowHelper::setGeometry(QRect rect) {
_window->setGeometry(rect.marginsAdded({ 0, _title->height(), 0, 0 }));
window()->setGeometry(rect.marginsAdded({ 0, _title->height(), 0, 0 }));
}
void WindowHelper::init() {
style::PaletteChanged(
) | rpl::start_with_next([=] {
_shadow.setColor(st::windowShadowFg->c);
}, _window->lifetime());
}, window()->lifetime());
rpl::combine(
_window->sizeValue(),
window()->sizeValue(),
_title->heightValue()
) | rpl::start_with_next([=](QSize size, int titleHeight) {
_body->setGeometry(
@ -165,14 +165,14 @@ void WindowHelper::init() {
const auto handleStateChanged = [=](Qt::WindowState state) {
updateSystemMenu(state);
if (fixedSize() && (state & Qt::WindowMaximized)) {
crl::on_main(_window.get(), [=] {
_window->setWindowState(
_window->windowState() & ~Qt::WindowMaximized);
crl::on_main(window().get(), [=] {
window()->setWindowState(
window()->windowState() & ~Qt::WindowMaximized);
});
}
};
Ui::Connect(
_window->windowHandle(),
window()->windowHandle(),
&QWindow::windowStateChanged,
handleStateChanged);
}
@ -186,14 +186,14 @@ bool WindowHelper::handleNativeEvent(
case WM_ACTIVATE: {
if (LOWORD(wParam) == WA_CLICKACTIVE) {
Ui::MarkInactivePress(_window, true);
Ui::MarkInactivePress(window(), true);
}
if (LOWORD(wParam) != WA_INACTIVE) {
_shadow.update(WindowShadow::Change::Activate);
} else {
_shadow.update(WindowShadow::Change::Deactivate);
}
_window->update();
window()->update();
} return false;
case WM_NCPAINT: {
@ -267,14 +267,14 @@ bool WindowHelper::handleNativeEvent(
|| wParam == SIZE_RESTORED
|| wParam == SIZE_MINIMIZED) {
if (wParam != SIZE_RESTORED
|| _window->windowState() != Qt::WindowNoState) {
|| window()->windowState() != Qt::WindowNoState) {
Qt::WindowState state = Qt::WindowNoState;
if (wParam == SIZE_MAXIMIZED) {
state = Qt::WindowMaximized;
} else if (wParam == SIZE_MINIMIZED) {
state = Qt::WindowMinimized;
}
emit _window->windowHandle()->windowStateChanged(state);
emit window()->windowHandle()->windowStateChanged(state);
}
updateMargins();
const auto changes = (wParam == SIZE_MINIMIZED
@ -310,10 +310,12 @@ bool WindowHelper::handleNativeEvent(
const auto mapped = QPoint(
p.x - r.left + _marginsDelta.left(),
p.y - r.top + _marginsDelta.top());
if (!_window->rect().contains(mapped)) {
if (!window()->rect().contains(mapped)) {
*result = HTTRANSPARENT;
} else if (!_title->geometry().contains(mapped)) {
*result = HTCLIENT;
*result = bodyTitleAreaHit(mapped - QPoint(0, _title->height()))
? HTCAPTION
: HTCLIENT;
} else switch (_title->hitTest(_title->pos() + mapped)) {
case HitTestResult::Client:
case HitTestResult::SysButton: *result = HTCLIENT; break;
@ -338,7 +340,7 @@ bool WindowHelper::handleNativeEvent(
case WM_SYSCOMMAND: {
if (wParam == SC_MOUSEMENU && !fixedSize()) {
POINTS p = MAKEPOINTS(lParam);
updateSystemMenu(_window->windowHandle()->windowState());
updateSystemMenu(window()->windowHandle()->windowState());
TrackPopupMenu(
_menu,
TPM_LEFTALIGN | TPM_TOPALIGN | TPM_LEFTBUTTON,
@ -357,19 +359,19 @@ bool WindowHelper::handleNativeEvent(
const auto command = LOWORD(wParam);
switch (command) {
case SC_CLOSE:
_window->close();
window()->close();
return true;
case SC_MINIMIZE:
_window->setWindowState(
_window->windowState() | Qt::WindowMinimized);
window()->setWindowState(
window()->windowState() | Qt::WindowMinimized);
return true;
case SC_MAXIMIZE:
if (!fixedSize()) {
_window->setWindowState(Qt::WindowMaximized);
window()->setWindowState(Qt::WindowMaximized);
}
return true;
case SC_RESTORE:
_window->setWindowState(Qt::WindowNoState);
window()->setWindowState(Qt::WindowNoState);
return true;
}
} return true;
@ -379,7 +381,7 @@ bool WindowHelper::handleNativeEvent(
}
bool WindowHelper::fixedSize() const {
return _window->minimumSize() == _window->maximumSize();
return window()->minimumSize() == window()->maximumSize();
}
void WindowHelper::updateMargins() {
@ -444,14 +446,14 @@ void WindowHelper::updateMargins() {
if (const auto native = QGuiApplication::platformNativeInterface()) {
native->setWindowProperty(
_window->windowHandle()->handle(),
window()->windowHandle()->handle(),
"WindowsCustomMargins",
QVariant::fromValue<QMargins>(margins));
}
}
void WindowHelper::updateSystemMenu() {
updateSystemMenu(_window->windowHandle()->windowState());
updateSystemMenu(window()->windowHandle()->windowState());
}
void WindowHelper::updateSystemMenu(Qt::WindowState state) {
@ -518,7 +520,7 @@ HWND GetWindowHandle(not_null<RpWidget*> widget) {
window));
}
std::unique_ptr<BasicWindowHelper> CreateWindowHelper(
std::unique_ptr<BasicWindowHelper> CreateSpecialWindowHelper(
not_null<RpWidget*> window) {
return std::make_unique<WindowHelper>(window);
}

View file

@ -32,6 +32,10 @@ private:
class NativeFilter;
friend class NativeFilter;
bool customBodyTitleAreaHandling() override {
return true;
}
void init();
void updateMargins();
void updateSystemMenu();
@ -45,7 +49,6 @@ private:
static not_null<NativeFilter*> GetNativeFilter();
const not_null<RpWidget*> _window;
const HWND _handle = nullptr;
const not_null<TitleWidget*> _title;
const not_null<RpWidget*> _body;

View file

@ -13,55 +13,43 @@ namespace Ui {
Window::Window(QWidget *parent)
: RpWidget(parent)
, _helper(Platform::CreateWindowHelper(this)) {
Expects(_helper != nullptr);
hide();
}
Window::~Window() = default;
not_null<RpWidget*> Window::body() {
return _helper ? _helper->body() : this;
return _helper->body();
}
not_null<const RpWidget*> Window::body() const {
return _helper ? _helper->body().get() : this;
return _helper->body().get();
}
void Window::setTitle(const QString &title) {
if (_helper) {
_helper->setTitle(title);
} else {
setWindowTitle(title);
}
_helper->setTitle(title);
}
void Window::setTitleStyle(const style::WindowTitle &st) {
if (_helper) {
_helper->setTitleStyle(st);
}
_helper->setTitleStyle(st);
}
void Window::setMinimumSize(QSize size) {
if (_helper) {
_helper->setMinimumSize(size);
} else {
RpWidget::setMinimumSize(size);
}
_helper->setMinimumSize(size);
}
void Window::setFixedSize(QSize size) {
if (_helper) {
_helper->setFixedSize(size);
} else {
RpWidget::setFixedSize(size);
}
_helper->setFixedSize(size);
}
void Window::setGeometry(QRect rect) {
if (_helper) {
_helper->setGeometry(rect);
} else {
RpWidget::setGeometry(rect);
}
_helper->setGeometry(rect);
}
void Window::setBodyTitleArea(Fn<bool(QPoint)> testMethod) {
_helper->setBodyTitleArea(std::move(testMethod));
}
} // namespace Ui

View file

@ -30,6 +30,7 @@ public:
void setMinimumSize(QSize size);
void setFixedSize(QSize size);
void setGeometry(QRect rect);
void setBodyTitleArea(Fn<bool(QPoint)> testMethod);
private:
const std::unique_ptr<Platform::BasicWindowHelper> _helper;