KDDockWidgets API Documentation 1.7
Loading...
Searching...
No Matches
WidgetResizeHandler.cpp
Go to the documentation of this file.
1/*
2 This file is part of KDDockWidgets.
3
4 SPDX-FileCopyrightText: 2019-2023 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
5 Author: SĂ©rgio Martins <sergio.martins@kdab.com>
6
7 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
8
9 Contact KDAB at <info@kdab.com> for commercial licensing options.
10*/
11
12#include "WidgetResizeHandler_p.h"
13#include "FloatingWindow_p.h"
14#include "TitleBar_p.h"
15#include "DragController_p.h"
16#include "Config.h"
17#include "Qt5Qt6Compat_p.h"
18#include "Utils_p.h"
19#include "DockRegistry_p.h"
20#include "MDILayoutWidget_p.h"
21
22#include <QEvent>
23#include <QMouseEvent>
24#include <QDebug>
25#include <QGuiApplication>
26#include <QScreen>
27#include <QWindow>
28#include <QScopedValueRollback>
29
30#if defined(Q_OS_WIN)
31#include <QtGui/private/qhighdpiscaling_p.h>
32#include <windowsx.h>
33#include <windows.h>
34#include <dwmapi.h>
35#if defined(Q_CC_MSVC)
36#pragma comment(lib, "Dwmapi.lib")
37#pragma comment(lib, "User32.lib")
38#endif
39#endif
40
41using namespace KDDockWidgets;
42
43bool WidgetResizeHandler::s_disableAllHandlers = false;
44WidgetResizeHandler::WidgetResizeHandler(EventFilterMode filterMode, WindowMode windowMode, QWidgetOrQuick *target)
45 : QObject(target)
46 , m_usesGlobalEventFilter(filterMode == EventFilterMode::Global)
47 , m_isTopLevelWindowResizer(windowMode == WindowMode::TopLevel)
48{
49 setTarget(target);
50}
51
52WidgetResizeHandler::~WidgetResizeHandler()
53{
54 restoreMouseCursor();
55}
56
57void WidgetResizeHandler::setAllowedResizeSides(CursorPositions sides)
58{
59 mAllowedResizeSides = sides;
60}
61
62void WidgetResizeHandler::setResizeGap(int gap)
63{
64 m_resizeGap = gap;
65}
66
67bool WidgetResizeHandler::isMDI() const
68{
69 auto frame = qobject_cast<Frame *>(mTarget);
70 return frame && frame->isMDI();
71}
72
73bool WidgetResizeHandler::isResizing() const
74{
75 return m_resizingInProgress;
76}
77
78int WidgetResizeHandler::widgetResizeHandlerMargin()
79{
80 return 4; // pixels
81}
82
83bool WidgetResizeHandler::eventFilter(QObject *o, QEvent *e)
84{
85 if (s_disableAllHandlers)
86 return false;
87
88 auto widget = qobject_cast<QWidgetOrQuick *>(o);
89 if (!widget)
90 return false;
91
92 auto me = mouseEvent(e);
93 if (!me)
94 return false;
95
96 if (m_isTopLevelWindowResizer) {
97 // Case #1.0: Resizing FloatingWindow
98
99 if (!widget->isTopLevel() || o != mTarget) {
100 if (m_usesGlobalEventFilter) {
101 // Case #1.1: FloatingWindows on EGLFS
102 // EGLFS doesn't support storing mouse cursor shape per window, so we need to use global filter
103 // do detect mouse leaving the window
104 if (!m_resizingInProgress) {
105 const QPoint globalPos = Qt5Qt6Compat::eventGlobalPos(me);
106 updateCursor(cursorPosition(globalPos));
107 }
108 }
109
110 // Case #1.2: FloatingWindows on all other platforms
111 // Not needed to mess with the cursor, it gets set when moving over another window.
112 return false;
113 }
114 } else if (isMDI()) {
115 // Case #2: Resizing an embedded MDI "Window"
116
117 // Each Frame has a WidgetResizeHandler instance.
118 // mTarget is the Frame we want to resize.
119 // but 'o' might not be mTarget, because we're using a global event filter.
120 // The global event filter is required because we allow the cursor to be outside the frame, a few pixels
121 // so we have a nice resize margin.
122 // Here we deal with the case where our mTarget, let's say "Frame 1" is on top of "Frame 2" but cursor
123 // is near "Frame 2"'s margins, and would show resize cursor.
124 // We only want to continue if the cursor is near the margins of our own frame (mTarget)
125
126 auto frame = firstParentOfType<Frame>(widget);
127 if (frame && frame->isMDIWrapper()) {
128 // We don't care about the inner Option_MDINestable helper frame
129 frame = frame->mdiFrame();
130 }
131
132 if (frame && frame != mTarget) {
133 const bool areSiblings = frame->QWidgetAdapter::parentWidget() == mTarget->parentWidget();
134 if (areSiblings)
135 return false;
136 }
137 }
138
139 switch (e->type()) {
141 if (mTarget->isMaximized())
142 break;
143
144 auto mouseEvent = static_cast<QMouseEvent *>(e);
145 auto cursorPos = cursorPosition(Qt5Qt6Compat::eventGlobalPos(mouseEvent));
146 updateCursor(cursorPos);
147 if (cursorPos == CursorPosition_Undefined)
148 return false;
149
150 const int m = widgetResizeHandlerMargin();
151 const QRect widgetRect = mTarget->rect().marginsAdded(QMargins(m, m, m, m));
152 const QPoint cursorPoint = mTarget->mapFromGlobal(Qt5Qt6Compat::eventGlobalPos(mouseEvent));
153 if (!widgetRect.contains(cursorPoint) || mouseEvent->button() != Qt::LeftButton)
154 return false;
155
156 m_resizingInProgress = true;
157 if (isMDI())
158 Q_EMIT DockRegistry::self()->frameInMDIResizeChanged();
159 mNewPosition = Qt5Qt6Compat::eventGlobalPos(mouseEvent);
160 mCursorPos = cursorPos;
161
162 return true;
163 }
165 m_resizingInProgress = false;
166 if (isMDI()) {
167 Q_EMIT DockRegistry::self()->frameInMDIResizeChanged();
168 auto frame = static_cast<Frame *>(mTarget);
169 // Usually in KDDW all geometry changes are done in the layout items, which propagate to the widgets
170 // When resizing a MDI however, we're resizing the widget directly. So update the corresponding layout
171 // item when we're finished.
172 frame->mdiLayoutWidget()->setDockWidgetGeometry(frame, frame->QWidgetAdapter::geometry());
173 }
174 auto mouseEvent = static_cast<QMouseEvent *>(e);
175 auto cursorPos = cursorPosition(Qt5Qt6Compat::eventGlobalPos(mouseEvent));
176 updateCursor(cursorPos);
177
178 if (mTarget->isMaximized() || !m_resizingInProgress || mouseEvent->button() != Qt::LeftButton)
179 break;
180
181 mTarget->releaseMouse();
182 mTarget->releaseKeyboard();
183 return true;
184
185 break;
186 }
187 case QEvent::MouseMove: {
188 if (mTarget->isMaximized())
189 break;
190
191 if (isMDI()) {
192 const Frame *frameBeingResized = DockRegistry::self()->frameInMDIResize();
193 const bool otherFrameBeingResized = frameBeingResized && frameBeingResized != mTarget;
194 if (otherFrameBeingResized) {
195 // only one at a time!
196 return false;
197 }
198 }
199
200 auto mouseEvent = static_cast<QMouseEvent *>(e);
201 m_resizingInProgress = m_resizingInProgress && (mouseEvent->buttons() & Qt::LeftButton);
202
203 const bool consumed = mouseMoveEvent(mouseEvent);
204 return consumed;
205 }
206 default:
207 break;
208 }
209 return false;
210}
211
212bool WidgetResizeHandler::mouseMoveEvent(QMouseEvent *e)
213{
214 const QPoint globalPos = Qt5Qt6Compat::eventGlobalPos(e);
215 if (!m_resizingInProgress) {
216 const CursorPosition pos = cursorPosition(globalPos);
217 updateCursor(pos);
218 return pos != CursorPosition_Undefined;
219 }
220
221 const QRect oldGeometry = KDDockWidgets::globalGeometry(mTarget);
222 QRect newGeometry = oldGeometry;
223
224 QRect parentGeometry;
225 if (!mTarget->isTopLevel()) {
226 auto parent = KDDockWidgets::Private::parentWidget(mTarget);
227 parentGeometry = KDDockWidgets::globalGeometry(parent);
228 }
229
230 {
231 int deltaWidth = 0;
232 int newWidth = 0;
233 const int maxWidth = Layouting::Widget::widgetMaxSize(mTarget).width();
234 const int minWidth = Layouting::Widget::widgetMinSize(mTarget).width();
235
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);
246 }
247
248 break;
249 }
250
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) {
259 newGeometry.setRight(oldGeometry.right() + deltaWidth);
260 }
261 break;
262 }
263 default:
264 break;
265 }
266 }
267
268 {
269 const int maxHeight = Layouting::Widget::widgetMaxSize(mTarget).height();
270 const int minHeight = Layouting::Widget::widgetMinSize(mTarget).height();
271 int deltaHeight = 0;
272 int newHeight = 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);
283 }
284
285 break;
286 }
287
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) {
296 newGeometry.setBottom(oldGeometry.bottom() + deltaHeight);
297 }
298 break;
299 }
300 default:
301 break;
302 }
303 }
304
305 if (newGeometry == mTarget->geometry()) {
306 // Nothing to do.
307 return true;
308 }
309
310 if (!mTarget->isTopLevel()) {
311
312 // Clip to parent's geometry.
313 newGeometry = newGeometry.intersected(parentGeometry);
314
315 // Back to local.
316 newGeometry.moveTopLeft(mTarget->mapFromGlobal(newGeometry.topLeft()) + mTarget->pos());
317 }
318
319 mTarget->setGeometry(newGeometry);
320 return true;
321}
322
323#ifdef Q_OS_WIN
324
326bool WidgetResizeHandler::handleWindowsNativeEvent(FloatingWindow *fw, const QByteArray &eventType,
327 void *message, Qt5Qt6Compat::qintptr *result)
328{
329 if (eventType != "windows_generic_MSG")
330 return false;
331
332 auto msg = static_cast<MSG *>(message);
333 if (msg->message == WM_NCHITTEST) {
334 if (DragController::instance()->isInClientDrag()) {
335 // There's a non-native drag going on.
336 *result = 0;
337 return false;
338 }
339
340 const QRect htCaptionRect = fw->dragRect();
341 const bool ret = handleWindowsNativeEvent(fw->windowHandle(), msg, result, htCaptionRect);
342
343 fw->setLastHitTest(*result);
344 return ret;
345 } else if (msg->message == WM_NCLBUTTONDBLCLK) {
347 return handleWindowsNativeEvent(fw->windowHandle(), msg, result, {});
348 } else {
349 // Let the title bar handle it. It will re-dock the window.
350 if (TitleBar *titleBar = fw->titleBar()) {
351 if (titleBar->isVisible()) { // can't be invisible afaik
352 titleBar->onDoubleClicked();
353 }
354 }
355
356 return true;
357 }
358 }
359
360 return handleWindowsNativeEvent(fw->windowHandle(), msg, result, {});
361}
362
363bool WidgetResizeHandler::handleWindowsNativeEvent(QWindow *w, MSG *msg,
364 Qt5Qt6Compat::qintptr *result,
365 const NativeFeatures &features)
366{
367 if (msg->message == WM_NCCALCSIZE && features.hasShadow()) {
368 *result = 0;
369 return true;
370 } else if (msg->message == WM_NCHITTEST && (features.hasResize() || features.hasDrag())) {
371 const int borderWidth = 8;
372 const bool hasFixedWidth = w->minimumWidth() == w->maximumWidth();
373 const bool hasFixedHeight = w->minimumHeight() == w->maximumHeight();
374 const bool hasFixedWidthOrHeight = hasFixedWidth || hasFixedHeight;
375
376 *result = 0;
377 const int xPos = GET_X_LPARAM(msg->lParam);
378 const int yPos = GET_Y_LPARAM(msg->lParam);
379 RECT rect;
380 GetWindowRect(reinterpret_cast<HWND>(w->winId()), &rect);
381
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()) {
387 *result = HTTOPLEFT;
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()) {
391 *result = HTLEFT;
392 } else if (!hasFixedHeight && yPos >= rect.top && yPos <= rect.top + borderWidth && features.hasResize()) {
393 *result = HTTOP;
394 } else if (!hasFixedHeight && yPos <= rect.bottom && yPos >= rect.bottom - borderWidth && features.hasResize()) {
395 *result = HTBOTTOM;
396 } else if (!hasFixedWidth && xPos <= rect.right && xPos >= rect.right - borderWidth && features.hasResize()) {
397 *result = HTRIGHT;
398 } else if (features.hasDrag()) {
399 const QPoint globalPosQt = QHighDpi::fromNativePixels(QPoint(xPos, yPos), w);
400 // htCaptionRect is the rect on which we allow for Windows to do a native drag
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)) { // Just makes sure the mouse isn't over the close button, we don't allow drag in that case.
404 *result = HTCAPTION;
405 }
406 }
407 }
408
409 return *result != 0;
410 } else if (msg->message == WM_NCLBUTTONDBLCLK && features.hasMaximize()) {
411 // By returning false we accept Windows native action, a maximize.
412 // We could also call titleBar->onDoubleClicked(); here which will maximize if Flag_DoubleClickMaximizes is set,
413 // but there's a bug in QWidget::showMaximized() on Windows when we're covering the native title bar, the window is maximized with an offset.
414 // So instead, use a native maximize which works well
415 return false;
416 } else if (msg->message == WM_GETMINMAXINFO) {
417 // Qt doesn't work well with windows that don't have title bar but have native frames.
418 // When maximized they go out of bounds and the title bar is clipped, so catch WM_GETMINMAXINFO
419 // and patch the size
420
421 // According to microsoft docs it only works for the primary screen, but extrapolates for the others
423 if (!screen || w->screen() != screen) {
424 return false;
425 }
426
427 DefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam);
428
429 const QRect availableGeometry = screen->availableGeometry();
430
431 auto mmi = reinterpret_cast<MINMAXINFO *>(msg->lParam);
432 const qreal dpr = screen->devicePixelRatio();
433
434 mmi->ptMaxSize.y = int(availableGeometry.height() * dpr);
435 mmi->ptMaxSize.x = int(availableGeometry.width() * dpr) - 1; // -1 otherwise it gets bogus size
436 mmi->ptMaxPosition.x = availableGeometry.x();
437 mmi->ptMaxPosition.y = availableGeometry.y();
438
439 mmi->ptMinTrackSize.x = int(w->minimumWidth() * dpr);
440 mmi->ptMinTrackSize.y = int(w->minimumHeight() * dpr);
441
442 *result = 0;
443 return true;
444 }
445
446 return false;
447}
448
449#endif
450
451void WidgetResizeHandler::setTarget(QWidgetOrQuick *w)
452{
453 if (w) {
454 mTarget = w;
455 mTarget->setMouseTracking(true);
456 if (m_usesGlobalEventFilter) {
457 qApp->installEventFilter(this);
458 } else {
459 mTarget->installEventFilter(this);
460 }
461 } else {
462 qWarning() << "Target widget is null!";
463 }
464}
465
466void WidgetResizeHandler::updateCursor(CursorPosition m)
467{
468#ifdef KDDOCKWIDGETS_QTWIDGETS
469 // Need for updating cursor when we change child widget
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))) {
473
474 if (!child->testAttribute(Qt::WA_SetCursor)) {
475 child->setCursor(Qt::ArrowCursor);
476 }
477 }
478 }
479#endif
480
481 switch (m) {
484 setMouseCursor(Qt::SizeFDiagCursor);
485 break;
488 setMouseCursor(Qt::SizeBDiagCursor);
489 break;
492 setMouseCursor(Qt::SizeVerCursor);
493 break;
496 setMouseCursor(Qt::SizeHorCursor);
497 break;
499 restoreMouseCursor();
500 break;
504 // Doesn't happen
505 break;
506 }
507}
508
509void WidgetResizeHandler::setMouseCursor(Qt::CursorShape cursor)
510{
511 if (m_usesGlobalEventFilter) {
512 if (m_overrideCursorSet) {
513 qApp->changeOverrideCursor(cursor);
514 } else {
515 qApp->setOverrideCursor(cursor);
516 m_overrideCursorSet = true;
517 }
518 } else {
519 mTarget->setCursor(cursor);
520 }
521}
522
523void WidgetResizeHandler::restoreMouseCursor()
524{
525 if (m_usesGlobalEventFilter) {
526 if (m_overrideCursorSet) {
527 qApp->restoreOverrideCursor();
528 m_overrideCursorSet = false;
529 }
530 } else {
531 mTarget->setCursor(Qt::ArrowCursor);
532 }
533}
534
535CursorPosition WidgetResizeHandler::cursorPosition(QPoint globalPos) const
536{
537 if (!mTarget)
539
540#ifdef KDDOCKWIDGETS_QTQUICK
541 if (isMDI()) {
542 // Special case for QtQuick. The MouseAreas are driving it and know better what's the
543 // cursor position
544 return CursorPosition(mTarget->property("cursorPosition").toInt());
545 }
546#endif
547
548 QPoint pos = mTarget->mapFromGlobal(globalPos);
549
550 const int x = pos.x();
551 const int y = pos.y();
552 const int margin = widgetResizeHandlerMargin();
553
555
556 if (y >= -margin && y <= mTarget->height() + margin) {
557 if (qAbs(x) <= margin)
558 result |= CursorPosition_Left;
559 else if (qAbs(x - (mTarget->width() - margin)) <= margin)
560 result |= CursorPosition_Right;
561 }
562
563 if (x >= -margin && x <= mTarget->width() + margin) {
564 if (qAbs(y) <= margin)
565 result |= CursorPosition_Top;
566 else if (qAbs(y - (mTarget->height() - margin)) <= margin)
567 result |= CursorPosition_Bottom;
568 }
569
570 // Filter out sides we don't allow
571 result = result & mAllowedResizeSides;
572
573 return static_cast<CursorPosition>(result);
574}
575
577void WidgetResizeHandler::setupWindow(QWindow *window)
578{
579 // Does some minor setup on our QWindow.
580 // Like adding the drop shadow on Windows and two other workarounds.
581
582#if defined(Q_OS_WIN)
583 if (KDDockWidgets::usesAeroSnapWithCustomDecos()) {
584 const auto wid = HWND(window->winId());
585 connect(window, &QWindow::screenChanged, window, [wid] {
586 // Qt honors our frame hijacking usually... but when screen changes we must give it a
587 // nudge. Otherwise what Qt thinks is the client area is not what Windows knows it is.
588 // SetWindowPos() will trigger an NCCALCSIZE message, which Qt will intercept and take
589 // note of the margins we're using.
590 SetWindowPos(wid, 0, 0, 0, 0, 0,
591 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
592 });
593
594 const bool usesTransparentFloatingWindow =
596 if (!usesTransparentFloatingWindow) {
597 // This enables the native drop shadow.
598 // Doesn't work well if the floating window has transparent round corners (shows weird white line).
599
600 MARGINS margins = { 0, 0, 0, 1 }; // arbitrary, just needs to be > 0 it seems
601 DwmExtendFrameIntoClientArea(wid, &margins);
602 }
603 }
604#else
605 Q_UNUSED(window);
606#endif // Q_OS_WIN
607}
608
609#ifdef Q_OS_WIN
610bool WidgetResizeHandler::isInterestingNativeEvent(unsigned int nativeEvent)
611{
612 switch (nativeEvent) {
613 case WM_NCHITTEST:
614 case WM_NCCALCSIZE:
615 case WM_NCLBUTTONDBLCLK:
616 case WM_GETMINMAXINFO:
617 return true;
618 default:
619 return false;
620 }
621}
622#endif
623
624#if defined(Q_OS_WIN) && defined(KDDOCKWIDGETS_QTWIDGETS)
625bool NCHITTESTEventFilter::nativeEventFilter(const QByteArray &eventType, void *message,
626 Qt5Qt6Compat::qintptr *result)
627
628{
629 if (eventType != "windows_generic_MSG" || !m_floatingWindow)
630 return false;
631
632 auto msg = static_cast<MSG *>(message);
633 if (msg->message != WM_NCHITTEST)
634 return false;
635 const WId wid = WId(msg->hwnd);
636
637 QWidget *child = QWidget::find(wid);
638 if (!child || child->window() != m_floatingWindow)
639 return false;
640 const bool isThisWindow = child == m_floatingWindow;
641
642 if (!isThisWindow) {
643 *result = HTTRANSPARENT;
644 return true;
645 }
646
647 return false;
648}
649#endif
650
651
652CustomFrameHelper::CustomFrameHelper(ShouldUseCustomFrame func, QObject *parent)
653 : QObject(parent)
655 , m_shouldUseCustomFrameFunc(func)
656{
657#ifdef Q_OS_WIN
658 qApp->installNativeEventFilter(this);
659#endif
660}
661
662CustomFrameHelper::~CustomFrameHelper()
663{
664 m_inDtor = true;
665}
666
667void CustomFrameHelper::applyCustomFrame(QWindow *window)
668{
669#ifdef Q_OS_WIN
670 WidgetResizeHandler::setupWindow(window);
671#else
672 Q_UNUSED(window);
673 qWarning() << Q_FUNC_INFO << "Not implemented on this platform";
674#endif
675}
676
677bool CustomFrameHelper::nativeEventFilter(const QByteArray &eventType, void *message,
678 Qt5Qt6Compat::qintptr *result)
679{
680 if (m_shouldUseCustomFrameFunc == nullptr || m_recursionGuard)
681 return false;
682
683 QScopedValueRollback<bool> guard(m_recursionGuard, true);
684
685#ifdef Q_OS_WIN
686 if (m_inDtor || !KDDockWidgets::usesAeroSnapWithCustomDecos())
687 return false;
688
689 if (eventType != "windows_generic_MSG")
690 return false;
691
692 auto msg = static_cast<MSG *>(message);
693 if (!WidgetResizeHandler::isInterestingNativeEvent(msg->message)) {
694 // Save some CPU cycles
695 return false;
696 }
697
698 QWindow *window = DockRegistry::self()->windowForHandle(WId(msg->hwnd));
699 if (!window)
700 return false;
701
702 const WidgetResizeHandler::NativeFeatures features = m_shouldUseCustomFrameFunc(window);
703 if (!features.hasFeatures()) {
704 // No native support for is desired for this window
705 return false;
706 }
707
708 const char *propertyName = "kddw_customframe_setup_ran";
709 const bool setupRan = window->property(propertyName).toBool();
710 if (!setupRan) {
711 // Add drop shadow
712 WidgetResizeHandler::setupWindow(window);
713 window->setProperty(propertyName, true);
714 }
715
716 return WidgetResizeHandler::handleWindowsNativeEvent(window, msg, result, features);
717#else
718 Q_UNUSED(eventType);
719 Q_UNUSED(message);
720 Q_UNUSED(result);
721 return false;
722#endif
723}
Application-wide config to tune certain behaviours of the framework.
InternalFlags internalFlags() const
Definition Config.cpp:225
@ InternalFlag_UseTransparentFloatingWindow
For QtQuick only. Allows to have round-corners. It's flaky when used with native Windows drop-shadow.
Definition Config.h:136
static Config & self()
returns the singleton Config instance
Definition Config.cpp:84
@ Flag_DoubleClickMaximizes
Double clicking the titlebar will maximize a floating window instead of re-docking it.
Definition Config.h:96
static QSize widgetMaxSize(const T *w)
Definition Widget.h:164
static QSize widgetMinSize(const T *w)
Definition Widget.h:152
MouseButtonPress
QEvent::Type type() const const
QVariant property(const char *name) const const
bool setProperty(const char *name, const QVariant &value)
int x() const const
int y() const const
QRect adjusted(int dx1, int dy1, int dx2, int dy2) const const
int bottom() const const
bool contains(const QRect &rectangle, bool proper) const const
int height() const const
QRect intersected(const QRect &rectangle) const const
int left() const const
QRect marginsAdded(const QMargins &margins) const const
void moveTopLeft(const QPoint &position)
int right() const const
void setBottom(int y)
void setLeft(int x)
void setRight(int x)
void setTop(int y)
int top() const const
QPoint topLeft() const const
int width() const const
int x() const const
int y() const const
int height() const const
int width() const const
ArrowCursor
LeftButton
WA_SetCursor
bool toBool() const const
QWidget * find(WId id)
void setMouseTracking(bool enable)
QWidget * window() const const
QScreen * screen() const const
void screenChanged(QScreen *screen)
WId winId() const const

© 2019-2023 Klarälvdalens Datakonsult AB (KDAB)
"The Qt, C++ and OpenGL Experts"
https://www.kdab.com/
KDDockWidgets
Advanced Dock Widget Framework for Qt
https://www.kdab.com/development-resources/qt-tools/kddockwidgets/
Generated on Wed Nov 1 2023 00:02:31 for KDDockWidgets API Documentation by doxygen 1.9.8