KDDockWidgets API Documentation 2.0
Loading...
Searching...
No Matches
core/TitleBar.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 "TitleBar.h"
13#include "TitleBar_p.h"
14#include "Config.h"
15#include "ViewFactory.h"
16#include "View.h"
17#include "WindowBeingDragged_p.h"
18#include "Utils_p.h"
19#include "Logging_p.h"
20#include "Group_p.h"
21
23#include "DockWidget_p.h"
24#include "FloatingWindow.h"
25#include "DockRegistry.h"
26#include "FloatingWindow_p.h"
27#include "TabBar.h"
28#include "MainWindow.h"
29#include "MDILayout.h"
30#include "Stack.h"
31
32#ifdef KDDW_FRONTEND_QT
33#include <QTimer>
34#endif
35
36#include <utility>
37
38using namespace KDDockWidgets;
39using namespace KDDockWidgets::Core;
40
41
43 : Controller(
45 Config::self().viewFactory()->createTitleBar(this, parent ? parent->view() : nullptr))
46 , Draggable(view())
47 , d(new Private())
48 , m_group(parent)
49 , m_floatingWindow(nullptr)
50 , m_supportsAutoHide((Config::self().flags() & Config::Flag_AutoHideSupport) == Config::Flag_AutoHideSupport)
51 , m_isStandalone(false)
52{
53 init();
54 d->numDockWidgetsChangedConnection = m_group->dptr()->numDockWidgetsChanged.connect([this] {
55 updateCloseButton();
56 d->numDockWidgetsChanged.emit();
57 });
58
59 d->isFocusedChangedConnection = m_group->dptr()->isFocusedChanged.connect([this] {
60 d->isFocusedChanged.emit();
61 });
62
63 d->isInMainWindowChangedConnection = m_group->dptr()->isInMainWindowChanged.connect([this] {
64 updateAutoHideButton();
65 });
66}
67
69 : Controller(
71 Config::self().viewFactory()->createTitleBar(this, parent ? parent->view() : nullptr))
72 , Draggable(view())
73 , d(new Private())
74 , m_group(nullptr)
75 , m_floatingWindow(parent)
76 , m_supportsAutoHide((Config::self().flags() & Config::Flag_AutoHideSupport) == Config::Flag_AutoHideSupport)
77 , m_isStandalone(false)
78{
79 init();
80 auto fwPrivate = m_floatingWindow->dptr();
81 fwPrivate->numGroupsChanged.connect([this] { updateButtons(); });
82 fwPrivate->numDockWidgetsChanged.connect([this] { d->numDockWidgetsChanged.emit(); });
83 fwPrivate->windowStateChanged.connect([this] { updateMaximizeButton(); });
84 fwPrivate->activatedChanged.connect([this] { d->isFocusedChanged.emit(); });
85}
86
89 , Draggable(view, /*enabled=*/false)
90 , d(new Private())
91 , m_group(nullptr)
92 , m_floatingWindow(nullptr)
93 , m_supportsAutoHide(false)
94 , m_isStandalone(true)
95{
96}
97
98void TitleBar::init()
99{
100 view()->init();
101
102 d->isFocusedChanged.connect([this] {
103 // repaint
104 view()->update();
105 });
106
108
109#ifdef KDDW_FRONTEND_QT
110 // Auto-hide not supported in flutter yet
111 QTimer::singleShot(0, this, &TitleBar::updateAutoHideButton); // have to wait after the group is
112 // constructed
113#endif
114}
115
117{
118 delete d;
119}
120
125
126
128{
129 if (m_floatingWindow || m_isStandalone)
130 return nullptr;
131
132 if (m_group)
133 return m_group->mainWindow();
134
135 KDDW_ERROR("null group and null floating window");
136 return nullptr;
137}
138
139bool TitleBar::isMDI() const
140{
141 auto p = view()->asWrapper();
142 while (p) {
143 if (p->is(ViewType::MDILayout))
144 return true;
145
146 if (p->is(ViewType::DropArea)) {
147 // Note that the TitleBar can be inside a DropArea that's inside a MDIArea
148 // so we need this additional check
149 return false;
150 }
151
152 p = p->parentView();
153 }
154
155 return false;
156}
157
159{
160 return m_title;
161}
162
163Icon TitleBar::icon() const
164{
165 return m_icon;
166}
167
169{
170 if ((Config::self().flags() & Config::Flag_DoubleClickMaximizes) && m_floatingWindow) {
171 // Not using isFloating(), as that can be a dock widget nested in a floating window. By
172 // convention it's floating, but it's not the title bar of the top-level window.
174 return true;
175 } else if (supportsFloatingButton()) {
177 return true;
178 }
179
180 return false;
181}
182
184{
185 return m_floatButtonVisible;
186}
187
189{
190 return m_maximizeButtonVisible;
191}
192
194{
195 if (m_isStandalone)
196 return {}; // not applicable
197
199 // Apps having a maximize/restore button traditionally don't have a floating one,
200 // QDockWidget style only has floating and no maximize/restore.
201 // We can add an option later if we need them to co-exist
202 return false;
203 }
204
206 // Was explicitly disabled
207 return false;
208 }
209
210 if (DockWidget *dw = singleDockWidget()) {
211 // Don't show the dock/undock button if the window is not dockable
212 if (dw->options() & DockWidgetOption_NotDockable)
213 return false;
214 }
215
216 // If we have a floating window with nested dock widgets we can't re-attach, because we don't
217 // know where to
218 return !m_floatingWindow || m_floatingWindow->hasSingleFrame();
219}
220
222{
223 return m_floatingWindow && m_floatingWindow->supportsMaximizeButton();
224}
225
227{
228 return m_floatingWindow && m_floatingWindow->supportsMinimizeButton();
229}
230
232{
233 // Only dock widgets docked into the MainWindow can minimize
234 return m_supportsAutoHide && m_group && (m_group->isInMainWindow() || m_group->isOverlayed());
235}
236
237#ifdef DOCKS_TESTING_METHODS
238bool TitleBar::isFloatButtonVisible() const
239{
240 return dynamic_cast<Core::TitleBarViewInterface *>(view())->isFloatButtonVisible();
241}
242
243bool TitleBar::isCloseButtonVisible() const
244{
245 return dynamic_cast<Core::TitleBarViewInterface *>(view())->isCloseButtonVisible();
246}
247
248bool TitleBar::isCloseButtonEnabled() const
249{
250 return dynamic_cast<Core::TitleBarViewInterface *>(view())->isCloseButtonEnabled();
251}
252#endif
253
255{
256 return !m_icon.isNull();
257}
258
260{
261 return m_group;
262}
263
265{
266 return m_floatingWindow;
267}
268
270{
272 return;
273
274 if (m_group) {
275 m_group->FocusScope::focus(reason);
276 } else if (m_floatingWindow) {
277 m_floatingWindow->focus(reason);
278 }
279}
280
282{
283 updateCloseButton();
284 updateFloatButton();
285 updateMaximizeButton();
286
287 d->minimizeButtonChanged.emit(supportsMinimizeButton(), true);
288
289 updateAutoHideButton();
290}
291
292void TitleBar::updateAutoHideButton()
293{
294 const bool visible = m_supportsAutoHide;
296
297 if (const Core::Group *group = this->group()) {
298 if (group->isOverlayed())
300 }
301
302 d->autoHideButtonChanged.emit(visible, /*enabled=*/true, type);
303}
304
305void TitleBar::updateMaximizeButton()
306{
307 m_maximizeButtonVisible = false;
308 m_maximizeButtonType = TitleBarButtonType::Maximize;
309
310 if (auto fw = floatingWindow()) {
311 m_maximizeButtonType =
312 fw->view()->isMaximized() ? TitleBarButtonType::Normal : TitleBarButtonType::Maximize;
313 m_maximizeButtonVisible = supportsMaximizeButton();
314 }
315
316 d->maximizeButtonChanged.emit(m_maximizeButtonVisible, /*enabled=*/true, m_maximizeButtonType);
317}
318
319void TitleBar::updateCloseButton()
320{
321 const bool anyNonClosable = group()
322 ? group()->anyNonClosable()
323 : (floatingWindow() ? floatingWindow()->anyNonClosable() : false);
324
325 setCloseButtonEnabled(!anyNonClosable);
326}
327
329{
330 if (!m_floatingWindow)
331 return;
332
333 if (m_floatingWindow->view()->isMaximized())
334 m_floatingWindow->view()->showNormal();
335 else
336 m_floatingWindow->view()->showMaximized();
337}
338
340{
341 return m_group && m_group->isOverlayed();
342}
343
345{
346 if (enabled != m_closeButtonEnabled) {
347 m_closeButtonEnabled = enabled;
348 d->closeButtonEnabledChanged.emit(enabled);
349 }
350}
351
353{
354 if (visible != m_floatButtonVisible) {
355 m_floatButtonVisible = visible;
356 d->floatButtonVisibleChanged.emit(visible);
357 }
358}
359
360void TitleBar::setFloatButtonToolTip(const QString &tip)
361{
362 if (tip != m_floatButtonToolTip) {
363 m_floatButtonToolTip = tip;
364 d->floatButtonToolTipChanged.emit(tip);
365 }
366}
367
368void TitleBar::setTitle(const QString &title)
369{
370 if (title != m_title) {
371 m_title = title;
372 view()->update();
373 d->titleChanged.emit();
374 }
375}
376
377void TitleBar::setIcon(const Icon &icon)
378{
379 m_icon = icon;
380 d->iconChanged.emit();
381}
382
384{
385 const bool closeOnlyCurrentTab = Config::self().flags() & Config::Flag_CloseOnlyCurrentTab;
386
387 if (m_group) {
388 if (closeOnlyCurrentTab) {
389 if (auto dw = m_group->currentDockWidget()) {
390 dw->view()->close();
391 } else {
392 // Doesn't happen
393 KDDW_ERROR("Frame with no dock widgets");
394 }
395 } else {
396 if (m_group->isTheOnlyGroup() && m_group->isInFloatingWindow()) {
397 m_group->view()->d->closeRootView();
398 } else {
399 m_group->view()->close();
400 }
401 }
402 } else if (m_floatingWindow) {
403
404 if (closeOnlyCurrentTab) {
405 if (Group *f = m_floatingWindow->singleFrame()) {
406 if (DockWidget *dw = f->currentDockWidget()) {
407 dw->view()->close();
408 } else {
409 // Doesn't happen
410 KDDW_ERROR("Frame with no dock widgets");
411 }
412 } else {
413 m_floatingWindow->view()->close();
414 }
415 } else {
416 m_floatingWindow->view()->close();
417 }
418 } else if (m_isStandalone) {
419 view()->d->closeRootView();
420 }
421}
422
424{
426 if (isFloating()) {
427 // Let's dock it
428
429 if (dockWidgets.isEmpty()) {
430 KDDW_ERROR("TitleBar::onFloatClicked: empty list. Shouldn't happen");
431 return;
432 }
433
434 if (dockWidgets.size() == 1) {
435 // Case 1: Single dockwidget floating
436 dockWidgets[0]->setFloating(false);
437 } else {
438 // Case 2: Multiple dockwidgets are tabbed together and floating
439 // Possible improvement: Just reuse the whole group and put it back.
440 // The group currently doesn't remember the position in the main window
441 // so use an hack for now
442
443 if (!dockWidgets.isEmpty()) { // could be empty during destruction, maybe
444 if (!dockWidgets.constFirst()->hasPreviousDockedLocation()) {
445 // Don't attempt, there's no previous docked location
446 return;
447 }
448
449 int i = 0;
450 DockWidget *current = nullptr;
451 for (auto dock : std::as_const(dockWidgets)) {
452
453 if (!current && dock->isCurrentTab())
454 current = dock;
455
456 dock->setFloating(true);
457 dock->dptr()->m_lastPosition->m_tabIndex = i;
458 dock->setFloating(false);
459 ++i;
460 }
461
462 // Restore the current tab
463 if (current)
464 current->setAsCurrentTab();
465 }
466 }
467 } else {
468 // Let's float it
469 if (dockWidgets.size() == 1) {
470 // If there's a single dock widget, just call DockWidget::setFloating(true). The only
471 // difference is that it has logic for using the last used geometry for the floating
472 // window
473 dockWidgets[0]->setFloating(true);
474 } else {
475 makeWindow();
476 }
477 }
478}
479
484
486{
487 if (!m_floatingWindow)
488 return;
489
490 if (m_floatingWindow->isUtilityWindow()) {
491 // Qt::Tool windows don't appear in the task bar.
492 // Unless someone tells me a good reason to allow this situation.
493 return;
494 }
495
496 m_floatingWindow->view()->showMinimized();
497}
498
500{
501 if (!m_group) {
502 // Doesn't happen
503 KDDW_ERROR("Minimize not supported on floating windows");
504 return;
505 }
506
507 const auto &dockwidgets = m_group->dockWidgets();
508 if (isOverlayed() && dockwidgets.size() != 1) {
509 // Doesn't happen
510 KDDW_ERROR("TitleBar::onAutoHideClicked: There can only be a single dock widget per titlebar overlayed");
511 return;
512 }
513
514 const bool groupedAutoHide = Config::hasFlag(Config::Flag_AutoHideAsTabGroups);
515 const auto currentDw = m_group->currentDockWidget();
516 auto registry = DockRegistry::self();
517
518 if (isOverlayed()) { // Restore it:
519 auto dw = dockwidgets.first();
520 MainWindow *mainWindow = dw->mainWindow();
521 auto sideBarGroup = groupedAutoHide ? registry->sideBarGroupingFor(dw) : DockWidget::List();
522 if (sideBarGroup.isEmpty()) {
524 } else {
525 // Config::Flag_AutoHideAsTabGroups case. Restore its friends too
526 for (auto it = sideBarGroup.rbegin(); it != sideBarGroup.rend(); ++it) {
528 }
529 dw->setAsCurrentTab();
530 registry->removeSideBarGrouping(sideBarGroup);
531 }
532 } else { // Send it to sidebar:
533 if (groupedAutoHide)
534 registry->addSideBarGrouping(dockwidgets);
535
536 for (DockWidget *dw : dockwidgets) {
537 if (groupedAutoHide || dw == currentDw)
538 dw->moveToSideBar();
539 }
540 }
541}
542
544{
545 return m_closeButtonEnabled;
546}
547
548std::unique_ptr<WindowBeingDragged> TitleBar::makeWindow()
549{
550 if (m_isStandalone)
551 return {}; // not applicable
552
553 if (!isVisible() && view()->rootView()->controller()->isVisible()
555
556 // When using Flag_ShowButtonsOnTabBarIfTitleBarHidden we forward the call from the tab
557 // bar's buttons to the title bar's buttons, just to reuse logic
558
559 KDDW_ERROR("TitleBar::makeWindow shouldn't be called on invisible title bar this={}, root.isVisible={}", ( void * )this, view()->rootView()->isVisible());
560 if (m_group) {
561 KDDW_ERROR("this={}; actual={}", ( void * )this, ( void * )m_group->actualTitleBar());
562 } else if (m_floatingWindow) {
563 KDDW_ERROR("Has floating window with titlebar={}, isVisible={}", ( void * )m_floatingWindow->titleBar(), m_floatingWindow->isVisible());
564 }
565
566 assert(false);
567 return {};
568 }
569
570 if (m_floatingWindow) {
571 // We're already a floating window, no detach needed
572 return std::make_unique<WindowBeingDragged>(m_floatingWindow, this);
573 }
574
575 if (FloatingWindow *fw = floatingWindow()) { // Already floating
576 if (m_group->isTheOnlyGroup()) { // We don't detach. This one drags the entire window
577 // instead.
578 KDDW_DEBUG("TitleBar::makeWindow no detach needed");
579 return std::make_unique<WindowBeingDragged>(fw, this);
580 }
581 }
582
583 Rect r = m_group->view()->geometry();
584 r.moveTopLeft(m_group->mapToGlobal(Point(0, 0)));
585
586 auto floatingWindow = new Core::FloatingWindow(m_group, {});
589
590 auto draggable = KDDockWidgets::usesNativeTitleBar() ? static_cast<Draggable *>(floatingWindow)
591 : static_cast<Draggable *>(this);
592 return std::make_unique<WindowBeingDragged>(floatingWindow, draggable);
593}
594
596{
597 return m_floatingWindow != nullptr;
598}
599
601{
602 if (m_floatingWindow) {
603 DockWidget::List result;
604 const auto groups = m_floatingWindow->groups();
605 for (Group *group : groups) {
606 result.append(group->dockWidgets());
607 }
608 return result;
609 }
610
611 if (m_group)
612 return m_group->dockWidgets();
613
614 if (m_isStandalone)
615 return {}; // not applicable
616
617 KDDW_ERROR("TitleBar::dockWidget: shouldn't happen");
618 return {};
619}
620
622{
624 return dockWidgets.isEmpty() ? nullptr : dockWidgets.first();
625}
626
628{
629 if (m_floatingWindow)
630 return true;
631
632 if (m_group)
633 return m_group->isFloating();
634
635 if (m_isStandalone)
636 return false; // not applicable
637
638 KDDW_ERROR("TitleBar::isFloating: shouldn't happen");
639 return false;
640}
641
643{
644 if (m_group)
645 return m_group->isFocused();
646 else if (m_floatingWindow)
647 return m_floatingWindow->view()->isActiveWindow();
648 else if (m_isStandalone)
649 return view()->isActiveWindow();
650
651 return false;
652}
653
654void TitleBar::updateFloatButton()
655{
656 setFloatButtonToolTip(floatingWindow() ? tr("Dock window") : tr("Undock window"));
658}
659
661{
662 return m_floatButtonToolTip;
663}
664
666{
667 if (m_floatingWindow && m_floatingWindow->hasSingleFrame()) {
668 if (Group *group = m_floatingWindow->singleFrame()) {
669 return group->stack()->tabBar();
670 } else {
671 // Shouldn't happen
672 KDDW_ERROR("Expected a group");
673 }
674
675 } else if (m_group) {
676 return m_group->stack()->tabBar();
677 }
678
679 return nullptr;
680}
681
683{
684 return m_maximizeButtonType;
685}
686
688{
689 return m_isStandalone;
690}
691
692TitleBar::Private *TitleBar::dptr() const
693{
694 return d;
695}
Application-wide config to tune certain behaviours of the framework.
Singleton to allow to choose certain behaviours of the framework.
Definition Config.h:64
static bool hasFlag(Flag)
Returns whether the specified flag is set or not.
Definition Config.cpp:130
static Config & self()
returns the singleton Config instance
Definition Config.cpp:87
Flags flags() const
returns the chosen flags
Definition Config.cpp:98
@ Flag_TitleBarNoFloatButton
The TitleBar won't show the float button.
Definition Config.h:113
@ Flag_ShowButtonsOnTabBarIfTitleBarHidden
Definition Config.h:125
@ Flag_TitleBarHasMaximizeButton
Definition Config.h:97
@ Flag_AutoHideAsTabGroups
If tabbed dockwidgets are sent to/from sidebar, they're all sent and restored together.
Definition Config.h:131
View * view() const
Returns the view associated with this controller, if any.
ViewType type() const
Returns the type of this controller.
Point mapToGlobal(Point) const
The DockWidget base-class. DockWidget and Core::DockWidget are only split in two so we can share some...
Vector< DockWidget * > List
bool setFloating(bool floats)
setter to make the dock widget float or dock.
void setAsCurrentTab()
Makes this dock widget current in its tab group.
bool isCurrentTab() const
Returns true if this dock widget is the current one in the tab widget that contains it....
bool supportsMaximizeButton() const
Returns whether this floating window supports showing a maximize button.
void setSuggestedGeometry(Rect suggestedRect, SuggestedGeometryHints=SuggestedGeometryHint_None)
Equivalent to setGeometry(), but the value might be adjusted.
bool hasSingleFrame() const
checks if this FloatingWindow only has one group. If true it means there's no side-by-side dock widge...
bool isUtilityWindow() const
Returns whether this window is a tool window Tool windows don't usually appear in the task bar.
Core::Group * singleFrame() const
If this floating window has only one Frame, it's returned, otherwise nullptr.
bool supportsMinimizeButton() const
Returns whether this floating window supports showing a minimize button.
Core::TitleBar * titleBar() const
Returns the title bar.
bool isFocused() const
Returns true if this FocusScope is focused. This is similar to the QWidget::hasFocus(),...
Vector< DockWidget * > dockWidgets() const
bool isInMainWindow() const
Returns whether this group is docked inside a MainWindow.
bool isFloating() const
Returns whether this group is floating. A floating group isn't attached to any other MainWindow,...
MainWindow * mainWindow() const
Returns the main window this group is in. nullptr if not inside a main window.
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);.
Core::TitleBar * actualTitleBar() const
bool isInFloatingWindow() const
Returns whether this group is in a FloatingWindow, as opposed to MainWindow.
The MainWindow base-class. MainWindow and MainWindowBase are only split in two so we can share some c...
void restoreFromSideBar(KDDockWidgets::Core::DockWidget *dw)
Removes the dock widget from the sidebar and docks it into the main window again.
Core::TabBar * tabBar() const
Returns the tab bar.
The interface that TitleBar views should implement.
TabBar * tabBar() const
Returns the tab bar which is under this title bar. It's only nullptr for the case of having a Floatin...
bool isFocused() const
returns whether any of the DockWidgets this TitleBar controls has a child focus Not to be confused wi...
bool supportsMinimizeButton() const
returns whether this title bar supports a minimize button
void setIcon(const Icon &icon)
bool isStandalone() const
Returns whether this titlebar is standalone. See comment in the ctor.
void setTitle(const QString &title)
Core::DockWidget * singleDockWidget() const override final
DockWidget::List dockWidgets() const
the list of dockwidgets under this TitleBar. There should always be at least 1. If more than 1 then t...
std::unique_ptr< WindowBeingDragged > makeWindow() override
Core::Group * group() const
getter for m_group
TitleBarButtonType maximizeButtonType() const
bool supportsAutoHideButton() const
returns whether this title bar supports the auto-hide button
friend class KDDockWidgets::QtWidgets::TitleBar
void focus(Qt::FocusReason reason)
bool isMDI() const override
From Draggable interface.
void updateButtons()
updates the close button enabled state
bool supportsMaximizeButton() const
returns whether this title bar supports a maximize/restore button
bool isFloating() const
Returns true if this title-bar is the title bar of a floating window.
bool hasIcon() const
returns whether this title bar has an icon
bool supportsFloatingButton() const
returns whether this title bar supports a floating/docking button
FloatingWindow * floatingWindow() const
getter for m_floatingWindow
MainWindow * mainWindow() const
If this title bar belongs to a dock widget docked into the main window, returns the main window Retur...
bool isWindow() const override
virtual void update()=0
virtual void show()=0
virtual Rect geometry() const =0
virtual bool isMaximized() const =0
virtual void showMinimized()=0
virtual bool isActiveWindow() const =0
virtual bool close()=0
virtual std::shared_ptr< View > asWrapper()=0
Returns this view, but as a wrapper.
virtual void showMaximized()=0
virtual void showNormal()=0
static DockRegistry * self()
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
@ DockWidgetOption_NotDockable
The DockWidget can't be docked, it's always floating.
TitleBarButtonType
describes a type of button you can have in the title bar
FocusReason
A factory class for allowing the user to customize some internal widgets.
QMainWindow sub-class to enable KDDockWidgets support.

© 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