QtCanpool 3.3.0
载入中...
搜索中...
未找到
docktabbar.cpp
浏览该文件的文档.
1
6#include "docktabbar.h"
7#include "dockpanel.h"
8#include "docktab.h"
9#include "dockutils.h"
10
11#include <QBoxLayout>
12#include <QWheelEvent>
13#include <QScrollBar>
14#include <QTimer>
15#include <QDebug>
16
18
19class DockTabBarPrivate
20{
21public:
22 QX_DECLARE_PUBLIC(DockTabBar)
23public:
24 DockTabBarPrivate();
25 void init();
26 void updateTabs();
27public:
28 DockPanel *m_panel = nullptr;
29 QWidget *m_tabsContainerWidget;
30 QBoxLayout *m_tabsLayout;
31 int m_currentIndex = -1;
32};
33
34DockTabBarPrivate::DockTabBarPrivate()
35{
36}
37
38void DockTabBarPrivate::init()
39{
40 Q_Q(DockTabBar);
41
42 m_tabsContainerWidget = new QWidget();
43 m_tabsContainerWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
44 m_tabsContainerWidget->setObjectName("tabsContainerWidget");
45 m_tabsLayout = new QBoxLayout(QBoxLayout::LeftToRight);
46 m_tabsLayout->setContentsMargins(0, 0, 0, 0);
47 m_tabsLayout->setSpacing(0);
48 m_tabsLayout->addStretch(1);
49 m_tabsContainerWidget->setLayout(m_tabsLayout);
50 q->setWidget(m_tabsContainerWidget);
51}
52
57void DockTabBarPrivate::updateTabs()
58{
59 Q_Q(DockTabBar);
60 // Set active tab and update all other tabs to be inactive
61 for (int i = 0; i < q->count(); ++i) {
62 auto tab = q->tab(i);
63 if (!tab) {
64 continue;
65 }
66 if (i == m_currentIndex) {
67 tab->show();
68 tab->setActive(true);
69 // Sometimes the synchronous calculation of the rectangular area fails
70 // Therefore we use QTimer::singleShot here to execute the call
71 // within the event loop - see #520
72 // FIXME: Execute the following code to finish the program unexpectedly
73 /*
74 QTimer::singleShot(0, q, [&, tab] {
75 q->ensureWidgetVisible(tab);
76 });
77 */
78 } else {
79 tab->setActive(false);
80 }
81 }
82}
83
84DockTabBar::DockTabBar(DockPanel *parent)
85 : QScrollArea(parent)
86{
88 Q_D(DockTabBar);
89 d->m_panel = parent;
90 d->init();
91
92 setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
93 setFrameStyle(QFrame::NoFrame);
94 setWidgetResizable(true);
95 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
96 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
97 setFocusPolicy(Qt::NoFocus);
98}
99
104
110void DockTabBar::insertTab(int index, DockTab *tab)
111{
112 Q_D(DockTabBar);
113 d->m_tabsLayout->insertWidget(index, tab);
114 connect(tab, SIGNAL(clicked()), this, SLOT(onTabClicked()));
115 connect(tab, SIGNAL(closeRequested()), this, SLOT(onTabCloseRequested()));
116 connect(tab, SIGNAL(closeOtherTabsRequested()), this, SLOT(onCloseOtherTabsRequested()));
117 connect(tab, SIGNAL(moved(QPoint)), this, SLOT(onTabWidgetMoved(QPoint)));
118 connect(tab, SIGNAL(elidedChanged(bool)), this, SIGNAL(elidedChanged(bool)));
119 tab->installEventFilter(this);
120 Q_EMIT tabInserted(index);
121 if (index <= d->m_currentIndex) {
122 setCurrentIndex(d->m_currentIndex + 1);
123 } else if (d->m_currentIndex == -1) {
124 setCurrentIndex(index);
125 }
126 updateGeometry();
127}
128
130{
131 if (!count()) {
132 return;
133 }
134 Q_D(DockTabBar);
135 int newCurrentIndex = currentIndex();
136 int removeIndex = d->m_tabsLayout->indexOf(tab);
137 if (count() == 1) {
138 newCurrentIndex = -1;
139 }
140 if (newCurrentIndex > removeIndex) {
141 newCurrentIndex--;
142 } else if (newCurrentIndex == removeIndex) {
143 newCurrentIndex = -1;
144 // First we walk to the right to search for the next visible tab
145 for (int i = (removeIndex + 1); i < count(); ++i) {
146 if (this->tab(i)->isVisibleTo(this)) {
147 newCurrentIndex = i - 1;
148 break;
149 }
150 }
151
152 // If there is no visible tab right to this tab then we walk to
153 // the left to find a visible tab
154 if (newCurrentIndex < 0) {
155 for (int i = (removeIndex - 1); i >= 0; --i) {
156 if (this->tab(i)->isVisibleTo(this)) {
157 newCurrentIndex = i;
158 break;
159 }
160 }
161 }
162 }
163
164 Q_EMIT removingTab(removeIndex);
165 d->m_tabsLayout->removeWidget(tab);
166 tab->disconnect(this);
167 tab->removeEventFilter(this);
168 QX_DOCK_PRINT("newCurrentIndex " << newCurrentIndex);
169 if (newCurrentIndex != d->m_currentIndex) {
170 setCurrentIndex(newCurrentIndex);
171 } else {
172 d->updateTabs();
173 }
174
175 updateGeometry();
176}
177
179{
180 Q_D(const DockTabBar);
181 // The tab bar contains a stretch item as last item
182 return d->m_tabsLayout->count() - 1;
183}
184
189{
190 Q_D(const DockTabBar);
191 return d->m_currentIndex;
192}
193
198{
199 Q_D(const DockTabBar);
200 if (d->m_currentIndex < 0 || d->m_currentIndex >= d->m_tabsLayout->count()) {
201 return nullptr;
202 } else {
203 return qobject_cast<DockTab *>(d->m_tabsLayout->itemAt(d->m_currentIndex)->widget());
204 }
205}
206
207DockTab *DockTabBar::tab(int index) const
208{
209 if (index < 0 || index >= count()) {
210 return nullptr;
211 }
212 Q_D(const DockTabBar);
213 return qobject_cast<DockTab *>(d->m_tabsLayout->itemAt(index)->widget());
214}
215
221int DockTabBar::tabAt(const QPoint &pos) const
222{
223 if (!isVisible()) {
225 }
226
227 const DockTab *t = tab(0);
228 if (!t || pos.x() < t->geometry().x()) {
229 return -1;
230 }
231
232 int cnt = count();
233 for (int i = 0; i < cnt; ++i) {
234 if (tab(i)->geometry().contains(pos)) {
235 return i;
236 }
237 }
238
239 return cnt;
240}
241
242int DockTabBar::tabInsertIndexAt(const QPoint &pos) const
243{
244 int index = tabAt(pos);
245 if (index == Qx::DockTabInvalidIndex) {
247 } else {
248 return (index < 0) ? 0 : index;
249 }
250}
251
257bool DockTabBar::isTabOpen(int index) const
258{
259 if (index < 0 || index >= count()) {
260 return false;
261 }
262
263 return !tab(index)->isHidden();
264}
265
274{
275 QSize Size = sizeHint();
276 Size.setWidth(10);
277 return Size;
278}
279
285{
286 Q_D(const DockTabBar);
287 // Limit the size of the entire QScrollArea based on the size of the widgets in the container,
288 // otherwise the size of the QScrollArea will be large.
289 // Notes: setting SizePolicy alone will not make the size smaller.
290 return d->m_tabsContainerWidget->sizeHint();
291}
292
298{
299 Q_D(const DockTabBar);
300 return d->m_tabsContainerWidget->width() > width();
301}
302
304{
305 Q_D(DockTabBar);
306 if (index == d->m_currentIndex) {
307 return;
308 }
309 if (index < -1 || index > (count() - 1)) {
310 qWarning() << Q_FUNC_INFO << "Invalid index" << index;
311 return;
312 }
313 Q_EMIT currentChanging(index);
314 d->m_currentIndex = index;
315 d->updateTabs();
316 updateGeometry();
317 Q_EMIT currentChanged(index);
318}
319
324void DockTabBar::closeTab(int index)
325{
326 if (index < 0 || index >= count()) {
327 return;
328 }
329
330 auto tab = this->tab(index);
331 if (tab->isHidden()) {
332 return;
333 }
334 Q_EMIT tabCloseRequested(index);
335}
336
338{
339 Q_D(DockTabBar);
340 DockTab *tab = qobject_cast<DockTab *>(sender());
341 if (!tab) {
342 return;
343 }
344 int index = d->m_tabsLayout->indexOf(tab);
345 if (index < 0) {
346 return;
347 }
348 setCurrentIndex(index);
349 Q_EMIT tabBarClicked(index);
350}
351
353{
354 Q_D(DockTabBar);
355 DockTab *tab = qobject_cast<DockTab *>(sender());
356 int index = d->m_tabsLayout->indexOf(tab);
357 closeTab(index);
358}
359
361{
362 auto senderTab = qobject_cast<DockTab *>(sender());
363 for (int i = 0; i < count(); ++i) {
364 auto otherTab = this->tab(i);
365 if (otherTab->isClosable() && !otherTab->isHidden() && otherTab != senderTab) {
366 // If the dock widget is deleted with the closeTab() call, its tab
367 // it will no longer be in the layout, and thus the index needs to
368 // be updated to not skip any tabs
369 int offset = otherTab->dockWidget()->features().testFlag(DockWidget::DockWidgetDeleteOnClose) ? 1 : 0;
370 closeTab(i);
371
372 // If the the dock widget blocks closing, i.e. if the flag
373 // CustomCloseHandling is set, and the dock widget is still open,
374 // then we do not need to correct the index
375 if (otherTab->dockWidget()->isClosed()) {
376 i -= offset;
377 }
378 }
379 }
380}
381
382void DockTabBar::onTabWidgetMoved(const QPoint &globalPos)
383{
384 Q_D(DockTabBar);
385 DockTab *movingTab = qobject_cast<DockTab *>(sender());
386 if (!movingTab) {
387 return;
388 }
389
390 int fromIndex = d->m_tabsLayout->indexOf(movingTab);
391 auto mousePos = mapFromGlobal(globalPos);
392 mousePos.rx() = qMax(0, mousePos.x());
393 mousePos.rx() = qMin(width(), mousePos.x());
394 int toIndex = -1;
395 // Find tab under mouse
396 for (int i = 0; i < count(); ++i) {
397 DockTab *dropTab = this->tab(i);
398 auto tabGeometry = dropTab->geometry();
399 tabGeometry.setTopLeft(d->m_tabsContainerWidget->mapToParent(tabGeometry.topLeft()));
400 tabGeometry.setBottomRight(d->m_tabsContainerWidget->mapToParent(tabGeometry.bottomRight()));
401 if (dropTab == movingTab || !dropTab->isVisibleTo(this) || !tabGeometry.contains(mousePos)) {
402 continue;
403 }
404
405 toIndex = d->m_tabsLayout->indexOf(dropTab);
406 if (toIndex == fromIndex) {
407 toIndex = -1;
408 }
409 break;
410 }
411
412 if (toIndex > -1) {
413 d->m_tabsLayout->removeWidget(movingTab);
414 d->m_tabsLayout->insertWidget(toIndex, movingTab);
415 Q_EMIT tabMoved(fromIndex, toIndex);
416 setCurrentIndex(toIndex);
417 } else {
418 // Ensure that the moved tab is reset to its start position
419 d->m_tabsLayout->update();
420 }
421}
422
423bool DockTabBar::eventFilter(QObject *watched, QEvent *event)
424{
425 Q_D(DockTabBar);
426 bool result = Super::eventFilter(watched, event);
427 DockTab *tab = qobject_cast<DockTab *>(watched);
428 if (!tab) {
429 return result;
430 }
431
432 switch (event->type()) {
433 case QEvent::Hide:
434 Q_EMIT tabClosed(d->m_tabsLayout->indexOf(tab));
435 updateGeometry();
436 break;
437
438 case QEvent::Show:
439 Q_EMIT tabOpened(d->m_tabsLayout->indexOf(tab));
440 updateGeometry();
441 break;
442
443 // Setting the text of a tab will cause a LayoutRequest event
444 case QEvent::LayoutRequest:
445 updateGeometry();
446 break;
447
448 // Manage wheel event
449 case QEvent::Wheel:
450 // Ignore wheel events if tab is currently dragged
452 wheelEvent((QWheelEvent *)event);
453 }
454 break;
455
456 default:
457 break;
458 }
459
460 return result;
461}
462
463void DockTabBar::wheelEvent(QWheelEvent *event)
464{
465 event->accept();
466 const int direction = event->angleDelta().y();
467 if (direction < 0) {
468 horizontalScrollBar()->setValue(horizontalScrollBar()->value() + 20);
469 } else {
470 horizontalScrollBar()->setValue(horizontalScrollBar()->value() - 20);
471 }
472}
473
DockPanel manages multiple instances of DockWidgets.
Definition dockpanel.h:31
Custom tabbar implementation for tab area that is shown on top of a dock panel widget.
Definition docktabbar.h:27
void tabMoved(int from, int to)
void tabOpened(int index)
void setCurrentIndex(int index)
void insertTab(int index, DockTab *tab)
Inserts the given dock tab at the given position.
void currentChanged(int index)
virtual void wheelEvent(QWheelEvent *event) override
void tabInserted(int index)
DockTab * tab(int index) const
bool isTabOpen(int index) const
This function returns true if the tab is open, that means if it is visible to the user.
void removeTab(DockTab *tab)
void removingTab(int index)
void tabCloseRequested(int index)
int tabInsertIndexAt(const QPoint &pos) const
int tabAt(const QPoint &pos) const
Returns the tab at the given pos.
void currentChanging(int index)
void onCloseOtherTabsRequested()
void tabBarClicked(int index)
void tabClosed(int index)
int currentIndex() const
Returns the current index or -1 if no tab is selected
void closeTab(int index)
This function will close the tab given in index param.
DockTab * currentTab() const
Returns the current tab or a nullptr if no tab is selected.
virtual QSize minimumSizeHint() const override
Overrides the minimumSizeHint() function of QScrollArea The minimumSizeHint() is bigger than the size...
void onTabWidgetMoved(const QPoint &globalPos)
virtual QSize sizeHint() const override
The function provides a sizeHint that matches the height of the internal viewport.
virtual bool eventFilter(QObject *watched, QEvent *event) override
bool areTabsOverflowing() const
This function returns true, if the tabs need more space than the size of the tab bar.
void elidedChanged(bool elided)
Qx::DockDragState dragState() const
Definition docktab.cpp:447
#define QX_DOCK_PRINT(s)
Copyleft (C) 2024 maminjie canpool@163.com SPDX-License-Identifier: MulanPSL-2.0
Definition dockutils.h:24
@ DockDraggingInactive
@ DockTabInvalidIndex
@ DockTabDefaultInsertIndex
#define QX_INIT_PRIVATE(Class)
#define QX_DECLARE_PUBLIC(Class)
#define QX_FINI_PRIVATE()
#define QX_DOCK_END_NAMESPACE
#define QX_DOCK_BEGIN_NAMESPACE