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"
26 #include <QCloseEvent>
27 #include <QScopedValueRollback>
32 #if defined(Q_CC_MSVC)
48 if (FloatingWindow::s_windowFlagsOverride) {
50 return FloatingWindow::s_windowFlagsOverride;
53 if (KDDockWidgets::usesNativeDraggingAndResizing())
74 return candidateParent;
81 if (windows.
size() == 1) {
82 return windows.
first();
85 const MainWindowBase::List mainWindows = DockRegistry::self()->mainWindowsWithAffinity(affinities);
88 qWarning() << Q_FUNC_INFO <<
"No window with affinity" << affinities <<
"found";
91 return mainWindows.
first();
106 , m_dropArea(new DropArea(this))
107 , m_titleBar(
Config::self().frameworkWidgetFactory()->createTitleBar(this))
109 if (!suggestedGeometry.
isNull())
110 setGeometry(suggestedGeometry);
112 if (kddwUsesQtWidgets()) {
116 #ifdef KDDOCKWIDGETS_QTWIDGETS
117 m_nchittestFilter =
new NCHITTESTEventFilter(
this);
118 qApp->installNativeEventFilter(m_nchittestFilter);
120 WidgetResizeHandler::setupWindow(windowHandle());
124 DockRegistry::self()->registerFloatingWindow(
this);
126 if (Config::self().flags() & Config::Flag_KeepAboveIfNotUtilityWindow)
129 if (kddwUsesQtWidgets()) {
131 maybeCreateResizeHandler();
134 updateTitleBarVisibility();
135 connect(m_dropArea, &LayoutWidget::visibleWidgetCountChanged,
this,
136 &FloatingWindow::onFrameCountChanged);
137 connect(m_dropArea, &LayoutWidget::visibleWidgetCountChanged,
this,
138 &FloatingWindow::numFramesChanged);
139 connect(m_dropArea, &LayoutWidget::visibleWidgetCountChanged,
this,
140 &FloatingWindow::onVisibleFrameCountChanged);
141 m_layoutDestroyedConnection = connect(m_dropArea, &
QObject::destroyed,
this, &FloatingWindow::scheduleDeleteLater);
144 FloatingWindow::FloatingWindow(Frame *frame,
QRect suggestedGeometry,
MainWindowBase *parent)
147 m_disableSetVisible =
true;
152 m_disableSetVisible =
false;
155 FloatingWindow::~FloatingWindow()
158 disconnect(m_layoutDestroyedConnection);
159 delete m_nchittestFilter;
161 DockRegistry::self()->unregisterFloatingWindow(
this);
164 #if defined(Q_OS_WIN) && defined(KDDOCKWIDGETS_QTWIDGETS)
165 bool FloatingWindow::nativeEvent(
const QByteArray &eventType,
void *message, Qt5Qt6Compat::qintptr *result)
167 if (m_inDtor || m_deleteScheduled)
170 if (KDDockWidgets::usesAeroSnapWithCustomDecos()) {
172 if (WidgetResizeHandler::handleWindowsNativeEvent(
this, eventType, message, result))
174 }
else if (KDDockWidgets::usesNativeTitleBar()) {
175 auto msg =
static_cast<MSG *
>(message);
176 if (msg->message == WM_SIZING) {
178 Q_EMIT DragController::instance()->dragCanceled();
186 void FloatingWindow::maybeCreateResizeHandler()
188 if (!KDDockWidgets::usesNativeDraggingAndResizing()) {
190 setWidgetResizeHandler(
new WidgetResizeHandler(
true,
this));
194 std::unique_ptr<WindowBeingDragged> FloatingWindow::makeWindow()
196 return std::unique_ptr<WindowBeingDragged>(
new WindowBeingDragged(
this,
this));
201 const Frame::List frames = this->frames();
202 if (frames.size() == 1) {
203 Frame *frame = frames.first();
204 if (frame->hasSingleDockWidget())
205 return frame->dockWidgetAt(0);
213 return m_dropArea->dockWidgets();
216 const Frame::List FloatingWindow::frames()
const
218 Q_ASSERT(m_dropArea);
219 return m_dropArea->frames();
222 QSize FloatingWindow::maxSizeHint()
const
224 QSize result = Layouting::Item::hardcodedMaximumSize;
231 const Frame::List frames = this->frames();
232 if (frames.size() == 1) {
237 Frame *frame = frames[0];
238 if (frame->dockWidgetCount() == 1) {
239 const QSize waste = (minimumSize() - frame->minSize()).expandedTo(
QSize(0, 0));
240 result = frame->maxSizeHint() + waste;
246 return result.
boundedTo(Layouting::Item::hardcodedMaximumSize);
249 void FloatingWindow::setSuggestedGeometry(
QRect suggestedRect, SuggestedGeometryHints hint)
251 const QSize maxSize = maxSizeHint();
252 const bool hasMaxSize = maxSize != Layouting::Item::hardcodedMaximumSize;
259 && (Config::self().flags() & Config::Flag_NativeTitleBar)) {
260 const QMargins margins = contentMargins();
269 ensureRectIsOnScreen(suggestedRect);
271 setGeometry(suggestedRect);
274 void FloatingWindow::scheduleDeleteLater()
276 m_deleteScheduled =
true;
277 DockRegistry::self()->unregisterFloatingWindow(
this);
281 MultiSplitter *FloatingWindow::multiSplitter()
const
286 LayoutWidget *FloatingWindow::layoutWidget()
const
291 bool FloatingWindow::isInDragArea(
QPoint globalPoint)
const
297 if (usesAeroSnapWithCustomDecos())
298 return m_lastHitTest == HTCAPTION;
301 return dragRect().contains(globalPoint);
304 bool FloatingWindow::anyNonClosable()
const
306 for (Frame *frame : frames()) {
307 if (frame->anyNonClosable())
313 bool FloatingWindow::anyNonDockable()
const
315 for (Frame *frame : frames()) {
316 if (frame->anyNonDockable())
322 bool FloatingWindow::hasSingleFrame()
const
324 return m_dropArea->visibleCount() == 1;
327 bool FloatingWindow::hasSingleDockWidget()
const
329 const Frame::List frames = this->frames();
330 if (frames.size() != 1)
333 Frame *frame = frames.first();
334 return frame->dockWidgetCount() == 1;
337 Frame *FloatingWindow::singleFrame()
const
339 const Frame::List frames = this->frames();
341 return frames.isEmpty() ? nullptr
345 bool FloatingWindow::beingDeleted()
const
347 if (m_deleteScheduled || m_inDtor)
351 for (Frame *f : frames()) {
352 if (!f->beingDeletedLater())
359 void FloatingWindow::onFrameCountChanged(
int count)
362 scheduleDeleteLater();
364 updateTitleBarVisibility();
366 dropArea()->updateFloatingActions();
370 void FloatingWindow::onVisibleFrameCountChanged(
int count)
372 if (m_disableSetVisible)
375 updateSizeConstraints();
376 setVisible(count > 0);
383 if (isMaximizedOverride())
385 else if (isMinimizedOverride())
391 void FloatingWindow::updateTitleBarVisibility()
393 if (m_updatingTitleBarVisibility)
397 updateTitleAndIcon();
401 for (Frame *frame : frames())
402 frame->updateTitleBarVisibility();
404 if (KDDockWidgets::usesClientTitleBar()) {
405 const auto flags = Config::self().flags();
406 if ((flags & Config::Flag_HideTitleBarWhenTabsVisible) && !(flags & Config::Flag_AlwaysTitleBarWhenFloating)) {
407 if (hasSingleFrame()) {
408 visible = !frames().first()->hasTabsVisible();
412 m_titleBar->updateButtons();
417 m_titleBar->setVisible(visible);
422 auto frames = this->frames();
423 return frames.isEmpty() ?
QStringList() : frames.constFirst()->affinities();
426 void FloatingWindow::updateTitleAndIcon()
430 if (hasSingleFrame()) {
431 const Frame *frame = frames().constFirst();
432 title = frame->title();
433 icon = frame->icon();
435 title = qApp->applicationName();
437 m_titleBar->setTitle(title);
438 m_titleBar->setIcon(icon);
442 setWindowTitle(title);
456 const Frame::List frames = this->frames();
457 for (Frame *frame : frames) {
458 qApp->sendEvent(frame, e);
464 bool FloatingWindow::deserialize(
const LayoutSaver::FloatingWindow &fw)
466 if (dropArea()->deserialize(fw.multiSplitterLayout)) {
467 updateTitleBarVisibility();
469 if (fw.normalGeometry.isValid() && !isNormalWindowState(fw.windowState)) {
471 setNormalGeometry(fw.normalGeometry);
489 LayoutSaver::FloatingWindow FloatingWindow::serialize()
const
491 LayoutSaver::FloatingWindow fw;
493 fw.geometry = geometry();
494 fw.normalGeometry = normalGeometry();
495 fw.isVisible = isVisible();
496 fw.multiSplitterLayout = dropArea()->serialize();
497 fw.screenIndex = screenNumberForWidget(
this);
498 fw.screenSize = screenSizeForWidget(
this);
499 fw.affinities = affinities();
500 fw.windowState = windowStateOverride();
502 auto mainWindow = qobject_cast<MainWindowBase *>(parentWidget());
503 fw.parentIndex = mainWindow ? DockRegistry::self()->mainwindows().indexOf(mainWindow)
509 QRect FloatingWindow::dragRect()
const
512 if (m_titleBar->isVisible()) {
513 rect = m_titleBar->rect();
515 }
else if (hasSingleFrame()) {
516 rect = frames().constFirst()->dragRect();
518 qWarning() << Q_FUNC_INFO <<
"Expected a title bar";
524 bool FloatingWindow::event(
QEvent *ev)
528 Q_EMIT activatedChanged();
531 return parent()->
event(ev);
533 updateSizeConstraints();
536 return QWidgetAdapter::event(ev);
541 const Frame::List frames = this->frames();
542 return std::all_of(frames.begin(), frames.end(), [option](Frame *frame) {
543 return frame->allDockWidgetsHave(option);
549 const Frame::List frames = this->frames();
550 return std::any_of(frames.begin(), frames.end(), [option](Frame *frame) {
551 return frame->anyDockWidgetsHas(option);
557 const Frame::List frames = this->frames();
558 return std::all_of(frames.begin(), frames.end(), [option](Frame *frame) {
559 return frame->allDockWidgetsHave(option);
565 const Frame::List frames = this->frames();
566 return std::any_of(frames.begin(), frames.end(), [option](Frame *frame) {
567 return frame->anyDockWidgetsHas(option);
574 m_dropArea->addDockWidget(dw, location, relativeTo, option);
577 bool FloatingWindow::isMDI()
const
582 bool FloatingWindow::isWindow()
const
589 return qobject_cast<MainWindowBase *>(parent());
592 QMargins FloatingWindow::contentMargins()
const
594 return { 4, 4, 4, 4 };
597 bool FloatingWindow::isMaximizedOverride()
const
599 return QWidgetAdapter::isMaximized();
602 bool FloatingWindow::isMinimizedOverride()
const
607 void FloatingWindow::showMaximized()
609 QWidgetAdapter::showMaximized();
612 void FloatingWindow::showNormal()
614 QWidgetAdapter::showNormal();
617 void FloatingWindow::showMinimized()
619 QWidgetAdapter::showMinimized();
622 QRect FloatingWindow::normalGeometry()
const
624 return QWidgetAdapter::normalGeometry();
627 int FloatingWindow::userType()
const
629 if (Frame *f = singleFrame())
630 return f->userType();
634 void FloatingWindow::updateSizeConstraints()
642 setMaximumSize(maxSizeHint());
646 void FloatingWindow::ensureRectIsOnScreen(
QRect &geometry)
648 const auto screens = qApp->screens();
652 int nearestDistSq = std::numeric_limits<int>::max();
653 int nearestIndex = -1;
655 const int screenCount = screens.count();
656 for (
int i = 0; i < screenCount; i++) {
657 auto scrGeom = screens[i]->geometry();
660 scrGeom.moveTopLeft(scrGeom.topLeft() - screens[i]->virtualGeometry().topLeft());
663 if (scrGeom.intersects(geometry))
667 const QPoint dist2D = geometry.
center() - scrGeom.center();
668 const int distSq = (dist2D.
x() * dist2D.
x()) + (dist2D.
y() * dist2D.
y());
669 if (distSq < nearestDistSq) {
670 nearestDistSq = distSq;
676 auto scrGeom = screens[nearestIndex]->geometry();
677 scrGeom.moveTopLeft(scrGeom.topLeft() - screens[nearestIndex]->virtualGeometry().topLeft());
679 if (geometry.
left() < scrGeom.left()) {
681 }
else if (geometry.
left() > scrGeom.right()) {
685 if (geometry.
top() < scrGeom.top()) {
686 geometry.
moveTop(scrGeom.top());
687 }
else if (geometry.
top() > scrGeom.bottom()) {