KDDockWidgets API Documentation 1.7
Loading...
Searching...
No Matches
FloatingWindow.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 "FloatingWindow_p.h"
13#include "KDDockWidgets.h"
14#include "MainWindowBase.h"
15#include "Logging_p.h"
16#include "Frame_p.h"
17#include "TitleBar_p.h"
18#include "WindowBeingDragged_p.h"
19#include "Utils_p.h"
20#include "WidgetResizeHandler_p.h"
21#include "DockRegistry_p.h"
22#include "Config.h"
24#include "DragController_p.h"
25#include "LayoutSaver_p.h"
26#include "DockWidgetBase_p.h"
27
28#include "multisplitter/Item_p.h"
29
30#include <QCloseEvent>
31#include <QScopedValueRollback>
32#include <QTimer>
33#include <QWindow>
34
35#if defined(Q_OS_WIN)
36#if defined(Q_CC_MSVC)
37// NOMINMAX tells windows.h not to define the max and min macros
38// which will interfere with the max() from std::numeric_limits
39#define NOMINMAX
40#endif
41#include <windows.h>
42#include <dwmapi.h>
43#endif
44
45using namespace KDDockWidgets;
46
48Qt::WindowFlags FloatingWindow::s_windowFlagsOverride = {};
49
50static Qt::WindowFlags windowFlagsToUse(FloatingWindowFlags requestedFlags)
51{
52 if (requestedFlags & FloatingWindowFlag::UseQtTool) {
53 // User has explicitly chosen Qt::Tool for this FloatingWindow
54 return Qt::Tool;
55 }
56
57 if (requestedFlags & FloatingWindowFlag::UseQtWindow) {
58 // User has explicitly chosen Qt::Window for this FloatingWindow
59 return Qt::Window;
60 }
61
62 if (FloatingWindow::s_windowFlagsOverride) {
63 // User overridden the default for all FloatingWindows
64 return FloatingWindow::s_windowFlagsOverride;
65 }
66
67 if (KDDockWidgets::usesNativeDraggingAndResizing())
68 return Qt::Window;
69
71 return Qt::Window;
72
73 return Qt::Tool;
74}
75
76static MainWindowBase *hackFindParentHarder(Frame *frame, MainWindowBase *candidateParent)
77{
78 const FloatingWindowFlags requestedFlags = frame ? frame->requestedFloatingWindowFlags() : FloatingWindowFlag::FromGlobalConfig;
79 if (requestedFlags & FloatingWindowFlag::DontUseParentForFloatingWindows) {
80 // User explicitly requested no parent for this floating window
81 return nullptr;
82 }
83
85 return nullptr;
86 }
87
88 // TODO: Using a parent helps the floating windows stay in front of the main window always.
89 // We're not receiving the parent via ctor argument as the app can have multiple-main windows,
90 // so use a hack here.
91 // Not quite clear what to do if the app supports multiple main windows though.
92
93 if (candidateParent)
94 return candidateParent;
95
96 const MainWindowBase::List windows = DockRegistry::self()->mainwindows();
97
98 if (windows.isEmpty())
99 return nullptr;
100
101 if (windows.size() == 1) {
102 return windows.first();
103 } else {
104 const QStringList affinities = frame ? frame->affinities() : QStringList();
105 const MainWindowBase::List mainWindows = DockRegistry::self()->mainWindowsWithAffinity(affinities);
106
107 if (mainWindows.isEmpty()) {
108 qWarning() << Q_FUNC_INFO << "No window with affinity" << affinities << "found";
109 return nullptr;
110 } else {
111 return mainWindows.first();
112 }
113 }
114}
115
117{
119 ? nullptr
120 : candidate;
121}
122
123static FloatingWindowFlags flagsForFloatingWindow(FloatingWindowFlags requestedFlags)
124{
125 if (!(requestedFlags & FloatingWindowFlag::FromGlobalConfig)) {
126 // User requested specific flags for this floating window
127 return requestedFlags;
128 }
129
130 // Use from KDDockWidgets::Config instead. This is app-wide and not per window.
131
132 FloatingWindowFlags flags = {};
133
135 flags |= FloatingWindowFlag::TitleBarHasMinimizeButton;
136
138 flags |= FloatingWindowFlag::TitleBarHasMaximizeButton;
139
141 flags |= FloatingWindowFlag::KeepAboveIfNotUtilityWindow;
142
144 flags |= FloatingWindowFlag::NativeTitleBar;
145
147 flags |= FloatingWindowFlag::HideTitleBarWhenTabsVisible;
148
150 flags |= FloatingWindowFlag::AlwaysTitleBarWhenFloating;
151
153 flags |= FloatingWindowFlag::DontUseParentForFloatingWindows;
154
156 flags |= FloatingWindowFlag::UseQtWindow;
157
158 return flags;
159}
160
161FloatingWindow::FloatingWindow(QRect suggestedGeometry, MainWindowBase *parent,
162 FloatingWindowFlags requestedFlags)
163 : QWidgetAdapter(actualParent(parent), windowFlagsToUse(requestedFlags))
164 , Draggable(this, KDDockWidgets::usesNativeDraggingAndResizing()) // FloatingWindow is only draggable when using a native title bar. Otherwise the KDDockWidgets::TitleBar is the draggable
165 , m_flags(flagsForFloatingWindow(requestedFlags))
166 , m_dropArea(new DropArea(this))
167 , m_titleBar(Config::self().frameworkWidgetFactory()->createTitleBar(this))
168{
169 if (!suggestedGeometry.isNull())
170 setGeometry(suggestedGeometry);
171
172 if (kddwUsesQtWidgets()) {
173 // For QtQuick we do it a bit later, once we have the QQuickWindow
174#ifdef Q_OS_WIN
175 create();
176#ifdef KDDOCKWIDGETS_QTWIDGETS
177 m_nchittestFilter = new NCHITTESTEventFilter(this);
178 qApp->installNativeEventFilter(m_nchittestFilter);
179#endif
180 WidgetResizeHandler::setupWindow(windowHandle());
181#endif
182 }
183
184 DockRegistry::self()->registerFloatingWindow(this);
185
186 if (m_flags & FloatingWindowFlag::KeepAboveIfNotUtilityWindow)
187 setWindowFlag(Qt::WindowStaysOnTopHint, true);
188
189 if (kddwUsesQtWidgets()) {
190 // QtQuick will do it a bit later, once it has a QWindow
191 maybeCreateResizeHandler();
192 }
193
194 updateTitleBarVisibility();
195 connect(m_dropArea, &LayoutWidget::visibleWidgetCountChanged, this,
196 &FloatingWindow::onFrameCountChanged);
197 connect(m_dropArea, &LayoutWidget::visibleWidgetCountChanged, this,
198 &FloatingWindow::numFramesChanged);
199 connect(m_dropArea, &LayoutWidget::visibleWidgetCountChanged, this,
200 &FloatingWindow::onVisibleFrameCountChanged);
201 m_layoutDestroyedConnection = connect(m_dropArea, &QObject::destroyed, this, &FloatingWindow::scheduleDeleteLater);
202}
203
204static FloatingWindowFlags floatingWindowFlagsForFrame(Frame *frame)
205{
206 if (!frame)
207 return FloatingWindowFlag::FromGlobalConfig;
208
209 return frame->requestedFloatingWindowFlags();
210}
211
212FloatingWindow::FloatingWindow(Frame *frame, QRect suggestedGeometry, MainWindowBase *parent)
213 : FloatingWindow(suggestedGeometry, hackFindParentHarder(frame, parent), floatingWindowFlagsForFrame(frame))
214{
215 QScopedValueRollback<bool> guard(m_disableSetVisible, true);
216
217 if (frame->hasNestedMDIDockWidgets()) {
218 // When using DockWidget::MDINestable, the docked MDI widget is wrapped by a drop area so we can drop things into it.
219 // When floating it, we can delete that helper drop area, as FloatingWindow already has one
220
221 if (frame->dockWidgetCount() == 0) {
222 // doesn't happen
223 qWarning() << Q_FUNC_INFO << "Unexpected empty frame";
224 return;
225 }
226
227 DockWidgetBase *dwMDIWrapper = frame->dockWidgetAt(0);
228 DropArea *dropAreaMDIWrapper = dwMDIWrapper->d->mdiDropAreaWrapper();
229
230 if (dropAreaMDIWrapper->hasSingleFrame()) {
231 Frame *innerFrame = dropAreaMDIWrapper->frames().constFirst();
232 if (innerFrame->hasSingleDockWidget()) {
233 // When pressing the unfloat button, the dock widgets gets docked to the previous
234 // position it was at. DockWidgetBase::Private::m_lastPosition stores that location,
235 // however, when having nested MDI, we have an extra Dock Widget, the wrapper, and it
236 // contains the last position. So, when floating, we need to transfer that and not lose it.
237 DockWidgetBase *dw = innerFrame->dockWidgetAt(0);
238 dw->d->lastPosition() = dwMDIWrapper->d->lastPosition();
239 }
240 }
241
242 m_dropArea->addMultiSplitter(dropAreaMDIWrapper, Location_OnTop);
243 dwMDIWrapper->setVisible(false);
244 if (!DragController::instance()->isIdle()) {
245 // We're dragging a MDI window and we reached the border, detaching it, and making it float. We can't delete the wrapper frame just yet,
246 // as that would delete the title bar which is currently being dragged. Delete it once the drag finishes
247 connect(DragController::instance(), &DragController::currentStateChanged, dwMDIWrapper, [dwMDIWrapper] {
248 if (DragController::instance()->isIdle())
249 delete dwMDIWrapper;
250 });
251 } else {
252 dwMDIWrapper->deleteLater();
253 }
254
255 } else {
256 // Adding a widget will trigger onFrameCountChanged, which triggers a setVisible(true).
257 // The problem with setVisible(true) will forget about or requested geometry and place the window at 0,0
258 // So disable the setVisible(true) call while in the ctor.
259 m_dropArea->addWidget(frame, KDDockWidgets::Location_OnTop, {});
260 }
261}
262
263FloatingWindow::~FloatingWindow()
264{
265 m_inDtor = true;
266 disconnect(m_layoutDestroyedConnection);
267 delete m_nchittestFilter;
268
269 DockRegistry::self()->unregisterFloatingWindow(this);
270}
271
272#if defined(Q_OS_WIN) && defined(KDDOCKWIDGETS_QTWIDGETS)
273bool FloatingWindow::nativeEvent(const QByteArray &eventType, void *message, Qt5Qt6Compat::qintptr *result)
274{
275 if (m_inDtor || m_deleteScheduled)
276 return QWidget::nativeEvent(eventType, message, result);
277
278 if (KDDockWidgets::usesAeroSnapWithCustomDecos()) {
279 // To enable aero snap we need to tell Windows where's our custom title bar
280 if (WidgetResizeHandler::handleWindowsNativeEvent(this, eventType, message, result))
281 return true;
282 } else if (KDDockWidgets::usesNativeTitleBar()) {
283 auto msg = static_cast<MSG *>(message);
284 if (msg->message == WM_SIZING) {
285 // Cancel any drag if we're resizing
286 Q_EMIT DragController::instance()->dragCanceled();
287 }
288 }
289
290 return QWidget::nativeEvent(eventType, message, result);
291}
292#endif
293
294void FloatingWindow::maybeCreateResizeHandler()
295{
296 if (!KDDockWidgets::usesNativeDraggingAndResizing()) {
297 setFlag(Qt::FramelessWindowHint, true);
298 // EGLFS can't have different mouse cursors per window, needs global filter hack to unset when cursor leaves
299 const auto filterMode = isEGLFS() ? WidgetResizeHandler::EventFilterMode::Global
300 : WidgetResizeHandler::EventFilterMode::Local;
301
302 setWidgetResizeHandler(new WidgetResizeHandler(filterMode,
303 WidgetResizeHandler::WindowMode::TopLevel,
304 this));
305 }
306}
307
308std::unique_ptr<WindowBeingDragged> FloatingWindow::makeWindow()
309{
310 return std::unique_ptr<WindowBeingDragged>(new WindowBeingDragged(this, this));
311}
312
313DockWidgetBase *FloatingWindow::singleDockWidget() const
314{
315 const Frame::List frames = this->frames();
316 if (frames.size() == 1) {
317 Frame *frame = frames.first();
318 if (frame->hasSingleDockWidget())
319 return frame->dockWidgetAt(0);
320 }
321
322 return nullptr;
323}
324
325const DockWidgetBase::List FloatingWindow::dockWidgets() const
326{
327 return m_dropArea->dockWidgets();
328}
329
330const Frame::List FloatingWindow::frames() const
331{
332 Q_ASSERT(m_dropArea);
333 return m_dropArea->frames();
334}
335
336QSize FloatingWindow::maxSizeHint() const
337{
338 QSize result = Layouting::Item::hardcodedMaximumSize;
339
340 if (!m_dropArea) {
341 // Still early, no layout set
342 return result;
343 }
344
345 const Frame::List frames = this->frames();
346 if (frames.size() == 1) {
347 // Let's honour max-size when we have a single-frame.
348 // multi-frame cases are more complicated and we're not sure if we want the window to
349 // bounce around. single-frame is the most common case, like floating a dock widget, so
350 // let's do that first, it's also easy.
351 Frame *frame = frames[0];
352 if (frame->dockWidgetCount() == 1) { // We don't support if there's tabbing
353 const QSize waste = (minimumSize() - frame->minSize()).expandedTo(QSize(0, 0));
354 result = frame->maxSizeHint() + waste;
355 }
356 }
357
358 // Semantically the result is fine, but bound it so we don't get:
359 // QWidget::setMaximumSize: (/KDDockWidgets::FloatingWindowWidget) The largest allowed size is (16777215,16777215)
360 return result.boundedTo(Layouting::Item::hardcodedMaximumSize);
361}
362
363void FloatingWindow::setSuggestedGeometry(QRect suggestedRect, SuggestedGeometryHints hint)
364{
365 const QSize maxSize = maxSizeHint();
366 const bool hasMaxSize = maxSize != Layouting::Item::hardcodedMaximumSize;
367 if (hasMaxSize) {
368 // Resize to new size but preserve center
369 const QPoint originalCenter = suggestedRect.center();
370 suggestedRect.setSize(maxSize.boundedTo(suggestedRect.size()));
371
373 && (m_flags & FloatingWindowFlag::NativeTitleBar)) {
374 const QMargins margins = contentMargins();
375 suggestedRect.setHeight(suggestedRect.height() - m_titleBar->height() + margins.top()
376 + margins.bottom());
377 }
378
380 suggestedRect.moveCenter(originalCenter);
381 }
382
383 ensureRectIsOnScreen(suggestedRect);
384
385 setGeometry(suggestedRect);
386}
387
388void FloatingWindow::scheduleDeleteLater()
389{
390 m_deleteScheduled = true;
391 DockRegistry::self()->unregisterFloatingWindow(this);
392 deleteLater();
393}
394
395MultiSplitter *FloatingWindow::multiSplitter() const
396{
397 return m_dropArea;
398}
399
400LayoutWidget *FloatingWindow::layoutWidget() const
401{
402 return m_dropArea;
403}
404
405bool FloatingWindow::isInDragArea(QPoint globalPoint) const
406{
407#ifdef Q_OS_WIN
408 // A click near the border will still send a Qt::NonClientMousePressEvent. We shouldn't
409 // interpret that as a drag, as it's for a native resize.
410 // Keep track of how we handled the WM_NCHITTEST
411 if (usesAeroSnapWithCustomDecos())
412 return m_lastHitTest == HTCAPTION;
413#endif
414
415 return dragRect().contains(globalPoint);
416}
417
418bool FloatingWindow::anyNonClosable() const
419{
420 for (Frame *frame : frames()) {
421 if (frame->anyNonClosable())
422 return true;
423 }
424 return false;
425}
426
427bool FloatingWindow::anyNonDockable() const
428{
429 for (Frame *frame : frames()) {
430 if (frame->anyNonDockable())
431 return true;
432 }
433 return false;
434}
435
436bool FloatingWindow::hasSingleFrame() const
437{
438 return m_dropArea->hasSingleFrame();
439}
440
441bool FloatingWindow::hasSingleDockWidget() const
442{
443 const Frame::List frames = this->frames();
444 if (frames.size() != 1)
445 return false;
446
447 Frame *frame = frames.first();
448 return frame->dockWidgetCount() == 1;
449}
450
451Frame *FloatingWindow::singleFrame() const
452{
453 const Frame::List frames = this->frames();
454
455 return frames.isEmpty() ? nullptr
456 : frames.first();
457}
458
459bool FloatingWindow::beingDeleted() const
460{
461 if (m_deleteScheduled || m_inDtor)
462 return true;
463
464 // TODO: Confusing logic
465 for (Frame *f : frames()) {
466 if (!f->beingDeletedLater())
467 return false;
468 }
469
470 return true;
471}
472
473void FloatingWindow::onFrameCountChanged(int count)
474{
475 if (count == 0) {
476 scheduleDeleteLater();
477 } else {
478 updateTitleBarVisibility();
479 if (count == 1) // if something was removed, then our single dock widget is floating, we need to check the QAction
480 dropArea()->updateFloatingActions();
481 }
482}
483
484void FloatingWindow::onVisibleFrameCountChanged(int count)
485{
486 if (m_disableSetVisible)
487 return;
488
489 updateSizeConstraints();
490 setVisible(count > 0);
491}
492
493Qt::WindowState FloatingWindow::windowStateOverride() const
494{
496
497 if (isMaximizedOverride())
498 state = Qt::WindowMaximized;
499 else if (isMinimizedOverride())
500 state = Qt::WindowMinimized;
501
502 return state;
503}
504
505void FloatingWindow::updateTitleBarVisibility()
506{
507 if (m_updatingTitleBarVisibility)
508 return; // Break recursion
509
510 QScopedValueRollback<bool> guard(m_updatingTitleBarVisibility, true);
511 updateTitleAndIcon();
512
513 bool visible = true;
514
515 for (Frame *frame : frames())
516 frame->updateTitleBarVisibility();
517
518 if (KDDockWidgets::usesClientTitleBar()) {
519 if ((m_flags & FloatingWindowFlag::HideTitleBarWhenTabsVisible) && !(m_flags & FloatingWindowFlag::AlwaysTitleBarWhenFloating)) {
520 if (hasSingleFrame()) {
521 visible = !frames().first()->hasTabsVisible();
522 }
523 }
524
525 m_titleBar->updateButtons();
526 } else {
527 visible = false;
528 }
529
530 m_titleBar->setVisible(visible);
531}
532
533QStringList FloatingWindow::affinities() const
534{
535 auto frames = this->frames();
536 return frames.isEmpty() ? QStringList() : frames.constFirst()->affinities();
537}
538
539void FloatingWindow::updateTitleAndIcon()
540{
541 QString title;
542 QIcon icon;
543 if (hasSingleFrame()) {
544 const Frame *frame = frames().constFirst();
545 title = frame->title();
546 icon = frame->icon();
547 } else {
548 title = qApp->applicationName();
549 }
550 m_titleBar->setTitle(title);
551 m_titleBar->setIcon(icon);
552
553 // Even without a native title bar it's nice to set the window title/icon, so it shows
554 // in the taskbar (when minimization is supported), or Alt-Tab (in supporting Window Managers)
555 setWindowTitle(title);
556 setWindowIcon(icon);
557}
558
559void FloatingWindow::onCloseEvent(QCloseEvent *e)
560{
561 if (e->spontaneous() && anyNonClosable()) {
562 // Event from the window system won't close us
563 e->ignore();
564 return;
565 }
566
567 m_dropArea->onCloseEvent(e);
568}
569
570bool FloatingWindow::deserialize(const LayoutSaver::FloatingWindow &fw)
571{
572 if (dropArea()->deserialize(fw.multiSplitterLayout)) {
573 updateTitleBarVisibility();
574
575 if (fw.windowState & Qt::WindowMaximized) {
576 showMaximized();
577 } else if (fw.windowState & Qt::WindowMinimized) {
578 showMinimized();
579 } else {
580 showNormal();
581 }
582
583 return true;
584 } else {
585 return false;
586 }
587}
588
589LayoutSaver::FloatingWindow FloatingWindow::serialize() const
590{
591 LayoutSaver::FloatingWindow fw;
592
593 fw.geometry = geometry();
594 fw.normalGeometry = normalGeometry();
595 fw.isVisible = isVisible();
596 fw.multiSplitterLayout = dropArea()->serialize();
597 fw.screenIndex = screenNumberForWidget(this);
598 fw.screenSize = screenSizeForWidget(this);
599 fw.affinities = affinities();
600 fw.windowState = windowStateOverride();
601 fw.flags = m_flags;
602
603 auto mainWindow = qobject_cast<MainWindowBase *>(parentWidget());
604 fw.parentIndex = mainWindow ? DockRegistry::self()->mainwindows().indexOf(mainWindow)
605 : -1;
606
607 return fw;
608}
609
610QRect FloatingWindow::dragRect() const
611{
612 QRect rect;
613 if (m_titleBar->isVisible()) {
614 rect = m_titleBar->rect();
615 rect.moveTopLeft(m_titleBar->mapToGlobal(QPoint(0, 0)));
616 } else if (hasSingleFrame()) {
617 rect = frames().constFirst()->dragRect();
618 } else {
619 qWarning() << Q_FUNC_INFO << "Expected a title bar";
620 }
621
622 return rect;
623}
624
625bool FloatingWindow::event(QEvent *ev)
626{
627 if (ev->type() == QEvent::ActivationChange) {
628 // Since QWidget is missing a signal for window activation
629 Q_EMIT activatedChanged();
630 } else if (ev->type() == QEvent::StatusTip && parent()) {
631 // show status tips in the main window
632 return parent()->event(ev);
633 } else if (ev->type() == QEvent::LayoutRequest) {
634 updateSizeConstraints();
635 }
636
637 return QWidgetAdapter::event(ev);
638}
639
640bool FloatingWindow::allDockWidgetsHave(DockWidgetBase::Option option) const
641{
642 const Frame::List frames = this->frames();
643 return std::all_of(frames.begin(), frames.end(), [option](Frame *frame) {
644 return frame->allDockWidgetsHave(option);
645 });
646}
647
648bool FloatingWindow::anyDockWidgetsHas(DockWidgetBase::Option option) const
649{
650 const Frame::List frames = this->frames();
651 return std::any_of(frames.begin(), frames.end(), [option](Frame *frame) {
652 return frame->anyDockWidgetsHas(option);
653 });
654}
655
656bool FloatingWindow::allDockWidgetsHave(DockWidgetBase::LayoutSaverOption option) const
657{
658 const Frame::List frames = this->frames();
659 return std::all_of(frames.begin(), frames.end(), [option](Frame *frame) {
660 return frame->allDockWidgetsHave(option);
661 });
662}
663
664bool FloatingWindow::anyDockWidgetsHas(DockWidgetBase::LayoutSaverOption option) const
665{
666 const Frame::List frames = this->frames();
667 return std::any_of(frames.begin(), frames.end(), [option](Frame *frame) {
668 return frame->anyDockWidgetsHas(option);
669 });
670}
671
672void FloatingWindow::addDockWidget(DockWidgetBase *dw, Location location,
673 DockWidgetBase *relativeTo, InitialOption option)
674{
675 m_dropArea->addDockWidget(dw, location, relativeTo, option);
676}
677
678bool FloatingWindow::isMDI() const
679{
680 return false;
681}
682
683bool FloatingWindow::isWindow() const
684{
685 return true;
686}
687
688MainWindowBase *FloatingWindow::mainWindow() const
689{
690 return qobject_cast<MainWindowBase *>(parent());
691}
692
693QMargins FloatingWindow::contentMargins() const
694{
695 return { 4, 4, 4, 4 };
696}
697
698bool FloatingWindow::isMaximizedOverride() const
699{
700 return QWidgetAdapter::isMaximized();
701}
702
703bool FloatingWindow::isMinimizedOverride() const
704{
705 return QWidgetAdapter::isMinimized();
706}
707
708void FloatingWindow::showMaximized()
709{
710 QWidgetAdapter::showMaximized();
711}
712
713void FloatingWindow::showNormal()
714{
715 QWidgetAdapter::showNormal();
716}
717
718void FloatingWindow::showMinimized()
719{
720 QWidgetAdapter::showMinimized();
721}
722
723QRect FloatingWindow::normalGeometry() const
724{
725 return QWidgetAdapter::normalGeometry();
726}
727
728Qt::WindowState FloatingWindow::lastWindowManagerState() const
729{
730 return m_lastWindowManagerState;
731}
732
733int FloatingWindow::userType() const
734{
735 if (Frame *f = singleFrame())
736 return f->userType();
737 return 0;
738}
739
740void FloatingWindow::updateSizeConstraints()
741{
742 // Doing a delayed call to make sure the layout has completled any ongoing operation.
743 QTimer::singleShot(0, this, [this] {
744 // Not simply using layout's max-size support because
745 // 1) that's not portable to QtQuick
746 // 2) QStackedLayout (from tab-widget) doesn't propagate size constraints up
747 // Doing it manually instead.
748 setMaximumSize(maxSizeHint());
749 });
750}
751
752void FloatingWindow::ensureRectIsOnScreen(QRect &geometry)
753{
754 const auto screens = qApp->screens();
755 if (screens.empty())
756 return;
757
758 int nearestDistSq = std::numeric_limits<int>::max();
759 int nearestIndex = -1;
760
761 const int screenCount = screens.count();
762 for (int i = 0; i < screenCount; i++) {
763 const QRect scrGeom = screens[i]->geometry();
764
765 // If the rectangle is visible at all, we need do nothing
766 if (scrGeom.intersects(geometry))
767 return;
768
769 // Find the nearest screen, so we can move the geometry onto it
770 const QPoint dist2D = geometry.center() - scrGeom.center();
771 const int distSq = (dist2D.x() * dist2D.x()) + (dist2D.y() * dist2D.y());
772 if (distSq < nearestDistSq) {
773 nearestDistSq = distSq;
774 nearestIndex = i;
775 }
776 }
777
778 // Move the rectangle to the nearest vertical and/or horizontal screen edge
779 auto scrGeom = screens[nearestIndex]->geometry();
780 scrGeom.moveTopLeft(scrGeom.topLeft() - screens[nearestIndex]->virtualGeometry().topLeft());
781
782 if (geometry.left() < scrGeom.left()) {
783 geometry.moveLeft(scrGeom.left());
784 } else if (geometry.left() > scrGeom.right()) {
785 geometry.moveRight(scrGeom.right());
786 }
787
788 if (geometry.top() < scrGeom.top()) {
789 geometry.moveTop(scrGeom.top());
790 } else if (geometry.top() > scrGeom.bottom()) {
791 geometry.moveBottom(scrGeom.bottom());
792 }
793}
794
795bool FloatingWindow::supportsMinimizeButton() const
796{
797 return m_flags & FloatingWindowFlag::TitleBarHasMinimizeButton;
798}
799
800bool FloatingWindow::supportsMaximizeButton() const
801{
802 return m_flags & FloatingWindowFlag::TitleBarHasMaximizeButton;
803}
804
805bool FloatingWindow::isUtilityWindow() const
806{
807 const bool dontUse = (m_flags & FloatingWindowFlag::DontUseParentForFloatingWindows) && (m_flags & FloatingWindowFlag::UseQtWindow);
808 return !dontUse;
809}
810
811FloatingWindowFlags FloatingWindow::floatingWindowFlags() const
812{
813 return m_flags;
814}
Application-wide config to tune certain behaviours of the framework.
static FloatingWindowFlags flagsForFloatingWindow(FloatingWindowFlags requestedFlags)
MainWindowBase * actualParent(MainWindowBase *candidate)
static FloatingWindowFlags floatingWindowFlagsForFrame(Frame *frame)
static Qt::WindowFlags windowFlagsToUse(FloatingWindowFlags requestedFlags)
static MainWindowBase * hackFindParentHarder(Frame *frame, MainWindowBase *candidateParent)
A factory class for allowing the user to customize some internal widgets.
File with KDDockWidgets namespace-level enums and methods.
The MainWindow base-class that's shared between QtWidgets and QtQuick stack.
Singleton to allow to choose certain behaviours of the framework.
Definition Config.h:75
@ InternalFlag_DontUseQtToolWindowsForFloatingWindows
FloatingWindows will use Qt::Window instead of Qt::Tool.
Definition Config.h:134
@ InternalFlag_DontUseParentForFloatingWindows
FloatingWindows won't have a parent top-level.
Definition Config.h:133
static Config & self()
returns the singleton Config instance
Definition Config.cpp:84
@ Flag_TitleBarHasMinimizeButton
The title bar will have a minimize button when floating. This implies Flag_DontUseUtilityFloatingWind...
Definition Config.h:101
@ Flag_HideTitleBarWhenTabsVisible
Hides the title bar if there's tabs visible. The empty space in the tab bar becomes draggable.
Definition Config.h:92
@ Flag_TitleBarHasMaximizeButton
The title bar will have a maximize/restore button when floating. This is mutually-exclusive with the ...
Definition Config.h:97
@ Flag_KeepAboveIfNotUtilityWindow
Only meaningful if Flag_DontUseUtilityFloatingWindows is set. If floating windows are normal windows,...
Definition Config.h:105
@ Flag_AlwaysTitleBarWhenFloating
Floating windows will have a title bar even if Flag_HideTitleBarWhenTabsVisible is specified....
Definition Config.h:91
@ Flag_NativeTitleBar
Enables the Native OS title bar on OSes that support it (Windows 10, macOS), ignored otherwise.
Definition Config.h:89
The DockWidget base-class. DockWidget and DockWidgetBase are only split in two so we can share some c...
LayoutSaverOption
Options which will affect LayoutSaver save/restore.
Option
DockWidget options to pass at construction time.
The MainWindow base-class. MainWindow and MainWindowBase are only split in two so we can share some c...
@ Location_OnTop
‍Left docking location
@ SuggestedGeometryHint_PreserveCenter
@ SuggestedGeometryHint_GeometryIsFromDocked
ActivationChange
void ignore()
bool spontaneous() const const
QEvent::Type type() const const
virtual bool event(QEvent *event) override
int bottom() const const
int top() const const
void destroyed(QObject *obj)
int x() const const
int y() const const
int bottom() const const
QPoint center() const const
int height() const const
bool intersects(const QRect &rectangle) const const
bool isNull() const const
int left() const const
void moveBottom(int y)
void moveCenter(const QPoint &position)
void moveLeft(int x)
void moveRight(int x)
void moveTop(int y)
void moveTopLeft(const QPoint &position)
int right() const const
void setHeight(int height)
void setSize(const QSize &size)
QSize size() const const
int top() const const
QPoint topLeft() const const
QSize boundedTo(const QSize &otherSize) const const
WindowState
typedef WindowFlags
T & first()
bool isEmpty() const const
int size() const const
virtual bool nativeEvent(const QByteArray &eventType, void *message, long *result)
Struct describing the preferred dock widget size and visibility when adding it to a layout.

© 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