18#include <QApplication>
24#pragma comment(lib, "User32.lib")
33static unsigned int s_zOrderCounterFloating = 0;
35class DockFloatingContainerPrivate
40 DockFloatingContainerPrivate();
42 void titleMouseReleaseEvent();
43 void updateDropOverlays(
const QPoint &globalPos);
45 static bool testConfigFlag(DockManager::ConfigFlag flag);
50 void setWindowTitle(
const QString &
text);
51 void reflectCurrentWidget(DockWidget *currentWidget);
53 static QString floatingContainersTitle();
55 void handleEscapeKey();
57 QPointer<DockWindow> m_window;
58 DockContainer *m_dockContainer;
59 DockContainer *m_dropContainer =
nullptr;
60 DockPanel *m_singlePanel =
nullptr;
61 QPoint m_dragStartMousePosition;
63 QPoint m_dragStartPos;
64 bool m_hiding =
false;
65 bool m_autoHideChildren =
true;
66 unsigned int m_zOrderIndex = ++s_zOrderCounterFloating;
68 QWidget *m_mouseEventHandler =
nullptr;
70 bool m_isResizing =
false;
71 bool m_mousePressed =
false;
75DockFloatingContainerPrivate::DockFloatingContainerPrivate()
79void DockFloatingContainerPrivate::titleMouseReleaseEvent()
81 Q_Q(DockFloatingContainer);
85 if (!m_dropContainer) {
91 DockOverlay *overlay = m_window->containerOverlay();
92 if (!overlay->dropOverlayRect().isValid()) {
93 overlay = m_window->panelOverlay();
97 if (!internal::isSideBarArea(overlay->dropAreaUnderCursor())) {
99 QRect rect = overlay->dropOverlayRect();
100 int frameWidth = (q->frameSize().width() - q->rect().width()) / 2;
101 int titleBarHeight = q->frameSize().height() - q->rect().height() - frameWidth;
102 if (rect.isValid()) {
103 QPoint topLeft = overlay->mapToGlobal(rect.topLeft());
104 topLeft.ry() += titleBarHeight;
105 q->setGeometry(QRect(topLeft, QSize(rect.width(), rect.height() - titleBarHeight)));
106 QApplication::processEvents();
109 m_dropContainer->dropFloatingWidget(q, QCursor::pos());
112 m_window->containerOverlay()->hideOverlay();
113 m_window->panelOverlay()->hideOverlay();
116void DockFloatingContainerPrivate::updateDropOverlays(
const QPoint &globalPos)
118 Q_Q(DockFloatingContainer);
119 if (!q->isVisible() || !m_window) {
125 if (qApp->activeModalWidget()) {
129 auto containers = m_window->dockContainers();
130 DockContainer *topContainer =
nullptr;
131 for (
const auto c : containers) {
132 if (!c->isVisible()) {
135 if (m_dockContainer == c) {
138 QPoint mappedPos = c->mapFromGlobal(globalPos);
139 if (c->rect().contains(mappedPos)) {
140 if (!topContainer || c->isInFrontOf(topContainer)) {
145 m_dropContainer = topContainer;
147 auto containerOverlay = m_window->containerOverlay();
148 auto panelOverlay = m_window->panelOverlay();
150 containerOverlay->hideOverlay();
151 panelOverlay->hideOverlay();
155 int visiblePanels = topContainer->visibleDockPanelCount();
157 auto panel = topContainer->dockPanelAt(globalPos);
161 if (visiblePanels == 1 && panel) {
165 if (m_dockContainer->features().testFlag(DockWidget::DockWidgetPinnable)) {
168 containerOverlay->setAllowedAreas(allowdContainerAreas);
172 if (panel && panel->isVisible() && visiblePanels > 0) {
173 panelOverlay->enableDropPreview(
true);
174 panelOverlay->setAllowedAreas((visiblePanels == 1) ?
Qx::NoDockWidgetArea : panel->allowedAreas());
182 panelOverlay->enableDropPreview(
false);
183 containerOverlay->enableDropPreview(
true);
188 panelOverlay->hideOverlay();
192bool DockFloatingContainerPrivate::testConfigFlag(DockManager::ConfigFlag flag)
194 return DockManager::testConfigFlag(flag);
199 return stateId == m_draggingState;
204 Q_Q(DockFloatingContainer);
205 if (m_draggingState == stateId) {
208 m_draggingState = stateId;
210 qApp->postEvent(q,
new QEvent((QEvent::Type)internal::FloatingWidgetDragStartEvent));
214void DockFloatingContainerPrivate::setWindowTitle(
const QString &
text)
216 Q_Q(DockFloatingContainer);
219 m_titleBar->setTitle(
text);
222 q->setWindowTitle(
text);
229void DockFloatingContainerPrivate::reflectCurrentWidget(DockWidget *currentWidget)
231 Q_Q(DockFloatingContainer);
233 if (testConfigFlag(DockManager::FloatingContainerHasWidgetTitle)) {
234 setWindowTitle(currentWidget->windowTitle());
236 setWindowTitle(floatingContainersTitle());
240 QIcon CurrentWidgetIcon = currentWidget->icon();
241 if (testConfigFlag(DockManager::FloatingContainerHasWidgetIcon) && !CurrentWidgetIcon.isNull()) {
242 q->setWindowIcon(currentWidget->icon());
244 q->setWindowIcon(QApplication::windowIcon());
255QString DockFloatingContainerPrivate::floatingContainersTitle()
257 return DockManager::floatingContainersTitle();
263void DockFloatingContainerPrivate::handleEscapeKey()
266 m_window->containerOverlay()->hideOverlay();
267 m_window->panelOverlay()->hideOverlay();
282 QDockWidget::setWidget(d->m_dockContainer);
283 QDockWidget::setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable |
284 QDockWidget::DockWidgetFloatable);
286 bool native_window =
true;
289 auto env = qgetenv(
"QXDOCK_UseNativeTitle").toUpper();
291 native_window =
true;
292 }
else if (env ==
"0") {
293 native_window =
false;
295 native_window =
true;
297 native_window =
false;
301 QString window_manager = internal::windowManager().toUpper().split(
" ")[0];
302 native_window = window_manager !=
"KWIN";
308 QString XdgSessionType = qgetenv(
"XDG_SESSION_TYPE").toLower();
309 if (
"wayland" == XdgSessionType) {
310 native_window =
false;
315 setTitleBarWidget(
new QWidget());
316 setWindowFlags(Qt::Window | Qt::WindowMaximizeButtonHint | Qt::CustomizeWindowHint | Qt::WindowCloseButtonHint);
319 setTitleBarWidget(d->m_titleBar);
320 setWindowFlags(Qt::Window | Qt::WindowMinMaxButtonsHint | Qt::FramelessWindowHint);
321 d->m_titleBar->enableCloseButton(
isClosable());
322 connect(d->m_titleBar, SIGNAL(closeRequested()), SLOT(close()));
324 &DockFloatingContainer::onMaximizeRequest);
327 setWindowFlags(Qt::Window | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint);
328 QBoxLayout *l =
new QBoxLayout(QBoxLayout::TopToBottom);
329 l->setContentsMargins(0, 0, 0, 0);
332 l->addWidget(d->m_dockContainer);
335 window->registerFloatingWidget(
this);
343 d->m_dockContainer->addDockPanel(panel);
345 if (topLevelWidget) {
346 topLevelWidget->emitTopLevelChanged(
true);
348 d->m_window->notifyDockAreaRelocation(panel);
357 if (topLevelWidget) {
358 topLevelWidget->emitTopLevelChanged(
true);
361 d->m_window->notifyDockAreaRelocation(widget);
368 d->m_window->removeFloatingWidget(
this);
386 return d->m_dockContainer;
397 return d->m_dockContainer->topLevelDockWidget();
408 return d->m_dockContainer->dockWidgets();
420 return d->m_dockContainer->hasTopLevelDockWidget();
429 d->m_autoHideChildren =
false;
435 d->m_dockContainer->removeAllDockPanels();
440 d->m_window->removeFloatingWidget(
this);
450void DockFloatingContainer::onMaximizeRequest()
452 if (windowState() == Qt::WindowMaximized) {
465void DockFloatingContainer::showNormal(
bool fixGeometry)
467 Q_D(DockFloatingContainer);
468 if ((windowState() & Qt::WindowMaximized) != 0 || (windowState() & Qt::WindowFullScreen) != 0) {
469 QRect oldNormal = normalGeometry();
472 setGeometry(oldNormal);
476 d->m_titleBar->setMaximizedIcon(
false);
480void DockFloatingContainer::showMaximized()
482 Q_D(DockFloatingContainer);
483 Super::showMaximized();
485 d->m_titleBar->setMaximizedIcon(
true);
489bool DockFloatingContainer::isMaximized()
const
491 return windowState() == Qt::WindowMaximized;
494void DockFloatingContainer::show()
497 internal::xcb_add_prop(
true, winId(),
"_NET_WM_STATE",
"_NET_WM_STATE_SKIP_TASKBAR");
498 internal::xcb_add_prop(
true, winId(),
"_NET_WM_STATE",
"_NET_WM_STATE_SKIP_PAGER");
506bool DockFloatingContainer::hasNativeTitleBar()
508 Q_D(DockFloatingContainer);
509 return d->m_titleBar ==
nullptr;
517 auto topLevelPanel = d->m_dockContainer->topLevelDockPanel();
519 d->m_singlePanel = topLevelPanel;
520 DockWidget *currentWidget = d->m_singlePanel->currentDockWidget();
521 d->reflectCurrentWidget(currentWidget);
524 if (d->m_singlePanel) {
526 d->m_singlePanel =
nullptr;
528 d->setWindowTitle(d->floatingContainersTitle());
529 setWindowIcon(QApplication::windowIcon());
537 DockWidget *currentWidget = d->m_singlePanel->currentDockWidget();
538 d->reflectCurrentWidget(currentWidget);
546 if (!isMaximized()) {
548 d->m_dragStartMousePosition = dragStartMousePos;
550 d->setState(dragState);
552 d->m_mouseEventHandler = mouseEventHandler;
553 if (d->m_mouseEventHandler) {
554 d->m_mouseEventHandler->grabMouse();
558 if (!isMaximized()) {
563 Q_UNUSED(mouseEventHandler)
565 d->m_dragStartMousePosition = dragStartMousePos;
566 d->setState(dragState);
578 if (d->m_mouseEventHandler) {
579 d->m_mouseEventHandler->releaseMouse();
580 d->m_mouseEventHandler =
nullptr;
583 d->titleMouseReleaseEvent();
589 int borderSize = (frameSize().width() - size().width()) / 2;
590 const QPoint moveToPos = QCursor::pos() - d->m_dragStartMousePosition - QPoint(borderSize, 0);
593 switch (d->m_draggingState) {
596 d->updateDropOverlays(QCursor::pos());
600 d->updateDropOverlays(QCursor::pos());
621 std::vector<QPointer<DockPanel>> panels;
625 for (
auto p : panels) {
629 std::vector<QPointer<QWidget>> deleteWidgets;
630 for (
auto w : p->dockWidgets()) {
631 deleteWidgets.push_back(w);
633 for (
auto w : deleteWidgets) {
647 auto topLevelPanel = d->m_dockContainer->topLevelDockPanel();
649 DockWidget *currentWidget = topLevelPanel->currentDockWidget();
651 d->reflectCurrentWidget(currentWidget);
654 d->setWindowTitle(d->floatingContainersTitle());
655 setWindowIcon(QApplication::windowIcon());
662 if (!d->m_dockContainer->restoreState(stream, testing)) {
668 d->m_titleBar->setMaximizedIcon(windowState() == Qt::WindowMaximized);
683 bool hasOpenDockWidgets =
false;
684 for (
auto w : d->m_dockContainer->openedDockWidgets()) {
687 bool closed = w->closeDockWidgetInternal();
689 hasOpenDockWidgets =
true;
692 w->toggleView(
false);
696 if (hasOpenDockWidgets) {
714 Super::hideEvent(event);
716 if (event->spontaneous()) {
720 if (d->m_window->isRestoringState()) {
723 if (d->m_autoHideChildren) {
725 for (
auto panel : d->m_dockContainer->openedDockPanels()) {
726 for (
auto w : panel->openedDockWidgets()) {
727 w->toggleView(
false);
736 Super::showEvent(event);
739 this->
window()->activateWindow();
746 Super::changeEvent(event);
748 switch (event->type()) {
749 case QEvent::ActivationChange:
750 if (isActiveWindow()) {
751 d->m_zOrderIndex = ++s_zOrderCounterFloating;
754 d->titleMouseReleaseEvent();
761 case QEvent::WindowStateChange:
768 if (d->m_window->isLeavingMinimizedState()) {
769 QWindowStateChangeEvent *ev =
static_cast<QWindowStateChangeEvent *
>(event);
770 if (ev->oldState().testFlag(Qt::WindowMaximized)) {
771 this->showMaximized();
783bool DockFloatingContainer::event(QEvent *e)
786 switch (d->m_draggingState) {
795#if (QT_VERSION >= QT_VERSION_CHECK(5, 12, 2))
797 QEvent::NonClientAreaMouseButtonPress )
799 if (e->type() == QEvent::NonClientAreaMouseButtonPress &&
800 QGuiApplication::mouseButtons().testFlag(Qt::LeftButton))
803 QX_DOCK_PRINT(
"FloatingWidget::event Event::NonClientAreaMouseButtonPress" << e->type());
804 d->m_dragStartPos = pos();
811 case QEvent::NonClientAreaMouseButtonDblClick:
812 QX_DOCK_PRINT(
"FloatingWidget::event QEvent::NonClientAreaMouseButtonDblClick");
825 if (!isMaximized()) {
836 if (e->type() == QEvent::NonClientAreaMouseButtonRelease) {
837 QX_DOCK_PRINT(
"FloatingWidget::event QEvent::NonClientAreaMouseButtonRelease");
838 d->titleMouseReleaseEvent();
846#if (QXDOCK_DEBUG_LEVEL > 0)
847 qDebug() << QTime::currentTime() <<
"DockFloatingContainer::event " << e->type();
849 return QWidget::event(e);
853void DockFloatingContainer::moveEvent(QMoveEvent *e)
855 Q_D(DockFloatingContainer);
856 QWidget::moveEvent(e);
857 switch (d->m_draggingState) {
860 d->updateDropOverlays(QCursor::pos());
864 d->updateDropOverlays(QCursor::pos());
878void DockFloatingContainer::moveEvent(QMoveEvent *e)
880 Q_D(DockFloatingContainer);
882 if (!d->m_isResizing && e->spontaneous() && d->m_mousePressed) {
884 d->updateDropOverlays(QCursor::pos());
886 d->m_isResizing =
false;
889void DockFloatingContainer::resizeEvent(QResizeEvent *e)
891 Q_D(DockFloatingContainer);
892 d->m_isResizing =
true;
893 Super::resizeEvent(e);
896bool DockFloatingContainer::event(QEvent *e)
898 Q_D(DockFloatingContainer);
899 bool result = Super::event(e);
901 case QEvent::WindowActivate:
902 d->m_mousePressed =
false;
904 case QEvent::WindowDeactivate:
905 d->m_mousePressed =
true;
916#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
917bool DockFloatingContainer::nativeEvent(
const QByteArray &eventType,
void *message,
long *result)
919bool DockFloatingContainer::nativeEvent(
const QByteArray &eventType,
void *message, qintptr *result)
922 Super::nativeEvent(eventType, message, result);
924 Q_D(DockFloatingContainer);
925 MSG *msg =
static_cast<MSG *
>(message);
926 switch (msg->message) {
929 d->updateDropOverlays(QCursor::pos());
933 case WM_NCLBUTTONDOWN:
935 d->m_dragStartPos = pos();
940 case WM_NCLBUTTONDBLCLK:
944 case WM_ENTERSIZEMOVE:
947 d->updateDropOverlays(QCursor::pos());
951 case WM_EXITSIZEMOVE:
953 if (GetAsyncKeyState(VK_ESCAPE) & 0x8000) {
954 d->handleEscapeKey();
956 d->titleMouseReleaseEvent();
Container that manages a number of dock panels with single dock widgets or tabyfied dock widgets in e...
int dockPanelCount() const
DockPanel * dockPanel(int index) const
This implements a floating widget that is a dock container that accepts docking of dock widgets like ...
DockContainer * dockContainer() const
virtual void showEvent(QShowEvent *event) override
QList< DockWidget * > dockWidgets() const
This function returns a list of all dock widget in this floating widget.
virtual void startFloating(const QPoint &dragStartMousePos, const QSize &size, Qx::DockDragState dragState, QWidget *mouseEventHandler) override
Starts floating.
virtual void changeEvent(QEvent *event) override
void onDockAreasAddedOrRemoved()
bool isClosable() const
This function returns true, if it can be closed.
void finishDropOperation()
virtual ~DockFloatingContainer()
virtual void closeEvent(QCloseEvent *event) override
bool hasTopLevelDockWidget() const
This function returns true, if this floating widget has only one single visible dock widget in a sing...
DockFloatingContainer(DockWindow *window)
void deleteContent()
This function deletes all dock widgets in it.
friend class DockFloatingTitleBar
void moveFloating() override
Moves the widget to a new position relative to the position given when startFloating() was called.
void onDockAreaCurrentChanged(int index)
virtual void hideEvent(QHideEvent *event) override
DockWidget * topLevelDockWidget() const
This function returns the first dock widget in the first dock panel.
bool restoreState(DockStateReader &stream, bool testing)
virtual void finishDragging() override
Tells the widget that to finish dragging if the mouse is released.
@ FloatingContainerForceNativeTitleBar
@ FloatingContainerForceQWidgetTitleBar
static bool testConfigFlag(ConfigFlag flag)
DockPanel manages multiple instances of DockWidgets.
Extends QXmlStreamReader with file version information
The central dock window that maintains the complete docking system.
#define DockFloatingContainerBase
Copyleft (C) 2024 maminjie canpool@163.com SPDX-License-Identifier: MulanPSL-2.0
#define QXDOCK_SET_BGCOLOR(w)
#define QX_DOCK_PRINT(s)
Copyleft (C) 2024 maminjie canpool@163.com SPDX-License-Identifier: MulanPSL-2.0
@ DockDraggingFloatingWidget
@ DockDraggingMousePressed
#define QX_INIT_PRIVATE(Class)
#define QX_DECLARE_PUBLIC(Class)
#define QX_FINI_PRIVATE()
#define QX_DOCK_END_NAMESPACE
#define QX_DOCK_BEGIN_NAMESPACE