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_window_win.h
ui/platform/win/ui_utility_win.cpp ui/platform/win/ui_utility_win.cpp
ui/platform/win/ui_utility_win.h ui/platform/win/ui_utility_win.h
ui/platform/ui_platform_window.cpp
ui/platform/ui_platform_window.h ui/platform/ui_platform_window.h
ui/platform/ui_platform_utility.h ui/platform/ui_platform_utility.h
ui/style/style_core.cpp ui/style/style_core.cpp

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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