12 #include "FloatingWindow_p.h"
14 #include "Logging_p.h"
16 #include "TitleBar_p.h"
17 #include "WindowBeingDragged_p.h"
19 #include "WidgetResizeHandler_p.h"
20 #include "DockRegistry_p.h"
23 #include "DragController_p.h"
24 #include "LayoutSaver_p.h"
25 #include "DockWidgetBase_p.h"
27 #include "multisplitter/Item_p.h"
29 #include <QCloseEvent>
30 #include <QScopedValueRollback>
35 #if defined(Q_CC_MSVC)
51 if (FloatingWindow::s_windowFlagsOverride) {
53 return FloatingWindow::s_windowFlagsOverride;
56 if (KDDockWidgets::usesNativeDraggingAndResizing())
77 return candidateParent;
84 if (windows.
size() == 1) {
85 return windows.
first();
88 const MainWindowBase::List mainWindows = DockRegistry::self()->mainWindowsWithAffinity(affinities);
91 qWarning() << Q_FUNC_INFO <<
"No window with affinity" << affinities <<
"found";
94 return mainWindows.
first();
109 , m_dropArea(new DropArea(this))
110 , m_titleBar(
Config::self().frameworkWidgetFactory()->createTitleBar(this))
112 if (!suggestedGeometry.
isNull())
113 setGeometry(suggestedGeometry);
115 if (kddwUsesQtWidgets()) {
119 #ifdef KDDOCKWIDGETS_QTWIDGETS
120 m_nchittestFilter =
new NCHITTESTEventFilter(
this);
121 qApp->installNativeEventFilter(m_nchittestFilter);
123 WidgetResizeHandler::setupWindow(windowHandle());
127 DockRegistry::self()->registerFloatingWindow(
this);
129 if (Config::self().flags() & Config::Flag_KeepAboveIfNotUtilityWindow)
132 if (kddwUsesQtWidgets()) {
134 maybeCreateResizeHandler();
137 updateTitleBarVisibility();
138 connect(m_dropArea, &LayoutWidget::visibleWidgetCountChanged,
this,
139 &FloatingWindow::onFrameCountChanged);
140 connect(m_dropArea, &LayoutWidget::visibleWidgetCountChanged,
this,
141 &FloatingWindow::numFramesChanged);
142 connect(m_dropArea, &LayoutWidget::visibleWidgetCountChanged,
this,
143 &FloatingWindow::onVisibleFrameCountChanged);
144 m_layoutDestroyedConnection = connect(m_dropArea, &
QObject::destroyed,
this, &FloatingWindow::scheduleDeleteLater);
147 FloatingWindow::FloatingWindow(Frame *frame,
QRect suggestedGeometry,
MainWindowBase *parent)
152 if (frame->hasNestedMDIDockWidgets()) {
156 if (frame->dockWidgetCount() == 0) {
158 qWarning() << Q_FUNC_INFO <<
"Unexpected empty frame";
163 DropArea *dropAreaMDIWrapper = dwMDIWrapper->d->mdiDropAreaWrapper();
165 if (dropAreaMDIWrapper->hasSingleFrame()) {
166 Frame *innerFrame = dropAreaMDIWrapper->frames().constFirst();
167 if (innerFrame->hasSingleDockWidget()) {
173 dw->d->lastPosition() = dwMDIWrapper->d->lastPosition();
178 dwMDIWrapper->setVisible(
false);
179 if (!DragController::instance()->isIdle()) {
182 connect(DragController::instance(), &DragController::currentStateChanged, dwMDIWrapper, [dwMDIWrapper] {
183 if (DragController::instance()->isIdle())
187 dwMDIWrapper->deleteLater();
198 FloatingWindow::~FloatingWindow()
201 disconnect(m_layoutDestroyedConnection);
202 delete m_nchittestFilter;
204 DockRegistry::self()->unregisterFloatingWindow(
this);
207 #if defined(Q_OS_WIN) && defined(KDDOCKWIDGETS_QTWIDGETS)
208 bool FloatingWindow::nativeEvent(
const QByteArray &eventType,
void *message, Qt5Qt6Compat::qintptr *result)
210 if (m_inDtor || m_deleteScheduled)
213 if (KDDockWidgets::usesAeroSnapWithCustomDecos()) {
215 if (WidgetResizeHandler::handleWindowsNativeEvent(
this, eventType, message, result))
217 }
else if (KDDockWidgets::usesNativeTitleBar()) {
218 auto msg =
static_cast<MSG *
>(message);
219 if (msg->message == WM_SIZING) {
221 Q_EMIT DragController::instance()->dragCanceled();
229 void FloatingWindow::maybeCreateResizeHandler()
231 if (!KDDockWidgets::usesNativeDraggingAndResizing()) {
234 const auto filterMode = isEGLFS() ? WidgetResizeHandler::EventFilterMode::Global
235 : WidgetResizeHandler::EventFilterMode::Local;
237 setWidgetResizeHandler(
new WidgetResizeHandler(filterMode,
238 WidgetResizeHandler::WindowMode::TopLevel,
243 std::unique_ptr<WindowBeingDragged> FloatingWindow::makeWindow()
245 return std::unique_ptr<WindowBeingDragged>(
new WindowBeingDragged(
this,
this));
250 const Frame::List frames = this->frames();
251 if (frames.size() == 1) {
252 Frame *frame = frames.first();
253 if (frame->hasSingleDockWidget())
254 return frame->dockWidgetAt(0);
262 return m_dropArea->dockWidgets();
265 const Frame::List FloatingWindow::frames()
const
267 Q_ASSERT(m_dropArea);
268 return m_dropArea->frames();
271 QSize FloatingWindow::maxSizeHint()
const
273 QSize result = Layouting::Item::hardcodedMaximumSize;
280 const Frame::List frames = this->frames();
281 if (frames.size() == 1) {
286 Frame *frame = frames[0];
287 if (frame->dockWidgetCount() == 1) {
288 const QSize waste = (minimumSize() - frame->minSize()).expandedTo(
QSize(0, 0));
289 result = frame->maxSizeHint() + waste;
295 return result.
boundedTo(Layouting::Item::hardcodedMaximumSize);
298 void FloatingWindow::setSuggestedGeometry(
QRect suggestedRect, SuggestedGeometryHints hint)
300 const QSize maxSize = maxSizeHint();
301 const bool hasMaxSize = maxSize != Layouting::Item::hardcodedMaximumSize;
308 && (Config::self().flags() & Config::Flag_NativeTitleBar)) {
309 const QMargins margins = contentMargins();
318 ensureRectIsOnScreen(suggestedRect);
320 setGeometry(suggestedRect);
323 void FloatingWindow::scheduleDeleteLater()
325 m_deleteScheduled =
true;
326 DockRegistry::self()->unregisterFloatingWindow(
this);
330 MultiSplitter *FloatingWindow::multiSplitter()
const
335 LayoutWidget *FloatingWindow::layoutWidget()
const
340 bool FloatingWindow::isInDragArea(
QPoint globalPoint)
const
346 if (usesAeroSnapWithCustomDecos())
347 return m_lastHitTest == HTCAPTION;
350 return dragRect().contains(globalPoint);
353 bool FloatingWindow::anyNonClosable()
const
355 for (Frame *frame : frames()) {
356 if (frame->anyNonClosable())
362 bool FloatingWindow::anyNonDockable()
const
364 for (Frame *frame : frames()) {
365 if (frame->anyNonDockable())
371 bool FloatingWindow::hasSingleFrame()
const
373 return m_dropArea->hasSingleFrame();
376 bool FloatingWindow::hasSingleDockWidget()
const
378 const Frame::List frames = this->frames();
379 if (frames.size() != 1)
382 Frame *frame = frames.first();
383 return frame->dockWidgetCount() == 1;
386 Frame *FloatingWindow::singleFrame()
const
388 const Frame::List frames = this->frames();
390 return frames.isEmpty() ? nullptr
394 bool FloatingWindow::beingDeleted()
const
396 if (m_deleteScheduled || m_inDtor)
400 for (Frame *f : frames()) {
401 if (!f->beingDeletedLater())
408 void FloatingWindow::onFrameCountChanged(
int count)
411 scheduleDeleteLater();
413 updateTitleBarVisibility();
415 dropArea()->updateFloatingActions();
419 void FloatingWindow::onVisibleFrameCountChanged(
int count)
421 if (m_disableSetVisible)
424 updateSizeConstraints();
425 setVisible(count > 0);
432 if (isMaximizedOverride())
434 else if (isMinimizedOverride())
440 void FloatingWindow::updateTitleBarVisibility()
442 if (m_updatingTitleBarVisibility)
446 updateTitleAndIcon();
450 for (Frame *frame : frames())
451 frame->updateTitleBarVisibility();
453 if (KDDockWidgets::usesClientTitleBar()) {
454 const auto flags = Config::self().flags();
455 if ((flags & Config::Flag_HideTitleBarWhenTabsVisible) && !(flags & Config::Flag_AlwaysTitleBarWhenFloating)) {
456 if (hasSingleFrame()) {
457 visible = !frames().first()->hasTabsVisible();
461 m_titleBar->updateButtons();
466 m_titleBar->setVisible(visible);
471 auto frames = this->frames();
472 return frames.isEmpty() ?
QStringList() : frames.constFirst()->affinities();
475 void FloatingWindow::updateTitleAndIcon()
479 if (hasSingleFrame()) {
480 const Frame *frame = frames().constFirst();
481 title = frame->title();
482 icon = frame->icon();
484 title = qApp->applicationName();
486 m_titleBar->setTitle(title);
487 m_titleBar->setIcon(icon);
491 setWindowTitle(title);
503 m_dropArea->onCloseEvent(e);
506 bool FloatingWindow::deserialize(
const LayoutSaver::FloatingWindow &fw)
508 if (dropArea()->deserialize(fw.multiSplitterLayout)) {
509 updateTitleBarVisibility();
525 LayoutSaver::FloatingWindow FloatingWindow::serialize()
const
527 LayoutSaver::FloatingWindow fw;
529 fw.geometry = geometry();
530 fw.normalGeometry = normalGeometry();
531 fw.isVisible = isVisible();
532 fw.multiSplitterLayout = dropArea()->serialize();
533 fw.screenIndex = screenNumberForWidget(
this);
534 fw.screenSize = screenSizeForWidget(
this);
535 fw.affinities = affinities();
536 fw.windowState = windowStateOverride();
538 auto mainWindow = qobject_cast<MainWindowBase *>(parentWidget());
539 fw.parentIndex = mainWindow ? DockRegistry::self()->mainwindows().indexOf(mainWindow)
545 QRect FloatingWindow::dragRect()
const
548 if (m_titleBar->isVisible()) {
549 rect = m_titleBar->rect();
551 }
else if (hasSingleFrame()) {
552 rect = frames().constFirst()->dragRect();
554 qWarning() << Q_FUNC_INFO <<
"Expected a title bar";
560 bool FloatingWindow::event(
QEvent *ev)
564 Q_EMIT activatedChanged();
567 return parent()->
event(ev);
569 updateSizeConstraints();
572 return QWidgetAdapter::event(ev);
577 const Frame::List frames = this->frames();
578 return std::all_of(frames.begin(), frames.end(), [option](Frame *frame) {
579 return frame->allDockWidgetsHave(option);
585 const Frame::List frames = this->frames();
586 return std::any_of(frames.begin(), frames.end(), [option](Frame *frame) {
587 return frame->anyDockWidgetsHas(option);
593 const Frame::List frames = this->frames();
594 return std::all_of(frames.begin(), frames.end(), [option](Frame *frame) {
595 return frame->allDockWidgetsHave(option);
601 const Frame::List frames = this->frames();
602 return std::any_of(frames.begin(), frames.end(), [option](Frame *frame) {
603 return frame->anyDockWidgetsHas(option);
610 m_dropArea->addDockWidget(dw, location, relativeTo, option);
613 bool FloatingWindow::isMDI()
const
618 bool FloatingWindow::isWindow()
const
625 return qobject_cast<MainWindowBase *>(parent());
628 QMargins FloatingWindow::contentMargins()
const
630 return { 4, 4, 4, 4 };
633 bool FloatingWindow::isMaximizedOverride()
const
635 return QWidgetAdapter::isMaximized();
638 bool FloatingWindow::isMinimizedOverride()
const
643 void FloatingWindow::showMaximized()
645 QWidgetAdapter::showMaximized();
648 void FloatingWindow::showNormal()
650 QWidgetAdapter::showNormal();
653 void FloatingWindow::showMinimized()
655 QWidgetAdapter::showMinimized();
658 QRect FloatingWindow::normalGeometry()
const
660 return QWidgetAdapter::normalGeometry();
665 return m_lastWindowManagerState;
668 int FloatingWindow::userType()
const
670 if (Frame *f = singleFrame())
671 return f->userType();
675 void FloatingWindow::updateSizeConstraints()
683 setMaximumSize(maxSizeHint());
687 void FloatingWindow::ensureRectIsOnScreen(
QRect &geometry)
689 const auto screens = qApp->screens();
693 int nearestDistSq = std::numeric_limits<int>::max();
694 int nearestIndex = -1;
696 const int screenCount = screens.count();
697 for (
int i = 0; i < screenCount; i++) {
698 auto scrGeom = screens[i]->geometry();
701 scrGeom.moveTopLeft(scrGeom.topLeft() - screens[i]->virtualGeometry().topLeft());
704 if (scrGeom.intersects(geometry))
708 const QPoint dist2D = geometry.
center() - scrGeom.center();
709 const int distSq = (dist2D.
x() * dist2D.
x()) + (dist2D.
y() * dist2D.
y());
710 if (distSq < nearestDistSq) {
711 nearestDistSq = distSq;
717 auto scrGeom = screens[nearestIndex]->geometry();
718 scrGeom.moveTopLeft(scrGeom.topLeft() - screens[nearestIndex]->virtualGeometry().topLeft());
720 if (geometry.
left() < scrGeom.left()) {
722 }
else if (geometry.
left() > scrGeom.right()) {
726 if (geometry.
top() < scrGeom.top()) {
727 geometry.
moveTop(scrGeom.top());
728 }
else if (geometry.
top() > scrGeom.bottom()) {