KDDockWidgets API Documentation 2.1
Loading...
Searching...
No Matches
core/DockWidget.cpp
Go to the documentation of this file.
1/*
2 This file is part of KDDockWidgets.
3
4 SPDX-FileCopyrightText: 2019 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 "DockWidget.h"
13#include "DockWidget_p.h"
14#include "DockRegistry.h"
15#include "core/LayoutSaver_p.h"
16#include "core/Logging_p.h"
17#include "core/MDILayout.h"
18#include "core/TitleBar.h"
19#include "core/Group.h"
20#include "core/Stack.h"
21#include "core/FloatingWindow.h"
22#include "core/SideBar.h"
23#include "core/DropArea.h"
24#include "core/TabBar.h"
25#include "core/TabBar_p.h"
26#include "core/MainWindow.h"
27#include "core/Utils_p.h"
28#include "core/WindowBeingDragged_p.h"
29#include "core/Position_p.h"
30#include "core/Platform_p.h"
31#include "core/Action_p.h"
32#include "core/DelayedCall_p.h"
33#include "core/DragController_p.h"
34#include "Platform.h"
35#include "core/layouting/Item_p.h"
36
37#include "Config.h"
38#include "core/ViewFactory.h"
39#include "core/ScopedValueRollback_p.h"
40
41#ifdef KDDW_FRONTEND_QT
42#include <QTimer>
43#endif
44
52using namespace KDDockWidgets;
53using namespace KDDockWidgets::Core;
54
55DockWidget::DockWidget(View *view, const QString &name, DockWidgetOptions options,
56 LayoutSaverOptions layoutSaverOptions)
58 , d(new Private(name, options, layoutSaverOptions, this))
59{
61
62 if (name.isEmpty())
63 KDDW_ERROR("Name can't be null");
64
65 d->m_windowActivatedConnection = Platform::instance()->d->windowActivated.connect(
66 &DockWidget::Private::onWindowActivated, d);
67 d->m_windowDeactivatedConnection = Platform::instance()->d->windowDeactivated.connect(
68 &DockWidget::Private::onWindowDeactivated, d);
69
70 // If user called LayoutSaver::restoreLayout() before creating the dock widget, we need to
71 // keep the previous known dock widget position
73 LayoutSaver::Private::restorePendingPositions(this);
74}
75
77{
78 m_inDtor = true;
79 d->m_windowActivatedConnection->disconnect();
80 d->m_windowDeactivatedConnection->disconnect();
81
82 d->aboutToDelete.emit(this);
84 delete d;
85}
86
88{
89 d->init();
91}
92
94{
95 if (other == this) {
96 KDDW_ERROR("Refusing to add dock widget into itself {}", ( void * )other);
97 return;
98 }
99
100 if (!other) {
101 KDDW_ERROR("dock widget is null");
102 return;
103 }
104
105 if (!DockRegistry::self()->affinitiesMatch(other->affinities(), d->affinities)) {
106 KDDW_ERROR("Refusing to dock widget with incompatible affinity. {} {}", other->affinities(), affinities());
107 return;
108 }
109
112 KDDW_ERROR("Refusing to dock non-dockable widget {}", ( void * )other);
113 return;
114 }
115
117 KDDW_ERROR("Not supported with MainWindowOption_HasCentralWidget."
118 "MainWindowOption_HasCentralWidget can only have 1 widget in the center."
119 "Use MainWindowOption_HasCentralFrame instead, which is similar but supports "
120 "tabbing.");
121 return;
122 }
123
124 Core::Group *group = d->group();
125
126 if (group) {
127 if (group->containsDockWidget(other)) {
128 KDDW_ERROR("Already contains {}", ( void * )other);
129 return;
130 }
131 } else {
132 if (view()->isRootView()) {
133 // Doesn't have a group yet
134 d->morphIntoFloatingWindow();
135 group = d->group();
136 assert(group);
137 } else {
138 // Doesn't happen
139 KDDW_ERROR("null group");
140 return;
141 }
142 }
143
144 other->setParentView(nullptr);
145 group->addTab(other, option);
146}
147
149 DockWidget *relativeTo,
150 const InitialOption &initialOption)
151{
152 if (!other)
153 return;
154
155 if (auto mw = mainWindow()) {
156 // It's inside a main window. Simply use the main window API.
157 mw->addDockWidget(other, location, relativeTo, initialOption);
158 return;
159 }
160
161 if (!DockRegistry::self()->affinitiesMatch(other->affinities(), d->affinities)) {
162 KDDW_ERROR("Refusing to dock widget with incompatible affinity. {} {}", other->affinities(), affinities());
163 return;
164 }
165
168 KDDW_ERROR("Refusing to dock non-dockable widget {}", ( void * )other);
169 return;
170 }
171
172 if (view()->isRootView())
173 d->morphIntoFloatingWindow();
174
175 if (auto fw = floatingWindow()) {
176 fw->addDockWidget(other, location, relativeTo, initialOption);
177 } else {
178 KDDW_ERROR("Couldn't find floating nested window");
179 }
180}
181
182std::shared_ptr<View> DockWidget::guestView() const
183{
184 return d->guest;
185}
186
187void DockWidget::setGuestView(std::shared_ptr<View> guest)
188{
189 if ((guest && guest->equals(d->guest)) || (!guest && !d->guest))
190 return;
191
192 if (d->guest) {
193 // Unparent the old widget, we're giving back ownership
194 d->guest->setParent(nullptr);
195 }
196
197 d->guest = guest;
198 if (guest)
199 guest->setParent(view());
200
201 d->guestViewChanged.emit();
202}
203
205{
206 if (view()->isRootView())
207 return true;
208
209 // Deal with case of main window nesting:
210 // Dock widget inside main window 1 inside mainwindow 2 and detach mw1, would
211 // still be in a floating window, but dockwidget is still docked to the inner main window
212 if (isInMainWindow())
213 return false;
214
215 auto fw = floatingWindow();
216 return fw && fw->hasSingleDockWidget();
217}
218
219bool DockWidget::setFloating(bool floats)
220{
221 const bool alreadyFloating = isFloating();
222
223 if (floats == alreadyFloating)
224 return true; // Nothing to do
225
226 if (!floats
227 && (Config::self().internalFlags()
229 && !isVisible()) {
230 // Mimics behaviour of QDockWidget, which you might need during porting.
231 // Not something we suggest though. For KDDW, setFloating(false) means dock, and that
232 // implies showing.
233 return false;
234 }
235
236 if (floats && isPersistentCentralDockWidget())
237 return false;
238
239 if (floats) {
240 d->saveTabIndex();
241 if (isTabbed()) {
242 auto group = d->group();
243 if (!group) {
244 KDDW_ERROR("DockWidget::setFloating: Tabbed but no group exists", ( void * )this);
245 assert(false);
246 return false;
247 }
248
249 group->detachTab(this);
250 } else {
251 titleBar()->makeWindow();
252 }
253
254 auto lastGeo = d->lastPosition()->lastFloatingGeometry();
255 if (lastGeo.isValid()) {
256 if (auto fw = floatingWindow())
257 fw->setSuggestedGeometry(lastGeo, SuggestedGeometryHint_PreserveCenter);
258 }
259 return true;
260 } else {
261 d->saveLastFloatingGeometry();
262 return d->restoreToPreviousPosition();
263 }
264}
265
267{
268 return d->toggleAction;
269}
270
272{
273 return d->floatAction;
274}
275
277{
278 return d->uniqueName();
279}
280
282{
283 if (d->isMDIWrapper()) {
284 // It's just a wrapper to help implementing Option_MDINestable. Return the title of the real
285 // dock widget we're hosting.
286 auto dropAreaGuest = d->guest ? guestView()->asDropAreaController() : nullptr;
287 assert(dropAreaGuest);
288 if (dropAreaGuest->hasSingleGroup()) {
289 return dropAreaGuest->groups().constFirst()->title();
290 } else {
292 }
293 }
294
295 return d->title;
296}
297
299{
300 if (title != d->title) {
301 d->title = title;
302 d->updateTitle();
303 d->titleChanged.emit(title);
304 }
305}
306
308{
309 if (Core::Group *f = d->group())
310 return f->view()->geometry();
311
312 // Means the dock widget isn't visible. Just fallback to its own geometry
313 return geometry();
314}
315
316DockWidgetOptions DockWidget::options() const
317{
318 return d->options;
319}
320
321LayoutSaverOptions DockWidget::layoutSaverOptions() const
322{
323 return d->layoutSaverOptions;
324}
325
326void DockWidget::setOptions(DockWidgetOptions options)
327{
329 KDDW_ERROR(
330 "DockWidget::setOptions: Option_NotDockable not allowed to change. Pass via ctor only.");
331 return;
332 }
333
334 if (options != d->options) {
335 d->options = options;
336 d->optionsChanged.emit(options);
337 if (auto tb = titleBar())
338 tb->updateButtons();
339 }
340}
341
343{
344 if (Core::Group *group = d->group()) {
345 return group->alwaysShowsTabs() || group->dockWidgetCount() > 1;
346 } else {
347 if (!isFloating())
348 KDDW_ERROR("DockWidget::isTabbed() Couldn't find any tab widget.");
349 return false;
350 }
351}
352
354{
355 if (Core::Group *group = d->group()) {
356 return group->currentIndex() == group->indexOfDockWidget(const_cast<DockWidget *>(this));
357 } else {
358 return true;
359 }
360}
361
363{
364 if (Core::Group *group = d->group())
365 group->setCurrentDockWidget(this);
366}
367
369{
370 if (Core::Group *group = d->group())
371 return group->indexOfDockWidget(this);
372
373 return 0;
374}
375
377{
378 if (Group *group = d->group())
379 return group->currentTabIndex();
380
381 return 0;
382}
383
384void DockWidget::setIcon(const Icon &icon, IconPlaces places)
385{
386 if (places & IconPlace::TitleBar)
387 d->titleBarIcon = icon;
388
389 if (places & IconPlace::TabBar)
390 d->tabBarIcon = icon;
391
392 if (places & IconPlace::ToggleAction)
393 d->toggleAction->setIcon(icon);
394
395 d->iconChanged.emit();
396}
397
399{
400 if (place == IconPlace::TitleBar)
401 return d->titleBarIcon;
402
403 if (place == IconPlace::TabBar)
404 return d->tabBarIcon;
405
406 if (place == IconPlace::ToggleAction)
407 return d->toggleAction->icon();
408
409 return {};
410}
411
413{
414 d->forceClose();
415}
416
418{
419 if (Core::Group *f = d->group())
420 return f->actualTitleBar();
421
422 return nullptr;
423}
424
426{
427 return d->m_isOpen;
428}
429
431{
432 return d->affinities;
433}
434
436{
437 open();
438}
439
441{
442 if (view()->isRootView()
443 && (d->m_lastPosition->wasFloating() || !d->m_lastPosition->isValid())) {
444 // Create the FloatingWindow already, instead of waiting for the show event.
445 // This reduces flickering on some platforms
446 d->morphIntoFloatingWindow();
447 } else {
448 d->setIsOpen(true);
449 }
450}
451
453{
454 if (!isOpen())
455 return;
456
458
459 if (auto fw = floatingWindow()) {
460 fw->view()->raise();
461 fw->view()->activateWindow();
462 } else if (Core::Group *group = d->group()) {
463 if (group->isMDI())
464 group->view()->raise();
465 }
466}
467
469{
470 if (auto guest = guestView())
471 return guest->is(ViewType::MainWindow);
472 return false;
473}
474
476{
477 return d->mainWindow() != nullptr;
478}
479
481{
482 return d->mainWindow();
483}
484
486{
487 return DockRegistry::self()->focusedDockWidget() == this;
488}
489
491{
492 setAffinities({ affinity });
493}
494
496{
497 Vector<QString> affinities = affinityNames;
499
500 if (d->affinities == affinities)
501 return;
502
503 if (!d->affinities.isEmpty()) {
509 KDDW_ERROR("Affinity is already set, refusing to change."
510 "Submit a feature request with a good justification.");
511 return;
512 }
513
514 d->affinities = affinities;
515}
516
518{
519 if (MainWindow *m = mainWindow())
520 m->moveToSideBar(this);
521}
522
524{
525 if (MainWindow *m = mainWindow()) {
526 if (auto sb = m->sideBarForDockWidget(this)) {
527 sb->removeDockWidget(this);
528 }
529 }
530}
531
533{
534 if (MainWindow *m = mainWindow())
535 return m->overlayedDockWidget() == this;
536
537 return false;
538}
539
544
546{
548}
549
551{
552 return d->m_lastPosition->isValid();
553}
554
556{
557 return d->m_lastOverlayedSize;
558}
559
564
566{
567 if (Core::Group *group = d->group()) {
568 group->tabBar()->removeDockWidget(this);
569 }
570
572 d->onParentChanged();
573}
574
576{
577 return d->layoutSaverOptions & LayoutSaverOption::Skip;
578}
579
581{
582 return d->m_lastCloseReason;
583}
584
586{
587 if (isOpen() && isFloating()) {
588 view()->rootView()->setGeometry(geometry);
589 } else {
590 d->m_lastPosition->setLastFloatingGeometry(geometry);
591 }
592}
593
595{
596 if (auto fw = view()->rootView()->asFloatingWindowController())
597 return fw;
598
599 return nullptr;
600}
601
602Core::FloatingWindow *DockWidget::Private::morphIntoFloatingWindow()
603{
604 if (auto fw = floatingWindow())
605 return fw; // Nothing to do
606
607 if (q->view()->isRootView()) {
608 Rect geo = m_lastPosition->lastFloatingGeometry();
609 if (geo.isNull()) {
610 geo = q->geometry();
611
612 if (!q->view()->hasAttribute(Qt::WA_PendingMoveEvent)) { // If user already moved it,
613 // we don't
614 // interfere
615 const Point center = defaultCenterPosForFloating();
616 if (!center.isNull())
617 geo.moveCenter(center);
618 }
619 }
620
621 auto group = new Core::Group();
622 group->addTab(q);
623 geo.setSize(geo.size().boundedTo(group->view()->maxSizeHint()));
624 geo.setSize(geo.size().expandedTo(group->view()->minSize()));
626 auto floatingWindow = new Core::FloatingWindow(group, geo);
627
628 Core::AtomicSanityChecks checks(floatingWindow->dropArea()->rootItem());
629 floatingWindow->view()->show();
630 setIsOpen(true);
631
632 return floatingWindow;
633 } else {
634 return nullptr;
635 }
636}
637
638void DockWidget::Private::maybeMorphIntoFloatingWindow()
639{
640 if (q->view()->isRootView() && q->isVisible())
641 morphIntoFloatingWindow();
642}
643
644MDILayout *DockWidget::Private::mdiLayout() const
645{
646 auto p = q->view()->parentView();
647 while (p) {
648 // We found a layout
649 if (auto mdiLayout = p->asMDILayoutController()) {
650 // And it's MDI
651 return mdiLayout;
652 } else if (auto dropArea = p->asDropAreaController()) {
653 // It's a DropArea. But maybe it's a drop area that's just helping
654 // making the MDI windows accept drops (Option_MDINestable)
655 if (!dropArea->isMDIWrapper())
656 return nullptr;
657
658 // It's a MDI wrapper, keep looking up.
659 }
660
661 p = p->parentView();
662 }
663
664 return nullptr;
665}
666
667bool DockWidget::Private::isMDIWrapper() const
668{
669 return mdiDropAreaWrapper() != nullptr;
670}
671
672DropArea *DockWidget::Private::mdiDropAreaWrapper() const
673{
674 if (auto dropAreaGuest = guest ? q->guestView()->asDropAreaController() : nullptr) {
675 if (dropAreaGuest->isMDIWrapper())
676 return dropAreaGuest;
677 }
678
679 return nullptr;
680}
681
682Core::DockWidget *DockWidget::Private::mdiDockWidgetWrapper() const
683{
684 if (isMDIWrapper()) {
685 // We are the wrapper
686 return q;
687 }
688
689 auto p = q->view()->parentView();
690 while (p) {
691
692 if (p->is(ViewType::DropArea) || p->is(ViewType::MDILayout)) {
693 if (auto dropArea = p->asDropAreaController()) {
694 if (dropArea->isMDIWrapper())
695 return dropArea->mdiDockWidgetWrapper();
696 }
697
698 return nullptr;
699 }
700
701 p = p->parentView();
702 }
703
704 return nullptr;
705}
706
707DockWidget::Private *DockWidget::dptr() const
708{
709 return d;
710}
711
712DockWidget::Private::~Private()
713{
714 delete toggleAction;
715 delete floatAction;
716}
717
718Point DockWidget::Private::defaultCenterPosForFloating()
719{
721 // We don't care about multiple mainwindows yet. Or, let's just say that the first one is more
722 // main than the others
723 MainWindow *mw = mainWindows.isEmpty() ? nullptr : mainWindows.constFirst();
724 if (!mw || !q->isFloating())
725 return {};
726
727 return mw->geometry().center();
728}
729
730void DockWidget::Private::onWindowActivated(std::shared_ptr<View> rootView)
731{
732 if (View::equals(rootView.get(), q->view()->rootView().get()))
733 windowActiveAboutToChange.emit(true);
734}
735
736void DockWidget::Private::onWindowDeactivated(std::shared_ptr<View> rootView)
737{
738 if (rootView->inDtor() || q->view()->inDtor())
739 return;
740
741 if (View::equals(rootView.get(), q->view()->rootView().get()))
742 windowActiveAboutToChange.emit(false);
743}
744
745void DockWidget::Private::updateTitle()
746{
747 if (q->isFloating())
748 q->view()->rootView()->setWindowTitle(title);
749
750 toggleAction->setText(title);
751}
752
753void DockWidget::Private::toggle(bool enabled)
754{
755 if (Core::SideBar *sb = sideBar()) {
756 // The widget is in the sidebar, let's toggle its overlayed state
757 ScopedValueRollback guard(m_removingFromOverlay, true);
758 sb->toggleOverlay(q);
759 } else {
760 // The most common case. The dock widget is not in the sidebar. just close or open it.
761 if (enabled) {
762 q->open();
763 } else {
764 q->view()->close();
765 }
766 }
767}
768
769void DockWidget::Private::updateToggleAction()
770{
771 ScopedValueRollback recursionGuard(m_updatingToggleAction,
772 true); // Guard against recursiveness
773
774 if (m_willUpdateActions || Group::s_inFloatHack) {
775 // We're in the middle of a DropArea::addDockWidget() call. No point in triggering actions right now
776 // they will be triggered at the end already.
777 // Fixes Actions being triggered when using StartsHidden.
778 return;
779 }
780
781 if ((q->isVisible() || group()) && !toggleAction->isChecked()) {
782 toggleAction->setChecked(true);
783 } else if ((!q->isVisible() && !group()) && toggleAction->isChecked()) {
784 toggleAction->setChecked(false);
785 }
786}
787
788void DockWidget::Private::updateFloatAction()
789{
790 if (m_willUpdateActions || m_removingFromOverlay || Group::s_inFloatHack)
791 return;
792
793 ScopedValueRollback recursionGuard(m_updatingFloatAction,
794 true); // Guard against recursiveness
795
796 if (q->isFloating()) {
797 floatAction->setEnabled(m_lastPosition->isValid());
798 floatAction->setChecked(true);
799 floatAction->setToolTip(Object::tr("Dock"));
800 } else {
801 floatAction->setEnabled(true);
802 floatAction->setChecked(false);
803 floatAction->setToolTip(Object::tr("Detach"));
804 }
805}
806
807void DockWidget::Private::close()
808{
809 if (m_inClose)
810 return;
811 ScopedValueRollback guard(m_inClose, true);
812
813 if (!m_processingToggleAction && !q->isOpen()) {
814 q->setParentView(nullptr);
815 return;
816 }
817
818 if (m_isPersistentCentralDockWidget)
819 return;
820
821 m_lastCloseReason = DockRegistry::self()->currentCloseReason();
822 setIsOpen(false);
823
824 // If it's overlayed and we're closing, we need to close the overlay
825 if (Core::SideBar *sb = DockRegistry::self()->sideBarForDockWidget(q)) {
826 auto mainWindow = sb->mainWindow();
827 if (mainWindow->overlayedDockWidget() == q) {
828 mainWindow->clearSideBarOverlay(/* deleteGroup=*/false);
829 }
830 }
831
832 if (!m_isForceClosing && q->isFloating()
833 && q->isVisible()) { // only user-closing is interesting to save the geometry
834 // We check for isVisible so we don't save geometry if you call close() on an already closed
835 // dock widget
836 m_lastPosition->setLastFloatingGeometry(q->view()->d->windowGeometry());
837 }
838
839 if (!m_removingFromOverlay)
840 saveTabIndex();
841
842 // Do some cleaning. Widget is hidden, but we must hide the tab containing it.
843 if (Core::Group *group = this->group()) {
844 q->setParent(nullptr);
845 q->setParentView(nullptr);
846 group->removeWidget(q);
847
848 if (Core::SideBar *sb = DockRegistry::self()->sideBarForDockWidget(q)) {
849 sb->removeDockWidget(q);
850 }
851 }
852
853 if (!m_isMovingToSideBar && (options & DockWidgetOption_DeleteOnClose)) {
854 aboutToDeleteOnClose.emit();
855 q->destroyLater();
856 }
857}
858
859bool DockWidget::Private::restoreToPreviousPosition()
860{
861 if (!m_lastPosition->isValid())
862 return false;
863
864 Core::Item *item = m_lastPosition->lastItem();
865
866 Layout *layout = DockRegistry::self()->layoutForItem(item);
867 assert(layout);
868 layout->restorePlaceholder(q, item, m_lastPosition->lastTabIndex());
869 return true;
870}
871
872void DockWidget::Private::maybeRestoreToPreviousPosition()
873{
874 // This is called when we open a dock widget. Let's see if we have to restore it to a previous
875 // position.
876
877 if (!m_lastPosition->isValid())
878 return;
879
880 Core::Item *layoutItem = m_lastPosition->lastItem();
881 if (!layoutItem)
882 return; // nothing to do, no last position
883
884 if (m_lastPosition->wasFloating())
885 return; // Nothing to do, it was floating before, now it'll just get visible
886
887 Core::Group *group = this->group();
888
889 if (group && group->view()->equals(DockRegistry::self()->layoutForItem(layoutItem)->view())) {
890 // There's a group already. Means the DockWidget was hidden instead of closed.
891 // Nothing to do, the dock widget will simply be shown
892 return;
893 }
894
895 // Now we deal with the case where the DockWidget was close()ed. In this case it doesn't have a
896 // parent.
897
898 if (q->view()->parentView()) {
899 // Was called due to it being made floating. Nothing to restore.
900 return;
901 }
902
903 // Finally, restore it
904 restoreToPreviousPosition();
905}
906
907int DockWidget::Private::currentTabIndex() const
908{
909 Core::Group *group = this->group();
910 return group ? group->indexOfDockWidget(q) : 0;
911}
912
913void DockWidget::Private::saveTabIndex()
914{
915 m_lastPosition->saveTabIndex(currentTabIndex(), q->isFloating());
916}
917
918void DockWidget::Private::onParentChanged()
919{
920 updateToggleAction();
921 updateFloatAction();
922
923 actualTitleBarChanged.emit();
924}
925
927{
928 if (isOverlayed()) {
929 if (auto group = d->group()) {
930 d->m_lastOverlayedSize = group->view()->size();
931 } else {
932 KDDW_ERROR("Overlayed dock widget without group shouldn't happen");
933 }
934 }
935}
936
937Core::DockWidget *DockWidget::deserialize(const LayoutSaver::DockWidget::Ptr &saved)
938{
939 if (saved->skipsRestore())
940 return nullptr;
941
942 auto dr = DockRegistry::self();
943 DockWidget *dw =
944 dr->dockByName(saved->uniqueName, DockRegistry::DockByNameFlags(DockRegistry::DockByNameFlag::CreateIfNotFound) | DockRegistry::DockByNameFlag::SilentIfNotFound);
945 if (dw) {
946 if (auto guest = dw->guestView())
947 guest->setVisible(true);
948 dw->d->m_wasRestored = true;
949
950 if (dw->affinities() != saved->affinities) {
951 KDDW_ERROR("Affinity name changed from {} to {}", dw->affinities(), "; to", saved->affinities);
952 dw->d->affinities = saved->affinities;
953 }
954
955 dw->dptr()->m_lastCloseReason = saved->lastCloseReason;
956 }
957
958 return dw;
959}
960
961void DockWidget::setUserType(int userType)
962{
963 d->m_userType = userType;
964}
965
967{
968 return d->m_userType;
969}
970
972{
973 if (MDILayout *layout = d->mdiLayout()) {
974 if (auto wrapperDW = d->mdiDockWidgetWrapper()) {
975 // Case of using Option_MDINestable. We need to layout the actual top level DW
976 layout->moveDockWidget(wrapperDW, pos);
977 } else {
978 layout->moveDockWidget(this, pos);
979 }
980 }
981}
982
984{
985 if (MDILayout *layout = d->mdiLayout()) {
986 if (auto wrapperDW = d->mdiDockWidgetWrapper()) {
987 // Case of using Option_MDINestable. We need to layout the actual top level DW
988 layout->resizeDockWidget(wrapperDW, size);
989 } else {
990 layout->resizeDockWidget(this, size);
991 }
992 }
993}
994
996{
997 if (Group *group = d->group()) {
998 if (group->isMDI())
999 group->view()->setZOrder(z);
1000 }
1001}
1002
1004{
1005 if (Group *group = d->group()) {
1006 if (group->isMDI())
1007 return group->view()->zOrder();
1008 }
1009
1010 return 0;
1011}
1012
1014{
1015 return d->m_isPersistentCentralDockWidget;
1016}
1017
1018LayoutSaver::DockWidget::Ptr DockWidget::Private::serialize() const
1019{
1020 auto ptr = LayoutSaver::DockWidget::dockWidgetForName(q->uniqueName());
1021 ptr->affinities = q->affinities();
1022 ptr->lastCloseReason = m_lastCloseReason;
1023
1024 return ptr;
1025}
1026
1027void DockWidget::Private::forceClose()
1028{
1029 ScopedValueRollback rollback(m_isForceClosing, true);
1030 close();
1031}
1032
1033DockWidget::Private::Private(const QString &dockName, DockWidgetOptions options_,
1034 LayoutSaverOptions layoutSaverOptions_, DockWidget *qq)
1035
1036 : m_uniqueName(dockName)
1037 , title(dockName)
1038 , q(qq)
1039 , options(options_)
1040 , layoutSaverOptions(layoutSaverOptions_)
1041 , toggleAction(Config::self().viewFactory()->createAction(q, "toggle"))
1042 , floatAction(Config::self().viewFactory()->createAction(q, "float"))
1043{
1044 m_toggleActionConnection = toggleAction->d->toggled.connect(
1045 [this](bool enabled) {
1046 if (!m_updatingToggleAction) { // guard against recursiveness
1047 toggleAction->blockSignals(true); // and don't emit spurious toggle. Like when a dock
1048 // widget is inserted into a tab widget it might get
1049 // hide events, ignore those. The Dock Widget is open.
1050 m_processingToggleAction = true;
1052 toggle(enabled);
1053 toggleAction->blockSignals(false);
1054 m_processingToggleAction = false;
1055 }
1056 });
1057
1058 m_floatActionConnection = floatAction->d->toggled.connect([this](bool checked) {
1059 if (!m_updatingFloatAction) { // guard against recursiveness
1060 q->setFloating(checked);
1061 }
1062
1063 KDDW_TRACE("Emitting DockWidget::isFloatingChanged({})", checked);
1064 isFloatingChanged.emit(checked);
1065
1066 // When floating, we remove from the sidebar
1067 if (checked && q->isOpen()) {
1068 if (Core::SideBar *sb = DockRegistry::self()->sideBarForDockWidget(q)) {
1069 sb->mainWindow()->clearSideBarOverlay(/* deleteGroup=*/false);
1070 sb->removeDockWidget(q);
1071 }
1072 }
1073 });
1074}
1075
1076void DockWidget::Private::addPlaceholderItem(Core::Item *item)
1077{
1078 assert(item);
1079 m_lastPosition->addPlaceholderItem(item);
1080}
1081
1082Position::Ptr &DockWidget::Private::lastPosition()
1083{
1084 return m_lastPosition;
1085}
1086
1087Core::Group *DockWidget::Private::group() const
1088{
1089 auto p = q->view()->parentView();
1090 while (p) {
1091 if (auto group = p->asGroupController())
1092 return group;
1093 p = p->parentView();
1094 }
1095 return nullptr;
1096}
1097
1098Core::Item *DockWidget::Private::item() const
1099{
1100 auto group = this->group();
1101 if (!group || group->inDtor())
1102 return nullptr;
1103
1104 return group->layoutItem();
1105}
1106
1107void DockWidget::Private::saveLastFloatingGeometry()
1108{
1109 if (q->isFloating() && q->isVisible()) {
1110 // It's getting docked, save last floating position
1111 lastPosition()->setLastFloatingGeometry(q->view()->d->windowGeometry());
1112 }
1113}
1114
1115void DockWidget::Private::onCloseEvent(CloseEvent *e)
1116{
1117 if (m_inCloseEvent)
1118 return;
1119 ScopedValueRollback guard(m_inCloseEvent, true);
1120
1121 e->accept(); // By default we accept, means DockWidget closes
1122
1123 if (auto v = q->view()) {
1124 // Give a chance for QtWidgets::DockWidgets::closeEvent(ev) to ignore
1126 if (!e->isAccepted())
1127 return;
1128 }
1129
1130 if (guest) {
1131 // Give a chance for the widget to ignore
1132 Platform::instance()->sendEvent(guest.get(), e);
1133 if (!e->isAccepted())
1134 return;
1135 }
1136
1137 close();
1138}
1139
1140void DockWidget::Private::setIsOpen(bool is)
1141{
1142 if (is == m_isOpen || m_inOpenSetter)
1143 return;
1144
1145 ScopedValueRollback guard(m_inOpenSetter, true);
1146
1147 if (!is)
1148 close();
1149
1150 m_isOpen = is;
1151
1152 if (is && !LayoutSaver::restoreInProgress()) {
1153 maybeRestoreToPreviousPosition();
1154
1155#ifdef KDDW_FRONTEND_QT
1156 // Transform into a FloatingWindow if this will be a regular floating dock widget.
1157 QTimer::singleShot(0, q, [this] { maybeMorphIntoFloatingWindow(); });
1158#endif
1159 }
1160
1161 updateToggleAction();
1162 updateFloatAction();
1163
1164 if (is && !q->isOverlayed()) {
1165 // If we're open, we can't be in the sidebar without overlay, both or nothing.
1166 // nothing in this case
1167 q->removeFromSideBar();
1168 }
1169
1170 if (!is) {
1171 closed.emit();
1172 }
1173
1174 isOpenChanged.emit(is);
1175}
1176
1177QString DockWidget::Private::uniqueName() const
1178{
1179 return m_uniqueName;
1180}
1181
1182void DockWidget::Private::setUniqueName(const QString &name)
1183{
1184 if (name.isEmpty()) {
1185 KDDW_ERROR("DockWidget::Private::setUniqueName: Name is empty");
1186 } else {
1187 m_uniqueName = name;
1188 }
1189}
1190
1191void DockWidget::setFloatingWindowFlags(FloatingWindowFlags flags)
1192{
1193 if (floatingWindow()) {
1194 KDDW_ERROR("Call this function only before having a floating window");
1195 } else {
1196 d->m_flags = flags;
1197 }
1198}
1199
1200KDDockWidgets::FloatingWindowFlags DockWidget::floatingWindowFlags() const
1201{
1202 return d->m_flags;
1203}
1204
1206{
1207 if (auto group = d->group())
1208 return group->size();
1209
1210 return size();
1211}
1212
1213void DockWidget::resizeInLayout(int left, int top, int right, int bottom)
1214{
1215 Item *item = d->item();
1216 if (!item || item->m_inDtor)
1217 return;
1218
1219 item->requestResize(left, top, right, bottom);
1220}
1221
1222namespace {
1223Draggable *draggableForDockWidget(DockWidget *dw, bool singleTab)
1224{
1225 Group *group = dw->d->group();
1226 if (!group) {
1227 KDDW_WARN("draggableForDockWidget: Expected a group");
1228 return nullptr;
1229 }
1230
1231 if (singleTab && !group->hasSingleDockWidget()) {
1232 // Case #1: Detaching a single tab if the group has multiple tabs
1233 return group->tabBar();
1234 }
1235
1236 // Case #2: Dragging a group of tabs, via titlebar
1237 Core::TitleBar *titleBar = dw->titleBar();
1238 if (titleBar && titleBar->isVisible())
1239 return titleBar;
1240
1241 // Case #3: Dragging a group of tabs, via tab bar background. QTabBar background is actually QTabWidget (aka stack)
1242 return group->stack();
1243}
1244}
1245
1246bool DockWidget::startDragging(bool singleTab)
1247{
1248 auto dc = DragController::instance();
1249 if (dc->isDragging()) {
1250 KDDW_WARN("DockWidget::startDragging: Dragging already ongoing");
1251 return false;
1252 }
1253
1254 Draggable *const draggable = draggableForDockWidget(this, singleTab);
1255 if (!draggable) {
1256 KDDW_WARN("DockWidget::startDragging: Could not find a suitable draggable");
1257 return false;
1258 }
1259
1260 Core::TabBar *tabBar = d->group()->tabBar();
1261 if (draggable->asView() == tabBar->view()) {
1262 // minor hack, so we can reuse the normal mouse interaction code path
1263 tabBar->dptr()->m_lastPressedDockWidget = this;
1264 }
1265
1266 const Point globalPos = Platform::instance()->cursorPos();
1267
1268 View *draggedView = draggable->asView();
1269 const Point offset = draggedView->mapFromGlobal(globalPos);
1270
1271 return dc->programmaticStartDrag(draggable, globalPos, offset);
1272}
1273
1275{
1276 return d->m_wasRestored;
1277}
1278
1279void DockWidget::setUniqueName(const QString &uniqueName)
1280{
1281 d->setUniqueName(uniqueName);
1282}
Application-wide config to tune certain behaviours of the framework.
Singleton to allow to choose certain behaviours of the framework.
Definition Config.h:64
@ InternalFlag_DontShowWhenUnfloatingHiddenWindow
DockWidget::setFloating(false) won't do anything if the window is hidden.
Definition Config.h:162
static Config & self()
returns the singleton Config instance
Definition Config.cpp:88
virtual bool blockSignals(bool)=0
void setParentView(View *parent)
View * view() const
Returns the view associated with this controller, if any.
virtual void setParentView_impl(View *parent)
The DockWidget base-class. DockWidget and Core::DockWidget are only split in two so we can share some...
void setMDISize(Size size)
like setMDIPosition(), but for the size.
bool setFloating(bool floats)
setter to make the dock widget float or dock.
Core::TitleBar * titleBar() const
Returns this dock widget's title bar.
void setAsCurrentTab()
Makes this dock widget current in its tab group.
DockWidget(View *view, const QString &uniqueName, DockWidgetOptions options={}, LayoutSaverOptions layoutSaverOptions={})
constructs a new DockWidget
void setTitle(const QString &title)
setter for the dock widget's title
void show()
Deprecated, use open() instead. Visibility isn't a good concept since tabbed non-current dock widgets...
bool isCurrentTab() const
Returns true if this dock widget is the current one in the tab widget that contains it....
SideBarLocation sideBarLocation() const
Returns whether this dock widget is in a side bar, and which. SideBarLocation::None is returned if it...
MainWindow * mainWindow() const
Returns the main window this dock widget is in. nullptr if it's not inside a main window Also returns...
Size lastOverlayedSize() const
returns the last size the widget has when overlayed Empty otherwise
void resizeInLayout(int left, int top, int right, int bottom)
void setUserType(int userType)
Allows the user to set a type on this dock widget The type is opaque and will not be interpreted by K...
QString uniqueName() const
the dock widget's unique name.
static DockWidget * deserialize(const std::shared_ptr< LayoutSaver::DockWidget > &)
Constructs a dock widget from its serialized form.
void addDockWidgetToContainingWindow(KDDockWidgets::Core::DockWidget *other, KDDockWidgets::Location location, KDDockWidgets::Core::DockWidget *relativeTo=nullptr, const KDDockWidgets::InitialOption &initialOption={})
docks other widget into the window that contains this one. Equivalent to MainWindow::addDockWidget() ...
KDDockWidgets::FloatingWindowFlags floatingWindowFlags() const
Rect groupGeometry() const
Returns the size of the dock widget's parent group.
bool skipsRestore() const
Returns whether this widget has the LayoutSaverOption::Skip flag.
void addDockWidgetAsTab(KDDockWidgets::Core::DockWidget *other, const KDDockWidgets::InitialOption &initialOption={})
docks other widget into this one. Tabs will be shown if not already.
void setOptions(DockWidgetOptions)
Setter for the options. Only DockWidetOption_NotClosable is allowed to change after construction....
Vector< QString > affinities() const
Returns the affinity name. Empty by default.
bool isOpen() const
Returns whether this dock widget is open. Equivalent to calling toggleAction().isChecked()
Action * toggleAction() const
Returns the Action that allows to hide/show the dock widget Useful to put in menus.
Icon icon(IconPlace place=IconPlace::TitleBar) const
Returns the dock widget's titlebar, tabbar, or toggle action icon (depending on the passed place)
void setIcon(const Icon &icon, IconPlaces places=IconPlace::All)
Sets an icon to show on title bars and tab bars.
void open()
Opens this dock widget. Does nothing if already open. The dock widget will appear floating unless it ...
bool isFocused() const
Returns whether This or any child of this dock widget is focused Not to be confused with QWidget::has...
void removeFromSideBar()
Removes itself from the sidebar, if it's in the sidebar.
bool isOverlayed() const
Returns whether this dock widget is overlayed from the side-bar.
void setMDIZ(int z)
like setMDIPosition(), but for the Z only implemented for QtQuick
bool isTabbed() const
returns if this dock widget is tabbed into another
QString title() const
Returns the dock widget's title. This title is visible in title bars and tab bars.
bool isInSideBar() const
Returns where this dockwidget is in a sidebar Similar to sideBarLocation(), but returns a bool.
int tabIndex() const
Returns the tab index this dock widget occupies Note that dock widgets are almost always tabbed,...
int currentTabIndex() const
Returns the index of the current tab of the tab group this dock widget is in.
void moveToSideBar()
Minimizes this dock widget to the MainWindow's side-bar.
bool isFloating() const
Returns whether the dock widget is floating. Floating means it's not docked and has a window of its o...
bool startDragging(bool singleTab=false)
void setParentView_impl(View *parent) override
Action * floatAction() const
Returns the Action that allows to dock/undock the dock widget Useful to put in menus.
static DockWidget * byName(const QString &uniqueName)
Returns a dock widget by its name This is the same name you passed to DockWidget CTOR....
void setMDIPosition(Point pos)
Sets this dock widgets position to pos within the MDI layout This only applies if the main window is ...
bool isInMainWindow() const
Returns whether this dock widget is docked into a main window (as opposed to floating)
void setFloatingGeometry(Rect geo)
If this dock widget is floating, then sets its geometry to geo.
void setFloatingWindowFlags(FloatingWindowFlags)
Sets the desired floating window flags, in case the defaults aren't desired. By default KDDW will use...
std::shared_ptr< View > guestView() const
Like widget() but returns a view.
void raise()
Brings the dock widget to the front.
void setAffinities(const Vector< QString > &)
Sets the affinity names. Dock widgets can only dock into dock widgets of the same affinity.
void setGuestView(std::shared_ptr< View > guest)
sets the widget which this dock widget hosts.
bool hasPreviousDockedLocation() const
Returns whether this floating dock widget knows its previous docked location Result only makes sense ...
bool isPersistentCentralDockWidget() const
Returns whether this dock widget is the main window persistent central widget This only applies when ...
bool isMainWindow() const
Returns whether widget() is a KDDockWidget::MainWindow.
FloatingWindow * floatingWindow() const
returns the FloatingWindow this widget is in, otherwise nullptr
DockWidgetOptions options() const
Returns the dock widget's options which control behaviour.
KDDockWidgets::LayoutSaverOptions layoutSaverOptions() const
returns the per-dockwidget options which will affect LayoutSaver These are the options which were pas...
CloseReason lastCloseReason() const
Returns the last reason why the dock widget was closed.
void forceClose()
Like QWidget::close() but the hosted widget won't be asked if we should close.
void setAffinityName(const QString &name)
static void ensureRectIsOnScreen(Rect &geometry)
bool isMDI() const
Returns whether this group is in a MDI layout Usually no, unless you're using an MDI main window.
bool hasSingleDockWidget() const
returns whether there's only 1 dock widget.
Definition core/Group.h:196
int indexOfDockWidget(const DockWidget *)
returns the index of the specified dock widget
Core::Item * layoutItem() const
returns the layout item that either contains this Frame in the layout or is a placeholder
void addTab(DockWidget *, const InitialOption &={})
Adds a widget into the Group's Stack.
Core::Stack * stack() const
returns the tab widget
Core::TabBar * tabBar() const
bool containsDockWidget(DockWidget *w) const
returns whether the dockwidget w is inside this group
The widget (QWidget or QQuickItem) which holds a layout of dock widgets.
Definition Layout.h:57
void restorePlaceholder(Core::DockWidget *dw, Core::Item *, int tabIndex)
restores the dockwidget dw to its previous position
Definition Layout.cpp:151
The MDILayout class implements a layout suitable for MDI style docking. Where dock widgets are free t...
The MainWindow base-class. MainWindow and MainWindowBase are only split in two so we can share some c...
virtual QString applicationName() const =0
Returns the application name This name will be used as title of floating dock widgets which contain m...
static Platform * instance()
Returns the platform singleton.
virtual void sendEvent(View *, Event *) const =0
Sends the specified event to the specified view.
virtual Point cursorPos() const =0
Returns the mouse cursor position in screen coordinates.
std::unique_ptr< WindowBeingDragged > makeWindow() override
bool equals(const View *other) const
Returns whether this view represents the same GUI element as the other.
virtual std::shared_ptr< View > rootView() const =0
Returns the top-level gui element which this view is inside It's the root view of the window.
virtual Point mapFromGlobal(Point) const =0
virtual std::shared_ptr< View > parentView() const =0
Returns the gui element's parent. Like QWidget::parentWidget()
virtual void enableAttribute(Qt::WidgetAttribute, bool enable=true)=0
virtual int zOrder() const
virtual void setZOrder(int)
Sets the z order Not supported on all platforms and only relevant for MDI mode.
@ CreateIfNotFound
Creates the dock widget via the user's widget factory in case it doesn't exist.
@ SilentIfNotFound
don't print errors if not found, it will be created later
Q_INVOKABLE KDDockWidgets::Core::DockWidget * dockByName(const QString &, KDDockWidgets::DockRegistry::DockByNameFlags={}) const
Q_INVOKABLE KDDockWidgets::Core::DockWidget * focusedDockWidget() const
void unregisterDockWidget(Core::DockWidget *)
static DockRegistry * self()
SideBarLocation sideBarLocationForDockWidget(const Core::DockWidget *) const
Returns whether the specified dock widget is in a side bar, and which. SideBarLocation::None is retur...
void registerDockWidget(Core::DockWidget *)
Vector< Core::MainWindow * > mainwindows() const
returns all MainWindow instances
Core::Layout * layoutForItem(const Core::Item *) const
Returns the Layout where the specified item is in.
static bool restoreInProgress()
returns whether a restore (restoreLayout) is in progress
A MultiSplitter with support for drop indicators when hovering over.
The MainWindow base-class that's shared between QtWidgets and QtQuick stack.
ViewType
Each View type also has a specific Controller associated with, except for ViewType::None.
Definition Controller.h:26
Class to abstract QAction, so code still works with QtQuick and Flutter.
@ SuggestedGeometryHint_PreserveCenter
SideBarLocation
Each main window supports 4 sidebars.
@ DockWidgetOption_DeleteOnClose
Deletes the DockWidget when closed.
@ DockWidgetOption_NotDockable
The DockWidget can't be docked, it's always floating.
@ Skip
The dock widget won't participate in save/restore.
@ CheckForPreviousRestore
before, and try to recover its previous main window position
bool isEmpty() const const
WA_PendingMoveEvent
Represents a dock widget.
Struct describing the preferred dock widget size and visibility when adding it to a layout.

© 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 by doxygen 1.9.8