Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qdesigner_tabwidget.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3// Qt-Security score:significant reason:default
4
10
11#include <QtDesigner/abstractformwindow.h>
12
13#include <QtWidgets/qapplication.h>
14#include <QtWidgets/qtabbar.h>
15#include <QtWidgets/qmenu.h>
16#include <QtWidgets/qlabel.h>
17#include <QtWidgets/qtabwidget.h>
18
19#include <QtGui/qaction.h>
20#include <QtGui/qevent.h>
21#include <QtGui/qdrag.h>
22
23#include <QtCore/qdebug.h>
24#include <QtCore/qmimedata.h>
25
27
28using namespace Qt::StringLiterals;
29
30namespace qdesigner_internal {
31// Store tab widget as drag source
32class MyMimeData : public QMimeData
33{
35public:
37 static bool fromMyTab(const QMimeData *mimeData, const QTabWidget *tab) {
38 if (!mimeData)
39 return false;
40 const MyMimeData *m = qobject_cast<const MyMimeData *>(mimeData);
41 return m && m->m_tab == tab;
42 }
43private:
44 const QTabWidget *m_tab;
45};
46
47} // namespace qdesigner_internal
48
49// ------------- QTabWidgetEventFilter
50
51QTabWidgetEventFilter::QTabWidgetEventFilter(QTabWidget *parent) :
52 QObject(parent),
53 m_tabWidget(parent),
54 m_actionDeletePage(new QAction(tr("Delete"), this)),
55 m_actionInsertPage(new QAction(tr("Before Current Page"), this)),
56 m_actionInsertPageAfter(new QAction(tr("After Current Page"), this)),
57 m_pagePromotionTaskMenu(new qdesigner_internal::PromotionTaskMenu(nullptr, qdesigner_internal::PromotionTaskMenu::ModeSingleWidget, this))
58{
59 tabBar()->setAcceptDrops(true);
60 tabBar()->installEventFilter(this);
61
62 connect(m_actionInsertPage, &QAction::triggered, this, &QTabWidgetEventFilter::addPage);
63 connect(m_actionInsertPageAfter, &QAction::triggered, this, &QTabWidgetEventFilter::addPageAfter);
64 connect(m_actionDeletePage, &QAction::triggered, this, &QTabWidgetEventFilter::removeCurrentPage);
65}
66
67QTabWidgetEventFilter::~QTabWidgetEventFilter() = default;
68
69void QTabWidgetEventFilter::install(QTabWidget *tabWidget)
70{
71 new QTabWidgetEventFilter(tabWidget);
72}
73
74QTabWidgetEventFilter *QTabWidgetEventFilter::eventFilterOf(const QTabWidget *tabWidget)
75{
76 // Look for 1st order children only..otherwise, we might get filters of nested tab widgets
77 for (QObject *o : tabWidget->children()) {
78 if (!o->isWidgetType())
79 if (QTabWidgetEventFilter *ef = qobject_cast<QTabWidgetEventFilter*>(o))
80 return ef;
81 }
82 return nullptr;
83}
84
85QMenu *QTabWidgetEventFilter::addTabWidgetContextMenuActions(const QTabWidget *tabWidget, QMenu *popup)
86{
87 QTabWidgetEventFilter *filter = eventFilterOf(tabWidget);
88 if (!filter)
89 return nullptr;
90 return filter->addContextMenuActions(popup);
91}
92
93QTabBar *QTabWidgetEventFilter::tabBar() const
94{
95 // QTabWidget::tabBar() accessor is protected, grmbl...
96 if (!m_cachedTabBar) {
97 const auto tabBars = m_tabWidget->findChildren<QTabBar *>();
98 Q_ASSERT(tabBars.size() == 1);
99 m_cachedTabBar = tabBars.constFirst();
100 }
101 return m_cachedTabBar;
102
103}
104
105static bool canMove(const QPoint &pressPoint, const QMouseEvent *e)
106{
107 const QPoint pt = pressPoint - e->position().toPoint();
108 return pt.manhattanLength() > QApplication::startDragDistance();
109}
110
111bool QTabWidgetEventFilter::eventFilter(QObject *o, QEvent *e)
112{
113 const QEvent::Type type = e->type();
114 // Do not try to locate tab bar and form window, etc. for uninteresting events and
115 // avoid asserts about missing tab bars when being destroyed
116 switch (type) {
117 case QEvent::MouseButtonDblClick:
118 case QEvent::MouseButtonPress:
119 case QEvent::MouseButtonRelease:
120 case QEvent::MouseMove:
121 case QEvent::DragLeave:
122 case QEvent::DragEnter:
123 case QEvent::DragMove:
124 case QEvent::Drop:
125 break;
126 default:
127 return false;
128 }
129
130 if (o != tabBar())
131 return false;
132
133 QDesignerFormWindowInterface *fw = formWindow();
134 if (!fw)
135 return false;
136
137 bool handled = true;
138 switch (type) {
139 case QEvent::MouseButtonDblClick:
140 break;
141 case QEvent::MouseButtonPress: {
142 QMouseEvent *mev = static_cast<QMouseEvent*>(e);
143 if (QDesignerFormWindowInterface *fw = formWindow()) {
144 fw->clearSelection();
145 fw->selectWidget(m_tabWidget, true);
146 }
147 if (mev->button() & Qt::LeftButton) {
148 m_mousePressed = true;
149 m_pressPoint = mev->position().toPoint();
150
151 QTabBar *tabbar = tabBar();
152 const int count = tabbar->count();
153 for (int i = 0; i < count; ++i) {
154 if (tabbar->tabRect(i).contains(m_pressPoint)) {
155 if (i != tabbar->currentIndex()) {
156 qdesigner_internal::SetPropertyCommand *cmd = new qdesigner_internal::SetPropertyCommand(fw);
157 cmd->init(m_tabWidget, u"currentIndex"_s, i);
158 fw->commandHistory()->push(cmd);
159 }
160 break;
161 }
162 }
163 }
164 } break;
165
166 case QEvent::MouseButtonRelease:
167 m_mousePressed = false;
168 break;
169
170 case QEvent::MouseMove: {
171 QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(e);
172 if (m_mousePressed && canMove(m_pressPoint, mouseEvent)) {
173 const int index = m_tabWidget->currentIndex();
174 if (index == -1)
175 break;
176
177 m_mousePressed = false;
178 QDrag *drg = new QDrag(m_tabWidget);
179 drg->setMimeData(new qdesigner_internal::MyMimeData(m_tabWidget));
180
181 m_dragIndex = index;
182 m_dragPage = m_tabWidget->currentWidget();
183 m_dragLabel = m_tabWidget->tabText(index);
184 m_dragIcon = m_tabWidget->tabIcon(index);
185 if (m_dragIcon.isNull()) {
186 QLabel *label = new QLabel(m_dragLabel);
187 label->adjustSize();
188 drg->setPixmap(label->grab(QRect(0, 0, -1, -1)));
189 label->deleteLater();
190 } else {
191 drg->setPixmap(m_dragIcon.pixmap(22, 22));
192 }
193
194 m_tabWidget->removeTab(m_dragIndex);
195
196 const Qt::DropActions dropAction = drg->exec(Qt::MoveAction);
197
198 if (dropAction == Qt::IgnoreAction) {
199 // abort
200 m_tabWidget->insertTab(m_dragIndex, m_dragPage, m_dragIcon, m_dragLabel);
201 m_tabWidget->setCurrentIndex(m_dragIndex);
202 }
203
204 if (m_dropIndicator)
205 m_dropIndicator->hide();
206 }
207 } break;
208
209 case QEvent::DragLeave: {
210 if (m_dropIndicator)
211 m_dropIndicator->hide();
212 } break;
213
214 case QEvent::DragEnter:
215 case QEvent::DragMove: {
216 QDragMoveEvent *de = static_cast<QDragMoveEvent*>(e);
217 if (!qdesigner_internal::MyMimeData::fromMyTab(de->mimeData(), m_tabWidget))
218 return false;
219
220 if (de->proposedAction() == Qt::MoveAction)
221 de->acceptProposedAction();
222 else {
223 de->setDropAction(Qt::MoveAction);
224 de->accept();
225 }
226
227 QRect rect;
228 const int index = pageFromPosition(de->position().toPoint(), rect);
229
230 if (!m_dropIndicator) {
231 m_dropIndicator = new QWidget(m_tabWidget);
232 QPalette p = m_dropIndicator->palette();
233 p.setColor(m_tabWidget->backgroundRole(), Qt::red);
234 m_dropIndicator->setPalette(p);
235 }
236
237 QPoint pos;
238 if (index == m_tabWidget->count())
239 pos = tabBar()->mapToParent(QPoint(rect.x() + rect.width(), rect.y()));
240 else
241 pos = tabBar()->mapToParent(QPoint(rect.x(), rect.y()));
242
243 m_dropIndicator->setGeometry(pos.x(), pos.y() , 3, rect.height());
244 m_dropIndicator->show();
245 } break;
246
247 case QEvent::Drop: {
248 QDropEvent *de = static_cast<QDropEvent*>(e);
249 if (!qdesigner_internal::MyMimeData::fromMyTab(de->mimeData(), m_tabWidget))
250 return false;
251 de->acceptProposedAction();
252 de->accept();
253
254 QRect rect;
255 const int newIndex = pageFromPosition(de->position().toPoint(), rect);
256
257 qdesigner_internal::MoveTabPageCommand *cmd = new qdesigner_internal::MoveTabPageCommand(fw);
258 m_tabWidget->insertTab(m_dragIndex, m_dragPage, m_dragIcon, m_dragLabel);
259 cmd->init(m_tabWidget, m_dragPage, m_dragIcon, m_dragLabel, m_dragIndex, newIndex);
260 fw->commandHistory()->push(cmd);
261 } break;
262
263 default:
264 handled = false;
265 break;
266 }
267
268 return handled;
269}
270
271void QTabWidgetEventFilter::removeCurrentPage()
272{
273 if (!m_tabWidget->currentWidget())
274 return;
275
276 if (QDesignerFormWindowInterface *fw = formWindow()) {
277 qdesigner_internal::DeleteTabPageCommand *cmd = new qdesigner_internal::DeleteTabPageCommand(fw);
278 cmd->init(m_tabWidget);
279 fw->commandHistory()->push(cmd);
280 }
281}
282
283void QTabWidgetEventFilter::addPage()
284{
285 if (QDesignerFormWindowInterface *fw = formWindow()) {
286 qdesigner_internal::AddTabPageCommand *cmd = new qdesigner_internal::AddTabPageCommand(fw);
287 cmd->init(m_tabWidget, qdesigner_internal::AddTabPageCommand::InsertBefore);
288 fw->commandHistory()->push(cmd);
289 }
290}
291
292void QTabWidgetEventFilter::addPageAfter()
293{
294 if (QDesignerFormWindowInterface *fw = formWindow()) {
295 qdesigner_internal::AddTabPageCommand *cmd = new qdesigner_internal::AddTabPageCommand(fw);
296 cmd->init(m_tabWidget, qdesigner_internal::AddTabPageCommand::InsertAfter);
297 fw->commandHistory()->push(cmd);
298 }
299}
300
301QDesignerFormWindowInterface *QTabWidgetEventFilter::formWindow() const
302{
303 return QDesignerFormWindowInterface::findFormWindow(const_cast<QTabWidget*>(m_tabWidget));
304}
305
306// Get page from mouse position. Default to new page if in right half of last page?
307int QTabWidgetEventFilter::pageFromPosition(const QPoint &pos, QRect &rect) const
308{
309 int index = 0;
310 const QTabBar *tabbar = tabBar();
311 const int count = m_tabWidget->count();
312 for (; index < count; index++) {
313 const QRect rc = tabbar->tabRect(index);
314 if (rc.contains(pos)) {
315 rect = rc;
316 break;
317 }
318 }
319
320 if (index == count -1) {
321 QRect rect2 = rect;
322 rect2.setLeft(rect2.left() + rect2.width() / 2);
323 if (rect2.contains(pos))
324 index++;
325 }
326 return index;
327}
328
329QMenu *QTabWidgetEventFilter::addContextMenuActions(QMenu *popup)
330{
331 QMenu *pageMenu = nullptr;
332 const int count = m_tabWidget->count();
333 m_actionDeletePage->setEnabled(count);
334 if (count) {
335 const int currentIndex = m_tabWidget->currentIndex();
336 const QString pageSubMenuLabel = tr("Page %1 of %2").arg(currentIndex + 1).arg(count);
337 pageMenu = popup->addMenu(pageSubMenuLabel);
338 pageMenu->addAction(m_actionDeletePage);
339 // Set up promotion menu for current widget.
340 if (QWidget *page = m_tabWidget->currentWidget ()) {
341 m_pagePromotionTaskMenu->setWidget(page);
342 m_pagePromotionTaskMenu->addActions(QDesignerFormWindowInterface::findFormWindow(m_tabWidget),
343 qdesigner_internal::PromotionTaskMenu::SuppressGlobalEdit,
344 pageMenu);
345 }
346 QMenu *insertPageMenu = popup->addMenu(tr("Insert Page"));
347 insertPageMenu->addAction(m_actionInsertPageAfter);
348 insertPageMenu->addAction(m_actionInsertPage);
349 } else {
350 QAction *insertPageAction = popup->addAction(tr("Insert Page"));
351 connect(insertPageAction, &QAction::triggered, this, &QTabWidgetEventFilter::addPage);
352 }
353 popup->addSeparator();
354 return pageMenu;
355}
356
357// ----------- QTabWidgetPropertySheet
358
359static constexpr auto currentTabTextKey = "currentTabText"_L1;
360static constexpr auto currentTabNameKey = "currentTabName"_L1;
361static constexpr auto currentTabIconKey = "currentTabIcon"_L1;
362static constexpr auto currentTabToolTipKey = "currentTabToolTip"_L1;
363static constexpr auto currentTabWhatsThisKey = "currentTabWhatsThis"_L1;
364static constexpr auto tabMovableKey = "movable"_L1;
365
366QTabWidgetPropertySheet::QTabWidgetPropertySheet(QTabWidget *object, QObject *parent) :
367 QDesignerPropertySheet(object, parent),
368 m_tabWidget(object)
369{
370 createFakeProperty(currentTabTextKey, QVariant::fromValue(qdesigner_internal::PropertySheetStringValue()));
371 createFakeProperty(currentTabNameKey, QString());
372 createFakeProperty(currentTabIconKey, QVariant::fromValue(qdesigner_internal::PropertySheetIconValue()));
373 if (formWindowBase())
374 formWindowBase()->addReloadableProperty(this, indexOf(currentTabIconKey));
375 createFakeProperty(currentTabToolTipKey, QVariant::fromValue(qdesigner_internal::PropertySheetStringValue()));
376 createFakeProperty(currentTabWhatsThisKey, QVariant::fromValue(qdesigner_internal::PropertySheetStringValue()));
377 // Prevent the tab widget's drag and drop handling from interfering with Designer's
378 createFakeProperty(tabMovableKey, QVariant(false));
379}
380
381QTabWidgetPropertySheet::TabWidgetProperty QTabWidgetPropertySheet::tabWidgetPropertyFromName(const QString &name)
382{
383 static const QHash<QString, TabWidgetProperty> tabWidgetPropertyHash = {
384 {currentTabTextKey, PropertyCurrentTabText},
385 {currentTabNameKey, PropertyCurrentTabName},
386 {currentTabIconKey, PropertyCurrentTabIcon},
387 {currentTabToolTipKey, PropertyCurrentTabToolTip},
388 {currentTabWhatsThisKey, PropertyCurrentTabWhatsThis}
389 };
390 return tabWidgetPropertyHash.value(name, PropertyTabWidgetNone);
391}
392
393void QTabWidgetPropertySheet::setProperty(int index, const QVariant &value)
394{
395 const TabWidgetProperty tabWidgetProperty = tabWidgetPropertyFromName(propertyName(index));
396 if (tabWidgetProperty == PropertyTabWidgetNone) {
397 QDesignerPropertySheet::setProperty(index, value);
398 return;
399 }
400
401 // index-dependent
402 const int currentIndex = m_tabWidget->currentIndex();
403 QWidget *currentWidget = m_tabWidget->currentWidget();
404 if (!currentWidget)
405 return;
406
407 switch (tabWidgetProperty) {
408 case PropertyCurrentTabText:
409 m_tabWidget->setTabText(currentIndex, qvariant_cast<QString>(resolvePropertyValue(index, value)));
410 m_pageToData[currentWidget].text = qvariant_cast<qdesigner_internal::PropertySheetStringValue>(value);
411 break;
412 case PropertyCurrentTabName:
413 currentWidget->setObjectName(value.toString());
414 break;
415 case PropertyCurrentTabIcon:
416 m_tabWidget->setTabIcon(currentIndex, qvariant_cast<QIcon>(resolvePropertyValue(index, value)));
417 m_pageToData[currentWidget].icon = qvariant_cast<qdesigner_internal::PropertySheetIconValue>(value);
418 break;
419 case PropertyCurrentTabToolTip:
420 m_tabWidget->setTabToolTip(currentIndex, qvariant_cast<QString>(resolvePropertyValue(index, value)));
421 m_pageToData[currentWidget].tooltip = qvariant_cast<qdesigner_internal::PropertySheetStringValue>(value);
422 break;
423 case PropertyCurrentTabWhatsThis:
424 m_tabWidget->setTabWhatsThis(currentIndex, qvariant_cast<QString>(resolvePropertyValue(index, value)));
425 m_pageToData[currentWidget].whatsthis = qvariant_cast<qdesigner_internal::PropertySheetStringValue>(value);
426 break;
427 case PropertyTabWidgetNone:
428 break;
429 }
430}
431
432bool QTabWidgetPropertySheet::isEnabled(int index) const
433{
434 if (tabWidgetPropertyFromName(propertyName(index)) == PropertyTabWidgetNone)
435 return QDesignerPropertySheet::isEnabled(index);
436 return m_tabWidget->currentIndex() != -1;
437}
438
439QVariant QTabWidgetPropertySheet::property(int index) const
440{
441 const TabWidgetProperty tabWidgetProperty = tabWidgetPropertyFromName(propertyName(index));
442 if (tabWidgetProperty == PropertyTabWidgetNone)
443 return QDesignerPropertySheet::property(index);
444
445 // index-dependent
446 QWidget *currentWidget = m_tabWidget->currentWidget();
447 if (!currentWidget) {
448 if (tabWidgetProperty == PropertyCurrentTabIcon)
449 return QVariant::fromValue(qdesigner_internal::PropertySheetIconValue());
450 if (tabWidgetProperty == PropertyCurrentTabText)
451 return QVariant::fromValue(qdesigner_internal::PropertySheetStringValue());
452 if (tabWidgetProperty == PropertyCurrentTabToolTip)
453 return QVariant::fromValue(qdesigner_internal::PropertySheetStringValue());
454 if (tabWidgetProperty == PropertyCurrentTabWhatsThis)
455 return QVariant::fromValue(qdesigner_internal::PropertySheetStringValue());
456 return QVariant(QString());
457 }
458
459 // index-dependent
460 switch (tabWidgetProperty) {
461 case PropertyCurrentTabText:
462 return QVariant::fromValue(m_pageToData.value(currentWidget).text);
463 case PropertyCurrentTabName:
464 return currentWidget->objectName();
465 case PropertyCurrentTabIcon:
466 return QVariant::fromValue(m_pageToData.value(currentWidget).icon);
467 case PropertyCurrentTabToolTip:
468 return QVariant::fromValue(m_pageToData.value(currentWidget).tooltip);
469 case PropertyCurrentTabWhatsThis:
470 return QVariant::fromValue(m_pageToData.value(currentWidget).whatsthis);
471 case PropertyTabWidgetNone:
472 break;
473 }
474 return QVariant();
475}
476
477bool QTabWidgetPropertySheet::reset(int index)
478{
479 const TabWidgetProperty tabWidgetProperty = tabWidgetPropertyFromName(propertyName(index));
480 if (tabWidgetProperty == PropertyTabWidgetNone)
481 return QDesignerPropertySheet::reset(index);
482
483 // index-dependent
484 QWidget *currentWidget = m_tabWidget->currentWidget();
485 if (!currentWidget)
486 return false;
487
488 // index-dependent
489 switch (tabWidgetProperty) {
490 case PropertyCurrentTabName:
491 setProperty(index, QString());
492 break;
493 case PropertyCurrentTabToolTip:
494 m_pageToData[currentWidget].tooltip = qdesigner_internal::PropertySheetStringValue();
495 setProperty(index, QString());
496 break;
497 case PropertyCurrentTabWhatsThis:
498 m_pageToData[currentWidget].whatsthis = qdesigner_internal::PropertySheetStringValue();
499 setProperty(index, QString());
500 break;
501 case PropertyCurrentTabText:
502 m_pageToData[currentWidget].text = qdesigner_internal::PropertySheetStringValue();
503 setProperty(index, QString());
504 break;
505 case PropertyCurrentTabIcon:
506 m_pageToData[currentWidget].icon = qdesigner_internal::PropertySheetIconValue();
507 setProperty(index, QIcon());
508 break;
509 case PropertyTabWidgetNone:
510 break;
511 }
512 return true;
513}
514
515bool QTabWidgetPropertySheet::checkProperty(const QString &propertyName)
516{
517 switch (tabWidgetPropertyFromName(propertyName)) {
518 case PropertyCurrentTabText:
519 case PropertyCurrentTabName:
520 case PropertyCurrentTabToolTip:
521 case PropertyCurrentTabWhatsThis:
522 case PropertyCurrentTabIcon:
523 return false;
524 default:
525 break;
526 }
527 return true;
528}
529
530QT_END_NAMESPACE
531
532#include "qdesigner_tabwidget.moc" // required for MyMimeData
Combined button and popup list for selecting options.
Auxiliary methods to store/retrieve settings.
static constexpr auto currentTabTextKey
static constexpr auto currentTabToolTipKey
static constexpr auto currentTabWhatsThisKey
static constexpr auto currentTabIconKey
static bool canMove(const QPoint &pressPoint, const QMouseEvent *e)
static constexpr auto currentTabNameKey
static constexpr auto tabMovableKey