diff options
Diffstat (limited to 'src/widgets')
35 files changed, 478 insertions, 195 deletions
diff --git a/src/widgets/accessible/complexwidgets.cpp b/src/widgets/accessible/complexwidgets.cpp index 4d9a629ab6..ce2ceac938 100644 --- a/src/widgets/accessible/complexwidgets.cpp +++ b/src/widgets/accessible/complexwidgets.cpp @@ -108,7 +108,10 @@ public: s.invalid = true; return s; } - return parent()->state(); + + QAccessible::State s = parent()->state(); + s.focused = (m_index == m_parent->currentIndex()); + return s; } QRect rect() const override { if (!isValid()) @@ -216,6 +219,16 @@ QTabBar *QAccessibleTabBar::tabBar() const return qobject_cast<QTabBar*>(object()); } +QAccessibleInterface* QAccessibleTabBar::focusChild() const +{ + for (int i = 0; i < childCount(); ++i) { + if (child(i)->state().focused) + return child(i); + } + + return nullptr; +} + QAccessibleInterface* QAccessibleTabBar::child(int index) const { if (QAccessible::Id id = m_childInterfaces.value(index)) diff --git a/src/widgets/accessible/complexwidgets_p.h b/src/widgets/accessible/complexwidgets_p.h index e7a32c7264..335e257476 100644 --- a/src/widgets/accessible/complexwidgets_p.h +++ b/src/widgets/accessible/complexwidgets_p.h @@ -112,6 +112,7 @@ public: explicit QAccessibleTabBar(QWidget *w); ~QAccessibleTabBar(); + QAccessibleInterface *focusChild() const override; int childCount() const override; QString text(QAccessible::Text t) const override; diff --git a/src/widgets/accessible/qaccessiblewidget.cpp b/src/widgets/accessible/qaccessiblewidget.cpp index 64083d09b9..1a7372728d 100644 --- a/src/widgets/accessible/qaccessiblewidget.cpp +++ b/src/widgets/accessible/qaccessiblewidget.cpp @@ -375,8 +375,12 @@ QAccessibleInterface *QAccessibleWidget::focusChild() const if (!fw) return nullptr; - if (isAncestor(widget(), fw) || fw == widget()) - return QAccessible::queryAccessibleInterface(fw); + if (isAncestor(widget(), fw)) { + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(fw); + if (!iface || iface == this || !iface->focusChild()) + return iface; + return iface->focusChild(); + } return nullptr; } diff --git a/src/widgets/dialogs/qdialog.cpp b/src/widgets/dialogs/qdialog.cpp index 362200a4fd..906022a185 100644 --- a/src/widgets/dialogs/qdialog.cpp +++ b/src/widgets/dialogs/qdialog.cpp @@ -920,7 +920,7 @@ void QDialog::adjustPosition(QWidget* w) if (w) { // Use pos() if the widget is embedded into a native window QPoint pp; - if (w->windowHandle() && w->windowHandle()->property("_q_embedded_native_parent_handle").value<WId>()) + if (w->windowHandle() && qvariant_cast<WId>(w->windowHandle()->property("_q_embedded_native_parent_handle"))) pp = w->pos(); else pp = w->mapToGlobal(QPoint(0,0)); diff --git a/src/widgets/dialogs/qfileinfogatherer.cpp b/src/widgets/dialogs/qfileinfogatherer.cpp index 0beca82f28..7342efbd0d 100644 --- a/src/widgets/dialogs/qfileinfogatherer.cpp +++ b/src/widgets/dialogs/qfileinfogatherer.cpp @@ -232,7 +232,7 @@ void QFileInfoGatherer::watchPaths(const QStringList &paths) void QFileInfoGatherer::unwatchPaths(const QStringList &paths) { #if QT_CONFIG(filesystemwatcher) - if (m_watcher) + if (m_watcher && !paths.isEmpty()) m_watcher->removePaths(paths); #else Q_UNUSED(paths); diff --git a/src/widgets/dialogs/qfilesystemmodel.cpp b/src/widgets/dialogs/qfilesystemmodel.cpp index 914c845565..d658f7fe0c 100644 --- a/src/widgets/dialogs/qfilesystemmodel.cpp +++ b/src/widgets/dialogs/qfilesystemmodel.cpp @@ -948,9 +948,8 @@ QVariant QFileSystemModel::headerData(int section, Qt::Orientation orientation, if (section == 0) { // ### TODO oh man this is ugly and doesn't even work all the way! // it is still 2 pixels off - QImage pixmap(16, 1, QImage::Format_Mono); - pixmap.fill(0); - pixmap.setAlphaChannel(pixmap.createAlphaMask()); + QImage pixmap(16, 1, QImage::Format_ARGB32_Premultiplied); + pixmap.fill(Qt::transparent); return pixmap; } break; diff --git a/src/widgets/doc/snippets/code/doc_src_layout.cpp b/src/widgets/doc/snippets/code/doc_src_layout.cpp index 5e9a740244..7bbd781bb2 100644 --- a/src/widgets/doc/snippets/code/doc_src_layout.cpp +++ b/src/widgets/doc/snippets/code/doc_src_layout.cpp @@ -53,14 +53,15 @@ #define CARD_H #include <QtWidgets> -#include <QList> +#include <QVector> class CardLayout : public QLayout { public: - CardLayout(QWidget *parent, int dist): QLayout(parent, 0, dist) {} - CardLayout(QLayout *parent, int dist): QLayout(parent, dist) {} - CardLayout(int dist): QLayout(dist) {} + CardLayout(int spacing): QLayout() + { setSpacing(spacing); } + CardLayout(int spacing, QWidget *parent): QLayout(parent) + { setSpacing(spacing); } ~CardLayout(); void addItem(QLayoutItem *item) override; @@ -72,7 +73,7 @@ public: void setGeometry(const QRect &rect) override; private: - QList<QLayoutItem*> list; + QVector<QLayoutItem*> m_items; }; #endif //! [0] @@ -85,23 +86,23 @@ private: //! [2] int CardLayout::count() const { - // QList::size() returns the number of QLayoutItems in the list - return list.size(); + // QVector::size() returns the number of QLayoutItems in m_items + return m_items.size(); } //! [2] //! [3] QLayoutItem *CardLayout::itemAt(int idx) const { - // QList::value() performs index checking, and returns 0 if we are + // QVector::value() performs index checking, and returns nullptr if we are // outside the valid range - return list.value(idx); + return m_items.value(idx); } QLayoutItem *CardLayout::takeAt(int idx) { - // QList::take does not do index checking - return idx >= 0 && idx < list.size() ? list.takeAt(idx) : 0; + // QVector::take does not do index checking + return idx >= 0 && idx < m_items.size() ? m_items.takeAt(idx) : 0; } //! [3] @@ -109,7 +110,7 @@ QLayoutItem *CardLayout::takeAt(int idx) //! [4] void CardLayout::addItem(QLayoutItem *item) { - list.append(item); + m_items.append(item); } //! [4] @@ -129,14 +130,14 @@ void CardLayout::setGeometry(const QRect &r) { QLayout::setGeometry(r); - if (list.size() == 0) + if (m_items.size() == 0) return; - int w = r.width() - (list.count() - 1) * spacing(); - int h = r.height() - (list.count() - 1) * spacing(); + int w = r.width() - (m_items.count() - 1) * spacing(); + int h = r.height() - (m_items.count() - 1) * spacing(); int i = 0; - while (i < list.size()) { - QLayoutItem *o = list.at(i); + while (i < m_items.size()) { + QLayoutItem *o = m_items.at(i); QRect geom(r.x() + i * spacing(), r.y() + i * spacing(), w, h); o->setGeometry(geom); ++i; @@ -148,29 +149,29 @@ void CardLayout::setGeometry(const QRect &r) //! [7] QSize CardLayout::sizeHint() const { - QSize s(0,0); - int n = list.count(); + QSize s(0, 0); + int n = m_items.count(); if (n > 0) - s = QSize(100,70); //start with a nice default size + s = QSize(100, 70); //start with a nice default size int i = 0; while (i < n) { - QLayoutItem *o = list.at(i); + QLayoutItem *o = m_items.at(i); s = s.expandedTo(o->sizeHint()); ++i; } - return s + n*QSize(spacing(), spacing()); + return s + n * QSize(spacing(), spacing()); } QSize CardLayout::minimumSize() const { - QSize s(0,0); - int n = list.count(); + QSize s(0, 0); + int n = m_items.count(); int i = 0; while (i < n) { - QLayoutItem *o = list.at(i); + QLayoutItem *o = m_items.at(i); s = s.expandedTo(o->minimumSize()); ++i; } - return s + n*QSize(spacing(), spacing()); + return s + n * QSize(spacing(), spacing()); } //! [7] diff --git a/src/widgets/doc/snippets/code/src_gui_dialogs_qfiledialog.cpp b/src/widgets/doc/snippets/code/src_gui_dialogs_qfiledialog.cpp index 7ccd827a04..ed6043564b 100644 --- a/src/widgets/doc/snippets/code/src_gui_dialogs_qfiledialog.cpp +++ b/src/widgets/doc/snippets/code/src_gui_dialogs_qfiledialog.cpp @@ -89,11 +89,10 @@ dialog.setNameFilter("*.cpp *.cc *.C *.cxx *.c++"); //! [7] -QStringList filters; -filters << "Image files (*.png *.xpm *.jpg)" - << "Text files (*.txt)" - << "Any files (*)"; - +const QStringList filters({"Image files (*.png *.xpm *.jpg)", + "Text files (*.txt)", + "Any files (*)" + }); QFileDialog dialog(this); dialog.setNameFilters(filters); dialog.exec(); @@ -131,10 +130,10 @@ QString dir = QFileDialog::getExistingDirectory(this, tr("Open Directory"), //! [12] //! [13] -QStringList mimeTypeFilters; -mimeTypeFilters << "image/jpeg" // will show "JPEG image (*.jpeg *.jpg *.jpe) - << "image/png" // will show "PNG image (*.png)" - << "application/octet-stream"; // will show "All files (*)" +QStringList mimeTypeFilters({"image/jpeg", // will show "JPEG image (*.jpeg *.jpg *.jpe) + "image/png", // will show "PNG image (*.png)" + "application/octet-stream" // will show "All files (*)" + }); QFileDialog dialog(this); dialog.setMimeTypeFilters(mimeTypeFilters); @@ -146,13 +145,13 @@ dialog.exec(); //! [14] //! [15] -auto fileOpenCompleted = [](const QString &fileName, const QByteArray &fileContent) { +auto fileContentReady = [](const QString &fileName, const QByteArray &fileContent) { if (fileName.isEmpty()) { // No file was selected } else { // Use fileName and fileContent } -} +}; QFileDialog::getOpenFileContent("Images (*.png *.xpm *.jpg)", fileContentReady); //! [15] diff --git a/src/widgets/doc/snippets/code/src_gui_itemviews_qtreewidget.cpp b/src/widgets/doc/snippets/code/src_gui_itemviews_qtreewidget.cpp index 792cc48ca7..f1e3b6ea45 100644 --- a/src/widgets/doc/snippets/code/src_gui_itemviews_qtreewidget.cpp +++ b/src/widgets/doc/snippets/code/src_gui_itemviews_qtreewidget.cpp @@ -53,6 +53,6 @@ QTreeWidget *treeWidget = new QTreeWidget(); treeWidget->setColumnCount(1); QList<QTreeWidgetItem *> items; for (int i = 0; i < 10; ++i) - items.append(new QTreeWidgetItem((QTreeWidget*)0, QStringList(QString("item: %1").arg(i)))); + items.append(new QTreeWidgetItem(static_cast<QTreeWidget *>(nullptr), QStringList(QString("item: %1").arg(i)))); treeWidget->insertTopLevelItems(0, items); //! [0] diff --git a/src/widgets/doc/snippets/code/src_gui_kernel_qapplication.cpp b/src/widgets/doc/snippets/code/src_gui_kernel_qapplication.cpp index 0a70c1d32a..5e1f0883e7 100644 --- a/src/widgets/doc/snippets/code/src_gui_kernel_qapplication.cpp +++ b/src/widgets/doc/snippets/code/src_gui_kernel_qapplication.cpp @@ -51,9 +51,10 @@ //! [0] QCoreApplication* createApplication(int &argc, char *argv[]) { - for (int i = 1; i < argc; ++i) + for (int i = 1; i < argc; ++i) { if (!qstrcmp(argv[i], "-no-gui")) return new QCoreApplication(argc, argv); + } return new QApplication(argc, argv); } @@ -187,14 +188,14 @@ for (const QString &command : commands) //! [12] -QWidget *widget = qApp->widgetAt(x, y); +QWidget *widget = QApplication::widgetAt(x, y); if (widget) widget = widget->window(); //! [12] //! [13] -QWidget *widget = qApp->widgetAt(point); +QWidget *widget = QApplication::widgetAt(point); if (widget) widget = widget->window(); //! [13] diff --git a/src/widgets/doc/snippets/code/src_gui_kernel_qlayout.cpp b/src/widgets/doc/snippets/code/src_gui_kernel_qlayout.cpp index 1a716029a9..c55834ebfb 100644 --- a/src/widgets/doc/snippets/code/src_gui_kernel_qlayout.cpp +++ b/src/widgets/doc/snippets/code/src_gui_kernel_qlayout.cpp @@ -70,8 +70,9 @@ void MyWidget::paintEvent(QPaintEvent *) //! [1] QLayoutItem *child; -while ((child = layout->takeAt(0)) != 0) { +while ((child = layout->takeAt(0)) != nullptr) { ... - delete child; + delete child->widget(); // delete the widget + delete child; // delete the layout item } //! [1] diff --git a/src/widgets/doc/snippets/code/src_gui_kernel_qlayoutitem.cpp b/src/widgets/doc/snippets/code/src_gui_kernel_qlayoutitem.cpp index d9f70b91ed..dd0f860c01 100644 --- a/src/widgets/doc/snippets/code/src_gui_kernel_qlayoutitem.cpp +++ b/src/widgets/doc/snippets/code/src_gui_kernel_qlayoutitem.cpp @@ -52,8 +52,7 @@ int MyLayout::heightForWidth(int w) const { if (cache_dirty || cached_width != w) { - // not all C++ compilers support "mutable" - MyLayout *that = (MyLayout*)this; + MyLayout *that = const_cast<MyLayout *>(this); int h = calculateHeightForWidth(w); that->cached_hfw = h; return h; diff --git a/src/widgets/doc/snippets/code/src_gui_qproxystyle.cpp b/src/widgets/doc/snippets/code/src_gui_qproxystyle.cpp index 3169d1c193..98dc0ff55b 100644 --- a/src/widgets/doc/snippets/code/src_gui_qproxystyle.cpp +++ b/src/widgets/doc/snippets/code/src_gui_qproxystyle.cpp @@ -53,8 +53,8 @@ class MyProxyStyle : public QProxyStyle { public: - int styleHint(StyleHint hint, const QStyleOption *option = 0, - const QWidget *widget = 0, QStyleHintReturn *returnData = 0) const override + int styleHint(StyleHint hint, const QStyleOption *option = nullptr, + const QWidget *widget = nullptr, QStyleHintReturn *returnData = nullptr) const override { if (hint == QStyle::SH_UnderlineShortcut) return 1; @@ -72,8 +72,8 @@ public: class MyProxyStyle : public QProxyStyle { public: - int styleHint(StyleHint hint, const QStyleOption *option = 0, - const QWidget *widget = 0, QStyleHintReturn *returnData = 0) const override + int styleHint(StyleHint hint, const QStyleOption *option = nullptr, + const QWidget *widget = nullptr, QStyleHintReturn *returnData = nullptr) const override { if (hint == QStyle::SH_UnderlineShortcut) return 0; diff --git a/src/widgets/doc/snippets/code/src_gui_widgets_qmenubar.cpp b/src/widgets/doc/snippets/code/src_gui_widgets_qmenubar.cpp index b82c67b379..b52b0064ad 100644 --- a/src/widgets/doc/snippets/code/src_gui_widgets_qmenubar.cpp +++ b/src/widgets/doc/snippets/code/src_gui_widgets_qmenubar.cpp @@ -54,5 +54,5 @@ menubar->addMenu(fileMenu); //! [1] -QMenuBar *menuBar = new QMenuBar(0); +QMenuBar *menuBar = new QMenuBar(nullptr); //! [1] diff --git a/src/widgets/doc/snippets/code/src_gui_widgets_qsplashscreen.cpp b/src/widgets/doc/snippets/code/src_gui_widgets_qsplashscreen.cpp index b9c0b1a38b..91aa8a9c4e 100644 --- a/src/widgets/doc/snippets/code/src_gui_widgets_qsplashscreen.cpp +++ b/src/widgets/doc/snippets/code/src_gui_widgets_qsplashscreen.cpp @@ -56,10 +56,10 @@ splash->show(); ... // Loading some items splash->showMessage("Loaded modules"); -qApp->processEvents(); +QCoreApplication::processEvents(); ... // Establishing connections splash->showMessage("Established connections"); -qApp->processEvents(); +QCoreApplication::processEvents(); //! [0] diff --git a/src/widgets/doc/src/widgets-and-layouts/layout.qdoc b/src/widgets/doc/src/widgets-and-layouts/layout.qdoc index 65569a9cd2..e42e6d42ec 100644 --- a/src/widgets/doc/src/widgets-and-layouts/layout.qdoc +++ b/src/widgets/doc/src/widgets-and-layouts/layout.qdoc @@ -300,7 +300,7 @@ \list \li A data structure to store the items handled by the layout. Each item is a \l{QLayoutItem}{QLayoutItem}. We will use a - QList in this example. + QVector in this example. \li \l{QLayout::}{addItem()}, how to add items to the layout. \li \l{QLayout::}{setGeometry()}, how to perform the layout. \li \l{QLayout::}{sizeHint()}, the preferred size of the layout. diff --git a/src/widgets/effects/qpixmapfilter.cpp b/src/widgets/effects/qpixmapfilter.cpp index 637c9c6aba..1f899c2660 100644 --- a/src/widgets/effects/qpixmapfilter.cpp +++ b/src/widgets/effects/qpixmapfilter.cpp @@ -1135,8 +1135,12 @@ void QPixmapColorizeFilter::draw(QPainter *painter, const QPointF &dest, const Q destImage = std::move(buffer); } - if (srcImage.hasAlphaChannel()) - destImage.setAlphaChannel(srcImage.alphaChannel()); + if (srcImage.hasAlphaChannel()) { + Q_ASSERT(destImage.format() == QImage::Format_ARGB32_Premultiplied); + QPainter maskPainter(&destImage); + maskPainter.setCompositionMode(QPainter::CompositionMode_DestinationIn); + maskPainter.drawImage(0, 0, srcImage); + } painter->drawImage(dest, destImage); } diff --git a/src/widgets/itemviews/qlistwidget.cpp b/src/widgets/itemviews/qlistwidget.cpp index 90e4c36a6e..4aca923151 100644 --- a/src/widgets/itemviews/qlistwidget.cpp +++ b/src/widgets/itemviews/qlistwidget.cpp @@ -301,16 +301,23 @@ bool QListModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int co { if (sourceRow < 0 || sourceRow + count - 1 >= rowCount(sourceParent) - || destinationChild <= 0 + || destinationChild < 0 || destinationChild > rowCount(destinationParent) + || sourceRow == destinationChild || sourceRow == destinationChild - 1 - || count <= 0) { + || count <= 0 + || sourceParent.isValid() + || destinationParent.isValid()) { return false; } if (!beginMoveRows(QModelIndex(), sourceRow, sourceRow + count - 1, QModelIndex(), destinationChild)) return false; - destinationChild--; - const int fromRow = destinationChild < sourceRow ? (sourceRow + count - 1) : sourceRow; + + int fromRow = sourceRow; + if (destinationChild < sourceRow) + fromRow += count - 1; + else + destinationChild--; while (count--) items.move(fromRow, destinationChild); endMoveRows(); diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp index 3e0a709677..85542a75f8 100644 --- a/src/widgets/kernel/qapplication.cpp +++ b/src/widgets/kernel/qapplication.cpp @@ -142,16 +142,19 @@ QApplicationPrivate *QApplicationPrivate::self = nullptr; static void initSystemPalette() { - if (!QApplicationPrivate::sys_pal) { - QPalette defaultPlatte; - if (QApplicationPrivate::app_style) - defaultPlatte = QApplicationPrivate::app_style->standardPalette(); - if (const QPalette *themePalette = QGuiApplicationPrivate::platformTheme()->palette()) { - QApplicationPrivate::setSystemPalette(themePalette->resolve(defaultPlatte)); - QApplicationPrivate::initializeWidgetPaletteHash(); - } else { - QApplicationPrivate::setSystemPalette(defaultPlatte); - } + if (QApplicationPrivate::sys_pal) + return; // Already initialized + + QPalette defaultPalette; + if (QApplicationPrivate::app_style) + defaultPalette = QApplicationPrivate::app_style->standardPalette(); + + auto *platformTheme = QGuiApplicationPrivate::platformTheme(); + if (const QPalette *themePalette = platformTheme ? platformTheme->palette() : nullptr) { + QApplicationPrivate::setSystemPalette(themePalette->resolve(defaultPalette)); + QApplicationPrivate::initializeWidgetPaletteHash(); + } else { + QApplicationPrivate::setSystemPalette(defaultPalette); } } @@ -379,7 +382,6 @@ QString QApplicationPrivate::styleSheet; // default application styles QPointer<QWidget> QApplicationPrivate::leaveAfterRelease = nullptr; QPalette *QApplicationPrivate::sys_pal = nullptr; // default system palette -QPalette *QApplicationPrivate::set_pal = nullptr; // default palette set by programmer QFont *QApplicationPrivate::sys_font = nullptr; // default system font QFont *QApplicationPrivate::set_font = nullptr; // default font set by programmer @@ -431,13 +433,6 @@ void QApplicationPrivate::process_cmdline() if (styleOverride.isEmpty() && qEnvironmentVariableIsSet("QT_STYLE_OVERRIDE")) styleOverride = QString::fromLocal8Bit(qgetenv("QT_STYLE_OVERRIDE")); - if (!styleOverride.isEmpty()) { - if (app_style) { - delete app_style; - app_style = nullptr; - } - } - // process platform-indep command line if (!qt_is_gui_used || !argc) return; @@ -552,6 +547,12 @@ void QApplicationPrivate::init() // Must be called before initialize() QColormap::initialize(); + if (sys_pal) { + // Now that we have a platform theme we need to reset + // the system palette to pick up the theme colors. + clearSystemPalette(); + initSystemPalette(); + } qt_init_tooltip_palette(); QApplicationPrivate::initializeWidgetFontHash(); @@ -597,8 +598,20 @@ void QApplicationPrivate::initialize() // needed for widgets in QML QAbstractDeclarativeData::setWidgetParent = QWidgetPrivate::setWidgetParentHelper; - if (application_type != QApplicationPrivate::Tty) - (void) QApplication::style(); // trigger creation of application style + if (application_type != QApplicationPrivate::Tty) { + if (!styleOverride.isEmpty()) { + if (auto *style = QStyleFactory::create(styleOverride.toLower())) { + QApplication::setStyle(style); + } else { + qWarning("QApplication: invalid style override '%s' passed, ignoring it.\n" + "\tAvailable styles: %s", qPrintable(styleOverride), + qPrintable(QStyleFactory::keys().join(QLatin1String(", ")))); + } + } + + // Trigger default style if none was set already + Q_UNUSED(QApplication::style()); + } #if QT_CONFIG(statemachine) // trigger registering of QStateMachine's GUI types qRegisterGuiStateMachine(); @@ -789,8 +802,6 @@ QApplication::~QApplication() delete QApplicationPrivate::app_pal; QApplicationPrivate::app_pal = nullptr; clearSystemPalette(); - delete QApplicationPrivate::set_pal; - QApplicationPrivate::set_pal = nullptr; app_palettes()->clear(); delete QApplicationPrivate::sys_font; @@ -1016,55 +1027,45 @@ void QApplication::setStyleSheet(const QString& styleSheet) */ QStyle *QApplication::style() { - if (QApplicationPrivate::app_style) - return QApplicationPrivate::app_style; - if (!qobject_cast<QApplication *>(QCoreApplication::instance())) { - Q_ASSERT(!"No style available without QApplication!"); - return nullptr; - } - if (!QApplicationPrivate::app_style) { - // Compile-time search for default style - // - QStyle *&app_style = QApplicationPrivate::app_style; - - if (!QApplicationPrivate::styleOverride.isEmpty()) { - const QString style = QApplicationPrivate::styleOverride.toLower(); - app_style = QStyleFactory::create(style); - if (Q_UNLIKELY(!app_style)) { - qWarning("QApplication: invalid style override passed, ignoring it.\n" - " Available styles: %s", qPrintable(QStyleFactory::keys().join(QLatin1String(", ")))); - } + // Create default style + if (!qobject_cast<QApplication *>(QCoreApplication::instance())) { + Q_ASSERT(!"No style available without QApplication!"); + return nullptr; } - if (!app_style) - app_style = QStyleFactory::create(QApplicationPrivate::desktopStyleKey()); - if (!app_style) { + auto &defaultStyle = QApplicationPrivate::app_style; + + defaultStyle = QStyleFactory::create(QApplicationPrivate::desktopStyleKey()); + if (!defaultStyle) { const QStringList styles = QStyleFactory::keys(); for (const auto &style : styles) { - if ((app_style = QStyleFactory::create(style))) + if ((defaultStyle = QStyleFactory::create(style))) break; } } - if (!app_style) { + if (!defaultStyle) { Q_ASSERT(!"No styles available!"); return nullptr; } - } - // take ownership of the style - QApplicationPrivate::app_style->setParent(qApp); - initSystemPalette(); + // Take ownership of the style + defaultStyle->setParent(qApp); + + initSystemPalette(); - if (QApplicationPrivate::set_pal) // repolish set palette with the new style - QApplication::setPalette(*QApplicationPrivate::set_pal); + if (testAttribute(Qt::AA_SetPalette)) + defaultStyle->polish(*QGuiApplicationPrivate::app_pal); #ifndef QT_NO_STYLE_STYLESHEET - if (!QApplicationPrivate::styleSheet.isEmpty()) { - qApp->setStyleSheet(QApplicationPrivate::styleSheet); - } else + if (!QApplicationPrivate::styleSheet.isEmpty()) { + qApp->setStyleSheet(QApplicationPrivate::styleSheet); + } else #endif - QApplicationPrivate::app_style->polish(qApp); + { + defaultStyle->polish(qApp); + } + } return QApplicationPrivate::app_style; } @@ -1128,17 +1129,21 @@ void QApplication::setStyle(QStyle *style) // take care of possible palette requirements of certain gui // styles. Do it before polishing the application since the style // might call QApplication::setPalette() itself - if (QApplicationPrivate::set_pal) { - QApplication::setPalette(*QApplicationPrivate::set_pal); - } else if (QApplicationPrivate::sys_pal) { - clearSystemPalette(); + if (testAttribute(Qt::AA_SetPalette)) { + QApplicationPrivate::app_style->polish(*QGuiApplicationPrivate::app_pal); + } else { + if (QApplicationPrivate::sys_pal) + clearSystemPalette(); initSystemPalette(); - QApplicationPrivate::initializeWidgetFontHash(); - } else if (!QApplicationPrivate::sys_pal) { - // Initialize the sys_pal if it hasn't happened yet... - QApplicationPrivate::setSystemPalette(QApplicationPrivate::app_style->standardPalette()); } + // The default widget font hash is based on the platform theme, + // not the style, but the widget fonts could in theory have been + // affected by polish of the previous style, without a proper + // cleanup in unpolish, so reset it now before polishing the + // new style. + QApplicationPrivate::initializeWidgetFontHash(); + // initialize the application with the new style QApplicationPrivate::app_style->polish(qApp); @@ -1349,15 +1354,14 @@ QPalette QApplication::palette(const QWidget* w) */ QPalette QApplication::palette(const char *className) { - if (!QApplicationPrivate::app_pal) - palette(); PaletteHash *hash = app_palettes(); if (className && hash && hash->size()) { QHash<QByteArray, QPalette>::ConstIterator it = hash->constFind(className); if (it != hash->constEnd()) return *it; } - return *QApplicationPrivate::app_pal; + + return QGuiApplication::palette(); } void QApplicationPrivate::setPalette_helper(const QPalette &palette, const char* className, bool clearWidgetPaletteHash) @@ -1376,6 +1380,10 @@ void QApplicationPrivate::setPalette_helper(const QPalette &palette, const char* QApplicationPrivate::app_pal = new QPalette(pal); else *QApplicationPrivate::app_pal = pal; + + if (!QApplicationPrivate::sys_pal || !palette.isCopyOf(*QApplicationPrivate::sys_pal)) + QCoreApplication::setAttribute(Qt::AA_SetPalette); + if (hash && hash->size()) { all = true; if (clearWidgetPaletteHash) @@ -1385,20 +1393,8 @@ void QApplicationPrivate::setPalette_helper(const QPalette &palette, const char* hash->insert(className, pal); } - if (QApplicationPrivate::is_app_running && !QApplicationPrivate::is_app_closing) { - // Send ApplicationPaletteChange to qApp itself, and to the widgets. + if (qApp) qApp->d_func()->sendApplicationPaletteChange(all, className); - } - if (!className && (!QApplicationPrivate::sys_pal || !palette.isCopyOf(*QApplicationPrivate::sys_pal))) { - if (!QApplicationPrivate::set_pal) - QApplicationPrivate::set_pal = new QPalette(palette); - else - *QApplicationPrivate::set_pal = palette; - QCoreApplication::setAttribute(Qt::AA_SetPalette); - - if (qGuiApp && QGuiApplicationPrivate::app_pal) - emit qGuiApp->paletteChanged(*QGuiApplicationPrivate::app_pal); - } } /*! @@ -1438,7 +1434,7 @@ void QApplicationPrivate::setSystemPalette(const QPalette &pal) else *sys_pal = pal; - if (!QApplicationPrivate::set_pal) + if (!testAttribute(Qt::AA_SetPalette)) QApplication::setPalette(*sys_pal); } @@ -4431,7 +4427,10 @@ void QApplicationPrivate::notifyThemeChanged() void QApplicationPrivate::sendApplicationPaletteChange(bool toAllWidgets, const char *className) { - QGuiApplicationPrivate::sendApplicationPaletteChange(); + if (!is_app_running || is_app_closing) + return; + + QGuiApplicationPrivate::sendApplicationPaletteChange(toAllWidgets, className); QEvent event(QEvent::ApplicationPaletteChange); const QWidgetList widgets = QApplication::allWidgets(); diff --git a/src/widgets/kernel/qapplication_p.h b/src/widgets/kernel/qapplication_p.h index 3167bd423f..79d06ed98c 100644 --- a/src/widgets/kernel/qapplication_p.h +++ b/src/widgets/kernel/qapplication_p.h @@ -162,7 +162,6 @@ public: static QWidgetList *popupWidgets; static QStyle *app_style; static QPalette *sys_pal; - static QPalette *set_pal; protected: void notifyThemeChanged() override; diff --git a/src/widgets/kernel/qmacgesturerecognizer.cpp b/src/widgets/kernel/qmacgesturerecognizer.cpp index d39b93e320..aac115a2cf 100644 --- a/src/widgets/kernel/qmacgesturerecognizer.cpp +++ b/src/widgets/kernel/qmacgesturerecognizer.cpp @@ -149,8 +149,8 @@ QMacPinchGestureRecognizer::recognize(QGesture *gesture, QObject *obj, QEvent *e void QMacPinchGestureRecognizer::reset(QGesture *gesture) { QPinchGesture *g = static_cast<QPinchGesture *>(gesture); - g->setChangeFlags(0); - g->setTotalChangeFlags(0); + g->setChangeFlags({}); + g->setTotalChangeFlags({}); g->setScaleFactor(1.0f); g->setTotalScaleFactor(1.0f); g->setLastScaleFactor(1.0f); diff --git a/src/widgets/styles/qcommonstyle.cpp b/src/widgets/styles/qcommonstyle.cpp index 03081658bb..c241f7a936 100644 --- a/src/widgets/styles/qcommonstyle.cpp +++ b/src/widgets/styles/qcommonstyle.cpp @@ -4569,7 +4569,7 @@ int QCommonStyle::pixelMetric(PixelMetric m, const QStyleOption *opt, const QWid case PM_MenuPanelWidth: case PM_TabBarBaseOverlap: case PM_TabBarBaseHeight: - ret = proxy()->pixelMetric(PM_DefaultFrameWidth, opt); + ret = proxy()->pixelMetric(PM_DefaultFrameWidth, opt, widget); break; case PM_MdiSubWindowFrameWidth: @@ -4801,7 +4801,7 @@ int QCommonStyle::pixelMetric(PixelMetric m, const QStyleOption *opt, const QWid break; case PM_TabBarIconSize: - ret = proxy()->pixelMetric(PM_SmallIconSize, opt); + ret = proxy()->pixelMetric(PM_SmallIconSize, opt, widget); break; case PM_ListViewIconSize: #if QT_CONFIG(filedialog) @@ -4809,7 +4809,7 @@ int QCommonStyle::pixelMetric(PixelMetric m, const QStyleOption *opt, const QWid ret = int(QStyleHelper::dpiScaled(24., opt)); else #endif - ret = proxy()->pixelMetric(PM_SmallIconSize, opt); + ret = proxy()->pixelMetric(PM_SmallIconSize, opt, widget); break; case PM_ButtonIconSize: @@ -4817,7 +4817,7 @@ int QCommonStyle::pixelMetric(PixelMetric m, const QStyleOption *opt, const QWid ret = int(QStyleHelper::dpiScaled(16, opt)); break; case PM_IconViewIconSize: - ret = proxy()->pixelMetric(PM_LargeIconSize, opt); + ret = proxy()->pixelMetric(PM_LargeIconSize, opt, widget); break; case PM_LargeIconSize: @@ -4855,13 +4855,13 @@ int QCommonStyle::pixelMetric(PixelMetric m, const QStyleOption *opt, const QWid ret = int(QStyleHelper::dpiScaled(16, opt)); break; case PM_ScrollView_ScrollBarSpacing: - ret = 2 * proxy()->pixelMetric(PM_DefaultFrameWidth, opt); + ret = 2 * proxy()->pixelMetric(PM_DefaultFrameWidth, opt, widget); break; case PM_ScrollView_ScrollBarOverlap: ret = 0; break; case PM_SubMenuOverlap: - ret = -proxy()->pixelMetric(QStyle::PM_MenuPanelWidth, opt); + ret = -proxy()->pixelMetric(QStyle::PM_MenuPanelWidth, opt, widget); break; case PM_TreeViewIndentation: ret = int(QStyleHelper::dpiScaled(20, opt)); @@ -5027,8 +5027,9 @@ QSize QCommonStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt, case CT_SpinBox: if (const QStyleOptionSpinBox *vopt = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) { // Add button + frame widths + const qreal dpi = QStyleHelper::dpi(opt); const bool hasButtons = (vopt->buttonSymbols != QAbstractSpinBox::NoButtons); - const int buttonWidth = hasButtons ? proxy()->subControlRect(CC_SpinBox, vopt, SC_SpinBoxUp, widget).width() : 0; + const int buttonWidth = hasButtons ? qRound(QStyleHelper::dpiScaled(16, dpi)) : 0; const int fw = vopt->frame ? proxy()->pixelMetric(PM_SpinBoxFrameWidth, vopt, widget) : 0; sz += QSize(buttonWidth + 2*fw, 2*fw); } diff --git a/src/widgets/styles/qfusionstyle.cpp b/src/widgets/styles/qfusionstyle.cpp index 0c668913e9..c4bc52aa87 100644 --- a/src/widgets/styles/qfusionstyle.cpp +++ b/src/widgets/styles/qfusionstyle.cpp @@ -1590,7 +1590,7 @@ void QFusionStyle::drawControl(ControlElement element, const QStyleOption *optio (option->styleObject && option->styleObject->property("_q_isComboBoxPopupItem").toBool())) ignoreCheckMark = true; //ignore the checkmarks provided by the QComboMenuDelegate - if (!ignoreCheckMark) { + if (!ignoreCheckMark || menuItem->state & (State_On | State_Off)) { // Check, using qreal and QRectF to avoid error accumulation const qreal boxMargin = dpiScaled(3.5, option); const qreal boxWidth = checkcol - 2 * boxMargin; @@ -1601,7 +1601,7 @@ void QFusionStyle::drawControl(ControlElement element, const QStyleOption *optio if (checkable) { if (menuItem->checkType & QStyleOptionMenuItem::Exclusive) { // Radio button - if (checked || sunken) { + if (menuItem->state & State_On || checked || sunken) { painter->setRenderHint(QPainter::Antialiasing); painter->setPen(Qt::NoPen); @@ -1617,8 +1617,10 @@ void QFusionStyle::drawControl(ControlElement element, const QStyleOption *optio QStyleOptionButton box; box.QStyleOption::operator=(*option); box.rect = checkRect; - if (checked) + if (checked || menuItem->state & State_On) box.state |= State_On; + else + box.state |= State_Off; proxy()->drawPrimitive(PE_IndicatorCheckBox, &box, painter, widget); } } @@ -2411,7 +2413,7 @@ void QFusionStyle::drawComplexControl(ComplexControl control, const QStyleOption int oldMin = styleObject->property("_q_stylemin").toInt(); int oldMax = styleObject->property("_q_stylemax").toInt(); QRect oldRect = styleObject->property("_q_stylerect").toRect(); - QStyle::State oldState = static_cast<QStyle::State>(styleObject->property("_q_stylestate").value<QStyle::State::Int>()); + QStyle::State oldState = static_cast<QStyle::State>(qvariant_cast<QStyle::State::Int>(styleObject->property("_q_stylestate"))); uint oldActiveControls = styleObject->property("_q_stylecontrols").toUInt(); // a scrollbar is transient when the the scrollbar itself and diff --git a/src/widgets/styles/qstyleoption.cpp b/src/widgets/styles/qstyleoption.cpp index 05ee566dd5..4b861d938e 100644 --- a/src/widgets/styles/qstyleoption.cpp +++ b/src/widgets/styles/qstyleoption.cpp @@ -1453,7 +1453,7 @@ QStyleOptionTab::QStyleOptionTab(int version) \value None A normal tab button. \value HasFrame The tab button is positioned on a tab frame - \sa features + \sa QStyleOptionToolBar::features */ /*! diff --git a/src/widgets/util/qscrollerproperties.cpp b/src/widgets/util/qscrollerproperties.cpp index 0306f54faa..be763f182e 100644 --- a/src/widgets/util/qscrollerproperties.cpp +++ b/src/widgets/util/qscrollerproperties.cpp @@ -261,9 +261,9 @@ void QScrollerProperties::setScrollMetric(ScrollMetric metric, const QVariant &v case OvershootDragDistanceFactor: d->overshootDragDistanceFactor = qBound(qreal(0), value.toReal(), qreal(1)); break; case OvershootScrollDistanceFactor: d->overshootScrollDistanceFactor = qBound(qreal(0), value.toReal(), qreal(1)); break; case OvershootScrollTime: d->overshootScrollTime = value.toReal(); break; - case HorizontalOvershootPolicy: d->hOvershootPolicy = value.value<QScrollerProperties::OvershootPolicy>(); break; - case VerticalOvershootPolicy: d->vOvershootPolicy = value.value<QScrollerProperties::OvershootPolicy>(); break; - case FrameRate: d->frameRate = value.value<QScrollerProperties::FrameRates>(); break; + case HorizontalOvershootPolicy: d->hOvershootPolicy = qvariant_cast<QScrollerProperties::OvershootPolicy>(value); break; + case VerticalOvershootPolicy: d->vOvershootPolicy = qvariant_cast<QScrollerProperties::OvershootPolicy>(value); break; + case FrameRate: d->frameRate = qvariant_cast<QScrollerProperties::FrameRates>(value); break; case ScrollMetricCount: break; } } diff --git a/src/widgets/widgets/qcombobox.cpp b/src/widgets/widgets/qcombobox.cpp index 19b442f477..d786c7ff83 100644 --- a/src/widgets/widgets/qcombobox.cpp +++ b/src/widgets/widgets/qcombobox.cpp @@ -129,7 +129,15 @@ QStyleOptionMenuItem QComboMenuDelegate::getStyleOption(const QStyleOptionViewIt if (option.state & QStyle::State_Selected) menuOption.state |= QStyle::State_Selected; menuOption.checkType = QStyleOptionMenuItem::NonExclusive; - menuOption.checked = mCombo->currentIndex() == index.row(); + // a valid checkstate means that the model has checkable items + const QVariant checkState = index.data(Qt::CheckStateRole); + if (!checkState.isValid()) { + menuOption.checked = mCombo->currentIndex() == index.row(); + } else { + menuOption.checked = qvariant_cast<int>(checkState) == Qt::Checked; + menuOption.state |= qvariant_cast<int>(checkState) == Qt::Checked + ? QStyle::State_On : QStyle::State_Off; + } if (QComboBoxDelegate::isSeparator(index)) menuOption.menuItemType = QStyleOptionMenuItem::Separator; else @@ -164,7 +172,7 @@ QStyleOptionMenuItem QComboMenuDelegate::getStyleOption(const QStyleOptionViewIt // that order, also override the font for the popup menu. QVariant fontRoleData = index.data(Qt::FontRole); if (fontRoleData.isValid()) { - menuOption.font = fontRoleData.value<QFont>(); + menuOption.font = qvariant_cast<QFont>(fontRoleData); } else if (mCombo->testAttribute(Qt::WA_SetFont) || mCombo->testAttribute(Qt::WA_MacSmallSize) || mCombo->testAttribute(Qt::WA_MacMiniSize) @@ -179,6 +187,55 @@ QStyleOptionMenuItem QComboMenuDelegate::getStyleOption(const QStyleOptionViewIt return menuOption; } +bool QComboMenuDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, + const QStyleOptionViewItem &option, const QModelIndex &index) +{ + Q_ASSERT(event); + Q_ASSERT(model); + + // make sure that the item is checkable + Qt::ItemFlags flags = model->flags(index); + if (!(flags & Qt::ItemIsUserCheckable) || !(option.state & QStyle::State_Enabled) + || !(flags & Qt::ItemIsEnabled)) + return false; + + // make sure that we have a check state + const QVariant checkState = index.data(Qt::CheckStateRole); + if (!checkState.isValid()) + return false; + + // make sure that we have the right event type + if ((event->type() == QEvent::MouseButtonRelease) + || (event->type() == QEvent::MouseButtonDblClick) + || (event->type() == QEvent::MouseButtonPress)) { + QMouseEvent *me = static_cast<QMouseEvent*>(event); + if (me->button() != Qt::LeftButton) + return false; + + if ((event->type() == QEvent::MouseButtonPress) + || (event->type() == QEvent::MouseButtonDblClick)) { + pressedIndex = index.row(); + return false; + } + + if (index.row() != pressedIndex) + return false; + pressedIndex = -1; + + } else if (event->type() == QEvent::KeyPress) { + if (static_cast<QKeyEvent*>(event)->key() != Qt::Key_Space + && static_cast<QKeyEvent*>(event)->key() != Qt::Key_Select) + return false; + } else { + return false; + } + + // we don't support user-tristate items in QComboBox (not implemented in any style) + Qt::CheckState newState = (static_cast<Qt::CheckState>(checkState.toInt()) == Qt::Checked) + ? Qt::Unchecked : Qt::Checked; + return model->setData(index, newState, Qt::CheckStateRole); +} + #if QT_CONFIG(completer) void QComboBoxPrivate::_q_completerActivated(const QModelIndex &index) { diff --git a/src/widgets/widgets/qcombobox_p.h b/src/widgets/widgets/qcombobox_p.h index c79406eafd..7a3fcf6e0f 100644 --- a/src/widgets/widgets/qcombobox_p.h +++ b/src/widgets/widgets/qcombobox_p.h @@ -266,7 +266,9 @@ class Q_AUTOTEST_EXPORT QComboMenuDelegate : public QAbstractItemDelegate { Q_OBJECT public: - QComboMenuDelegate(QObject *parent, QComboBox *cmb) : QAbstractItemDelegate(parent), mCombo(cmb) {} + QComboMenuDelegate(QObject *parent, QComboBox *cmb) + : QAbstractItemDelegate(parent), mCombo(cmb), pressedIndex(-1) + {} protected: void paint(QPainter *painter, @@ -282,11 +284,14 @@ protected: return mCombo->style()->sizeFromContents( QStyle::CT_MenuItem, &opt, option.rect.size(), mCombo); } + bool editorEvent(QEvent *event, QAbstractItemModel *model, + const QStyleOptionViewItem &option, const QModelIndex &index) override; private: QStyleOptionMenuItem getStyleOption(const QStyleOptionViewItem &option, const QModelIndex &index) const; QComboBox *mCombo; + int pressedIndex; }; // ### Qt6: QStyledItemDelegate ? diff --git a/src/widgets/widgets/qmaccocoaviewcontainer_mac.mm b/src/widgets/widgets/qmaccocoaviewcontainer_mac.mm index 88baf0410b..f261314c64 100644 --- a/src/widgets/widgets/qmaccocoaviewcontainer_mac.mm +++ b/src/widgets/widgets/qmaccocoaviewcontainer_mac.mm @@ -138,7 +138,7 @@ QMacCocoaViewContainerPrivate::~QMacCocoaViewContainerPrivate() */ QMacCocoaViewContainer::QMacCocoaViewContainer(NSView *view, QWidget *parent) - : QWidget(*new QMacCocoaViewContainerPrivate, parent, 0) + : QWidget(*new QMacCocoaViewContainerPrivate, parent, {}) { // Ensures that we have a QWindow, even if we're not a top level widget setAttribute(Qt::WA_NativeWindow); diff --git a/src/widgets/widgets/qmainwindow.cpp b/src/widgets/widgets/qmainwindow.cpp index 7f0f3342b6..4f94b81b19 100644 --- a/src/widgets/widgets/qmainwindow.cpp +++ b/src/widgets/widgets/qmainwindow.cpp @@ -1364,6 +1364,8 @@ void QMainWindow::setUnifiedTitleAndToolBarOnMac(bool set) createWinId(); QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface(); + if (!nativeInterface) + return; // Not Cocoa platform plugin. QPlatformNativeInterface::NativeResourceForIntegrationFunction function = nativeInterface->nativeResourceFunctionForIntegration("setContentBorderEnabled"); if (!function) diff --git a/src/widgets/widgets/qtabbar.cpp b/src/widgets/widgets/qtabbar.cpp index 4e75cca704..aff95b0931 100644 --- a/src/widgets/widgets/qtabbar.cpp +++ b/src/widgets/widgets/qtabbar.cpp @@ -207,8 +207,8 @@ void QTabBarPrivate::initBasicStyleOption(QStyleOptionTab *option, int tabIndex) else option->selectedPosition = QStyleOptionTab::NotAdjacent; - const bool paintBeginning = (tabIndex == 0) || (dragInProgress && tabIndex == pressedIndex + 1); - const bool paintEnd = (tabIndex == totalTabs - 1) || (dragInProgress && tabIndex == pressedIndex - 1); + const bool paintBeginning = (tabIndex == firstVisible) || (dragInProgress && tabIndex == pressedIndex + 1); + const bool paintEnd = (tabIndex == lastVisible - 1) || (dragInProgress && tabIndex == pressedIndex - 1); if (paintBeginning) { if (paintEnd) option->position = QStyleOptionTab::OnlyOneTab; @@ -459,6 +459,7 @@ void QTabBarPrivate::layoutTabs() int i; bool vertTabs = verticalTabs(shape); int tabChainIndex = 0; + int hiddenTabs = 0; Qt::Alignment tabAlignment = Qt::Alignment(q->style()->styleHint(QStyle::SH_TabBar_Alignment, nullptr, q)); QVector<QLayoutStruct> tabChain(tabList.count() + 2); @@ -484,7 +485,11 @@ void QTabBarPrivate::layoutTabs() int minx = 0; int x = 0; int maxHeight = 0; - for (i = 0; i < tabList.count(); ++i, ++tabChainIndex) { + for (i = 0; i < tabList.count(); ++i) { + if (!tabList.at(i).visible) { + ++hiddenTabs; + continue; + } QSize sz = q->tabSizeHint(i); tabList[i].maxRect = QRect(x, 0, sz.width(), sz.height()); x += sz.width(); @@ -500,6 +505,7 @@ void QTabBarPrivate::layoutTabs() if (!expanding) tabChain[tabChainIndex].maximumSize = tabChain[tabChainIndex].sizeHint; + ++tabChainIndex; } last = minx; @@ -509,7 +515,11 @@ void QTabBarPrivate::layoutTabs() int miny = 0; int y = 0; int maxWidth = 0; - for (i = 0; i < tabList.count(); ++i, ++tabChainIndex) { + for (i = 0; i < tabList.count(); ++i) { + if (!tabList.at(i).visible) { + ++hiddenTabs; + continue; + } QSize sz = q->tabSizeHint(i); tabList[i].maxRect = QRect(0, y, sz.width(), sz.height()); y += sz.height(); @@ -525,6 +535,7 @@ void QTabBarPrivate::layoutTabs() if (!expanding) tabChain[tabChainIndex].maximumSize = tabChain[tabChainIndex].sizeHint; + ++tabChainIndex; } last = miny; @@ -538,14 +549,20 @@ void QTabBarPrivate::layoutTabs() && (tabAlignment != Qt::AlignRight) && (tabAlignment != Qt::AlignJustify); tabChain[tabChainIndex].empty = true; - Q_ASSERT(tabChainIndex == tabChain.count() - 1); // add an assert just to make sure. + Q_ASSERT(tabChainIndex == tabChain.count() - 1 - hiddenTabs); // add an assert just to make sure. // Do the calculation qGeomCalc(tabChain, 0, tabChain.count(), 0, qMax(available, last), 0); // Use the results + hiddenTabs = 0; for (i = 0; i < tabList.count(); ++i) { - const QLayoutStruct &lstruct = tabChain.at(i + 1); + if (!tabList.at(i).visible) { + tabList[i].rect = QRect(); + ++hiddenTabs; + continue; + } + const QLayoutStruct &lstruct = tabChain.at(i + 1 - hiddenTabs); if (!vertTabs) tabList[i].rect.setRect(lstruct.pos, 0, lstruct.size, maxExtent); else @@ -975,11 +992,15 @@ int QTabBar::insertTab(int index, const QIcon& icon, const QString &text) #ifndef QT_NO_SHORTCUT d->tabList[index].shortcutId = grabShortcut(QKeySequence::mnemonic(text)); #endif + d->firstVisible = qMax(qMin(index, d->firstVisible), 0); + d->lastVisible = qMax(index, d->lastVisible); d->refresh(); if (d->tabList.count() == 1) setCurrentIndex(index); - else if (index <= d->currentIndex) + else if (index <= d->currentIndex) { ++d->currentIndex; + ++d->lastVisible; + } if (d->closeButtonOnTabs) { QStyleOptionTab opt; @@ -1035,6 +1056,9 @@ void QTabBar::removeTab(int index) if (d->tabList[i].lastTab > index) --d->tabList[i].lastTab; } + + d->calculateFirstLastVisible(index, false, true); + if (index == d->currentIndex) { // The current tab is going away, in order to make sure // we emit that "current has changed", we need to reset this @@ -1045,16 +1069,14 @@ void QTabBar::removeTab(int index) case SelectPreviousTab: if (newIndex > index) newIndex--; - if (d->validIndex(newIndex)) + if (d->validIndex(newIndex) && d->tabList.at(newIndex).visible) break; Q_FALLTHROUGH(); case SelectRightTab: - newIndex = index; - if (newIndex >= d->tabList.size()) - newIndex = d->tabList.size() - 1; + newIndex = qBound(d->firstVisible, index, d->lastVisible); break; case SelectLeftTab: - newIndex = index - 1; + newIndex = qBound(d->firstVisible, index-1, d->lastVisible); if (newIndex < 0) newIndex = 0; break; @@ -1118,9 +1140,52 @@ void QTabBar::setTabEnabled(int index, bool enabled) #endif update(); if (!enabled && index == d->currentIndex) - setCurrentIndex(d->validIndex(index+1)?index+1:0); - else if (enabled && !d->validIndex(d->currentIndex)) - setCurrentIndex(index); + setCurrentIndex(d->selectNewCurrentIndexFrom(index+1)); + else if (enabled && !isTabVisible(d->currentIndex)) + setCurrentIndex(d->selectNewCurrentIndexFrom(index)); + } +} + + +/*! + Returns true if the tab at position \a index is visible; otherwise + returns false. + \since 5.15 +*/ +bool QTabBar::isTabVisible(int index) const +{ + Q_D(const QTabBar); + if (d->validIndex(index)) + return d->tabList.at(index).visible; + return false; +} + +/*! + If \a visible is true, make the tab at position \a index visible, + otherwise make it hidden. + \since 5.15 +*/ +void QTabBar::setTabVisible(int index, bool visible) +{ + Q_D(QTabBar); + if (QTabBarPrivate::Tab *tab = d->at(index)) { + d->layoutDirty = (visible != tab->visible); + if (!d->layoutDirty) + return; + tab->visible = visible; + if (tab->leftWidget) + tab->leftWidget->setVisible(visible); + if (tab->rightWidget) + tab->rightWidget->setVisible(visible); +#ifndef QT_NO_SHORTCUT + setShortcutEnabled(tab->shortcutId, visible); +#endif + d->calculateFirstLastVisible(index, visible, false); + if (!visible && index == d->currentIndex) { + const int newindex = d->selectNewCurrentIndexFrom(index+1); + setCurrentIndex(newindex); + } + update(); } } @@ -1291,7 +1356,7 @@ QVariant QTabBar::tabData(int index) const /*! Returns the visual rectangle of the tab at position \a - index, or a null rectangle if \a index is out of range. + index, or a null rectangle if \a index is hidden, or out of range. */ QRect QTabBar::tabRect(int index) const { @@ -1299,6 +1364,8 @@ QRect QTabBar::tabRect(int index) const if (const QTabBarPrivate::Tab *tab = d->at(index)) { if (d->layoutDirty) const_cast<QTabBarPrivate*>(d)->layoutTabs(); + if (!tab->visible) + return QRect(); QRect r = tab->rect; if (verticalTabs(d->shape)) r.translate(0, -d->scrollOffset); @@ -1429,8 +1496,10 @@ QSize QTabBar::sizeHint() const if (d->layoutDirty) const_cast<QTabBarPrivate*>(d)->layoutTabs(); QRect r; - for (int i = 0; i < d->tabList.count(); ++i) - r = r.united(d->tabList.at(i).maxRect); + for (int i = 0; i < d->tabList.count(); ++i) { + if (d->tabList.at(i).visible) + r = r.united(d->tabList.at(i).maxRect); + } QSize sz = QApplication::globalStrut(); return r.size().expandedTo(sz); } @@ -1444,8 +1513,10 @@ QSize QTabBar::minimumSizeHint() const const_cast<QTabBarPrivate*>(d)->layoutTabs(); if (!d->useScrollButtons) { QRect r; - for (int i = 0; i < d->tabList.count(); ++i) - r = r.united(d->tabList.at(i).minRect); + for (int i = 0; i < d->tabList.count(); ++i) { + if (d->tabList.at(i).visible) + r = r.united(d->tabList.at(i).minRect); + } return r.size().expandedTo(QApplication::globalStrut()); } if (verticalTabs(d->shape)) @@ -1746,6 +1817,8 @@ void QTabBar::paintEvent(QPaintEvent *) p.drawPrimitive(QStyle::PE_FrameTabBarBase, optTabBase); for (int i = 0; i < d->tabList.count(); ++i) { + if (!d->at(i)->visible) + continue; QStyleOptionTab tab; initStyleOption(&tab, i); if (d->paintWithOffsets && d->tabList[i].dragOffset != 0) { @@ -1820,6 +1893,65 @@ void QTabBar::paintEvent(QPaintEvent *) } /* + When index changes visibility, we have to find first & last visible indexes. + If remove is set, we force both + */ +void QTabBarPrivate::calculateFirstLastVisible(int index, bool visible, bool remove) +{ + if (visible) { + firstVisible = qMin(index, firstVisible); + lastVisible = qMax(index, lastVisible); + } else { + if (remove || (index == firstVisible)) { + firstVisible = -1; + for (int i = 0; i < tabList.count(); ++i) { + if (tabList.at(i).visible) { + firstVisible = i; + break; + } + } + if (firstVisible < 0) + firstVisible = 0; + } + if (remove || (index == lastVisible)) { + lastVisible = -1; + for (int i = tabList.count() - 1; i >= 0; --i) { + if (tabList.at(i).visible) { + lastVisible = i; + break; + } + } + } + } +} + +/* + Selects the new current index starting at "fromIndex". If "fromIndex" is visible we're done. + Else it tries any index AFTER fromIndex, then any BEFORE fromIndex and, if everything fails, + it returns -1 indicating that no index is available + */ +int QTabBarPrivate::selectNewCurrentIndexFrom(int fromIndex) +{ + int newindex = -1; + for (int i = fromIndex; i < tabList.count(); ++i) { + if (at(i)->visible && at(i)->enabled) { + newindex = i; + break; + } + } + if (newindex < 0) { + for (int i = fromIndex-1; i > -1; --i) { + if (at(i)->visible && at(i)->enabled) { + newindex = i; + break; + } + } + } + + return newindex; +} + +/* Given that index at position from moved to position to where return where index goes. */ int QTabBarPrivate::calculateNewPosition(int from, int to, int index) const diff --git a/src/widgets/widgets/qtabbar.h b/src/widgets/widgets/qtabbar.h index fc619355f0..c49c12f38c 100644 --- a/src/widgets/widgets/qtabbar.h +++ b/src/widgets/widgets/qtabbar.h @@ -105,6 +105,9 @@ public: bool isTabEnabled(int index) const; void setTabEnabled(int index, bool); + bool isTabVisible(int index) const; + void setTabVisible(int index, bool); + QString tabText(int index) const; void setTabText(int index, const QString &text); diff --git a/src/widgets/widgets/qtabbar_p.h b/src/widgets/widgets/qtabbar_p.h index 6f77579108..ac4cbd32a8 100644 --- a/src/widgets/widgets/qtabbar_p.h +++ b/src/widgets/widgets/qtabbar_p.h @@ -88,7 +88,7 @@ class Q_WIDGETS_EXPORT QTabBarPrivate : public QWidgetPrivate Q_DECLARE_PUBLIC(QTabBar) public: QTabBarPrivate() - :currentIndex(-1), pressedIndex(-1), shape(QTabBar::RoundedNorth), layoutDirty(false), + :currentIndex(-1), pressedIndex(-1), firstVisible(0), lastVisible(-1), shape(QTabBar::RoundedNorth), layoutDirty(false), drawBase(true), scrollOffset(0), hoverIndex(-1), elideModeSetByUser(false), useScrollButtonsSetByUser(false), expanding(true), closeButtonOnTabs(false), selectionBehaviorOnRemove(QTabBar::SelectRightTab), paintWithOffsets(true), movable(false), dragInProgress(false), documentMode(false), autoHide(false), changeCurrentOnDrag(false), @@ -97,6 +97,8 @@ public: int currentIndex; int pressedIndex; + int firstVisible; + int lastVisible; QTabBar::Shape shape; bool layoutDirty; bool drawBase; @@ -104,7 +106,7 @@ public: struct Tab { inline Tab(const QIcon &ico, const QString &txt) - : enabled(true) , shortcutId(0), text(txt), icon(ico), + : enabled(true) , visible(true), shortcutId(0), text(txt), icon(ico), leftWidget(nullptr), rightWidget(nullptr), lastTab(-1), dragOffset(0) #if QT_CONFIG(animation) , animation(nullptr) @@ -112,6 +114,7 @@ public: {} bool operator==(const Tab &other) const { return &other == this; } bool enabled; + bool visible; int shortcutId; QString text; #ifndef QT_NO_TOOLTIP @@ -170,6 +173,8 @@ public: QList<Tab> tabList; mutable QHash<QString, QSize> textSizes; + void calculateFirstLastVisible(int index, bool visible, bool remove); + int selectNewCurrentIndexFrom(int currentIndex); int calculateNewPosition(int from, int to, int index) const; void slide(int from, int to); void init(); diff --git a/src/widgets/widgets/qtabwidget.cpp b/src/widgets/widgets/qtabwidget.cpp index 28c91a89e7..f0bfe67e3a 100644 --- a/src/widgets/widgets/qtabwidget.cpp +++ b/src/widgets/widgets/qtabwidget.cpp @@ -544,8 +544,8 @@ bool QTabWidget::isTabEnabled(int index) const } /*! - If \a enable is true, the page at position \a index is enabled; otherwise the page at position \a index is - disabled. The page's tab is redrawn appropriately. + If \a enable is true, the page at position \a index is enabled; otherwise the page at + position \a index is disabled. The page's tab is redrawn appropriately. QTabWidget uses QWidget::setEnabled() internally, rather than keeping a separate flag. @@ -566,6 +566,44 @@ void QTabWidget::setTabEnabled(int index, bool enable) } /*! + Returns true if the page at position \a index is visible; otherwise returns false. + + \sa setTabVisible() + \since 5.15 +*/ + +bool QTabWidget::isTabVisible(int index) const +{ + Q_D(const QTabWidget); + return d->tabs->isTabVisible(index); +} + +/*! + If \a visible is true, the page at position \a index is visible; otherwise the page at + position \a index is hidden. The page's tab is redrawn appropriately. + + \sa isTabVisible() + \since 5.15 +*/ + +void QTabWidget::setTabVisible(int index, bool visible) +{ + Q_D(QTabWidget); + QWidget *widget = d->stack->widget(index); + bool currentVisible = d->tabs->isTabVisible(d->tabs->currentIndex()); + d->tabs->setTabVisible(index, visible); + if (!visible) { + if (widget) + widget->setVisible(false); + } else if (!currentVisible) { + setCurrentIndex(index); + if (widget) + widget->setVisible(true); + } + setUpLayout(); +} + +/*! \fn void QTabWidget::setCornerWidget(QWidget *widget, Qt::Corner corner) Sets the given \a widget to be shown in the specified \a corner of the @@ -848,7 +886,13 @@ QSize QTabWidget::sizeHint() const QTabWidget *that = const_cast<QTabWidget*>(this); that->setUpLayout(true); } - QSize s(d->stack->sizeHint()); + QSize s; + for (int i=0; i< d->stack->count(); ++i) { + if (const QWidget* w = d->stack->widget(i)) { + if (d->tabs->isTabVisible(i)) + s = s.expandedTo(w->sizeHint()); + } + } QSize t; if (!d->isAutoHidden()) { t = d->tabs->sizeHint(); diff --git a/src/widgets/widgets/qtabwidget.h b/src/widgets/widgets/qtabwidget.h index f55e71488b..e6b3f93303 100644 --- a/src/widgets/widgets/qtabwidget.h +++ b/src/widgets/widgets/qtabwidget.h @@ -82,6 +82,9 @@ public: bool isTabEnabled(int index) const; void setTabEnabled(int index, bool); + bool isTabVisible(int index) const; + void setTabVisible(int index, bool); + QString tabText(int index) const; void setTabText(int index, const QString &); diff --git a/src/widgets/widgets/qtoolbarlayout.cpp b/src/widgets/widgets/qtoolbarlayout.cpp index c95fa4ceee..93aded6876 100644 --- a/src/widgets/widgets/qtoolbarlayout.cpp +++ b/src/widgets/widgets/qtoolbarlayout.cpp @@ -360,6 +360,8 @@ void QToolBarLayout::updateMacBorderMetrics() return; QPlatformNativeInterface *nativeInterface = QApplication::platformNativeInterface(); + if (!nativeInterface) + return; // Not Cocoa platform plugin. QPlatformNativeInterface::NativeResourceForIntegrationFunction function = nativeInterface->nativeResourceFunctionForIntegration("registerContentBorderArea"); if (!function) |