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