12#include "WidgetResizeHandler_p.h"
13#include "FloatingWindow_p.h"
14#include "TitleBar_p.h"
15#include "DragController_p.h"
17#include "Qt5Qt6Compat_p.h"
19#include "DockRegistry_p.h"
20#include "MDILayoutWidget_p.h"
25#include <QGuiApplication>
28#include <QScopedValueRollback>
31#include <QtGui/private/qhighdpiscaling_p.h>
36#pragma comment(lib, "Dwmapi.lib")
37#pragma comment(lib, "User32.lib")
43bool WidgetResizeHandler::s_disableAllHandlers =
false;
44WidgetResizeHandler::WidgetResizeHandler(EventFilterMode filterMode, WindowMode windowMode,
QWidgetOrQuick *target)
46 , m_usesGlobalEventFilter(filterMode == EventFilterMode::Global)
47 , m_isTopLevelWindowResizer(windowMode == WindowMode::TopLevel)
52WidgetResizeHandler::~WidgetResizeHandler()
57void WidgetResizeHandler::setAllowedResizeSides(CursorPositions sides)
59 mAllowedResizeSides = sides;
62void WidgetResizeHandler::setResizeGap(
int gap)
67bool WidgetResizeHandler::isMDI()
const
69 auto frame = qobject_cast<Frame *>(mTarget);
70 return frame && frame->isMDI();
73bool WidgetResizeHandler::isResizing()
const
75 return m_resizingInProgress;
78int WidgetResizeHandler::widgetResizeHandlerMargin()
85 if (s_disableAllHandlers)
88 auto widget = qobject_cast<QWidgetOrQuick *>(o);
92 auto me = mouseEvent(e);
96 if (m_isTopLevelWindowResizer) {
99 if (!widget->isTopLevel() || o != mTarget) {
100 if (m_usesGlobalEventFilter) {
104 if (!m_resizingInProgress) {
105 const QPoint globalPos = Qt5Qt6Compat::eventGlobalPos(me);
106 updateCursor(cursorPosition(globalPos));
114 }
else if (isMDI()) {
126 auto frame = firstParentOfType<Frame>(widget);
127 if (frame && frame->isMDIWrapper()) {
129 frame = frame->mdiFrame();
132 if (frame && frame != mTarget) {
133 const bool areSiblings = frame->QWidgetAdapter::parentWidget() == mTarget->parentWidget();
141 if (mTarget->isMaximized())
145 auto cursorPos = cursorPosition(Qt5Qt6Compat::eventGlobalPos(mouseEvent));
146 updateCursor(cursorPos);
150 const int m = widgetResizeHandlerMargin();
152 const QPoint cursorPoint = mTarget->mapFromGlobal(Qt5Qt6Compat::eventGlobalPos(mouseEvent));
156 m_resizingInProgress =
true;
158 Q_EMIT DockRegistry::self()->frameInMDIResizeChanged();
159 mNewPosition = Qt5Qt6Compat::eventGlobalPos(mouseEvent);
160 mCursorPos = cursorPos;
165 m_resizingInProgress =
false;
167 Q_EMIT DockRegistry::self()->frameInMDIResizeChanged();
168 auto frame =
static_cast<Frame *
>(mTarget);
172 frame->mdiLayoutWidget()->setDockWidgetGeometry(frame, frame->QWidgetAdapter::geometry());
175 auto cursorPos = cursorPosition(Qt5Qt6Compat::eventGlobalPos(mouseEvent));
176 updateCursor(cursorPos);
178 if (mTarget->isMaximized() || !m_resizingInProgress || mouseEvent->button() !=
Qt::LeftButton)
181 mTarget->releaseMouse();
182 mTarget->releaseKeyboard();
188 if (mTarget->isMaximized())
192 const Frame *frameBeingResized = DockRegistry::self()->frameInMDIResize();
193 const bool otherFrameBeingResized = frameBeingResized && frameBeingResized != mTarget;
194 if (otherFrameBeingResized) {
201 m_resizingInProgress = m_resizingInProgress && (mouseEvent->buttons() &
Qt::LeftButton);
203 const bool consumed = mouseMoveEvent(mouseEvent);
212bool WidgetResizeHandler::mouseMoveEvent(
QMouseEvent *e)
214 const QPoint globalPos = Qt5Qt6Compat::eventGlobalPos(e);
215 if (!m_resizingInProgress) {
221 const QRect oldGeometry = KDDockWidgets::globalGeometry(mTarget);
222 QRect newGeometry = oldGeometry;
224 QRect parentGeometry;
225 if (!mTarget->isTopLevel()) {
226 auto parent = KDDockWidgets::Private::parentWidget(mTarget);
227 parentGeometry = KDDockWidgets::globalGeometry(parent);
236 switch (mCursorPos) {
240 parentGeometry = parentGeometry.
adjusted(m_resizeGap, 0, 0, 0);
241 deltaWidth = oldGeometry.
left() - globalPos.
x();
242 newWidth = qBound(minWidth, mTarget->width() + deltaWidth, maxWidth);
243 deltaWidth = newWidth - mTarget->width();
244 if (deltaWidth != 0) {
245 newGeometry.
setLeft(newGeometry.
left() - deltaWidth);
254 parentGeometry = parentGeometry.
adjusted(0, 0, -m_resizeGap, 0);
255 deltaWidth = globalPos.
x() - newGeometry.
right();
256 newWidth = qBound(minWidth, mTarget->width() + deltaWidth, maxWidth);
257 deltaWidth = newWidth - mTarget->width();
258 if (deltaWidth != 0) {
273 switch (mCursorPos) {
277 parentGeometry = parentGeometry.
adjusted(0, m_resizeGap, 0, 0);
278 deltaHeight = oldGeometry.
top() - globalPos.
y();
279 newHeight = qBound(minHeight, mTarget->height() + deltaHeight, maxHeight);
280 deltaHeight = newHeight - mTarget->height();
281 if (deltaHeight != 0) {
282 newGeometry.
setTop(newGeometry.
top() - deltaHeight);
291 parentGeometry = parentGeometry.
adjusted(0, 0, 0, -m_resizeGap);
292 deltaHeight = globalPos.
y() - newGeometry.
bottom();
293 newHeight = qBound(minHeight, mTarget->height() + deltaHeight, maxHeight);
294 deltaHeight = newHeight - mTarget->height();
295 if (deltaHeight != 0) {
305 if (newGeometry == mTarget->geometry()) {
310 if (!mTarget->isTopLevel()) {
313 newGeometry = newGeometry.
intersected(parentGeometry);
316 newGeometry.
moveTopLeft(mTarget->mapFromGlobal(newGeometry.
topLeft()) + mTarget->pos());
319 mTarget->setGeometry(newGeometry);
326bool WidgetResizeHandler::handleWindowsNativeEvent(FloatingWindow *fw,
const QByteArray &eventType,
327 void *message, Qt5Qt6Compat::qintptr *result)
329 if (eventType !=
"windows_generic_MSG")
332 auto msg =
static_cast<MSG *
>(message);
333 if (msg->message == WM_NCHITTEST) {
334 if (DragController::instance()->isInClientDrag()) {
340 const QRect htCaptionRect = fw->dragRect();
341 const bool ret = handleWindowsNativeEvent(fw->windowHandle(), msg, result, htCaptionRect);
343 fw->setLastHitTest(*result);
345 }
else if (msg->message == WM_NCLBUTTONDBLCLK) {
347 return handleWindowsNativeEvent(fw->windowHandle(), msg, result, {});
350 if (TitleBar *titleBar = fw->titleBar()) {
351 if (titleBar->isVisible()) {
352 titleBar->onDoubleClicked();
360 return handleWindowsNativeEvent(fw->windowHandle(), msg, result, {});
363bool WidgetResizeHandler::handleWindowsNativeEvent(
QWindow *w, MSG *msg,
364 Qt5Qt6Compat::qintptr *result,
365 const NativeFeatures &features)
367 if (msg->message == WM_NCCALCSIZE && features.hasShadow()) {
370 }
else if (msg->message == WM_NCHITTEST && (features.hasResize() || features.hasDrag())) {
371 const int borderWidth = 8;
374 const bool hasFixedWidthOrHeight = hasFixedWidth || hasFixedHeight;
377 const int xPos = GET_X_LPARAM(msg->lParam);
378 const int yPos = GET_Y_LPARAM(msg->lParam);
380 GetWindowRect(
reinterpret_cast<HWND
>(w->
winId()), &rect);
382 if (!hasFixedWidthOrHeight && xPos >= rect.left && xPos <= rect.left + borderWidth && yPos <= rect.bottom && yPos >= rect.bottom - borderWidth && features.hasResize()) {
383 *result = HTBOTTOMLEFT;
384 }
else if (!hasFixedWidthOrHeight && xPos < rect.right && xPos >= rect.right - borderWidth && yPos <= rect.bottom && yPos >= rect.bottom - borderWidth && features.hasResize()) {
385 *result = HTBOTTOMRIGHT;
386 }
else if (!hasFixedWidthOrHeight && xPos >= rect.left && xPos <= rect.left + borderWidth && yPos >= rect.top && yPos <= rect.top + borderWidth && features.hasResize()) {
388 }
else if (!hasFixedWidthOrHeight && xPos <= rect.right && xPos >= rect.right - borderWidth && yPos >= rect.top && yPos < rect.top + borderWidth && features.hasResize()) {
389 *result = HTTOPRIGHT;
390 }
else if (!hasFixedWidth && xPos >= rect.left && xPos <= rect.left + borderWidth && features.hasResize()) {
392 }
else if (!hasFixedHeight && yPos >= rect.top && yPos <= rect.top + borderWidth && features.hasResize()) {
394 }
else if (!hasFixedHeight && yPos <= rect.bottom && yPos >= rect.bottom - borderWidth && features.hasResize()) {
396 }
else if (!hasFixedWidth && xPos <= rect.right && xPos >= rect.right - borderWidth && features.hasResize()) {
398 }
else if (features.hasDrag()) {
399 const QPoint globalPosQt = QHighDpi::fromNativePixels(
QPoint(xPos, yPos), w);
401 const QRect htCaptionRect = features.htCaptionRect;
402 if (globalPosQt.
y() >= htCaptionRect.
top() && globalPosQt.
y() <= htCaptionRect.
bottom() && globalPosQt.
x() >= htCaptionRect.
left() && globalPosQt.
x() <= htCaptionRect.
right()) {
403 if (!KDDockWidgets::inDisallowDragWidget(globalPosQt)) {
410 }
else if (msg->message == WM_NCLBUTTONDBLCLK && features.hasMaximize()) {
416 }
else if (msg->message == WM_GETMINMAXINFO) {
423 if (!screen || w->
screen() != screen) {
427 DefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam);
431 auto mmi =
reinterpret_cast<MINMAXINFO *
>(msg->lParam);
434 mmi->ptMaxSize.y = int(availableGeometry.
height() * dpr);
435 mmi->ptMaxSize.x = int(availableGeometry.
width() * dpr) - 1;
436 mmi->ptMaxPosition.x = availableGeometry.
x();
437 mmi->ptMaxPosition.y = availableGeometry.
y();
456 if (m_usesGlobalEventFilter) {
457 qApp->installEventFilter(
this);
459 mTarget->installEventFilter(
this);
462 qWarning() <<
"Target widget is null!";
468#ifdef KDDOCKWIDGETS_QTWIDGETS
470 const QObjectList children = mTarget->children();
471 for (
int i = 0, total = children.size(); i < total; ++i) {
472 if (
auto child = qobject_cast<WidgetType *>(children.at(i))) {
499 restoreMouseCursor();
511 if (m_usesGlobalEventFilter) {
512 if (m_overrideCursorSet) {
513 qApp->changeOverrideCursor(cursor);
515 qApp->setOverrideCursor(cursor);
516 m_overrideCursorSet =
true;
519 mTarget->setCursor(cursor);
523void WidgetResizeHandler::restoreMouseCursor()
525 if (m_usesGlobalEventFilter) {
526 if (m_overrideCursorSet) {
527 qApp->restoreOverrideCursor();
528 m_overrideCursorSet =
false;
540#ifdef KDDOCKWIDGETS_QTQUICK
544 return CursorPosition(mTarget->property(
"cursorPosition").toInt());
548 QPoint pos = mTarget->mapFromGlobal(globalPos);
550 const int x = pos.
x();
551 const int y = pos.
y();
552 const int margin = widgetResizeHandlerMargin();
556 if (y >= -margin && y <= mTarget->height() + margin) {
557 if (qAbs(x) <= margin)
559 else if (qAbs(x - (mTarget->width() - margin)) <= margin)
563 if (x >= -margin && x <= mTarget->width() + margin) {
564 if (qAbs(y) <= margin)
566 else if (qAbs(y - (mTarget->height() - margin)) <= margin)
571 result = result & mAllowedResizeSides;
577void WidgetResizeHandler::setupWindow(
QWindow *window)
583 if (KDDockWidgets::usesAeroSnapWithCustomDecos()) {
584 const auto wid = HWND(window->
winId());
590 SetWindowPos(wid, 0, 0, 0, 0, 0,
591 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
594 const bool usesTransparentFloatingWindow =
596 if (!usesTransparentFloatingWindow) {
600 MARGINS margins = { 0, 0, 0, 1 };
601 DwmExtendFrameIntoClientArea(wid, &margins);
610bool WidgetResizeHandler::isInterestingNativeEvent(
unsigned int nativeEvent)
612 switch (nativeEvent) {
615 case WM_NCLBUTTONDBLCLK:
616 case WM_GETMINMAXINFO:
624#if defined(Q_OS_WIN) && defined(KDDOCKWIDGETS_QTWIDGETS)
625bool NCHITTESTEventFilter::nativeEventFilter(
const QByteArray &eventType,
void *message,
626 Qt5Qt6Compat::qintptr *result)
629 if (eventType !=
"windows_generic_MSG" || !m_floatingWindow)
632 auto msg =
static_cast<MSG *
>(message);
633 if (msg->message != WM_NCHITTEST)
635 const WId wid = WId(msg->hwnd);
638 if (!child || child->
window() != m_floatingWindow)
640 const bool isThisWindow = child == m_floatingWindow;
643 *result = HTTRANSPARENT;
652CustomFrameHelper::CustomFrameHelper(ShouldUseCustomFrame func,
QObject *parent)
655 , m_shouldUseCustomFrameFunc(func)
658 qApp->installNativeEventFilter(
this);
662CustomFrameHelper::~CustomFrameHelper()
667void CustomFrameHelper::applyCustomFrame(
QWindow *window)
670 WidgetResizeHandler::setupWindow(window);
673 qWarning() << Q_FUNC_INFO <<
"Not implemented on this platform";
677bool CustomFrameHelper::nativeEventFilter(
const QByteArray &eventType,
void *message,
678 Qt5Qt6Compat::qintptr *result)
680 if (m_shouldUseCustomFrameFunc ==
nullptr || m_recursionGuard)
686 if (m_inDtor || !KDDockWidgets::usesAeroSnapWithCustomDecos())
689 if (eventType !=
"windows_generic_MSG")
692 auto msg =
static_cast<MSG *
>(message);
693 if (!WidgetResizeHandler::isInterestingNativeEvent(msg->message)) {
698 QWindow *window = DockRegistry::self()->windowForHandle(WId(msg->hwnd));
702 const WidgetResizeHandler::NativeFeatures features = m_shouldUseCustomFrameFunc(window);
703 if (!features.hasFeatures()) {
708 const char *propertyName =
"kddw_customframe_setup_ran";
712 WidgetResizeHandler::setupWindow(window);
716 return WidgetResizeHandler::handleWindowsNativeEvent(window, msg, result, features);
Application-wide config to tune certain behaviours of the framework.
QEvent::Type type() const const
QVariant property(const char *name) const const
bool setProperty(const char *name, const QVariant &value)
QRect adjusted(int dx1, int dy1, int dx2, int dy2) const const
bool contains(const QRect &rectangle, bool proper) const const
QRect intersected(const QRect &rectangle) const const
QRect marginsAdded(const QMargins &margins) const const
void moveTopLeft(const QPoint &position)
QPoint topLeft() const const
bool toBool() const const
QScreen * screen() const const
void screenChanged(QScreen *screen)