12#include "FloatingWindow_p.h"
17#include "TitleBar_p.h"
18#include "WindowBeingDragged_p.h"
20#include "WidgetResizeHandler_p.h"
21#include "DockRegistry_p.h"
24#include "DragController_p.h"
25#include "LayoutSaver_p.h"
26#include "DockWidgetBase_p.h"
28#include "multisplitter/Item_p.h"
31#include <QScopedValueRollback>
52 if (requestedFlags & FloatingWindowFlag::UseQtTool) {
57 if (requestedFlags & FloatingWindowFlag::UseQtWindow) {
62 if (FloatingWindow::s_windowFlagsOverride) {
64 return FloatingWindow::s_windowFlagsOverride;
67 if (KDDockWidgets::usesNativeDraggingAndResizing())
78 const FloatingWindowFlags requestedFlags = frame ? frame->requestedFloatingWindowFlags() : FloatingWindowFlag::FromGlobalConfig;
79 if (requestedFlags & FloatingWindowFlag::DontUseParentForFloatingWindows) {
94 return candidateParent;
101 if (windows.
size() == 1) {
102 return windows.
first();
105 const MainWindowBase::List mainWindows = DockRegistry::self()->mainWindowsWithAffinity(affinities);
108 qWarning() << Q_FUNC_INFO <<
"No window with affinity" << affinities <<
"found";
111 return mainWindows.
first();
125 if (!(requestedFlags & FloatingWindowFlag::FromGlobalConfig)) {
127 return requestedFlags;
132 FloatingWindowFlags flags = {};
135 flags |= FloatingWindowFlag::TitleBarHasMinimizeButton;
138 flags |= FloatingWindowFlag::TitleBarHasMaximizeButton;
141 flags |= FloatingWindowFlag::KeepAboveIfNotUtilityWindow;
144 flags |= FloatingWindowFlag::NativeTitleBar;
147 flags |= FloatingWindowFlag::HideTitleBarWhenTabsVisible;
150 flags |= FloatingWindowFlag::AlwaysTitleBarWhenFloating;
153 flags |= FloatingWindowFlag::DontUseParentForFloatingWindows;
156 flags |= FloatingWindowFlag::UseQtWindow;
162 FloatingWindowFlags requestedFlags)
164 , Draggable(this,
KDDockWidgets::usesNativeDraggingAndResizing())
166 , m_dropArea(new DropArea(this))
167 , m_titleBar(
Config::self().frameworkWidgetFactory()->createTitleBar(this))
169 if (!suggestedGeometry.
isNull())
170 setGeometry(suggestedGeometry);
172 if (kddwUsesQtWidgets()) {
176#ifdef KDDOCKWIDGETS_QTWIDGETS
177 m_nchittestFilter =
new NCHITTESTEventFilter(
this);
178 qApp->installNativeEventFilter(m_nchittestFilter);
180 WidgetResizeHandler::setupWindow(windowHandle());
184 DockRegistry::self()->registerFloatingWindow(
this);
186 if (m_flags & FloatingWindowFlag::KeepAboveIfNotUtilityWindow)
189 if (kddwUsesQtWidgets()) {
191 maybeCreateResizeHandler();
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);
207 return FloatingWindowFlag::FromGlobalConfig;
209 return frame->requestedFloatingWindowFlags();
212FloatingWindow::FloatingWindow(Frame *frame,
QRect suggestedGeometry,
MainWindowBase *parent)
217 if (frame->hasNestedMDIDockWidgets()) {
221 if (frame->dockWidgetCount() == 0) {
223 qWarning() << Q_FUNC_INFO <<
"Unexpected empty frame";
228 DropArea *dropAreaMDIWrapper = dwMDIWrapper->d->mdiDropAreaWrapper();
230 if (dropAreaMDIWrapper->hasSingleFrame()) {
231 Frame *innerFrame = dropAreaMDIWrapper->frames().constFirst();
232 if (innerFrame->hasSingleDockWidget()) {
238 dw->d->lastPosition() = dwMDIWrapper->d->lastPosition();
243 dwMDIWrapper->setVisible(
false);
244 if (!DragController::instance()->isIdle()) {
247 connect(DragController::instance(), &DragController::currentStateChanged, dwMDIWrapper, [dwMDIWrapper] {
248 if (DragController::instance()->isIdle())
252 dwMDIWrapper->deleteLater();
263FloatingWindow::~FloatingWindow()
266 disconnect(m_layoutDestroyedConnection);
267 delete m_nchittestFilter;
269 DockRegistry::self()->unregisterFloatingWindow(
this);
272#if defined(Q_OS_WIN) && defined(KDDOCKWIDGETS_QTWIDGETS)
273bool FloatingWindow::nativeEvent(
const QByteArray &eventType,
void *message, Qt5Qt6Compat::qintptr *result)
275 if (m_inDtor || m_deleteScheduled)
278 if (KDDockWidgets::usesAeroSnapWithCustomDecos()) {
280 if (WidgetResizeHandler::handleWindowsNativeEvent(
this, eventType, message, result))
282 }
else if (KDDockWidgets::usesNativeTitleBar()) {
283 auto msg =
static_cast<MSG *
>(message);
284 if (msg->message == WM_SIZING) {
286 Q_EMIT DragController::instance()->dragCanceled();
294void FloatingWindow::maybeCreateResizeHandler()
296 if (!KDDockWidgets::usesNativeDraggingAndResizing()) {
299 const auto filterMode = isEGLFS() ? WidgetResizeHandler::EventFilterMode::Global
300 : WidgetResizeHandler::EventFilterMode::Local;
302 setWidgetResizeHandler(
new WidgetResizeHandler(filterMode,
303 WidgetResizeHandler::WindowMode::TopLevel,
308std::unique_ptr<WindowBeingDragged> FloatingWindow::makeWindow()
310 return std::unique_ptr<WindowBeingDragged>(
new WindowBeingDragged(
this,
this));
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);
327 return m_dropArea->dockWidgets();
330const Frame::List FloatingWindow::frames()
const
332 Q_ASSERT(m_dropArea);
333 return m_dropArea->frames();
336QSize FloatingWindow::maxSizeHint()
const
338 QSize result = Layouting::Item::hardcodedMaximumSize;
345 const Frame::List frames = this->frames();
346 if (frames.size() == 1) {
351 Frame *frame = frames[0];
352 if (frame->dockWidgetCount() == 1) {
353 const QSize waste = (minimumSize() - frame->minSize()).expandedTo(
QSize(0, 0));
354 result = frame->maxSizeHint() + waste;
360 return result.
boundedTo(Layouting::Item::hardcodedMaximumSize);
363void FloatingWindow::setSuggestedGeometry(
QRect suggestedRect, SuggestedGeometryHints hint)
365 const QSize maxSize = maxSizeHint();
366 const bool hasMaxSize = maxSize != Layouting::Item::hardcodedMaximumSize;
373 && (m_flags & FloatingWindowFlag::NativeTitleBar)) {
374 const QMargins margins = contentMargins();
383 ensureRectIsOnScreen(suggestedRect);
385 setGeometry(suggestedRect);
388void FloatingWindow::scheduleDeleteLater()
390 m_deleteScheduled =
true;
391 DockRegistry::self()->unregisterFloatingWindow(
this);
395MultiSplitter *FloatingWindow::multiSplitter()
const
400LayoutWidget *FloatingWindow::layoutWidget()
const
405bool FloatingWindow::isInDragArea(
QPoint globalPoint)
const
411 if (usesAeroSnapWithCustomDecos())
412 return m_lastHitTest == HTCAPTION;
415 return dragRect().contains(globalPoint);
418bool FloatingWindow::anyNonClosable()
const
420 for (Frame *frame : frames()) {
421 if (frame->anyNonClosable())
427bool FloatingWindow::anyNonDockable()
const
429 for (Frame *frame : frames()) {
430 if (frame->anyNonDockable())
436bool FloatingWindow::hasSingleFrame()
const
438 return m_dropArea->hasSingleFrame();
441bool FloatingWindow::hasSingleDockWidget()
const
443 const Frame::List frames = this->frames();
444 if (frames.size() != 1)
447 Frame *frame = frames.first();
448 return frame->dockWidgetCount() == 1;
451Frame *FloatingWindow::singleFrame()
const
453 const Frame::List frames = this->frames();
455 return frames.isEmpty() ? nullptr
459bool FloatingWindow::beingDeleted()
const
461 if (m_deleteScheduled || m_inDtor)
465 for (Frame *f : frames()) {
466 if (!f->beingDeletedLater())
473void FloatingWindow::onFrameCountChanged(
int count)
476 scheduleDeleteLater();
478 updateTitleBarVisibility();
480 dropArea()->updateFloatingActions();
484void FloatingWindow::onVisibleFrameCountChanged(
int count)
486 if (m_disableSetVisible)
489 updateSizeConstraints();
490 setVisible(count > 0);
497 if (isMaximizedOverride())
499 else if (isMinimizedOverride())
505void FloatingWindow::updateTitleBarVisibility()
507 if (m_updatingTitleBarVisibility)
511 updateTitleAndIcon();
515 for (Frame *frame : frames())
516 frame->updateTitleBarVisibility();
518 if (KDDockWidgets::usesClientTitleBar()) {
519 if ((m_flags & FloatingWindowFlag::HideTitleBarWhenTabsVisible) && !(m_flags & FloatingWindowFlag::AlwaysTitleBarWhenFloating)) {
520 if (hasSingleFrame()) {
521 visible = !frames().first()->hasTabsVisible();
525 m_titleBar->updateButtons();
530 m_titleBar->setVisible(visible);
535 auto frames = this->frames();
536 return frames.isEmpty() ?
QStringList() : frames.constFirst()->affinities();
539void FloatingWindow::updateTitleAndIcon()
543 if (hasSingleFrame()) {
544 const Frame *frame = frames().constFirst();
545 title = frame->title();
546 icon = frame->icon();
548 title = qApp->applicationName();
550 m_titleBar->setTitle(title);
551 m_titleBar->setIcon(icon);
555 setWindowTitle(title);
567 m_dropArea->onCloseEvent(e);
570bool FloatingWindow::deserialize(
const LayoutSaver::FloatingWindow &fw)
572 if (dropArea()->deserialize(fw.multiSplitterLayout)) {
573 updateTitleBarVisibility();
589LayoutSaver::FloatingWindow FloatingWindow::serialize()
const
591 LayoutSaver::FloatingWindow fw;
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();
603 auto mainWindow = qobject_cast<MainWindowBase *>(parentWidget());
604 fw.parentIndex = mainWindow ? DockRegistry::self()->mainwindows().indexOf(mainWindow)
610QRect FloatingWindow::dragRect()
const
613 if (m_titleBar->isVisible()) {
614 rect = m_titleBar->rect();
616 }
else if (hasSingleFrame()) {
617 rect = frames().constFirst()->dragRect();
619 qWarning() << Q_FUNC_INFO <<
"Expected a title bar";
625bool FloatingWindow::event(
QEvent *ev)
629 Q_EMIT activatedChanged();
632 return parent()->
event(ev);
634 updateSizeConstraints();
637 return QWidgetAdapter::event(ev);
642 const Frame::List frames = this->frames();
643 return std::all_of(frames.begin(), frames.end(), [option](Frame *frame) {
644 return frame->allDockWidgetsHave(option);
650 const Frame::List frames = this->frames();
651 return std::any_of(frames.begin(), frames.end(), [option](Frame *frame) {
652 return frame->anyDockWidgetsHas(option);
658 const Frame::List frames = this->frames();
659 return std::all_of(frames.begin(), frames.end(), [option](Frame *frame) {
660 return frame->allDockWidgetsHave(option);
666 const Frame::List frames = this->frames();
667 return std::any_of(frames.begin(), frames.end(), [option](Frame *frame) {
668 return frame->anyDockWidgetsHas(option);
675 m_dropArea->addDockWidget(dw, location, relativeTo, option);
678bool FloatingWindow::isMDI()
const
683bool FloatingWindow::isWindow()
const
690 return qobject_cast<MainWindowBase *>(parent());
693QMargins FloatingWindow::contentMargins()
const
695 return { 4, 4, 4, 4 };
698bool FloatingWindow::isMaximizedOverride()
const
700 return QWidgetAdapter::isMaximized();
703bool FloatingWindow::isMinimizedOverride()
const
705 return QWidgetAdapter::isMinimized();
708void FloatingWindow::showMaximized()
710 QWidgetAdapter::showMaximized();
713void FloatingWindow::showNormal()
715 QWidgetAdapter::showNormal();
718void FloatingWindow::showMinimized()
720 QWidgetAdapter::showMinimized();
723QRect FloatingWindow::normalGeometry()
const
725 return QWidgetAdapter::normalGeometry();
730 return m_lastWindowManagerState;
733int FloatingWindow::userType()
const
735 if (Frame *f = singleFrame())
736 return f->userType();
740void FloatingWindow::updateSizeConstraints()
748 setMaximumSize(maxSizeHint());
752void FloatingWindow::ensureRectIsOnScreen(
QRect &geometry)
754 const auto screens = qApp->screens();
758 int nearestDistSq = std::numeric_limits<int>::max();
759 int nearestIndex = -1;
761 const int screenCount = screens.count();
762 for (
int i = 0; i < screenCount; i++) {
763 const QRect scrGeom = screens[i]->geometry();
771 const int distSq = (dist2D.
x() * dist2D.
x()) + (dist2D.
y() * dist2D.
y());
772 if (distSq < nearestDistSq) {
773 nearestDistSq = distSq;
779 auto scrGeom = screens[nearestIndex]->geometry();
780 scrGeom.
moveTopLeft(scrGeom.
topLeft() - screens[nearestIndex]->virtualGeometry().topLeft());
782 if (geometry.
left() < scrGeom.
left()) {
784 }
else if (geometry.
left() > scrGeom.
right()) {
788 if (geometry.
top() < scrGeom.
top()) {
790 }
else if (geometry.
top() > scrGeom.
bottom()) {
795bool FloatingWindow::supportsMinimizeButton()
const
797 return m_flags & FloatingWindowFlag::TitleBarHasMinimizeButton;
800bool FloatingWindow::supportsMaximizeButton()
const
802 return m_flags & FloatingWindowFlag::TitleBarHasMaximizeButton;
805bool FloatingWindow::isUtilityWindow()
const
807 const bool dontUse = (m_flags & FloatingWindowFlag::DontUseParentForFloatingWindows) && (m_flags & FloatingWindowFlag::UseQtWindow);
811FloatingWindowFlags FloatingWindow::floatingWindowFlags()
const
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)
The MainWindow base-class that's shared between QtWidgets and QtQuick stack.
The MainWindow base-class. MainWindow and MainWindowBase are only split in two so we can share some c...
bool spontaneous() const const
QEvent::Type type() const const
virtual bool event(QEvent *event) override
void destroyed(QObject *obj)
QPoint center() const const
bool intersects(const QRect &rectangle) const const
bool isNull() const const
void moveCenter(const QPoint &position)
void moveTopLeft(const QPoint &position)
void setHeight(int height)
void setSize(const QSize &size)
QPoint topLeft() const const
QSize boundedTo(const QSize &otherSize) const const
bool isEmpty() const const