KDDockWidgets API Documentation 2.0
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
56using namespace KDDockWidgets;
57using namespace KDDockWidgets::Core;
58
59namespace KDDockWidgets {
60
61static FrameOptions actualOptions(FrameOptions options)
62{
63 const bool isCentralGroup = options & FrameOption_IsCentralFrame;
64 if (!isCentralGroup && (Config::self().flags() & Config::Flag_AlwaysShowTabs))
66
67 return options;
68}
69
70static StackOptions tabWidgetOptions(FrameOptions options)
71{
72 if (options & FrameOption_NonDockable) {
75 }
76
77 return StackOption_None;
78}
79
80}
81
82Group::Group(View *parent, FrameOptions options, int userType)
83 : Controller(ViewType::Frame, Config::self().viewFactory()->createGroup(this, parent))
84 , FocusScope(view())
85 , d(new Private(this, userType, actualOptions(options)))
86 , m_stack(new Core::Stack(this, tabWidgetOptions(options)))
87 , m_tabBar(m_stack->tabBar())
88 , m_titleBar(new Core::TitleBar(this))
89{
92
93 m_tabBar->dptr()->currentDockWidgetChanged.connect([this] {
95 });
96
97 setLayout(parent ? parent->asLayout() : nullptr);
99 view()->init();
100 view()->d->closeRequested.connect([this](CloseEvent *ev) { onCloseEvent(ev); });
101
102 // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
103 m_inCtor = false;
104}
105
107{
108 m_inDtor = true;
110 if (d->m_layoutItem)
111 d->m_layoutItem->unref();
112
113 delete m_resizeHandler;
114 m_resizeHandler = nullptr;
115
117
118 // Run some disconnects() too, so we don't receive signals during destruction:
119 setLayout(nullptr);
120 delete m_titleBar;
121 delete m_stack;
122 delete d;
123}
124
125void Group::onCloseEvent(CloseEvent *e)
126{
127 e->accept(); // Accepted by default (will close unless ignored)
128 const DockWidget::List docks = dockWidgets();
129 for (DockWidget *dock : docks) {
130 dock->view()->d->requestClose(e);
131 if (!e->isAccepted())
132 break; // Stop when the first dockwidget prevents closing
133 }
134}
135
137{
138 if (dt == m_layout)
139 return;
140
141 const bool wasInMainWindow = dt && isInMainWindow();
142
143 m_layout = dt;
144 delete m_resizeHandler;
145 m_resizeHandler = nullptr;
146
147 if (m_layout) {
148 if (isMDI())
149 m_resizeHandler = new WidgetResizeHandler(WidgetResizeHandler::EventFilterMode::Global,
150 WidgetResizeHandler::WindowMode::MDI, view());
151
152 // We keep the connect result so we don't dereference m_layout at shutdown
153 d->m_visibleWidgetCountChangedConnection =
154 m_layout->d_ptr()->visibleWidgetCountChanged.connect(&Group::updateTitleBarVisibility, this);
156 if (wasInMainWindow != isInMainWindow())
157 d->isInMainWindowChanged.emit();
158 }
159
160 d->isMDIChanged.emit();
161}
162
163void Group::renameTab(int index, const QString &title)
164{
165 m_tabBar->renameTab(index, title);
166}
167
168void Group::changeTabIcon(int index, const Icon &icon)
169{
170 m_tabBar->changeTabIcon(index, icon);
171}
172
174{
175 return dynamic_cast<Core::GroupViewInterface *>(view())->nonContentsHeight();
176}
177
179{
180 return m_stack;
181}
182
184{
185 return m_stack->tabBar();
186}
187
189{
190 if (DockWidget *dw = currentDockWidget()) {
191 m_titleBar->setTitle(dw->title());
192 m_titleBar->setIcon(dw->icon());
193
194 if (auto fw = floatingWindow()) {
195 if (fw->hasSingleFrame()) {
196 fw->updateTitleAndIcon();
197 }
198 }
199
200 setObjectName(dw->uniqueName());
201
202 } else if (currentTabIndex() != -1) {
203 KDDW_ERROR("Invalid dock widget for group. index={}", currentTabIndex());
204 }
205}
206
208{
210
211 if (!m_inCtor) { // don't call pure virtual in ctor
212 int index = indexOfDockWidget(dw);
213 renameTab(index, dw->title());
215 }
216}
217
218void Group::addTab(DockWidget *dockWidget, InitialOption addingOption)
219{
220 insertWidget(dockWidget, dockWidgetCount(), addingOption); // append
221}
222
223void Group::addTab(Group *group, InitialOption addingOption)
224{
225 if (group->isEmpty()) {
226 KDDW_ERROR("Group::addTab: group is empty. group={}", ( void * )group);
227 return;
228 }
229
230 const auto &docks = group->dockWidgets();
231 for (DockWidget *dockWidget : docks)
232 addTab(dockWidget, addingOption);
233}
234
235void Group::addTab(FloatingWindow *floatingWindow, InitialOption addingOption)
236{
237 assert(floatingWindow);
238 const auto groups = floatingWindow->groups();
239 for (Group *f : groups)
240 addTab(f, addingOption);
241}
242
243void Group::insertWidget(DockWidget *dockWidget, int index, InitialOption addingOption)
244{
245 assert(dockWidget);
246 if (containsDockWidget(dockWidget)) {
247 if (!dockWidget->isPersistentCentralDockWidget())
248 KDDW_ERROR("Group::addTab dockWidget already exists. this={} ; dockWidget={}", ( void * )this, ( void * )dockWidget);
249 return;
250 }
251 if (d->m_layoutItem)
252 dockWidget->d->addPlaceholderItem(d->m_layoutItem);
253
254 const int originalCurrentIndex = currentIndex();
255 insertDockWidget(dockWidget, index);
256
257 if (addingOption.startsHidden()) {
258 dockWidget->view()->close(); // Ensure closed.
259 } else {
260 if (hasSingleDockWidget()) {
261 setObjectName(dockWidget->uniqueName());
262
263 if (!d->m_layoutItem) {
264 // When adding the 1st dock widget of a fresh group, let's give the group the size
265 // of the dock widget, so that when adding it to the main window, the main window
266 // can use that size as the initial suggested size.
267 view()->resize(dockWidget->size());
268 }
269 } else if (addingOption.preservesCurrentTab() && originalCurrentIndex != -1) {
270 setCurrentTabIndex(originalCurrentIndex);
271 }
272
273 dockWidget->d->setIsOpen(true);
274 }
275
276 KDBindings::ScopedConnection titleChangedConnection = dockWidget->d->titleChanged.connect(
277 [this, dockWidget] { onDockWidgetTitleChanged(dockWidget); });
278
279 KDBindings::ScopedConnection iconChangedConnection = dockWidget->d->iconChanged.connect(
280 [this, dockWidget] { onDockWidgetTitleChanged(dockWidget); });
281
282 d->titleChangedConnections[dockWidget] = std::move(titleChangedConnection);
283 d->iconChangedConnections[dockWidget] = std::move(iconChangedConnection);
284}
285
287{
288 auto it = d->titleChangedConnections.find(dw);
289 if (it != d->titleChangedConnections.end())
290 d->titleChangedConnections.erase(it);
291
292 it = d->iconChangedConnections.find(dw);
293 if (it != d->iconChangedConnections.end())
294 d->iconChangedConnections.erase(it);
295
296 if (auto gvi = dynamic_cast<Core::GroupViewInterface *>(view()))
297 gvi->removeDockWidget(dw);
298}
299
301{
302 if (m_inCtor || m_inDtor)
303 return nullptr;
304
305 dockWidget->d->saveTabIndex();
306
307 Rect r = dockWidget->geometry();
308 removeWidget(dockWidget);
309
310 auto newGroup = new Group();
311 const Point globalPoint = mapToGlobal(Point(0, 0));
312 newGroup->addTab(dockWidget);
313
314 // We're potentially already dead at this point, as groups with 0 tabs auto-destruct. Don't
315 // access members from this point.
316
317 auto floatingWindow = new FloatingWindow(newGroup, {});
318 r.moveTopLeft(globalPoint);
321
322 return floatingWindow;
323}
324
326{
327 if (m_inCtor || m_inDtor)
328 return -1;
329
330 return m_tabBar->indexOfDockWidget(dw);
331}
332
334{
335 if (m_inCtor || m_inDtor)
336 return -1;
337
338 return m_tabBar->currentIndex();
339}
340
342{
343 if (m_inCtor || m_inDtor)
344 return;
345
347}
348
350{
351 if (m_inCtor || m_inDtor)
352 return;
353
355}
356
358{
359 if (m_inCtor || m_inDtor)
360 return;
361
362 dynamic_cast<Core::GroupViewInterface *>(view())->insertDockWidget(dw, index);
363 dw->d->onParentChanged();
364}
365
367{
368 if (m_inCtor || m_inDtor)
369 return nullptr;
370
371 return m_tabBar->dockWidgetAt(index);
372}
373
375{
376 if (m_inCtor || m_inDtor)
377 return nullptr;
378
379 return m_tabBar->currentDockWidget();
380}
381
383{
384 if (m_inCtor || m_inDtor)
385 return 0;
386
387 return m_stack->numDockWidgets();
388}
389
391{
392 if (isEmpty() && !isCentralFrame()) {
393 scheduleDeleteLater();
394 } else {
396
397 // We don't really keep track of the state, so emit even if the visibility didn't change. No
398 // biggie.
399 if (!(d->m_options & FrameOption_AlwaysShowsTabs))
400 d->hasTabsVisibleChanged.emit();
401
402 const DockWidget::List docks = dockWidgets();
403 for (DockWidget *dock : docks) {
404 if (!dock->inDtor())
405 dock->d->updateFloatAction();
406 }
407
408 if (auto fw = floatingWindow()) {
409 fw->dptr()->numDockWidgetsChanged.emit();
410 }
411 }
412
413 d->numDockWidgetsChanged.emit();
414}
415
417{
418 d->isFocusedChanged.emit();
419}
420
422{
423 d->focusedWidgetChanged.emit();
424}
425
427{
428 if (m_updatingTitleBar || m_beingDeleted) {
429 // To break a cyclic dependency
430 return;
431 }
432
433 ScopedValueRollback guard(m_updatingTitleBar, true);
434
435 bool visible = false;
436 if (isCentralFrame()) {
437 visible = false;
439 && hasTabsVisible()) {
440 visible = false;
441 } else if (FloatingWindow *fw = floatingWindow()) {
442 // If there's nested groups then show each Frame's title bar
443 visible = !fw->hasSingleFrame();
444 } else if (isMDIWrapper()) {
445 auto dropArea = this->mdiDropAreaWrapper();
446 visible = !dropArea->hasSingleFrame();
447 } else {
448 visible = true;
449 }
450
451 const bool wasVisible = m_titleBar->isVisible();
452 m_titleBar->setVisible(visible);
453
454 if (wasVisible != visible) {
455 d->actualTitleBarChanged.emit();
456 const auto docks = dockWidgets();
457 for (auto dw : docks)
458 dw->d->actualTitleBarChanged.emit();
459 }
460
461 if (auto fw = floatingWindow()) {
462 // Update the floating window which might be using Flag_HideTitleBarWhenTabsVisible
463 // In that case it might not show title bar depending on the number of tabs that the group
464 // has
465 fw->updateTitleBarVisibility();
466 }
467}
468
470{
471 const Vector<DockWidget *> widgets = dockWidgets();
472 for (DockWidget *dw : widgets)
473 dw->d->updateFloatAction();
474}
475
476bool Group::containsMouse(Point globalPos) const
477{
478 return rect().contains(view()->mapFromGlobal(globalPos));
479}
480
482{
483 return m_titleBar;
484}
485
487{
488 if (FloatingWindow *fw = floatingWindow()) {
489 // If there's nested groups then show each Group's title bar
490 if (fw->hasSingleFrame())
491 return fw->titleBar();
492 } else if (auto mdiDropArea = mdiDropAreaWrapper()) {
493 if (mdiDropArea->hasSingleFrame()) {
494 return mdiFrame()->titleBar();
495 }
496 }
497
498 return titleBar();
499}
500
502{
503 return m_titleBar->title();
504}
505
506Icon Group::icon() const
507{
508 return m_titleBar->icon();
509}
510
512{
513 if (m_inCtor || m_inDtor)
514 return {};
515
517 const int count = dockWidgetCount();
518 dockWidgets.reserve(count);
519 for (int i = 0; i < count; ++i)
520 dockWidgets.push_back(dockWidgetAt(i));
521
522 return dockWidgets;
523}
524
526{
527 const int count = dockWidgetCount();
528 for (int i = 0, e = count; i != e; ++i) {
529 if (dockWidget == dockWidgetAt(i))
530 return true;
531 }
532 return false;
533}
534
536{
537 // Returns the first FloatingWindow* parent in the hierarchy.
538 // However, if there's a MainWindow in the hierarchy it stops, which can
539 // happen with nested main windows.
540
541 auto p = view()->parentView();
542 while (p) {
543 if (p->is(ViewType::MainWindow))
544 return nullptr;
545
546 if (auto fw = p->asFloatingWindowController())
547 return fw;
548
549 if (p->equals(view()->rootView())) {
550 // We stop at the window. (top-levels can have parent, but we're not interested)
551 return nullptr;
552 }
553
554 p = p->parentView();
555 }
556
557 return nullptr;
558}
559
561{
562 if (hasSingleDockWidget()) {
563 KDDW_ERROR("Invalid usage, there's no tabs");
564 return;
565 }
566
567 if (!d->m_layoutItem) {
568 KDDW_DEBUG("Group::restoreToPreviousPosition: There's no previous position known");
569 return;
570 }
571
572 if (!d->m_layoutItem->isPlaceholder()) {
573 // Maybe in this case just fold the group into the placeholder, which probably has other
574 // dockwidgets which were added meanwhile. TODO
575 KDDW_DEBUG("Group::restoreToPreviousPosition: Previous position isn't a placeholder");
576 return;
577 }
578
579 d->m_layoutItem->restore(d);
580}
581
583{
584 return currentIndex();
585}
586
588{
589 const auto docks = dockWidgets();
590 for (auto dw : docks) {
591 if ((dw->options() & DockWidgetOption_NotClosable)
593 return true;
594 }
595
596 return false;
597}
598
600{
601 const auto docks = dockWidgets();
602 for (auto dw : docks) {
603 if (dw->options() & DockWidgetOption_NotDockable)
604 return true;
605 }
606
607 return false;
608}
609
610void Group::Private::setLayoutItem_impl(Item *item)
611{
612 m_layoutItem = item;
613
614 const auto docks = q->dockWidgets();
615 if (item) {
616 for (DockWidget *dw : docks)
617 dw->d->addPlaceholderItem(item);
618 } else {
619 for (DockWidget *dw : docks)
620 dw->d->lastPosition()->removePlaceholders();
621 }
622}
623
624LayoutingHost *Group::Private::host() const
625{
626 return q->m_layout ? q->m_layout->asLayoutingHost() : nullptr;
627}
628
629void Group::Private::setHost(LayoutingHost *host)
630{
631 Core::View *parent = nullptr;
632 if (auto layout = Layout::fromLayoutingHost(host)) {
633 parent = layout->view();
634 }
635
636 q->setParentView(parent);
637}
638
639Item *Group::layoutItem() const
640{
641 return d->m_layoutItem;
642}
643
645{
646 return s_dbg_numFrames;
647}
648
650{
651 return m_beingDeleted;
652}
653
655{
656 if (m_beingDeleted)
657 return false;
658
659 return alwaysShowsTabs() || dockWidgetCount() > 1;
660}
661
663{
664 if (isEmpty()) {
665 if (auto m = mainWindow())
666 return m->affinities();
667 return {};
668 } else {
669 return dockWidgetAt(0)->affinities();
670 }
671}
672
673void Group::setLayoutItem(Core::Item *item)
674{
675 d->setLayoutItem(item);
676}
677
679{
680 return m_layout && m_layout->visibleCount() == 1;
681}
682
684{
685 return d->m_options & FrameOption_IsOverlayed;
686}
687
689{
690 d->m_options &= ~FrameOption_IsOverlayed;
691}
692
694{
695 if (isInMainWindow() || isMDI())
696 return false;
697
698 return isTheOnlyGroup();
699}
700
702{
703 return floatingWindow() != nullptr;
704}
705
707{
708 return mainWindow() != nullptr;
709}
710
711Group *Group::deserialize(const LayoutSaver::Group &f)
712{
713 if (!f.isValid())
714 return nullptr;
715
716 const FrameOptions options = FrameOptions(f.options);
717 Group *group = nullptr;
718 const bool isPersistentCentralFrame = options & FrameOption::FrameOption_IsCentralFrame;
719
720 if (isPersistentCentralFrame) {
721 // Don't create a new Group if we're restoring the Persistent Central group (the one created
722 // by MainWindowOption_HasCentralFrame). It already exists.
723
724 if (f.mainWindowUniqueName.isEmpty()) {
725 // Can happen with older serialization formats
726 KDDW_ERROR("Group is the persistent central group but doesn't have"
727 "an associated window name");
728 } else {
729 if (MainWindow *mw = DockRegistry::self()->mainWindowByName(f.mainWindowUniqueName)) {
730 group = mw->dropArea()->centralGroup();
731 if (!group) {
732 // Doesn't happen...
733 KDDW_ERROR("Main window {} doesn't have central group", f.mainWindowUniqueName);
734 }
735 } else {
736 // Doesn't happen...
737 KDDW_ERROR("Couldn't find main window {}", f.mainWindowUniqueName);
738 }
739 }
740 }
741
742 if (!group)
743 group = new Group(nullptr, options);
744
745 group->setObjectName(f.objectName);
746
747 for (const auto &savedDock : std::as_const(f.dockWidgets)) {
748 if (DockWidget *dw = DockWidget::deserialize(savedDock)) {
749 group->addTab(dw);
750 }
751 }
752
753 group->setCurrentTabIndex(f.currentTabIndex);
754 group->view()->setGeometry(f.geometry);
755
756 return group;
757}
758
759LayoutSaver::Group Group::serialize() const
760{
761 LayoutSaver::Group group;
762 group.isNull = false;
763
764 const DockWidget::List docks = dockWidgets();
765
766 group.objectName = objectName();
767 group.geometry = geometry();
768 group.options = options();
769 group.currentTabIndex = currentTabIndex();
770 group.id = view()->d->id(); // for coorelation purposes
771
772 if (MainWindow *mw = mainWindow()) {
773 group.mainWindowUniqueName = mw->uniqueName();
774 }
775
776 for (DockWidget *dock : docks)
777 group.dockWidgets.push_back(dock->d->serialize());
778
779 if (group.currentTabIndex == -1 && !docks.isEmpty()) {
780 KDDW_ERROR("Group::serialize: Current index shouldn't be -1. Setting to 0 instead.");
781 group.currentTabIndex = 0;
782 }
783
784 return group;
785}
786
787void Group::scheduleDeleteLater()
788{
789 KDDW_TRACE("Group::scheduleDeleteLater: {}", ( void * )this);
790 m_beingDeleted = true;
791
792 // Can't use deleteLater() here due to QTBUG-83030 (deleteLater() never delivered if
793 // triggered by a sendEvent() before event loop starts)
794 destroyLater();
795}
796
798{
799 Size size = Item::hardcodedMinimumSize;
800 const auto docks = dockWidgets();
801 for (DockWidget *dw : docks) {
802 if (!dw->inDtor())
803 size = size.expandedTo(dw->view()->minSize());
804 }
805
806
807 return size;
808}
809
811{
812 Size size = Item::hardcodedMaximumSize;
813 const auto docks = dockWidgets();
814 for (DockWidget *dw : docks) {
815 if (dw->inDtor())
816 continue;
817 const Size dwMax = dw->view()->maxSizeHint();
818 if (size == Item::hardcodedMaximumSize) {
819 size = dwMax;
820 continue;
821 }
822
823 const bool hasMaxSize = dwMax != Item::hardcodedMaximumSize;
824 if (hasMaxSize)
825 size = dwMax.expandedTo(size);
826 }
827
828 // Interpret 0 max-size as not having one too.
829 if (size.width() == 0)
830 size.setWidth(Item::hardcodedMaximumSize.width());
831 if (size.height() == 0)
832 size.setHeight(Item::hardcodedMaximumSize.height());
833
834 return size;
835}
836
837Rect Group::dragRect() const
838{
839 Rect rect;
840 if (m_titleBar->isVisible()) {
841 rect = m_titleBar->view()->rect();
842 rect.moveTopLeft(m_titleBar->view()->mapToGlobal(Point(0, 0)));
843 }
844
845 if (rect.isValid())
846 return rect;
847
848 if (auto gvi = dynamic_cast<Core::GroupViewInterface *>(view()))
849 return gvi->dragRect();
850
851 return {};
852}
853
855{
856 return m_layout ? m_layout->mainWindow() : nullptr;
857}
858
861{
862 const DockWidget::List docks = dockWidgets();
863 return std::all_of(docks.cbegin(), docks.cend(),
864 [option](DockWidget *dw) { return dw->options() & option; });
865}
866
869{
870 const DockWidget::List docks = dockWidgets();
871 return std::any_of(docks.cbegin(), docks.cend(),
872 [option](DockWidget *dw) { return dw->options() & option; });
873}
874
876{
877 const DockWidget::List docks = dockWidgets();
878 return std::all_of(docks.cbegin(), docks.cend(),
879 [option](DockWidget *dw) { return dw->layoutSaverOptions() & option; });
880}
881
883{
884 const DockWidget::List docks = dockWidgets();
885 return std::any_of(docks.cbegin(), docks.cend(),
886 [option](DockWidget *dw) { return dw->layoutSaverOptions() & option; });
887}
888
889void Group::setAllowedResizeSides(CursorPositions sides)
890{
891 if (sides) {
892 delete m_resizeHandler;
893 m_resizeHandler = new WidgetResizeHandler(WidgetResizeHandler::EventFilterMode::Global,
894 WidgetResizeHandler::WindowMode::MDI, view());
895 m_resizeHandler->setAllowedResizeSides(sides);
896 } else {
897 delete m_resizeHandler;
898 m_resizeHandler = nullptr;
899 }
900}
901
902bool Group::isMDI() const
903{
904 return mdiLayout() != nullptr;
905}
906
908{
909 return mdiDropAreaWrapper() != nullptr;
910}
911
913{
914 if (auto dwWrapper = mdiDockWidgetWrapper()) {
915 return dwWrapper->d->group();
916 }
917
918 return nullptr;
919}
920
922{
923 if (auto dropArea = mdiDropAreaWrapper())
924 return dropArea->view()->parentView()->asDockWidgetController();
925
926 return nullptr;
927}
928
930{
931 auto p = view()->parentView();
932 auto dropArea = p ? p->asDropAreaController() : nullptr;
933 if (dropArea && dropArea->isMDIWrapper())
934 return dropArea;
935 return nullptr;
936}
937
939{
940 return m_layout ? m_layout->asMDILayout() : nullptr;
941}
942
944{
945 if (!isMDI() || dockWidgetCount() == 0)
946 return false;
947
948 if (dockWidgetCount() != 1) {
949 KDDW_ERROR("Expected a single dock widget wrapper as group child");
950 return false;
951 }
952
953 return dockWidgetAt(0)->d->isMDIWrapper();
954}
955
957{
958 return d->m_userType;
959}
960
961WidgetResizeHandler *Group::resizeHandler() const
962{
963 return m_resizeHandler;
964}
965
967{
969 setLayout(parent ? parent->asLayout() : nullptr);
970}
971
972FloatingWindowFlags Group::requestedFloatingWindowFlags() const
973{
974 const auto dockwidgets = this->dockWidgets();
975 if (!dockwidgets.isEmpty())
976 return dockwidgets.first()->floatingWindowFlags();
977
979}
980
981FrameOptions Core::Group::options() const
982{
983 return d->m_options;
984}
986{
987 return d->m_options & FrameOption_AlwaysShowsTabs;
988}
990{
991 return !(d->m_options & FrameOption_NonDockable);
992}
994{
995 return d->m_options & FrameOption_IsCentralFrame;
996}
997
998Group::Private *Group::dptr() const
999{
1000 return d;
1001}
1002
1003LayoutingGuest *Group::asLayoutingGuest() const
1004{
1005 return d;
1006}
1007
1008Group::Private::Private(Group *qq, int userType, FrameOptions options)
1009 : q(qq)
1010 , m_userType(userType)
1011 , m_options(options)
1012{
1013 m_parentViewChangedConnection = q->Controller::dptr()->parentViewChanged.connect([this] {
1014 hostChanged.emit(host());
1015 });
1016
1017 q->view()->d->layoutInvalidated.connect([this] { layoutInvalidated.emit(); });
1018}
1019
1020Group::Private::~Private()
1021{
1022 m_visibleWidgetCountChangedConnection->disconnect();
1023
1024 beingDestroyed.emit();
1025}
1026
1027Core::Group *Group::fromItem(const Core::Item *item)
1028{
1029 if (!item)
1030 return nullptr;
1031
1032 if (auto guest = item->guest()) {
1033 if (auto group = dynamic_cast<Core::Group::Private *>(guest))
1034 return group->q;
1035 }
1036
1037 return nullptr;
1038}
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:87
@ 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 isCentralFrame() const
returns if this widget is the central group MainWindow supports a mode where the middle group is pers...
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:335
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.
void isFocusedChangedCallback() override
reimplement in the 1st QObject derived class
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)
Usually we do resize via the native window manager, but if a widget is docked like in MDI mode,...
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
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 DockWidget::O...
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:334
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:336
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 insertWidget(DockWidget *, int index, InitialOption={})
Inserts a widget into the Group's TabWidget at index.
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:375
Core::MDILayout * asMDILayout() const
Definition Layout.cpp:345
int visibleCount() const
Returns the number of visible Items in this layout. Which is count minus placeholderCount.
Definition Layout.cpp:217
static Layout * fromLayoutingHost(LayoutingHost *)
Definition Layout.cpp:399
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