KDDockWidgets API Documentation 2.0
Loading...
Searching...
No Matches
core/TabBar.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 "TabBar.h"
13#include "TabBar_p.h"
14#include "core/Draggable_p.h"
15#include "Controller.h"
16#include "core/ScopedValueRollback_p.h"
17#include "core/Stack.h"
18#include "core/FloatingWindow.h"
19#include "core/DockWidget_p.h"
20#include "core/Logging_p.h"
22#include "Platform.h"
23
24#include "core/DragController_p.h"
25#include "core/Utils_p.h"
26#include "Config.h"
27#include "core/ViewFactory.h"
28
29#include <cstdlib>
30
31using namespace KDDockWidgets;
32using namespace KDDockWidgets::Core;
33
35 : Controller(ViewType::TabBar, Config::self().viewFactory()->createTabBar(this, stack->view()))
36 , Draggable(view())
37 , d(new Private(stack))
38{
39 view()->init();
40 if (auto tvi = dynamic_cast<Core::TabBarViewInterface *>(view()))
41 tvi->setTabsAreMovable(tabsAreMovable());
42}
43
45{
46 delete d;
47}
48
53
54bool Core::TabBar::dragCanStart(Point pressPos, Point pos) const
55{
56 // Here we allow the user to re-order tabs instead of dragging them off.
57 // To do that we just return false here, and QTabBar will handle the mouse event, assuming
58 // QTabBar::isMovable.
59
60 const bool defaultResult = Draggable::dragCanStart(pressPos, pos);
61
62 if (!defaultResult || !tabsAreMovable()) {
63 // Nothing more to do. If the drag wouldn't start anyway, return false.
64 // And if the tabs aren't movable, just return the default result, which just considers
65 // startDragDistance
66 return defaultResult;
67 }
68
69 const int index =
70 dynamic_cast<Core::TabBarViewInterface *>(view())->tabAt(view()->mapFromGlobal(pos));
71 if (index == -1)
72 return defaultResult;
73
74 const int deltaX = std::abs(pos.x() - pressPos.x());
75 const int deltaY = std::abs(pos.y() - pressPos.y());
76
77 const int startDragDistance = Platform::instance()->startDragDistance();
78
79 if (deltaY > 5 * startDragDistance) {
80 // Moving up or down too much results in a detach. No tab re-ordering allowed.
81 return true;
82 } else if (deltaY > startDragDistance && deltaX < startDragDistance) {
83 // Moved a bit up or down, but not left/right, then detach too.
84 // Only if it's going considerably left/right we allow to re-order tabs.
85 return true;
86 }
87
88 return false;
89}
90
92{
93 if (index < 0 || index >= numDockWidgets())
94 return nullptr;
95
96 return const_cast<DockWidget *>(d->m_dockWidgets.value(index));
97}
98
100{
101 if (auto tvi = dynamic_cast<Core::TabBarViewInterface *>(view()))
102 return dockWidgetAt(tvi->tabAt(localPos));
103
104 return nullptr;
105}
106
108{
109 return d->m_dockWidgets.indexOf(dw);
110}
111
113{
114 if (m_inDtor)
115 return;
116
117 auto it = d->aboutToDeleteConnections.find(dw);
118 if (it != d->aboutToDeleteConnections.end())
119 d->aboutToDeleteConnections.erase(it);
120
121 const bool wasCurrent = dw == d->m_currentDockWidget;
122 const int index = d->m_dockWidgets.indexOf(dw);
123
124 if (wasCurrent) {
125 const bool isLast = index == d->m_dockWidgets.count() - 1;
126 const int newCurrentIndex = isLast ? index - 1 : index + 1;
127 setCurrentIndex(newCurrentIndex);
128 }
129
130 d->m_removeGuard = true;
131 // The view might call setCurrenteIndex() before our d->m_dockWidgets reflectig the state.
132 // d->m_removeGuard protects against that.
133 if (auto tvi = dynamic_cast<Core::TabBarViewInterface *>(view()))
134 tvi->removeDockWidget(dw);
135 d->m_removeGuard = false;
136
137 d->m_dockWidgets.removeOne(dw);
139}
140
141void TabBar::insertDockWidget(int index, Core::DockWidget *dw, const Icon &icon,
142 const QString &title)
143{
144 if (auto oldGroup = dw->dptr()->group()) {
145 if (auto oldTabBar = oldGroup->tabBar()) {
146 if (oldTabBar != this) {
147 oldTabBar->removeDockWidget(dw);
148 }
149 }
150 }
151
152 d->m_dockWidgets.insert(index, dw);
153 KDBindings::ScopedConnection conn = dw->d->aboutToDelete.connect([this, dw] {
155 });
156 d->aboutToDeleteConnections[dw] = std::move(conn);
157
158 dynamic_cast<Core::TabBarViewInterface *>(view())->insertDockWidget(index, dw, icon, title);
159 if (!d->m_currentDockWidget)
161
163}
164
165std::unique_ptr<WindowBeingDragged> Core::TabBar::makeWindow()
166{
167 auto dock = d->m_lastPressedDockWidget;
168 d->m_lastPressedDockWidget = nullptr;
169
170 const bool hideTitleBarWhenTabsVisible =
172 const bool alwaysShowTabs = Config::self().flags() & Config::Flag_AlwaysShowTabs;
173
174 if (hideTitleBarWhenTabsVisible) {
175 if (dock) {
176 if (alwaysShowTabs && hasSingleDockWidget()) {
177 // Case #1. User is dragging a tab but there's only 1 tab (and tabs are always
178 // visible), so drag everything instead, no detaching happens
179 return d->m_stack->makeWindow();
180 }
181 } else {
182 // Case #2. User is dragging on the QTabBar background, not on an actual tab.
183 // As Flag_HideTitleBarWhenTabsVisible is set, we let the user drag through the tab
184 // widget background.
185 return d->m_stack->makeWindow();
186 }
187 } else {
188 if (dock && hasSingleDockWidget() && alwaysShowTabs) {
189 // Case #3. window with title bar and single tab, no detaching should happen, just use
190 // the title bar.
191 return {};
192 }
193 }
194
195 if (!dock)
196 return {};
197
198 FloatingWindow *floatingWindow = group()->detachTab(dock);
199 if (!floatingWindow)
200 return {};
201
202 auto draggable = KDDockWidgets::usesNativeTitleBar() ? static_cast<Draggable *>(floatingWindow)
203 : static_cast<Draggable *>(this);
204 return std::make_unique<WindowBeingDragged>(floatingWindow, draggable);
205}
206
208{
209 // Same semantics as tab widget, no need to duplicate logic
210 return d->m_stack->isWindow();
211}
212
213void Core::TabBar::onMousePress(Point localPos)
214{
215 d->m_lastPressedDockWidget = dockWidgetAt(localPos);
216 Group *group = this->group();
218 // User clicked on a tab which was already focused
219 // A tab changing also counts as a change of scope
220 group->FocusScope::focus(Qt::MouseFocusReason);
221 }
222}
223
225{
226 if (DockWidget *dw = dockWidgetAt(localPos))
227 dw->setFloating(true);
228}
229
231{
232 return numDockWidgets() == 1;
233}
234
236{
237 return d->m_dockWidgets.size();
238}
239
241{
242 return d->m_stack->singleDockWidget();
243}
244
246{
247 Group *f = group();
248 return f && f->isMDI();
249}
250
252{
253 return d->m_stack->group();
254}
255
257{
258 return d->m_stack;
259}
260
261// NOLINTNEXTLINE(bugprone-easily-swappable-parameters
262void Core::TabBar::Private::moveTabTo(int from, int to)
263{
264 auto fromDw = m_dockWidgets.takeAt(from);
265 m_dockWidgets.insert(to, fromDw);
266}
267
268void Core::TabBar::moveTabTo(int from, int to)
269{
270 ScopedValueRollback guard(d->m_isMovingTab, true);
271
272 d->moveTabTo(from, to);
273
274 // Tell GUI:
275 if (auto tvi = dynamic_cast<Core::TabBarViewInterface *>(view()))
276 tvi->moveTabTo(from, to);
277}
278
280{
281 if (auto tvi = dynamic_cast<Core::TabBarViewInterface *>(view()))
282 return tvi->text(index);
283
284 return {};
285}
286
287Rect Core::TabBar::rectForTab(int index) const
288{
289 if (auto tvi = dynamic_cast<Core::TabBarViewInterface *>(view()))
290 return tvi->rectForTab(index);
291
292 return {};
293}
294
296{
297 return d->m_currentDockWidget;
298}
299
301{
302 if (d->m_removeGuard) // We're in the middle of a remove.
303 return;
304
305 if (dw == d->m_currentDockWidget)
306 return;
307
309}
310
312{
313 if (!d->m_currentDockWidget)
314 return -1;
315
316 return d->m_dockWidgets.indexOf(d->m_currentDockWidget);
317}
318
320{
321 if (d->m_removeGuard) // We're in the middle of a remove.
322 return;
323
324 auto newCurrentDw = dockWidgetAt(index);
325 if (newCurrentDw == d->m_currentDockWidget)
326 return;
327
328 if (d->m_currentDockWidget) {
329 d->m_currentDockWidget->d->isCurrentTabChanged.emit(false);
330 }
331
332 d->m_currentDockWidget = newCurrentDw;
333 d->currentDockWidgetChanged.emit(newCurrentDw);
334 if (auto tvi = dynamic_cast<Core::TabBarViewInterface *>(view()))
335 tvi->setCurrentIndex(index);
336
337 if (newCurrentDw)
338 newCurrentDw->d->isCurrentTabChanged.emit(true);
339}
340
341void TabBar::renameTab(int index, const QString &text)
342{
343 if (auto tvi = dynamic_cast<Core::TabBarViewInterface *>(view()))
344 tvi->renameTab(index, text);
345}
346
347void TabBar::changeTabIcon(int index, const Icon &icon)
348{
349 if (auto tvi = dynamic_cast<Core::TabBarViewInterface *>(view()))
350 tvi->changeTabIcon(index, icon);
351}
352
354{
355 return d->m_isMovingTab;
356}
357
358TabBar::Private *TabBar::dptr() const
359{
360 return d;
361}
Application-wide config to tune certain behaviours of the framework.
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
Flags flags() const
returns the chosen flags
Definition Config.cpp:102
@ Flag_AllowReorderTabs
Allows user to re-order tabs by dragging them.
Definition Config.h:92
@ 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.
The DockWidget base-class. DockWidget and Core::DockWidget are only split in two so we can share some...
bool isMDI() const
Returns whether this group is in a MDI layout Usually no, unless you're using an MDI main window.
Core::Stack *const m_stack
Definition core/Group.h:335
static Platform * instance()
Returns the platform singleton.
int startDragDistance() const
Returns how many pixels the mouse must move for a drag to start This is usually 4 by default (QApplic...
Group * group() const
getter for the group
The interface that TabBar views should implement.
bool dragCanStart(Point pressPos, Point pos) const override
void renameTab(int index, const QString &)
rename's the tab's text
void removeDockWidget(Core::DockWidget *dw)
int currentIndex() const
Returns the index of the current tab.
TabBar(Stack *tabWidget=nullptr)
DockWidget * currentDockWidget() const
Returns the current dock widget.
virtual ~TabBar() override
void onMouseDoubleClick(Point localPos)
bool isMovingTab() const
Returns whether we're inside Core::TabBar::moveTab()
bool hasSingleDockWidget() const
returns whether there's only 1 tab
void moveTabTo(int from, int to)
void setCurrentIndex(int index)
DockWidget * singleDockWidget() const override final
DockWidget * dockWidgetAt(int index) const
returns the dock widgets at tab number index
void onMousePress(Point localPos)
void insertDockWidget(int index, Core::DockWidget *dw, const Icon &icon, const QString &title)
std::unique_ptr< WindowBeingDragged > makeWindow() override
Rect rectForTab(int index) const
int indexOfDockWidget(const Core::DockWidget *dw) const
Returns the tab index of the specified dock widget.
QString text(int index) const
void setCurrentDockWidget(DockWidget *dw)
void changeTabIcon(int index, const Icon &)
change the tab's icon
bool isMDI() const override
Reimplemented for internal purposes. .
bool isWindow() const override
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.
MouseFocusReason

© 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