KDDockWidgets API Documentation 2.1
Loading...
Searching...
No Matches
core/Group.cpp
Go to the documentation of this file.
1/*
2 This file is part of KDDockWidgets.
3
4 SPDX-FileCopyrightText: 2020 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 "Group.h"
13#include "Group_p.h"
14
15#include "kddockwidgets/Config.h"
16#include "core/ViewFactory.h"
17
18#include "Controller.h"
19#include "View.h"
20#include "Layout_p.h"
21#include "FloatingWindow_p.h"
22#include "ScopedValueRollback_p.h"
23#include "Platform.h"
25
26#include "core/TitleBar.h"
27#include "core/Stack.h"
28#include "core/FloatingWindow.h"
29#include "core/MDILayout.h"
30#include "core/DropArea.h"
31#include "core/Layout.h"
32#include "core/MainWindow.h"
33#include "core/TabBar_p.h"
34
35#include "DockRegistry.h"
36#include "DockWidget_p.h"
37#include "ObjectGuard_p.h"
38
39#include "core/Logging_p.h"
40#include "core/Utils_p.h"
41#include "core/View_p.h"
42#include "core/LayoutSaver_p.h"
43#include "core/Position_p.h"
44#include "core/WidgetResizeHandler_p.h"
45#include "core/DelayedCall_p.h"
46#include "core/layouting/Item_p.h"
47
48#include "kdbindings/signal.h"
49
50#include <utility>
51
52#define MARGIN_THRESHOLD 100
53
54static int s_dbg_numFrames = 0;
55
57
58using namespace KDDockWidgets;
59using namespace KDDockWidgets::Core;
60
61namespace KDDockWidgets {
62
63static FrameOptions actualOptions(FrameOptions options)
64{
65 // Center group has custom logic for showing tabs or not
66 const bool isCentralGroup = options & FrameOption_IsCentralFrame;
67
68 if (!isCentralGroup) {
71 } else {
72 // options could have came from a JSON layout which was saved from a Config with Flag_AlwaysShowTabs
73 // If current Config doesn't have this flag then remove it here as well
74 options &= ~FrameOption_AlwaysShowsTabs;
75 }
76 }
77
78 return options;
79}
80
81static StackOptions tabWidgetOptions(FrameOptions options)
82{
83 if (options & FrameOption_NonDockable) {
86 }
87
88 return StackOption_None;
89}
90
91}
92
93Group::Group(View *parent, FrameOptions options, int userType)
94 : Controller(ViewType::Group, Config::self().viewFactory()->createGroup(this, parent))
95 , FocusScope(view())
96 , d(new Private(this, userType, actualOptions(options)))
97 , m_stack(new Core::Stack(this, tabWidgetOptions(options)))
98 , m_tabBar(m_stack->tabBar())
99 , m_titleBar(new Core::TitleBar(this))
100{
103
104 m_tabBar->dptr()->currentDockWidgetChanged.connect([this] {
106 });
107
108 setLayout(parent ? parent->asLayout() : nullptr);
110 view()->init();
111 view()->d->closeRequested.connect([this](CloseEvent *ev) { onCloseEvent(ev); });
112
113 // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
114 m_inCtor = false;
115}
116
118{
119 m_inDtor = true;
121 if (d->m_layoutItem)
122 d->m_layoutItem->unref();
123
124 delete m_resizeHandler;
125 m_resizeHandler = nullptr;
126
128
129 // Run some disconnects() too, so we don't receive signals during destruction:
130 setLayout(nullptr);
131 delete m_titleBar;
132 delete m_stack;
133 delete d;
134}
135
136void Group::onCloseEvent(CloseEvent *e)
137{
138 e->accept(); // Accepted by default (will close unless ignored)
139 const DockWidget::List docks = dockWidgets();
140 for (DockWidget *dock : docks) {
141 dock->view()->d->requestClose(e);
142 if (!e->isAccepted())
143 break; // Stop when the first dockwidget prevents closing
144 }
145}
146
148{
149 if (dt == m_layout)
150 return;
151
152 const bool wasInMainWindow = dt && isInMainWindow();
153
154 m_layout = dt;
155 delete m_resizeHandler;
156 m_resizeHandler = nullptr;
157
158 if (m_layout) {
159 if (isMDI())
160 createMDIResizeHandler();
161
162 // We keep the connect result so we don't dereference m_layout at shutdown
163 d->m_visibleWidgetCountChangedConnection =
164 m_layout->d_ptr()->visibleWidgetCountChanged.connect(&Group::updateTitleBarVisibility, this);
166 if (wasInMainWindow != isInMainWindow())
167 d->isInMainWindowChanged.emit();
168 }
169
170 d->isMDIChanged.emit();
171}
172
173void Group::renameTab(int index, const QString &title)
174{
175 m_tabBar->renameTab(index, title);
176}
177
178void Group::changeTabIcon(int index, const Icon &icon)
179{
180 m_tabBar->changeTabIcon(index, icon);
181}
182
184{
185 return dynamic_cast<Core::GroupViewInterface *>(view())->nonContentsHeight();
186}
187
189{
190 return m_stack;
191}
192
194{
195 return m_stack->tabBar();
196}
197
199{
200 if (DockWidget *dw = currentDockWidget()) {
201 m_titleBar->setTitle(dw->title());
202 m_titleBar->setIcon(dw->icon());
203
204 if (auto fw = floatingWindow()) {
205 if (fw->hasSingleGroup()) {
206 fw->updateTitleAndIcon();
207 }
208 }
209
210 setObjectName(dw->uniqueName());
211
212 } else if (currentTabIndex() != -1) {
213 KDDW_ERROR("Invalid dock widget for group. index={}", currentTabIndex());
214 }
215}
216
218{
220
221 if (!m_inCtor) { // don't call pure virtual in ctor
222 int index = indexOfDockWidget(dw);
223 renameTab(index, dw->title());
225 }
226}
227
228void Group::addTab(DockWidget *dockWidget, const InitialOption &addingOption)
229{
230 insertWidget(dockWidget, dockWidgetCount(), addingOption); // append
231
232 // The dock widget might have changed title *while* being inserted
233 // For example, if the text depends on whether it's floating or not.
234 // In that case tabbar won't notice the title change, as the titleChanged signal
235 // is emitted with the old parent still. (#468)
236 // Simply refresh title now:
237 onDockWidgetTitleChanged(dockWidget);
238}
239
240void Group::addTab(Group *group, const InitialOption &addingOption)
241{
242 if (group->isEmpty()) {
243 KDDW_ERROR("Group::addTab: group is empty. group={}", ( void * )group);
244 return;
245 }
246
247 const auto &docks = group->dockWidgets();
248 for (DockWidget *dockWidget : docks)
249 addTab(dockWidget, addingOption);
250}
251
252void Group::addTab(FloatingWindow *floatingWindow, const InitialOption &addingOption)
253{
254 assert(floatingWindow);
255 const auto groups = floatingWindow->groups();
256 for (Group *f : groups)
257 addTab(f, addingOption);
258}
259
260void Group::insertWidget(DockWidget *dockWidget, int index, const InitialOption &addingOption)
261{
262 assert(dockWidget);
263 if (containsDockWidget(dockWidget)) {
264 if (!dockWidget->isPersistentCentralDockWidget())
265 KDDW_ERROR("Group::addTab dockWidget already exists. this={} ; dockWidget={}", ( void * )this, ( void * )dockWidget);
266 return;
267 }
268 if (d->m_layoutItem)
269 dockWidget->d->addPlaceholderItem(d->m_layoutItem);
270
271 const int originalCurrentIndex = currentIndex();
272 insertDockWidget(dockWidget, index);
273
274 if (addingOption.startsHidden()) {
275 dockWidget->view()->close(); // Ensure closed.
276 } else {
277 if (hasSingleDockWidget()) {
278 setObjectName(dockWidget->uniqueName());
279
280 if (!d->m_layoutItem) {
281 // When adding the 1st dock widget of a fresh group, let's give the group the size
282 // of the dock widget, so that when adding it to the main window, the main window
283 // can use that size as the initial suggested size.
284 view()->resize(dockWidget->size());
285 }
286 } else if (addingOption.preservesCurrentTab() && originalCurrentIndex != -1) {
287 setCurrentTabIndex(originalCurrentIndex);
288 }
289
290 dockWidget->d->setIsOpen(true);
291 }
292
293 KDBindings::ScopedConnection titleChangedConnection = dockWidget->d->titleChanged.connect(
294 [this, dockWidget] { onDockWidgetTitleChanged(dockWidget); });
295
296 KDBindings::ScopedConnection iconChangedConnection = dockWidget->d->iconChanged.connect(
297 [this, dockWidget] { onDockWidgetTitleChanged(dockWidget); });
298
299 d->titleChangedConnections[dockWidget] = std::move(titleChangedConnection);
300 d->iconChangedConnections[dockWidget] = std::move(iconChangedConnection);
301}
302
304{
305 auto it = d->titleChangedConnections.find(dw);
306 if (it != d->titleChangedConnections.end())
307 d->titleChangedConnections.erase(it);
308
309 it = d->iconChangedConnections.find(dw);
310 if (it != d->iconChangedConnections.end())
311 d->iconChangedConnections.erase(it);
312
313 if (auto gvi = dynamic_cast<Core::GroupViewInterface *>(view()))
314 gvi->removeDockWidget(dw);
315}
316
318{
319 if (m_inCtor || m_inDtor)
320 return nullptr;
321
322 dockWidget->d->saveTabIndex();
323
324 Rect r = dockWidget->geometry();
325 removeWidget(dockWidget);
326
327 auto newGroup = new Group();
328 const Point globalPoint = mapToGlobal(Point(0, 0));
329 newGroup->addTab(dockWidget);
330
331 // We're potentially already dead at this point, as groups with 0 tabs auto-destruct. Don't
332 // access members from this point.
333
334 auto floatingWindow = new FloatingWindow(newGroup, {});
335 r.moveTopLeft(globalPoint);
338
339 return floatingWindow;
340}
341
343{
344 if (m_inCtor || m_inDtor)
345 return -1;
346
347 return m_tabBar->indexOfDockWidget(dw);
348}
349
351{
352 if (m_inCtor || m_inDtor)
353 return -1;
354
355 return m_tabBar->currentIndex();
356}
357
359{
360 if (m_inCtor || m_inDtor)
361 return;
362
364}
365
367{
368 if (m_inCtor || m_inDtor)
369 return;
370
372}
373
375{
376 if (m_inCtor || m_inDtor)
377 return;
378
379 dynamic_cast<Core::GroupViewInterface *>(view())->insertDockWidget(dw, index);
380 dw->d->onParentChanged();
382}
383
385{
386 if (m_inCtor || m_inDtor)
387 return nullptr;
388
389 return m_tabBar->dockWidgetAt(index);
390}
391
393{
394 if (m_inCtor || m_inDtor)
395 return nullptr;
396
397 return m_tabBar->currentDockWidget();
398}
399
401{
402 if (m_inCtor || m_inDtor)
403 return 0;
404
405 return m_stack->numDockWidgets();
406}
407
409{
410 if (isEmpty() && !isCentralGroup()) {
411 scheduleDeleteLater();
412 } else {
414
415 // We don't really keep track of the state, so emit even if the visibility didn't change. No
416 // biggie.
417 if (!(d->m_options & FrameOption_AlwaysShowsTabs))
418 d->hasTabsVisibleChanged.emit();
419
420 const DockWidget::List docks = dockWidgets();
421 for (DockWidget *dock : docks) {
422 if (!dock->inDtor())
423 dock->d->updateFloatAction();
424 }
425
426 if (auto fw = floatingWindow()) {
427 fw->dptr()->numDockWidgetsChanged.emit();
428 }
429 }
430
431 d->numDockWidgetsChanged.emit();
432}
433
435{
436 d->isFocusedChanged.emit();
437}
438
440{
441 d->focusedWidgetChanged.emit();
442}
443
445{
446 if (m_updatingTitleBar || m_beingDeleted) {
447 // To break a cyclic dependency
448 return;
449 }
450
451 ScopedValueRollback guard(m_updatingTitleBar, true);
452
453 bool visible = false;
454 if (isCentralGroup()) {
455 visible = false;
457 && hasTabsVisible()) {
458 visible = false;
459 } else if (FloatingWindow *fw = floatingWindow()) {
460 // If there's nested groups then show each Frame's title bar
461 visible = !fw->hasSingleGroup();
462 } else if (isMDIWrapper()) {
463 auto dropArea = this->mdiDropAreaWrapper();
464 visible = !dropArea->hasSingleGroup();
465 } else {
466 visible = true;
467 }
468
469 const bool wasVisible = m_titleBar->isVisible();
470 m_titleBar->setVisible(visible);
471
472 if (wasVisible != visible) {
473 d->actualTitleBarChanged.emit();
474 const auto docks = dockWidgets();
475 for (auto dw : docks)
476 dw->d->actualTitleBarChanged.emit();
477 }
478
479 if (auto fw = floatingWindow()) {
480 // Update the floating window which might be using Flag_HideTitleBarWhenTabsVisible
481 // In that case it might not show title bar depending on the number of tabs that the group
482 // has
483 fw->updateTitleBarVisibility();
484 }
485}
486
488{
489 const Vector<DockWidget *> widgets = dockWidgets();
490 for (DockWidget *dw : widgets)
491 dw->d->updateFloatAction();
492}
493
494bool Group::containsMouse(Point globalPos) const
495{
496 return rect().contains(view()->mapFromGlobal(globalPos));
497}
498
500{
501 return m_titleBar;
502}
503
505{
506 if (FloatingWindow *fw = floatingWindow()) {
507 // If there's nested groups then show each Group's title bar
508 if (fw->hasSingleGroup())
509 return fw->titleBar();
510 } else if (auto mdiDropArea = mdiDropAreaWrapper()) {
511 if (mdiDropArea->hasSingleGroup()) {
512 return mdiFrame()->titleBar();
513 }
514 }
515
516 return titleBar();
517}
518
520{
521 return m_titleBar->title();
522}
523
524Icon Group::icon() const
525{
526 return m_titleBar->icon();
527}
528
530{
531 if (m_inCtor || m_inDtor)
532 return {};
533
535 const int count = dockWidgetCount();
536 dockWidgets.reserve(count);
537 for (int i = 0; i < count; ++i)
538 dockWidgets.push_back(dockWidgetAt(i));
539
540 return dockWidgets;
541}
542
544{
545 const int count = dockWidgetCount();
546 for (int i = 0, e = count; i != e; ++i) {
547 if (dockWidget == dockWidgetAt(i))
548 return true;
549 }
550 return false;
551}
552
554{
555 // Returns the first FloatingWindow* parent in the hierarchy.
556 // However, if there's a MainWindow in the hierarchy it stops, which can
557 // happen with nested main windows.
558
559 auto p = view()->parentView();
560 while (p) {
561 if (p->is(ViewType::MainWindow))
562 return nullptr;
563
564 if (auto fw = p->asFloatingWindowController())
565 return fw;
566
567 if (p->equals(view()->rootView())) {
568 // We stop at the window. (top-levels can have parent, but we're not interested)
569 return nullptr;
570 }
571
572 p = p->parentView();
573 }
574
575 return nullptr;
576}
577
579{
580 if (hasSingleDockWidget()) {
581 KDDW_ERROR("Invalid usage, there's no tabs");
582 return;
583 }
584
585 if (!d->m_layoutItem) {
586 KDDW_DEBUG("Group::restoreToPreviousPosition: There's no previous position known");
587 return;
588 }
589
590 if (!d->m_layoutItem->isPlaceholder()) {
591 // Maybe in this case just fold the group into the placeholder, which probably has other
592 // dockwidgets which were added meanwhile. TODO
593 KDDW_DEBUG("Group::restoreToPreviousPosition: Previous position isn't a placeholder");
594 return;
595 }
596
597 d->m_layoutItem->restore(d);
598}
599
601{
602 return currentIndex();
603}
604
606{
607 const auto docks = dockWidgets();
608 for (auto dw : docks) {
609 if ((dw->options() & DockWidgetOption_NotClosable)
611 return true;
612 }
613
614 return false;
615}
616
618{
619 const auto docks = dockWidgets();
620 for (auto dw : docks) {
621 if (dw->options() & DockWidgetOption_NotDockable)
622 return true;
623 }
624
625 return false;
626}
627
628void Group::Private::setLayoutItem_impl(Item *item)
629{
630 m_layoutItem = item;
631
632 const auto docks = q->dockWidgets();
633 if (item) {
634 for (DockWidget *dw : docks)
635 dw->d->addPlaceholderItem(item);
636 } else {
637 for (DockWidget *dw : docks)
638 dw->d->lastPosition()->removePlaceholders();
639 }
640}
641
642LayoutingHost *Group::Private::host() const
643{
644 return q->m_layout ? q->m_layout->asLayoutingHost() : nullptr;
645}
646
647void Group::Private::setHost(LayoutingHost *host)
648{
649 Core::View *parent = nullptr;
650 if (auto layout = Layout::fromLayoutingHost(host)) {
651 parent = layout->view();
652 }
653
654 q->setParentView(parent);
655}
656
657Item *Group::layoutItem() const
658{
659 return d->m_layoutItem;
660}
661
663{
664 return s_dbg_numFrames;
665}
666
668{
669 return m_beingDeleted;
670}
671
673{
674 if (m_beingDeleted)
675 return false;
676
677 return alwaysShowsTabs() || dockWidgetCount() > 1;
678}
679
681{
682 if (isEmpty()) {
683 if (auto m = mainWindow())
684 return m->affinities();
685 return {};
686 } else {
687 return dockWidgetAt(0)->affinities();
688 }
689}
690
691void Group::setLayoutItem(Core::Item *item)
692{
693 d->setLayoutItem(item);
694}
695
697{
698 return m_layout && m_layout->visibleCount() == 1;
699}
700
702{
703 return d->m_options & FrameOption_IsOverlayed;
704}
705
707{
708 d->m_options &= ~FrameOption_IsOverlayed;
709}
710
712{
713 if (isInMainWindow() || isMDI())
714 return false;
715
716 return isTheOnlyGroup();
717}
718
720{
721 return floatingWindow() != nullptr;
722}
723
725{
726 return mainWindow() != nullptr;
727}
728
729Group *Group::deserialize(const LayoutSaver::Group &f)
730{
731 if (!f.isValid())
732 return nullptr;
733
734 const FrameOptions options = actualOptions(FrameOptions(f.options));
735 Group *group = nullptr;
736 const bool isPersistentCentralFrame = options & FrameOption::FrameOption_IsCentralFrame;
737
738 if (isPersistentCentralFrame) {
739 // Don't create a new Group if we're restoring the Persistent Central group (the one created
740 // by MainWindowOption_HasCentralFrame). It already exists.
741
742 if (f.mainWindowUniqueName.isEmpty()) {
743 // Can happen with older serialization formats
744 KDDW_ERROR("Group is the persistent central group but doesn't have"
745 "an associated window name");
746 } else {
747 if (MainWindow *mw = DockRegistry::self()->mainWindowByName(f.mainWindowUniqueName)) {
748 group = mw->dropArea()->centralGroup();
749 if (!group) {
750 // Doesn't happen...
751 KDDW_ERROR("Main window {} doesn't have central group", f.mainWindowUniqueName);
752 }
753 } else {
754 // Doesn't happen...
755 KDDW_ERROR("Couldn't find main window {}", f.mainWindowUniqueName);
756 }
757 }
758 }
759
760 if (!group)
761 group = new Group(nullptr, options);
762
763 group->setObjectName(f.objectName);
764
765 for (const auto &savedDock : std::as_const(f.dockWidgets)) {
766 if (DockWidget *dw = DockWidget::deserialize(savedDock)) {
767 group->addTab(dw);
768 }
769 }
770
771 group->setCurrentTabIndex(f.currentTabIndex);
772 group->view()->setGeometry(f.geometry);
773
774 return group;
775}
776
777LayoutSaver::Group Group::serialize() const
778{
779 LayoutSaver::Group group;
780 group.isNull = false;
781
782 const DockWidget::List docks = dockWidgets();
783
784 group.objectName = objectName();
785 group.geometry = geometry();
786 group.options = options();
787 group.currentTabIndex = currentTabIndex();
788 group.id = view()->d->id(); // for coorelation purposes
789
790 if (MainWindow *mw = mainWindow()) {
791 group.mainWindowUniqueName = mw->uniqueName();
792 }
793
794 for (DockWidget *dock : docks)
795 group.dockWidgets.push_back(dock->d->serialize());
796
797 if (group.currentTabIndex == -1 && !docks.isEmpty()) {
798 KDDW_ERROR("Group::serialize: Current index shouldn't be -1. Setting to 0 instead.");
799 group.currentTabIndex = 0;
800 }
801
802 return group;
803}
804
805void Group::scheduleDeleteLater()
806{
807 KDDW_TRACE("Group::scheduleDeleteLater: {}", ( void * )this);
808 m_beingDeleted = true;
809
810 if (auto item = layoutItem()) {
811 if (item->parentContainer())
812 item->turnIntoPlaceholder();
813 }
814
815 // Can't use deleteLater() here due to QTBUG-83030 (deleteLater() never delivered if
816 // triggered by a sendEvent() before event loop starts)
817 destroyLater();
818}
819
820void Group::createMDIResizeHandler()
821{
822 delete m_resizeHandler;
823 m_resizeHandler = new WidgetResizeHandler(WidgetResizeHandler::EventFilterMode::Global,
824 WidgetResizeHandler::WindowMode::MDI, view());
825
826 if (Platform::instance()->isQtQuick()) {
827 // Our C++ WidgetResizeHandler is triggered manually by MDIResizeHandlerHelper.qml's MouseArea
828 m_resizeHandler->setEventFilterStartsManually();
829
830 // MouseCursor set by QML as well
831 m_resizeHandler->setHandlesMouseCursor(false);
832 }
833}
834
836{
837 Size size = Item::hardcodedMinimumSize;
838 const auto docks = dockWidgets();
839 for (DockWidget *dw : docks) {
840 if (!dw->inDtor())
841 size = size.expandedTo(dw->view()->minSize());
842 }
843
844 return size;
845}
846
848{
849 Size size = Item::hardcodedMaximumSize;
850 const auto docks = dockWidgets();
851 for (DockWidget *dw : docks) {
852 if (dw->inDtor())
853 continue;
854 const Size dwMax = dw->view()->maxSizeHint();
855 if (size == Item::hardcodedMaximumSize) {
856 size = dwMax;
857 continue;
858 }
859
860 const bool hasMaxSize = dwMax != Item::hardcodedMaximumSize;
861 if (hasMaxSize)
862 size = dwMax.expandedTo(size);
863 }
864
865 // Interpret 0 max-size as not having one too.
866 if (size.width() == 0)
867 size.setWidth(Item::hardcodedMaximumSize.width());
868 if (size.height() == 0)
869 size.setHeight(Item::hardcodedMaximumSize.height());
870
871 return size;
872}
873
874Rect Group::dragRect() const
875{
876 Rect rect;
877 if (m_titleBar->isVisible()) {
878 rect = m_titleBar->view()->rect();
879 rect.moveTopLeft(m_titleBar->view()->mapToGlobal(Point(0, 0)));
880 }
881
882 if (rect.isValid())
883 return rect;
884
885 if (auto gvi = dynamic_cast<Core::GroupViewInterface *>(view()))
886 return gvi->dragRect();
887
888 return {};
889}
890
892{
893 return m_layout ? m_layout->mainWindow() : nullptr;
894}
895
898{
899 const DockWidget::List docks = dockWidgets();
900 return std::all_of(docks.cbegin(), docks.cend(),
901 [option](DockWidget *dw) { return dw->options() & option; });
902}
903
906{
907 const DockWidget::List docks = dockWidgets();
908 return std::any_of(docks.cbegin(), docks.cend(),
909 [option](DockWidget *dw) { return dw->options() & option; });
910}
911
913{
914 const DockWidget::List docks = dockWidgets();
915 return std::all_of(docks.cbegin(), docks.cend(),
916 [option](DockWidget *dw) { return dw->layoutSaverOptions() & option; });
917}
918
920{
921 const DockWidget::List docks = dockWidgets();
922 return std::any_of(docks.cbegin(), docks.cend(),
923 [option](DockWidget *dw) { return dw->layoutSaverOptions() & option; });
924}
925
926void Group::setAllowedResizeSides(CursorPositions sides)
927{
928 if (sides) {
929 createMDIResizeHandler();
930 m_resizeHandler->setAllowedResizeSides(sides);
931 } else {
932 delete m_resizeHandler;
933 m_resizeHandler = nullptr;
934 }
935}
936
937bool Group::isMDI() const
938{
939 return mdiLayout() != nullptr;
940}
941
943{
944 return mdiDropAreaWrapper() != nullptr;
945}
946
948{
949 if (auto dwWrapper = mdiDockWidgetWrapper()) {
950 return dwWrapper->d->group();
951 }
952
953 return nullptr;
954}
955
957{
958 if (auto dropArea = mdiDropAreaWrapper())
959 return dropArea->view()->parentView()->asDockWidgetController();
960
961 return nullptr;
962}
963
965{
966 auto p = view()->parentView();
967 auto dropArea = p ? p->asDropAreaController() : nullptr;
968 if (dropArea && dropArea->isMDIWrapper())
969 return dropArea;
970 return nullptr;
971}
972
974{
975 return m_layout ? m_layout->asMDILayout() : nullptr;
976}
977
979{
980 if (!isMDI() || dockWidgetCount() == 0)
981 return false;
982
983 if (dockWidgetCount() != 1) {
984 KDDW_ERROR("Expected a single dock widget wrapper as group child");
985 return false;
986 }
987
988 return dockWidgetAt(0)->d->isMDIWrapper();
989}
990
992{
993 return d->m_userType;
994}
995
996WidgetResizeHandler *Group::resizeHandler() const
997{
998 return m_resizeHandler;
999}
1000
1002{
1004 setLayout(parent ? parent->asLayout() : nullptr);
1005}
1006
1007FloatingWindowFlags Group::requestedFloatingWindowFlags() const
1008{
1009 const auto dockwidgets = this->dockWidgets();
1010 if (!dockwidgets.isEmpty())
1011 return dockwidgets.first()->floatingWindowFlags();
1012
1014}
1015
1016FrameOptions Core::Group::options() const
1017{
1018 return d->m_options;
1019}
1021{
1022 return d->m_options & FrameOption_AlwaysShowsTabs;
1023}
1025{
1026 return !(d->m_options & FrameOption_NonDockable);
1027}
1029{
1030 return d->m_options & FrameOption_IsCentralFrame;
1031}
1032
1033Group::Private *Group::dptr() const
1034{
1035 return d;
1036}
1037
1038LayoutingGuest *Group::asLayoutingGuest() const
1039{
1040 return d;
1041}
1042
1043Group::Private::Private(Group *qq, int userType, FrameOptions options)
1044 : q(qq)
1045 , m_userType(userType)
1046 , m_options(options)
1047{
1048 m_parentViewChangedConnection = q->Controller::dptr()->parentViewChanged.connect([this] {
1049 hostChanged.emit(host());
1050 });
1051
1052 q->view()->d->layoutInvalidated.connect([this] {
1053 if (auto item = q->layoutItem()) {
1054
1055 if (item->m_sizingInfo.minSize == minSize() && item->m_sizingInfo.maxSizeHint == maxSizeHint()) {
1056 // No point in disturbing the layout if constraints didn't change.
1057 // QTabWidget::resizeEvent for example will issue layout invalidation even if constraints haven't changed
1058 return;
1059 }
1060
1061 if (m_invalidatingLayout) {
1062 // Fixes case where we're in the middle of adding a widget to layout and that triggers
1063 // another unrelated widget to emit layoutInvalidated due to resize. It would trigger a relayout while
1064 // we were in a middle of adding a dock widget.
1065 // An example is QTabWidget::resizeEvent(), it calls updateGeometry() unconditionally.
1066 return;
1067 }
1068
1069 ScopedValueRollback guard(m_invalidatingLayout, true);
1070
1071 // Here we tell the KDDW layout that a widget change min/max sizes.
1072 // KDDW will do some resizing to honour the new min/max constraint
1073 layoutInvalidated.emit();
1074 }
1075 });
1076}
1077
1078Group::Private::~Private()
1079{
1080 m_visibleWidgetCountChangedConnection->disconnect();
1081
1082 beingDestroyed.emit();
1083}
1084
1085Core::Group *Group::fromItem(const Core::Item *item)
1086{
1087 if (!item)
1088 return nullptr;
1089
1090 if (auto guest = item->guest()) {
1091 if (auto group = dynamic_cast<Core::Group::Private *>(guest))
1092 return group->q;
1093 }
1094
1095 return nullptr;
1096}
A widget that supports an arbitrary number of splitters (called Separators) in any combination of ver...
A ScopedConnection is a RAII-style way to make sure a Connection is disconnected.
Definition signal.h:533
Singleton to allow to choose certain behaviours of the framework.
Definition Config.h:64
static Config & self()
returns the singleton Config instance
Definition Config.cpp:88
@ Flag_HideTitleBarWhenTabsVisible
Definition Config.h:89
@ Flag_AlwaysShowTabs
Always show tabs, even if there's only one,.
Definition Config.h:91
View * view() const
Returns the view associated with this controller, if any.
virtual void setParentView_impl(View *parent)
Point mapToGlobal(Point) const
The DockWidget base-class. DockWidget and Core::DockWidget are only split in two so we can share some...
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.
Vector< QString > affinities() const
Returns the affinity name. Empty by default.
Icon icon(IconPlace place=IconPlace::TitleBar) const
Returns the dock widget's titlebar, tabbar, or toggle action icon (depending on the passed place)
QString title() const
Returns the dock widget's title. This title is visible in title bars and tab bars.
bool isPersistentCentralDockWidget() const
Returns whether this dock widget is the main window persistent central widget This only applies when ...
void setSuggestedGeometry(Rect suggestedRect, SuggestedGeometryHints=SuggestedGeometryHint_None)
Equivalent to setGeometry(), but the value might be adjusted.
Allows to implement a similar functionality to QtQuick's FocusScope item, in QtWidgets.
Definition FocusScope.h:31
The interface that Frame views should implement.
void changeTabIcon(int index, const Icon &)
virtual Rect dragRect() const
bool isMDI() const
Returns whether this group is in a MDI layout Usually no, unless you're using an MDI main window.
Core::TabBar *const m_tabBar
Definition core/Group.h:336
bool hasSingleDockWidget() const
returns whether there's only 1 dock widget.
Definition core/Group.h:196
void isFocusedChangedCallback() override
reimplement in the 1st QObject derived class
bool isCentralGroup() const
returns if this widget is the central group MainWindow supports a mode where the middle group is pers...
void unoverlay()
clears the FrameOption_IsOverlayed flag. For example, if you drag a side-bar overlay,...
int indexOfDockWidget(const DockWidget *)
returns the index of the specified dock widget
DockWidget * dockWidgetAt(int index) const
Returns the dock widget at index.
Core::TitleBar * titleBar() const
Vector< DockWidget * > dockWidgets() const
void setAllowedResizeSides(CursorPositions sides)
To allow resizing the overlayed dock widget (auto-hide feature)
void removeWidget(DockWidget *)
removes a dockwidget from the group
bool isEmpty() const
returns whether there's 0 dock widgets. If not persistent then the Frame will delete itself.
Definition core/Group.h:190
bool hasNestedMDIDockWidgets() const
If this group is a MDI group (isMDI() == true), returns whether it contains nested dock widgets (Dock...
bool isInMainWindow() const
Returns whether this group is docked inside a MainWindow.
void setLayout(Layout *)
Sets the Layout which this group is in.
bool alwaysShowsTabs() const
whether the tab widget will always show tabs, even if there's only 1 dock widget
bool isFloating() const
Returns whether this group is floating. A floating group isn't attached to any other MainWindow,...
Group(View *parent=nullptr, FrameOptions=FrameOption_None, int userType=0)
void setCurrentTabIndex(int index)
sets the current tab index
void insertWidget(DockWidget *, int index, const InitialOption &={})
Inserts a widget into the Group's TabWidget at index.
DropArea * mdiDropAreaWrapper() const
If this is an MDI wrapper group, return the DropArea MDI wrapper.
bool isMDIWrapper() const
Returns whether this group was created automatically just for the purpose of supporting DockWidgetOpt...
void renameTab(int index, const QString &)
bool containsMouse(Point globalPos) const
WidgetResizeHandler * resizeHandler() const
Returns the resize handler. Used mostly in MDI mode.
bool isDockable() const
Returns whether you can DND dock widgets over this group and tab into it.
Core::Item * layoutItem() const
returns the layout item that either contains this Frame in the layout or is a placeholder
void setCurrentDockWidget(DockWidget *)
Sets the specified dock widget to be the current tab.
FloatingWindow * detachTab(DockWidget *)
detaches this dock widget
MainWindow * mainWindow() const
Returns the main window this group is in. nullptr if not inside a main window.
Core::Stack *const m_stack
Definition core/Group.h:335
static Core::Group * fromItem(const Core::Item *)
Returns the group that's in the specified item.
int currentIndex() const
returns the index of the current tab
DockWidget * mdiDockWidgetWrapper() const
If this is an MDI wrapper group, return the DockWidget MDI wrapper.
static Group * deserialize(const LayoutSaver::Group &)
Vector< QString > affinities() const
static int dbg_numFrames()
For tests-only. Returns the number of Frame instances in the whole application.
Size dockWidgetsMinSize() const
Returns the minimum size of the dock widgets. This might be slightly smaller than Frame::minSize() du...
bool hasTabsVisible() const
returns true if tabs are visible
void onDockWidgetTitleChanged(DockWidget *)
FrameOptions options() const
Core::TitleBar *const m_titleBar
Definition core/Group.h:337
void addTab(DockWidget *, const InitialOption &={})
Adds a widget into the Group's Stack.
FloatingWindowFlags requestedFloatingWindowFlags() const
User requested floating window flags for when this group floats.
DockWidget * currentDockWidget() const
Returns the current dock widget.
Core::Stack * stack() const
returns the tab widget
bool isOverlayed() const
Returns whether this group is overlayed on top of the MainWindow (auto-hide feature);.
FloatingWindow * floatingWindow() const
returns the FloatingWindow this group is in, if any
Core::TitleBar * actualTitleBar() const
bool isInFloatingWindow() const
Returns whether this group is in a FloatingWindow, as opposed to MainWindow.
Group * mdiFrame() const
If this group is an MDI wrapper, returns the MDI group. That is the group you actually drag inside th...
bool anyDockWidgetsHas(DockWidgetOption) const
Returns whether at least one dock widget has the specified option set.
Size biggestDockWidgetMaxSize() const
Returns the biggest combined maxSize of all dock widgets.
void restoreToPreviousPosition()
Puts the Group back in its previous main window position.
Core::TabBar * tabBar() const
LayoutingGuest * asLayoutingGuest() const
int userType() const
See Core::DockWidget::userType()
bool containsDockWidget(DockWidget *w) const
returns whether the dockwidget w is inside this group
void focusedWidgetChangedCallback() override
bool beingDeletedLater() const
Returns whether a deleteLater has already been issued.
int dockWidgetCount() const
returns the number of dock widgets inside the group
virtual ~Group() override
bool allDockWidgetsHave(DockWidgetOption) const
Returns whether all dock widgets have the specified option set.
void insertDockWidget(DockWidget *, int index)
Inserts a dock widget into the specified index.
void setParentView_impl(View *parent) override
void setLayoutItem(Core::Item *item)
sets the layout item that either contains this Group in the layout or is a placeholder
LayoutSaver::Group serialize() const
MDILayout * mdiLayout() const
Returns the MDI layout. Or nullptr if this group isn't in a MDI layout.
The widget (QWidget or QQuickItem) which holds a layout of dock widgets.
Definition Layout.h:57
Layout::Private * d_ptr()
Definition Layout.cpp:372
Core::MDILayout * asMDILayout() const
Definition Layout.cpp:342
int visibleCount() const
Returns the number of visible Items in this layout. Which is count minus placeholderCount.
Definition Layout.cpp:214
static Layout * fromLayoutingHost(LayoutingHost *)
Definition Layout.cpp:396
Core::MainWindow * mainWindow(bool honourNesting=false) const
Definition Layout.cpp:73
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 bool isProcessingAppQuitEvent() const =0
Returns whether we're processing a Event::Quit.
static Platform * instance()
Returns the platform singleton.
int numDockWidgets() const
returns the number of dock widgets in this TabWidget
Core::TabBar * tabBar() const
Returns the tab bar.
void renameTab(int index, const QString &)
rename's the tab's text
int currentIndex() const
Returns the index of the current tab.
DockWidget * currentDockWidget() const
Returns the current dock widget.
void setCurrentIndex(int index)
DockWidget * dockWidgetAt(int index) const
returns the dock widgets at tab number index
int indexOfDockWidget(const Core::DockWidget *dw) const
Returns the tab index of the specified dock widget.
void setCurrentDockWidget(DockWidget *dw)
void changeTabIcon(int index, const Icon &)
change the tab's icon
void setIcon(const Icon &icon)
void setTitle(const QString &title)
virtual void show()=0
virtual std::shared_ptr< View > parentView() const =0
Returns the gui element's parent. Like QWidget::parentWidget()
virtual Point mapToGlobal(Point) const =0
virtual bool close()=0
Core::Layout * asLayout() const
virtual void setGeometry(Rect)=0
void unregisterGroup(Core::Group *)
static DockRegistry * self()
void registerGroup(Core::Group *)
A MultiSplitter with support for drop indicators when hovering over.
static int s_dbg_numFrames
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_GeometryIsFromDocked
static FrameOptions actualOptions(FrameOptions options)
DockWidgetOption
DockWidget options to pass at construction time.
@ DockWidgetOption_NotClosable
The DockWidget can't be closed on the [x], only programmatically.
@ DockWidgetOption_NotDockable
The DockWidget can't be docked, it's always floating.
LayoutSaverOption
Options which will affect LayoutSaver save/restore.
static StackOptions tabWidgetOptions(FrameOptions options)
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