diff --git a/CMakeLists.txt b/CMakeLists.txt index 15d69e0..ecf3f2b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/ui/platform/linux/ui_window_linux.cpp b/ui/platform/linux/ui_window_linux.cpp index 8e2a83a..c98869a 100644 --- a/ui/platform/linux/ui_window_linux.cpp +++ b/ui/platform/linux/ui_window_linux.cpp @@ -9,7 +9,7 @@ namespace Ui { namespace Platform { -std::unique_ptr CreateWindowHelper( +std::unique_ptr CreateSpecialWindowHelper( not_null window) { return nullptr; } diff --git a/ui/platform/mac/ui_window_mac.h b/ui/platform/mac/ui_window_mac.h index ab43e42..85af0e9 100644 --- a/ui/platform/mac/ui_window_mac.h +++ b/ui/platform/mac/ui_window_mac.h @@ -32,7 +32,6 @@ private: void init(); void toggleCustomTitle(bool visible); - const not_null _window; const std::unique_ptr _private; const not_null _title; const not_null _body; diff --git a/ui/platform/mac/ui_window_mac.mm b/ui/platform/mac/ui_window_mac.mm index df9039f..f2a1837 100644 --- a/ui/platform/mac/ui_window_mac.mm +++ b/ui/platform/mac/ui_window_mac.mm @@ -132,13 +132,13 @@ Fn WindowHelper::Private::enforceStyleCallback() { } void WindowHelper::Private::initOpenGL() { - auto forceOpenGL = std::make_unique(_owner->_window); + auto forceOpenGL = std::make_unique(_owner->window()); } void WindowHelper::Private::resolveWeakPointers() { - _owner->_window->createWinId(); + _owner->window()->createWinId(); - _nativeView = reinterpret_cast(_owner->_window->winId()); + _nativeView = reinterpret_cast(_owner->window()->winId()); _nativeWindow = _nativeView ? [_nativeView window] : nullptr; Ensures(_nativeWindow != nullptr); @@ -189,14 +189,14 @@ void WindowHelper::Private::init() { } WindowHelper::WindowHelper(not_null window) -: _window(window) +: BasicWindowHelper(window) , _private(std::make_unique(this)) , _title(_private->customTitleHeight() ? Ui::CreateChild( - _window.get(), + window.get(), _private->customTitleHeight()) : nullptr) -, _body(Ui::CreateChild(_window.get())) { +, _body(Ui::CreateChild(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 CreateWindowHelper( +std::unique_ptr CreateSpecialWindowHelper( not_null window) { return std::make_unique(window); } diff --git a/ui/platform/ui_platform_window.cpp b/ui/platform/ui_platform_window.cpp new file mode 100644 index 0000000..9e38667 --- /dev/null +++ b/ui/platform/ui_platform_window.cpp @@ -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 +#include + +namespace Ui { +namespace Platform { + +BasicWindowHelper::BasicWindowHelper(not_null window) +: _window(window) { +} + +not_null 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 testMethod) { + Expects(!_bodyTitleAreaTestMethod); + + if (!testMethod) { + return; + } + _bodyTitleAreaTestMethod = std::move(testMethod); + if (customBodyTitleAreaHandling()) { + return; + } + body()->events() | rpl::start_with_next([=](not_null e) { + if (e->type() == QEvent::MouseButtonDblClick) { + if (bodyTitleAreaHit(static_cast(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(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 diff --git a/ui/platform/ui_platform_window.h b/ui/platform/ui_platform_window.h index 4fff950..533875f 100644 --- a/ui/platform/ui_platform_window.h +++ b/ui/platform/ui_platform_window.h @@ -18,18 +18,45 @@ namespace Platform { class BasicWindowHelper { public: - [[nodiscard]] virtual not_null 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 window); + + [[nodiscard]] virtual not_null 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 testMethod); + +protected: + [[nodiscard]] not_null window() const { + return _window; + } + [[nodiscard]] bool bodyTitleAreaHit(QPoint point) const { + return _bodyTitleAreaTestMethod && _bodyTitleAreaTestMethod(point); + } + [[nodiscard]] virtual bool customBodyTitleAreaHandling() { + return false; + } + +private: + const not_null _window; + Fn _bodyTitleAreaTestMethod; + }; -[[nodiscard]] std::unique_ptr CreateWindowHelper( +[[nodiscard]] std::unique_ptr CreateSpecialWindowHelper( not_null window); +[[nodiscard]] inline std::unique_ptr CreateWindowHelper( + not_null window) { + if (auto special = CreateSpecialWindowHelper(window)) { + return special; + } + return std::make_unique(window); +} + } // namespace Platform } // namespace Ui diff --git a/ui/platform/win/ui_window_win.cpp b/ui/platform/win/ui_window_win.cpp index 934edd6..3428e1e 100644 --- a/ui/platform/win/ui_window_win.cpp +++ b/ui/platform/win/ui_window_win.cpp @@ -93,11 +93,11 @@ bool WindowHelper::NativeFilter::nativeEventFilter( } WindowHelper::WindowHelper(not_null window) -: _window(window) -, _handle(GetWindowHandle(_window)) -, _title(Ui::CreateChild(_window.get())) -, _body(Ui::CreateChild(_window.get())) -, _shadow(_window, st::windowShadowFg->c) { +: BasicWindowHelper(window) +, _handle(GetWindowHandle(window)) +, _title(Ui::CreateChild(window.get())) +, _body(Ui::CreateChild(window.get())) +, _shadow(window, st::windowShadowFg->c) { Expects(_handle != nullptr); GetNativeFilter()->registerWindow(_handle, this); @@ -114,7 +114,7 @@ not_null 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(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 widget) { window)); } -std::unique_ptr CreateWindowHelper( +std::unique_ptr CreateSpecialWindowHelper( not_null window) { return std::make_unique(window); } diff --git a/ui/platform/win/ui_window_win.h b/ui/platform/win/ui_window_win.h index ff88284..d8003bc 100644 --- a/ui/platform/win/ui_window_win.h +++ b/ui/platform/win/ui_window_win.h @@ -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 GetNativeFilter(); - const not_null _window; const HWND _handle = nullptr; const not_null _title; const not_null _body; diff --git a/ui/widgets/window.cpp b/ui/widgets/window.cpp index 113fb7f..2b8538e 100644 --- a/ui/widgets/window.cpp +++ b/ui/widgets/window.cpp @@ -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 Window::body() { - return _helper ? _helper->body() : this; + return _helper->body(); } not_null 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 testMethod) { + _helper->setBodyTitleArea(std::move(testMethod)); } } // namespace Ui diff --git a/ui/widgets/window.h b/ui/widgets/window.h index 7a96a6a..3d2c6a8 100644 --- a/ui/widgets/window.h +++ b/ui/widgets/window.h @@ -30,6 +30,7 @@ public: void setMinimumSize(QSize size); void setFixedSize(QSize size); void setGeometry(QRect rect); + void setBodyTitleArea(Fn testMethod); private: const std::unique_ptr _helper;