KDDockWidgets API Documentation 2.0
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
72{
73 m_inDtor = true;
74 d->m_windowActivatedConnection->disconnect();
75 d->m_windowDeactivatedConnection->disconnect();
76
77 d->aboutToDelete.emit(this);
79 delete d;
80}
81
83{
84 d->init();
86}
87
89{
90 if (other == this) {
91 KDDW_ERROR("Refusing to add dock widget into itself {}", ( void * )other);
92 return;
93 }
94
95 if (!other) {
96 KDDW_ERROR("dock widget is null");
97 return;
98 }
99
100 if (!DockRegistry::self()->affinitiesMatch(other->affinities(), d->affinities)) {
101 KDDW_ERROR("Refusing to dock widget with incompatible affinity. {} {}", other->affinities(), affinities());
102 return;
103 }
104
107 KDDW_ERROR("Refusing to dock non-dockable widget {}", ( void * )other);
108 return;
109 }
110
112 KDDW_ERROR("Not supported with MainWindowOption_HasCentralWidget."
113 "MainWindowOption_HasCentralWidget can only have 1 widget in the center."
114 "Use MainWindowOption_HasCentralFrame instead, which is similar but supports "
115 "tabbing.");
116 return;
117 }
118
119 Core::Group *group = d->group();
120
121 if (group) {
122 if (group->containsDockWidget(other)) {
123 KDDW_ERROR("Already contains {}", ( void * )other);
124 return;
125 }
126 } else {
127 if (view()->isRootView()) {
128 // Doesn't have a group yet
129 d->morphIntoFloatingWindow();
130 group = d->group();
131 assert(group);
132 } else {
133 // Doesn't happen
134 KDDW_ERROR("null group");
135 return;
136 }
137 }
138
139 other->setParentView(nullptr);
140 group->addTab(other, option);
141}
142
144 DockWidget *relativeTo,
145 InitialOption initialOption)
146{
147 if (!other)
148 return;
149
150 if (auto mainWindow = view()->rootView()->asMainWindowController()) {
151 // It's inside a main window. Simply use the main window API.
152 mainWindow->addDockWidget(other, location, relativeTo, initialOption);
153 return;
154 }
155
156 if (!DockRegistry::self()->affinitiesMatch(other->affinities(), d->affinities)) {
157 KDDW_ERROR("Refusing to dock widget with incompatible affinity. {} {}", other->affinities(), affinities());
158 return;
159 }
160
163 KDDW_ERROR("Refusing to dock non-dockable widget {}", ( void * )other);
164 return;
165 }
166
167 if (view()->isRootView())
168 d->morphIntoFloatingWindow();
169
170 if (auto fw = floatingWindow()) {
171 fw->addDockWidget(other, location, relativeTo, initialOption);
172 } else {
173 KDDW_ERROR("Couldn't find floating nested window");
174 }
175}
176
177std::shared_ptr<View> DockWidget::guestView() const
178{
179 return d->guest;
180}
181
182void DockWidget::setGuestView(std::shared_ptr<View> guest)
183{
184 if ((guest && guest->equals(d->guest)) || (!guest && !d->guest))
185 return;
186
187 if (d->guest) {
188 // Unparent the old widget, we're giving back ownership
189 d->guest->setParent(nullptr);
190 }
191
192 d->guest = guest;
193 if (guest)
194 guest->setParent(view());
195
196 d->guestViewChanged.emit();
197}
198
200{
201 if (view()->isRootView())
202 return true;
203
204 auto fw = floatingWindow();
205 return fw && fw->hasSingleDockWidget();
206}
207
208bool DockWidget::setFloating(bool floats)
209{
210 const bool alreadyFloating = isFloating();
211
212 if (floats == alreadyFloating)
213 return true; // Nothing to do
214
215 if (!floats
216 && (Config::self().internalFlags()
218 && !isVisible()) {
219 // Mimics behaviour of QDockWidget, which you might need during porting.
220 // Not something we suggest though. For KDDW, setFloating(false) means dock, and that
221 // implies showing.
222 return false;
223 }
224
225 if (floats && isPersistentCentralDockWidget())
226 return false;
227
228 if (floats) {
229 d->saveTabIndex();
230 if (isTabbed()) {
231 auto group = d->group();
232 if (!group) {
233 KDDW_ERROR("DockWidget::setFloating: Tabbed but no group exists", ( void * )this);
234 assert(false);
235 return false;
236 }
237
238 group->detachTab(this);
239 } else {
240 titleBar()->makeWindow();
241 }
242
243 auto lastGeo = d->lastPosition()->lastFloatingGeometry();
244 if (lastGeo.isValid()) {
245 if (auto fw = floatingWindow())
246 fw->setSuggestedGeometry(lastGeo, SuggestedGeometryHint_PreserveCenter);
247 }
248 return true;
249 } else {
250 d->saveLastFloatingGeometry();
251 return d->restoreToPreviousPosition();
252 }
253}
254
256{
257 return d->toggleAction;
258}
259
261{
262 return d->floatAction;
263}
264
266{
267 return d->name;
268}
269
271{
272 if (d->isMDIWrapper()) {
273 // It's just a wrapper to help implementing Option_MDINestable. Return the title of the real
274 // dock widget we're hosting.
275 auto dropAreaGuest = d->guest ? guestView()->asDropAreaController() : nullptr;
276 assert(dropAreaGuest);
277 if (dropAreaGuest->hasSingleFrame()) {
278 return dropAreaGuest->groups().constFirst()->title();
279 } else {
281 }
282 }
283
284 return d->title;
285}
286
288{
289 if (title != d->title) {
290 d->title = title;
291 d->updateTitle();
292 d->titleChanged.emit(title);
293 }
294}
295
297{
298 if (Core::Group *f = d->group())
299 return f->view()->geometry();
300
301 // Means the dock widget isn't visible. Just fallback to its own geometry
302 return geometry();
303}
304
305DockWidgetOptions DockWidget::options() const
306{
307 return d->options;
308}
309
310LayoutSaverOptions DockWidget::layoutSaverOptions() const
311{
312 return d->layoutSaverOptions;
313}
314
315void DockWidget::setOptions(DockWidgetOptions options)
316{
318 KDDW_ERROR(
319 "DockWidget::setOptions: Option_NotDockable not allowed to change. Pass via ctor only.");
320 return;
321 }
322
323 if (options != d->options) {
324 d->options = options;
325 d->optionsChanged.emit(options);
326 if (auto tb = titleBar())
327 tb->updateButtons();
328 }
329}
330
332{
333 if (Core::Group *group = d->group()) {
334 return group->alwaysShowsTabs() || group->dockWidgetCount() > 1;
335 } else {
336 if (!isFloating())
337 KDDW_ERROR("DockWidget::isTabbed() Couldn't find any tab widget.");
338 return false;
339 }
340}
341
343{
344 if (Core::Group *group = d->group()) {
345 return group->currentIndex() == group->indexOfDockWidget(const_cast<DockWidget *>(this));
346 } else {
347 return true;
348 }
349}
350
352{
353 if (Core::Group *group = d->group())
354 group->setCurrentDockWidget(this);
355}
356
358{
359 if (Core::Group *group = d->group())
360 return group->indexOfDockWidget(this);
361
362 return 0;
363}
364
366{
367 if (Group *group = d->group())
368 return group->currentTabIndex();
369
370 return 0;
371}
372
373void DockWidget::setIcon(const Icon &icon, IconPlaces places)
374{
375 if (places & IconPlace::TitleBar)
376 d->titleBarIcon = icon;
377
378 if (places & IconPlace::TabBar)
379 d->tabBarIcon = icon;
380
381 if (places & IconPlace::ToggleAction)
382 d->toggleAction->setIcon(icon);
383
384 d->iconChanged.emit();
385}
386
388{
389 if (place == IconPlace::TitleBar)
390 return d->titleBarIcon;
391
392 if (place == IconPlace::TabBar)
393 return d->tabBarIcon;
394
395 if (place == IconPlace::ToggleAction)
396 return d->toggleAction->icon();
397
398 return {};
399}
400
402{
403 d->forceClose();
404}
405
407{
408 if (Core::Group *f = d->group())
409 return f->actualTitleBar();
410
411 return nullptr;
412}
413
415{
416 return d->m_isOpen;
417}
418
420{
421 return d->affinities;
422}
423
425{
426 open();
427}
428
430{
431 if (view()->isRootView()
432 && (d->m_lastPosition->wasFloating() || !d->m_lastPosition->isValid())) {
433 // Create the FloatingWindow already, instead of waiting for the show event.
434 // This reduces flickering on some platforms
435 d->morphIntoFloatingWindow();
436 } else {
437 d->setIsOpen(true);
438 }
439}
440
442{
443 if (!isOpen())
444 return;
445
447
448 if (auto fw = floatingWindow()) {
449 fw->view()->raise();
450 fw->view()->activateWindow();
451 } else if (Core::Group *group = d->group()) {
452 if (group->isMDI())
453 group->view()->raise();
454 }
455}
456
458{
459 if (auto guest = guestView())
460 return guest->is(ViewType::MainWindow);
461 return false;
462}
463
465{
466 return d->mainWindow() != nullptr;
467}
468
470{
471 return d->mainWindow();
472}
473
475{
476 auto group = d->group();
477 return group && group->isFocused() && isCurrentTab();
478}
479
481{
482 setAffinities({ affinity });
483}
484
486{
487 Vector<QString> affinities = affinityNames;
489
490 if (d->affinities == affinities)
491 return;
492
493 if (!d->affinities.isEmpty()) {
494 KDDW_ERROR("Affinity is already set, refusing to change."
495 "Submit a feature request with a good justification.");
496 return;
497 }
498
499 d->affinities = affinities;
500}
501
503{
504 if (MainWindow *m = mainWindow())
505 m->moveToSideBar(this);
506}
507
509{
510 if (MainWindow *m = mainWindow())
511 return m->overlayedDockWidget() == this;
512
513 return false;
514}
515
520
522{
524}
525
527{
528 return d->m_lastPosition->isValid();
529}
530
532{
533 return d->m_lastOverlayedSize;
534}
535
540
542{
543 if (Core::Group *group = d->group()) {
544 group->tabBar()->removeDockWidget(this);
545 }
546
548 d->onParentChanged();
549}
550
552{
553 return d->layoutSaverOptions & LayoutSaverOption::Skip;
554}
555
557{
558 if (isOpen() && isFloating()) {
559 view()->rootView()->setGeometry(geometry);
560 } else {
561 d->m_lastPosition->setLastFloatingGeometry(geometry);
562 }
563}
564
566{
567 if (auto fw = view()->rootView()->asFloatingWindowController())
568 return fw;
569
570 return nullptr;
571}
572
573Core::FloatingWindow *DockWidget::Private::morphIntoFloatingWindow()
574{
575 if (auto fw = floatingWindow())
576 return fw; // Nothing to do
577
578 if (q->view()->isRootView()) {
579 Rect geo = m_lastPosition->lastFloatingGeometry();
580 if (geo.isNull()) {
581 geo = q->geometry();
582
583 if (!q->view()->hasAttribute(Qt::WA_PendingMoveEvent)) { // If user already moved it,
584 // we don't
585 // interfere
586 const Point center = defaultCenterPosForFloating();
587 if (!center.isNull())
588 geo.moveCenter(center);
589 }
590 }
591
592 auto group = new Core::Group();
593 group->addTab(q);
594 geo.setSize(geo.size().boundedTo(group->view()->maxSizeHint()));
595 geo.setSize(geo.size().expandedTo(group->view()->minSize()));
597 auto floatingWindow = new Core::FloatingWindow(group, geo);
598
599 Core::AtomicSanityChecks checks(floatingWindow->dropArea()->rootItem());
600 floatingWindow->view()->show();
601 setIsOpen(true);
602
603 return floatingWindow;
604 } else {
605 return nullptr;
606 }
607}
608
609void DockWidget::Private::maybeMorphIntoFloatingWindow()
610{
611 if (q->view()->isRootView() && q->isVisible())
612 morphIntoFloatingWindow();
613}
614
615MDILayout *DockWidget::Private::mdiLayout() const
616{
617 auto p = q->view()->parentView();
618 while (p) {
619 // We found a layout
620 if (auto mdiLayout = p->asMDILayoutController()) {
621 // And it's MDI
622 return mdiLayout;
623 } else if (auto dropArea = p->asDropAreaController()) {
624 // It's a DropArea. But maybe it's a drop area that's just helping
625 // making the MDI windows accept drops (Option_MDINestable)
626 if (!dropArea->isMDIWrapper())
627 return nullptr;
628
629 // It's a MDI wrapper, keep looking up.
630 }
631
632 p = p->parentView();
633 }
634
635 return nullptr;
636}
637
638bool DockWidget::Private::isMDIWrapper() const
639{
640 return mdiDropAreaWrapper() != nullptr;
641}
642
643DropArea *DockWidget::Private::mdiDropAreaWrapper() const
644{
645 if (auto dropAreaGuest = guest ? q->guestView()->asDropAreaController() : nullptr) {
646 if (dropAreaGuest->isMDIWrapper())
647 return dropAreaGuest;
648 }
649
650 return nullptr;
651}
652
653Core::DockWidget *DockWidget::Private::mdiDockWidgetWrapper() const
654{
655 if (isMDIWrapper()) {
656 // We are the wrapper
657 return q;
658 }
659
660 auto p = q->view()->parentView();
661 while (p) {
662
663 if (p->is(ViewType::DropArea) || p->is(ViewType::MDILayout)) {
664 if (auto dropArea = p->asDropAreaController()) {
665 if (dropArea->isMDIWrapper())
666 return dropArea->mdiDockWidgetWrapper();
667 }
668
669 return nullptr;
670 }
671
672 p = p->parentView();
673 }
674
675 return nullptr;
676}
677
678DockWidget::Private *DockWidget::dptr() const
679{
680 return d;
681}
682
683DockWidget::Private::~Private()
684{
685 delete toggleAction;
686 delete floatAction;
687}
688
689Point DockWidget::Private::defaultCenterPosForFloating()
690{
692 // We don't care about multiple mainwindows yet. Or, let's just say that the first one is more
693 // main than the others
694 MainWindow *mw = mainWindows.isEmpty() ? nullptr : mainWindows.constFirst();
695 if (!mw || !q->isFloating())
696 return {};
697
698 return mw->geometry().center();
699}
700
701void DockWidget::Private::onWindowActivated(std::shared_ptr<View> rootView)
702{
703 if (View::equals(rootView.get(), q->view()->rootView().get()))
704 windowActiveAboutToChange.emit(true);
705}
706
707void DockWidget::Private::onWindowDeactivated(std::shared_ptr<View> rootView)
708{
709 if (rootView->inDtor() || q->view()->inDtor())
710 return;
711
712 if (View::equals(rootView.get(), q->view()->rootView().get()))
713 windowActiveAboutToChange.emit(false);
714}
715
716void DockWidget::Private::updateTitle()
717{
718 if (q->isFloating())
719 q->view()->rootView()->setWindowTitle(title);
720
721 toggleAction->setText(title);
722}
723
724void DockWidget::Private::toggle(bool enabled)
725{
726 if (Core::SideBar *sb = sideBar()) {
727 // The widget is in the sidebar, let's toggle its overlayed state
728 ScopedValueRollback guard(m_removingFromOverlay, true);
729 sb->toggleOverlay(q);
730 } else {
731 // The most common case. The dock widget is not in the sidebar. just close or open it.
732 if (enabled) {
733 q->open();
734 } else {
735 q->view()->close();
736 }
737 }
738}
739
740void DockWidget::Private::updateToggleAction()
741{
742 ScopedValueRollback recursionGuard(m_updatingToggleAction,
743 true); // Guard against recursiveness
744
745 if ((q->isVisible() || group()) && !toggleAction->isChecked()) {
746 toggleAction->setChecked(true);
747 } else if ((!q->isVisible() && !group()) && toggleAction->isChecked()) {
748 toggleAction->setChecked(false);
749 }
750}
751
752void DockWidget::Private::updateFloatAction()
753{
754 if (m_willUpdateActions || m_removingFromOverlay)
755 return;
756
757 ScopedValueRollback recursionGuard(m_updatingFloatAction,
758 true); // Guard against recursiveness
759
760 if (q->isFloating()) {
761 floatAction->setEnabled(m_lastPosition->isValid());
762 floatAction->setChecked(true);
763 floatAction->setToolTip(Object::tr("Dock"));
764 } else {
765 floatAction->setEnabled(true);
766 floatAction->setChecked(false);
767 floatAction->setToolTip(Object::tr("Detach"));
768 }
769}
770
771void DockWidget::Private::close()
772{
773 if (m_inClose)
774 return;
775 ScopedValueRollback guard(m_inClose, true);
776
777 if (!m_processingToggleAction && !q->isOpen()) {
778 q->setParentView(nullptr);
779 return;
780 }
781
782 if (m_isPersistentCentralDockWidget)
783 return;
784
785 setIsOpen(false);
786
787 // If it's overlayed and we're closing, we need to close the overlay
788 if (Core::SideBar *sb = DockRegistry::self()->sideBarForDockWidget(q)) {
789 auto mainWindow = sb->mainWindow();
790 if (mainWindow->overlayedDockWidget() == q) {
791 mainWindow->clearSideBarOverlay(/* deleteFrame=*/false);
792 }
793 }
794
795 if (!m_isForceClosing && q->isFloating()
796 && q->isVisible()) { // only user-closing is interesting to save the geometry
797 // We check for isVisible so we don't save geometry if you call close() on an already closed
798 // dock widget
799 m_lastPosition->setLastFloatingGeometry(q->view()->d->windowGeometry());
800 }
801
802 saveTabIndex();
803
804 // Do some cleaning. Widget is hidden, but we must hide the tab containing it.
805 if (Core::Group *group = this->group()) {
806 q->setParent(nullptr);
807 q->setParentView(nullptr);
808 group->removeWidget(q);
809
810 if (Core::SideBar *sb = DockRegistry::self()->sideBarForDockWidget(q)) {
811 sb->removeDockWidget(q);
812 }
813 }
814
815 if (!m_isMovingToSideBar && (options & DockWidgetOption_DeleteOnClose)) {
816 aboutToDeleteOnClose.emit();
817 q->destroyLater();
818 }
819}
820
821bool DockWidget::Private::restoreToPreviousPosition()
822{
823 if (!m_lastPosition->isValid())
824 return false;
825
826 Core::Item *item = m_lastPosition->lastItem();
827
828 Layout *layout = DockRegistry::self()->layoutForItem(item);
829 assert(layout);
830 layout->restorePlaceholder(q, item, m_lastPosition->lastTabIndex());
831 return true;
832}
833
834void DockWidget::Private::maybeRestoreToPreviousPosition()
835{
836 // This is called when we open a dock widget. Let's see if we have to restore it to a previous
837 // position.
838
839 if (!m_lastPosition->isValid())
840 return;
841
842 Core::Item *layoutItem = m_lastPosition->lastItem();
843 if (!layoutItem)
844 return; // nothing to do, no last position
845
846 if (m_lastPosition->wasFloating())
847 return; // Nothing to do, it was floating before, now it'll just get visible
848
849 Core::Group *group = this->group();
850
851 if (group && group->view()->equals(DockRegistry::self()->layoutForItem(layoutItem)->view())) {
852 // There's a group already. Means the DockWidget was hidden instead of closed.
853 // Nothing to do, the dock widget will simply be shown
854 return;
855 }
856
857 // Now we deal with the case where the DockWidget was close()ed. In this case it doesn't have a
858 // parent.
859
860 if (q->view()->parentView()) {
861 // Was called due to it being made floating. Nothing to restore.
862 return;
863 }
864
865 // Finally, restore it
866 restoreToPreviousPosition();
867}
868
869int DockWidget::Private::currentTabIndex() const
870{
871 Core::Group *group = this->group();
872 return group ? group->indexOfDockWidget(q) : 0;
873}
874
875void DockWidget::Private::saveTabIndex()
876{
877 m_lastPosition->saveTabIndex(currentTabIndex(), q->isFloating());
878}
879
880void DockWidget::Private::onParentChanged()
881{
882 updateToggleAction();
883 updateFloatAction();
884
885 actualTitleBarChanged.emit();
886}
887
889{
890 if (isOverlayed()) {
891 if (auto group = d->group()) {
892 d->m_lastOverlayedSize = group->view()->size();
893 } else {
894 KDDW_ERROR("Overlayed dock widget without group shouldn't happen");
895 }
896 }
897}
898
899Core::DockWidget *DockWidget::deserialize(const LayoutSaver::DockWidget::Ptr &saved)
900{
901 auto dr = DockRegistry::self();
902 DockWidget *dw =
903 dr->dockByName(saved->uniqueName, DockRegistry::DockByNameFlag::CreateIfNotFound);
904 if (dw) {
905 if (auto guest = dw->guestView())
906 guest->setVisible(true);
907 dw->d->m_wasRestored = true;
908
909 if (dw->affinities() != saved->affinities) {
910 KDDW_ERROR("Affinity name changed from {} to {}", dw->affinities(), "; to", saved->affinities);
911 dw->d->affinities = saved->affinities;
912 }
913 }
914
915 return dw;
916}
917
918void DockWidget::setUserType(int userType)
919{
920 d->m_userType = userType;
921}
922
924{
925 return d->m_userType;
926}
927
929{
930 if (MDILayout *layout = d->mdiLayout()) {
931 if (auto wrapperDW = d->mdiDockWidgetWrapper()) {
932 // Case of using Option_MDINestable. We need to layout the actual top level DW
933 layout->moveDockWidget(wrapperDW, pos);
934 } else {
935 layout->moveDockWidget(this, pos);
936 }
937 }
938}
939
941{
942 if (MDILayout *layout = d->mdiLayout()) {
943 if (auto wrapperDW = d->mdiDockWidgetWrapper()) {
944 // Case of using Option_MDINestable. We need to layout the actual top level DW
945 layout->resizeDockWidget(wrapperDW, size);
946 } else {
947 layout->resizeDockWidget(this, size);
948 }
949 }
950}
951
953{
954 if (Group *group = d->group()) {
955 if (group->isMDI())
956 group->view()->setZOrder(z);
957 }
958}
959
961{
962 return d->m_isPersistentCentralDockWidget;
963}
964
965LayoutSaver::DockWidget::Ptr DockWidget::Private::serialize() const
966{
967 auto ptr = LayoutSaver::DockWidget::dockWidgetForName(q->uniqueName());
968 ptr->affinities = q->affinities();
969
970 return ptr;
971}
972
973void DockWidget::Private::forceClose()
974{
975 ScopedValueRollback rollback(m_isForceClosing, true);
976 close();
977}
978
979DockWidget::Private::Private(const QString &dockName, DockWidgetOptions options_,
980 LayoutSaverOptions layoutSaverOptions_, DockWidget *qq)
981
982 : name(dockName)
983 , title(dockName)
984 , q(qq)
985 , options(options_)
986 , layoutSaverOptions(layoutSaverOptions_)
987 , toggleAction(Config::self().viewFactory()->createAction(q, "toggle"))
988 , floatAction(Config::self().viewFactory()->createAction(q, "float"))
989{
990 m_toggleActionConnection = toggleAction->d->toggled.connect(
991 [this](bool enabled) {
992 if (!m_updatingToggleAction) { // guard against recursiveness
993 toggleAction->blockSignals(true); // and don't emit spurious toggle. Like when a dock
994 // widget is inserted into a tab widget it might get
995 // hide events, ignore those. The Dock Widget is open.
996 m_processingToggleAction = true;
997 toggle(enabled);
999 m_processingToggleAction = false;
1000 }
1001 });
1002
1003 m_floatActionConnection = floatAction->d->toggled.connect([this](bool checked) {
1004 if (!m_updatingFloatAction) { // guard against recursiveness
1005 q->setFloating(checked);
1006 }
1007
1008 KDDW_TRACE("Emitting DockWidget::isFloatingChanged({})", checked);
1009 isFloatingChanged.emit(checked);
1010
1011 // When floating, we remove from the sidebar
1012 if (checked && q->isOpen()) {
1013 if (Core::SideBar *sb = DockRegistry::self()->sideBarForDockWidget(q)) {
1014 sb->mainWindow()->clearSideBarOverlay(/* deleteFrame=*/false);
1015 sb->removeDockWidget(q);
1016 }
1017 }
1018 });
1019}
1020
1021void DockWidget::Private::addPlaceholderItem(Core::Item *item)
1022{
1023 assert(item);
1024 m_lastPosition->addPlaceholderItem(item);
1025}
1026
1027Position::Ptr &DockWidget::Private::lastPosition()
1028{
1029 return m_lastPosition;
1030}
1031
1032Core::Group *DockWidget::Private::group() const
1033{
1034 auto p = q->view()->parentView();
1035 while (p) {
1036 if (auto group = p->asGroupController())
1037 return group;
1038 p = p->parentView();
1039 }
1040 return nullptr;
1041}
1042
1043Core::Item *DockWidget::Private::item() const
1044{
1045 auto group = this->group();
1046 if (!group || group->inDtor())
1047 return nullptr;
1048
1049 return group->layoutItem();
1050}
1051
1052void DockWidget::Private::saveLastFloatingGeometry()
1053{
1054 if (q->isFloating() && q->isVisible()) {
1055 // It's getting docked, save last floating position
1056 lastPosition()->setLastFloatingGeometry(q->view()->d->windowGeometry());
1057 }
1058}
1059
1060void DockWidget::Private::onCloseEvent(CloseEvent *e)
1061{
1062 e->accept(); // By default we accept, means DockWidget closes
1063 if (guest) {
1064 // Give a chance for the widget to ignore
1065 Platform::instance()->sendEvent(guest.get(), e);
1066 }
1067
1068 if (e->isAccepted())
1069 close();
1070}
1071
1072void DockWidget::Private::setIsOpen(bool is)
1073{
1074 if (is == m_isOpen || m_inOpenSetter)
1075 return;
1076
1077 ScopedValueRollback guard(m_inOpenSetter, true);
1078
1079 if (!is)
1080 close();
1081
1082 m_isOpen = is;
1083
1084 if (is) {
1085 maybeRestoreToPreviousPosition();
1086
1087#ifdef KDDW_FRONTEND_QT
1088 // Transform into a FloatingWindow if this will be a regular floating dock widget.
1089 QTimer::singleShot(0, q, [this] { maybeMorphIntoFloatingWindow(); });
1090#endif
1091 }
1092
1093 updateToggleAction();
1094 updateFloatAction();
1095
1096 if (!is) {
1097 closed.emit();
1098 }
1099
1100 isOpenChanged.emit(is);
1101}
1102
1103void DockWidget::setFloatingWindowFlags(FloatingWindowFlags flags)
1104{
1105 if (floatingWindow()) {
1106 KDDW_ERROR("Call this function only before having a floating window");
1107 } else {
1108 d->m_flags = flags;
1109 }
1110}
1111
1112KDDockWidgets::FloatingWindowFlags DockWidget::floatingWindowFlags() const
1113{
1114 return d->m_flags;
1115}
1116
1118{
1119 if (auto group = d->group())
1120 return group->size();
1121
1122 return size();
1123}
1124
1125void DockWidget::resizeInLayout(int left, int top, int right, int bottom)
1126{
1127 Item *item = d->item();
1128 if (!item || item->m_inDtor)
1129 return;
1130
1131 item->requestResize(left, top, right, bottom);
1132}
1133
1134namespace {
1135Draggable *draggableForDockWidget(DockWidget *dw, bool singleTab)
1136{
1137 Group *group = dw->d->group();
1138 if (!group) {
1139 KDDW_WARN("draggableForDockWidget: Expected a group");
1140 return nullptr;
1141 }
1142
1143 if (singleTab && !group->hasSingleDockWidget()) {
1144 // Case #1: Detaching a single tab if the group has multiple tabs
1145 return group->tabBar();
1146 }
1147
1148 // Case #2: Dragging a group of tabs, via titlebar
1149 Core::TitleBar *titleBar = dw->titleBar();
1150 if (titleBar && titleBar->isVisible())
1151 return titleBar;
1152
1153 // Case #3: Dragging a group of tabs, via tab bar background. QTabBar background is actually QTabWidget (aka stack)
1154 return group->stack();
1155}
1156}
1157
1158bool DockWidget::startDragging(bool singleTab)
1159{
1160 auto dc = DragController::instance();
1161 if (dc->isDragging()) {
1162 KDDW_WARN("DockWidget::startDragging: Dragging already ongoing");
1163 return false;
1164 }
1165
1166 Draggable *const draggable = draggableForDockWidget(this, singleTab);
1167 if (!draggable) {
1168 KDDW_WARN("DockWidget::startDragging: Could not find a suitable draggable");
1169 return false;
1170 }
1171
1172 Core::TabBar *tabBar = d->group()->tabBar();
1173 if (draggable->asView() == tabBar->view()) {
1174 // minor hack, so we can reuse the normal mouse interaction code path
1175 tabBar->dptr()->m_lastPressedDockWidget = this;
1176 }
1177
1178 const Point globalPos = Platform::instance()->cursorPos();
1179
1180 View *draggedView = draggable->asView();
1181 const Point offset = draggedView->mapFromGlobal(globalPos);
1182
1183 return dc->programmaticStartDrag(draggable, globalPos, offset);
1184}
1185
1187{
1188 return d->m_wasRestored;
1189}
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:87
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 addDockWidgetToContainingWindow(KDDockWidgets::Core::DockWidget *other, KDDockWidgets::Location location, KDDockWidgets::Core::DockWidget *relativeTo=nullptr, KDDockWidgets::InitialOption initialOption={})
docks other widget into the window that contains this one. Equivalent to MainWindow::addDockWidget() ...
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 addDockWidgetAsTab(KDDockWidgets::Core::DockWidget *other, KDDockWidgets::InitialOption initialOption={})
docks other widget into this one. Tabs will be shown if not already.
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 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...
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...
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
void addTab(DockWidget *, InitialOption={})
Adds a widget into the Group's Stack.
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
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:154
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...
void addDockWidget(KDDockWidgets::Core::DockWidget *dockWidget, KDDockWidgets::Location location, KDDockWidgets::Core::DockWidget *relativeTo=nullptr, KDDockWidgets::InitialOption initialOption={})
Docks a DockWidget into this main window.
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 void setZOrder(int)
Sets the z order Not supported on all platforms.
@ CreateIfNotFound
Creates the dock widget via the user's widget factory in case it doesn't exist.
Q_INVOKABLE KDDockWidgets::Core::DockWidget * dockByName(const QString &, KDDockWidgets::DockRegistry::DockByNameFlags={}) 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.
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.
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