Support custom drag area for Ui::Window on macOS.
This commit is contained in:
parent
c45b62084c
commit
6735ee93dc
4 changed files with 100 additions and 2 deletions
|
|
@ -29,6 +29,8 @@ private:
|
||||||
class Private;
|
class Private;
|
||||||
friend class Private;
|
friend class Private;
|
||||||
|
|
||||||
|
void setupBodyTitleAreaEvents() override;
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
void toggleCustomTitle(bool visible);
|
void toggleCustomTitle(bool visible);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,13 @@
|
||||||
#include "ui/platform/mac/ui_window_mac.h"
|
#include "ui/platform/mac/ui_window_mac.h"
|
||||||
|
|
||||||
#include "ui/platform/mac/ui_window_title_mac.h"
|
#include "ui/platform/mac/ui_window_title_mac.h"
|
||||||
|
#include "ui/widgets/window.h"
|
||||||
#include "base/platform/base_platform_info.h"
|
#include "base/platform/base_platform_info.h"
|
||||||
#include "styles/palette.h"
|
#include "styles/palette.h"
|
||||||
|
|
||||||
#include <QtCore/QCoreApplication>
|
#include <QtCore/QCoreApplication>
|
||||||
|
#include <QtCore/QAbstractNativeEventFilter>
|
||||||
|
#include <QtGui/QWindow>
|
||||||
#include <QtWidgets/QOpenGLWidget>
|
#include <QtWidgets/QOpenGLWidget>
|
||||||
#include <Cocoa/Cocoa.h>
|
#include <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
|
@ -78,6 +81,30 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class EventFilter : public QObject, public QAbstractNativeEventFilter {
|
||||||
|
public:
|
||||||
|
EventFilter(not_null<QObject*> parent, Fn<bool(void*)> checkPerformDrag)
|
||||||
|
: QObject(parent)
|
||||||
|
, _checkPerformDrag(std::move(checkPerformDrag)) {
|
||||||
|
Expects(_checkPerformDrag != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nativeEventFilter(
|
||||||
|
const QByteArray &eventType,
|
||||||
|
void *message,
|
||||||
|
long *result) {
|
||||||
|
NSEvent *e = static_cast<NSEvent*>(message);
|
||||||
|
return (e && [e type] == NSEventTypeLeftMouseDown)
|
||||||
|
? _checkPerformDrag([e window])
|
||||||
|
: false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Fn<bool(void*)> _checkPerformDrag;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
class WindowHelper::Private final {
|
class WindowHelper::Private final {
|
||||||
|
|
@ -85,6 +112,9 @@ public:
|
||||||
explicit Private(not_null<WindowHelper*> owner);
|
explicit Private(not_null<WindowHelper*> owner);
|
||||||
|
|
||||||
[[nodiscard]] int customTitleHeight() const;
|
[[nodiscard]] int customTitleHeight() const;
|
||||||
|
[[nodiscard]] QRect controlsRect() const;
|
||||||
|
[[nodiscard]] bool checkNativeMove(void *nswindow) const;
|
||||||
|
void activateBeforeNativeMove();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void init();
|
void init();
|
||||||
|
|
@ -117,6 +147,51 @@ int WindowHelper::Private::customTitleHeight() const {
|
||||||
return _customTitleHeight;
|
return _customTitleHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QRect WindowHelper::Private::controlsRect() const {
|
||||||
|
const auto button = [&](NSWindowButton type) {
|
||||||
|
auto view = [_nativeWindow standardWindowButton:type];
|
||||||
|
if (!view) {
|
||||||
|
return QRect();
|
||||||
|
}
|
||||||
|
auto result = [view frame];
|
||||||
|
for (auto parent = [view superview]; parent != nil; parent = [parent superview]) {
|
||||||
|
const auto origin = [parent frame].origin;
|
||||||
|
result.origin.x += origin.x;
|
||||||
|
result.origin.y += origin.y;
|
||||||
|
}
|
||||||
|
return QRect(result.origin.x, result.origin.y, result.size.width, result.size.height);
|
||||||
|
};
|
||||||
|
auto result = QRect();
|
||||||
|
const auto buttons = {
|
||||||
|
NSWindowCloseButton,
|
||||||
|
NSWindowMiniaturizeButton,
|
||||||
|
NSWindowZoomButton,
|
||||||
|
};
|
||||||
|
for (const auto type : buttons) {
|
||||||
|
result = result.united(button(type));
|
||||||
|
}
|
||||||
|
return QRect(
|
||||||
|
result.x(),
|
||||||
|
[_nativeWindow frame].size.height - result.y() - result.height(),
|
||||||
|
result.width(),
|
||||||
|
result.height());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WindowHelper::Private::checkNativeMove(void *nswindow) const {
|
||||||
|
if (_nativeWindow != nswindow
|
||||||
|
|| ([_nativeWindow styleMask] & NSFullScreenWindowMask) == NSFullScreenWindowMask) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto real = QPointF::fromCGPoint([NSEvent mouseLocation]);
|
||||||
|
const auto frame = QRectF::fromCGRect([_nativeWindow frame]);
|
||||||
|
const auto border = QMarginsF{ 3., 3., 3., 3. };
|
||||||
|
return frame.marginsRemoved(border).contains(real);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowHelper::Private::activateBeforeNativeMove() {
|
||||||
|
[_nativeWindow makeKeyAndOrderFront:_nativeWindow];
|
||||||
|
}
|
||||||
|
|
||||||
Fn<void(bool)> WindowHelper::Private::toggleCustomTitleCallback() {
|
Fn<void(bool)> WindowHelper::Private::toggleCustomTitleCallback() {
|
||||||
return [=](bool visible) {
|
return [=](bool visible) {
|
||||||
_owner->toggleCustomTitle(visible);
|
_owner->toggleCustomTitle(visible);
|
||||||
|
|
@ -255,6 +330,21 @@ void WindowHelper::setGeometry(QRect rect) {
|
||||||
rect.marginsAdded({ 0, (_title ? _title->height() : 0), 0, 0 }));
|
rect.marginsAdded({ 0, (_title ? _title->height() : 0), 0, 0 }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WindowHelper::setupBodyTitleAreaEvents() {
|
||||||
|
const auto controls = _private->controlsRect();
|
||||||
|
qApp->installNativeEventFilter(new EventFilter(window(), [=](void *nswindow) {
|
||||||
|
const auto point = body()->mapFromGlobal(QCursor::pos());
|
||||||
|
if (_private->checkNativeMove(nswindow)
|
||||||
|
&& !controls.contains(point)
|
||||||
|
&& (bodyTitleAreaHit(point) & WindowTitleHitTestFlag::Move)) {
|
||||||
|
_private->activateBeforeNativeMove();
|
||||||
|
window()->windowHandle()->startSystemMove();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
void WindowHelper::init() {
|
void WindowHelper::init() {
|
||||||
rpl::combine(
|
rpl::combine(
|
||||||
window()->sizeValue(),
|
window()->sizeValue(),
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,12 @@ void BasicWindowHelper::setBodyTitleArea(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_bodyTitleAreaTestMethod = std::move(testMethod);
|
_bodyTitleAreaTestMethod = std::move(testMethod);
|
||||||
|
setupBodyTitleAreaEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BasicWindowHelper::setupBodyTitleAreaEvents() {
|
||||||
|
// This is not done on macOS, because startSystemMove
|
||||||
|
// doesn't work from event handler there.
|
||||||
body()->events() | rpl::start_with_next([=](not_null<QEvent*> e) {
|
body()->events() | rpl::start_with_next([=](not_null<QEvent*> e) {
|
||||||
const auto hitTest = [&] {
|
const auto hitTest = [&] {
|
||||||
return bodyTitleAreaHit(
|
return bodyTitleAreaHit(
|
||||||
|
|
@ -87,7 +93,6 @@ void BasicWindowHelper::setBodyTitleArea(
|
||||||
_mousePressed = true;
|
_mousePressed = true;
|
||||||
|
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) || defined DESKTOP_APP_QT_PATCHED
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) || defined DESKTOP_APP_QT_PATCHED
|
||||||
#ifndef Q_OS_MAC // On macOS startSystemMove() doesn't work from here.
|
|
||||||
} else if (e->type() == QEvent::MouseMove) {
|
} else if (e->type() == QEvent::MouseMove) {
|
||||||
const auto mouseEvent = static_cast<QMouseEvent*>(e.get());
|
const auto mouseEvent = static_cast<QMouseEvent*>(e.get());
|
||||||
if (_mousePressed
|
if (_mousePressed
|
||||||
|
|
@ -107,7 +112,6 @@ void BasicWindowHelper::setBodyTitleArea(
|
||||||
_mousePressed = false;
|
_mousePressed = false;
|
||||||
_window->windowHandle()->startSystemMove();
|
_window->windowHandle()->startSystemMove();
|
||||||
}
|
}
|
||||||
#endif // !Q_OS_MAC
|
|
||||||
#endif // Qt >= 5.15 || DESKTOP_APP_QT_PATCHED
|
#endif // Qt >= 5.15 || DESKTOP_APP_QT_PATCHED
|
||||||
}
|
}
|
||||||
}, body()->lifetime());
|
}, body()->lifetime());
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,8 @@ protected:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
virtual void setupBodyTitleAreaEvents();
|
||||||
|
|
||||||
const not_null<RpWidget*> _window;
|
const not_null<RpWidget*> _window;
|
||||||
Fn<WindowTitleHitTestFlags(QPoint)> _bodyTitleAreaTestMethod;
|
Fn<WindowTitleHitTestFlags(QPoint)> _bodyTitleAreaTestMethod;
|
||||||
bool _mousePressed = false;
|
bool _mousePressed = false;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue