diff --git a/src/plugins/coreplugin/CMakeLists.txt b/src/plugins/coreplugin/CMakeLists.txt index 825884c6559..b86eb7ea422 100644 --- a/src/plugins/coreplugin/CMakeLists.txt +++ b/src/plugins/coreplugin/CMakeLists.txt @@ -148,6 +148,7 @@ add_qtc_plugin(Core variablechooser.cpp variablechooser.h vcsmanager.cpp vcsmanager.h versiondialog.cpp versiondialog.h + welcomepagehelper.cpp welcomepagehelper.h windowsupport.cpp windowsupport.h EXPLICIT_MOC dialogs/filepropertiesdialog.h ) diff --git a/src/plugins/coreplugin/coreplugin.pro b/src/plugins/coreplugin/coreplugin.pro index ad6d1f0a89a..129c1c2fef8 100644 --- a/src/plugins/coreplugin/coreplugin.pro +++ b/src/plugins/coreplugin/coreplugin.pro @@ -112,7 +112,8 @@ SOURCES += corejsextensions.cpp \ systemsettings.cpp \ coreicons.cpp \ diffservice.cpp \ - menubarfilter.cpp + menubarfilter.cpp \ + welcomepagehelper.cpp HEADERS += corejsextensions.h \ mainwindow.h \ @@ -228,7 +229,8 @@ HEADERS += corejsextensions.h \ editormanager/documentmodel_p.h \ diffservice.h \ menubarfilter.h \ - editormanager/ieditorfactory_p.h + editormanager/ieditorfactory_p.h \ + welcomepagehelper.h FORMS += dialogs/newdialog.ui \ dialogs/saveitemsdialog.ui \ diff --git a/src/plugins/coreplugin/coreplugin.qbs b/src/plugins/coreplugin/coreplugin.qbs index adc10de5871..627711dfb5d 100644 --- a/src/plugins/coreplugin/coreplugin.qbs +++ b/src/plugins/coreplugin/coreplugin.qbs @@ -186,6 +186,8 @@ Project { "vcsmanager.h", "versiondialog.cpp", "versiondialog.h", + "welcomepagehelper.cpp", + "welcomepagehelper.h", "windowsupport.cpp", "windowsupport.h", ] diff --git a/src/plugins/coreplugin/welcomepagehelper.cpp b/src/plugins/coreplugin/welcomepagehelper.cpp new file mode 100644 index 00000000000..86159f7cb7f --- /dev/null +++ b/src/plugins/coreplugin/welcomepagehelper.cpp @@ -0,0 +1,198 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "welcomepagehelper.h" + +#include +#include +#include + +#include +#include +#include + +namespace Core { + +using namespace Utils; + +SearchBox::SearchBox(QWidget *parent) + : WelcomePageFrame(parent) +{ + QPalette pal; + pal.setColor(QPalette::Base, Utils::creatorTheme()->color(Theme::Welcome_BackgroundColor)); + + m_lineEdit = new FancyLineEdit; + m_lineEdit->setFiltering(true); + m_lineEdit->setFrame(false); + QFont f = font(); + f.setPixelSize(14); + m_lineEdit->setFont(f); + m_lineEdit->setAttribute(Qt::WA_MacShowFocusRect, false); + m_lineEdit->setPalette(pal); + + auto box = new QHBoxLayout(this); + box->setContentsMargins(10, 3, 3, 3); + box->addWidget(m_lineEdit); +} + +GridView::GridView(QWidget *parent) + : QTableView(parent) +{ + setVerticalScrollMode(ScrollPerPixel); + horizontalHeader()->hide(); + horizontalHeader()->setDefaultSectionSize(GridProxyModel::GridItemWidth); + verticalHeader()->hide(); + verticalHeader()->setDefaultSectionSize(GridProxyModel::GridItemHeight); + setMouseTracking(true); // To enable hover. + setSelectionMode(QAbstractItemView::NoSelection); + setFrameShape(QFrame::NoFrame); + setGridStyle(Qt::NoPen); + + QPalette pal; + pal.setColor(QPalette::Base, Utils::creatorTheme()->color(Theme::Welcome_BackgroundColor)); + setPalette(pal); // Makes a difference on Mac. +} + +void GridView::leaveEvent(QEvent *) +{ + QHoverEvent hev(QEvent::HoverLeave, QPointF(), QPointF()); + viewportEvent(&hev); // Seemingly needed to kill the hover paint. +} + +void GridProxyModel::setSourceModel(QAbstractItemModel *newModel) +{ + if (m_sourceModel == newModel) + return; + if (m_sourceModel) + disconnect(m_sourceModel, nullptr, this, nullptr); + m_sourceModel = newModel; + if (newModel) { + connect(newModel, &QAbstractItemModel::layoutAboutToBeChanged, this, [this] { + emit layoutAboutToBeChanged(); + }); + connect(newModel, &QAbstractItemModel::layoutChanged, this, [this] { + emit layoutChanged(); + }); + connect(newModel, &QAbstractItemModel::modelAboutToBeReset, this, [this] { + beginResetModel(); + }); + connect(newModel, &QAbstractItemModel::modelReset, this, [this] { endResetModel(); }); + connect(newModel, &QAbstractItemModel::rowsAboutToBeInserted, this, [this] { + beginResetModel(); + }); + connect(newModel, &QAbstractItemModel::rowsInserted, this, [this] { endResetModel(); }); + connect(newModel, &QAbstractItemModel::rowsAboutToBeRemoved, this, [this] { + beginResetModel(); + }); + connect(newModel, &QAbstractItemModel::rowsRemoved, this, [this] { endResetModel(); }); + } +} + +QAbstractItemModel *GridProxyModel::sourceModel() const +{ + return m_sourceModel; +} + +QVariant GridProxyModel::data(const QModelIndex &index, int role) const +{ + const OptModelIndex sourceIndex = mapToSource(index); + if (sourceIndex) + return sourceModel()->data(*sourceIndex, role); + return QVariant(); +} + +Qt::ItemFlags GridProxyModel::flags(const QModelIndex &index) const +{ + const OptModelIndex sourceIndex = mapToSource(index); + if (sourceIndex) + return sourceModel()->flags(*sourceIndex); + return Qt::ItemFlags(); +} + +bool GridProxyModel::hasChildren(const QModelIndex &parent) const +{ + const OptModelIndex sourceParent = mapToSource(parent); + if (sourceParent) + return sourceModel()->hasChildren(*sourceParent); + return false; +} + +void GridProxyModel::setColumnCount(int columnCount) +{ + if (columnCount == m_columnCount) + return; + QTC_ASSERT(columnCount >= 1, columnCount = 1); + m_columnCount = columnCount; + emit layoutChanged(); +} + +int GridProxyModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + int rows = sourceModel()->rowCount(QModelIndex()); + return (rows + m_columnCount - 1) / m_columnCount; +} + +int GridProxyModel::columnCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + return m_columnCount; +} + +QModelIndex GridProxyModel::index(int row, int column, const QModelIndex &) const +{ + return createIndex(row, column, nullptr); +} + +QModelIndex GridProxyModel::parent(const QModelIndex &) const +{ + return QModelIndex(); +} + +// The items at the lower right of the grid might not correspond to source items, if +// source's row count is not N*columnCount +OptModelIndex GridProxyModel::mapToSource(const QModelIndex &proxyIndex) const +{ + if (!proxyIndex.isValid()) + return QModelIndex(); + int sourceRow = proxyIndex.row() * m_columnCount + proxyIndex.column(); + if (sourceRow < sourceModel()->rowCount()) + return sourceModel()->index(sourceRow, 0); + return OptModelIndex(); +} + +QModelIndex GridProxyModel::mapFromSource(const QModelIndex &sourceIndex) const +{ + if (!sourceIndex.isValid()) + return QModelIndex(); + QTC_CHECK(sourceIndex.column() == 0); + int proxyRow = sourceIndex.row() / m_columnCount; + int proxyColumn = sourceIndex.row() % m_columnCount; + return index(proxyRow, proxyColumn, QModelIndex()); +} + +} // namespace Core diff --git a/src/plugins/coreplugin/welcomepagehelper.h b/src/plugins/coreplugin/welcomepagehelper.h new file mode 100644 index 00000000000..be5d3a274e0 --- /dev/null +++ b/src/plugins/coreplugin/welcomepagehelper.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "core_global.h" +#include "iwelcomepage.h" + +#include + +#include + +namespace Utils { class FancyLineEdit; } + +namespace Core { + +class CORE_EXPORT SearchBox : public WelcomePageFrame +{ +public: + SearchBox(QWidget *parent); + + Utils::FancyLineEdit *m_lineEdit = nullptr; +}; + +class CORE_EXPORT GridView : public QTableView +{ +public: + explicit GridView(QWidget *parent); +protected: + void leaveEvent(QEvent *) final; +}; + +using OptModelIndex = Utils::optional; + +class CORE_EXPORT GridProxyModel : public QAbstractItemModel +{ +public: + void setSourceModel(QAbstractItemModel *newModel); + QAbstractItemModel *sourceModel() const; + QVariant data(const QModelIndex &index, int role) const final; + Qt::ItemFlags flags(const QModelIndex &index) const final; + bool hasChildren(const QModelIndex &parent) const final; + void setColumnCount(int columnCount); + int rowCount(const QModelIndex &parent) const final; + int columnCount(const QModelIndex &parent) const final; + QModelIndex index(int row, int column, const QModelIndex &) const final; + QModelIndex parent(const QModelIndex &) const final; + + OptModelIndex mapToSource(const QModelIndex &proxyIndex) const; + QModelIndex mapFromSource(const QModelIndex &sourceIndex) const; + + static constexpr int GridItemWidth = 230; + static constexpr int GridItemHeight = 230; + static constexpr int GridItemGap = 10; + static constexpr int TagsSeparatorY = GridItemHeight - 60; + +private: + QAbstractItemModel *m_sourceModel = nullptr; + int m_columnCount = 1; +}; + +} // namespace Core diff --git a/src/plugins/qtsupport/gettingstartedwelcomepage.cpp b/src/plugins/qtsupport/gettingstartedwelcomepage.cpp index 9a9fdfe213f..a301ceb3ed9 100644 --- a/src/plugins/qtsupport/gettingstartedwelcomepage.cpp +++ b/src/plugins/qtsupport/gettingstartedwelcomepage.cpp @@ -38,30 +38,20 @@ #include #include #include +#include #include #include -#include -#include -#include #include #include #include -#include #include #include -#include -#include -#include -#include #include #include #include -#include #include #include -#include -#include #include using namespace Core; @@ -72,11 +62,6 @@ namespace Internal { const char C_FALLBACK_ROOT[] = "ProjectsFallbackRoot"; -const int itemWidth = 230; -const int itemHeight = 230; -const int itemGap = 10; -const int tagsSeparatorY = itemHeight - 60; - ExamplesWelcomePage::ExamplesWelcomePage(bool showExamples) : m_showExamples(showExamples) { @@ -243,182 +228,6 @@ static QFont sizedFont(int size, const QWidget *widget, bool underline = false) return f; } -class SearchBox : public WelcomePageFrame -{ -public: - SearchBox(QWidget *parent) - : WelcomePageFrame(parent) - { - QPalette pal; - pal.setColor(QPalette::Base, themeColor(Theme::Welcome_BackgroundColor)); - - m_lineEdit = new FancyLineEdit; - m_lineEdit->setFiltering(true); - m_lineEdit->setFrame(false); - m_lineEdit->setFont(sizedFont(14, this)); - m_lineEdit->setAttribute(Qt::WA_MacShowFocusRect, false); - m_lineEdit->setPalette(pal); - - auto box = new QHBoxLayout(this); - box->setContentsMargins(10, 3, 3, 3); - box->addWidget(m_lineEdit); - } - - FancyLineEdit *m_lineEdit; -}; - -class GridView : public QTableView -{ -public: - GridView(QWidget *parent) - : QTableView(parent) - { - setVerticalScrollMode(ScrollPerPixel); - horizontalHeader()->hide(); - horizontalHeader()->setDefaultSectionSize(itemWidth); - verticalHeader()->hide(); - verticalHeader()->setDefaultSectionSize(itemHeight); - setMouseTracking(true); // To enable hover. - setSelectionMode(QAbstractItemView::NoSelection); - setFrameShape(QFrame::NoFrame); - setGridStyle(Qt::NoPen); - - QPalette pal; - pal.setColor(QPalette::Base, themeColor(Theme::Welcome_BackgroundColor)); - setPalette(pal); // Makes a difference on Mac. - } - - void leaveEvent(QEvent *) final - { - QHoverEvent hev(QEvent::HoverLeave, QPointF(), QPointF()); - viewportEvent(&hev); // Seemingly needed to kill the hover paint. - } -}; - -class GridProxyModel : public QAbstractItemModel -{ -public: - using OptModelIndex = Utils::optional; - - void setSourceModel(QAbstractItemModel *newModel) - { - if (m_sourceModel == newModel) - return; - if (m_sourceModel) - disconnect(m_sourceModel, nullptr, this, nullptr); - m_sourceModel = newModel; - if (newModel) { - connect(newModel, &QAbstractItemModel::layoutAboutToBeChanged, this, [this] { - emit layoutAboutToBeChanged(); - }); - connect(newModel, &QAbstractItemModel::layoutChanged, this, [this] { - emit layoutChanged(); - }); - connect(newModel, &QAbstractItemModel::modelAboutToBeReset, this, [this] { - beginResetModel(); - }); - connect(newModel, &QAbstractItemModel::modelReset, this, [this] { endResetModel(); }); - connect(newModel, &QAbstractItemModel::rowsAboutToBeInserted, this, [this] { - beginResetModel(); - }); - connect(newModel, &QAbstractItemModel::rowsInserted, this, [this] { endResetModel(); }); - connect(newModel, &QAbstractItemModel::rowsAboutToBeRemoved, this, [this] { - beginResetModel(); - }); - connect(newModel, &QAbstractItemModel::rowsRemoved, this, [this] { endResetModel(); }); - } - } - - QAbstractItemModel *sourceModel() const - { - return m_sourceModel; - } - - QVariant data(const QModelIndex &index, int role) const final - { - const OptModelIndex sourceIndex = mapToSource(index); - if (sourceIndex) - return sourceModel()->data(*sourceIndex, role); - return QVariant(); - } - - Qt::ItemFlags flags(const QModelIndex &index) const final - { - const OptModelIndex sourceIndex = mapToSource(index); - if (sourceIndex) - return sourceModel()->flags(*sourceIndex); - return Qt::ItemFlags(); - } - - bool hasChildren(const QModelIndex &parent) const final - { - const OptModelIndex sourceParent = mapToSource(parent); - if (sourceParent) - return sourceModel()->hasChildren(*sourceParent); - return false; - } - - void setColumnCount(int columnCount) - { - if (columnCount == m_columnCount) - return; - QTC_ASSERT(columnCount >= 1, columnCount = 1); - m_columnCount = columnCount; - emit layoutChanged(); - } - - int rowCount(const QModelIndex &parent) const final - { - if (parent.isValid()) - return 0; - int rows = sourceModel()->rowCount(QModelIndex()); - return (rows + m_columnCount - 1) / m_columnCount; - } - - int columnCount(const QModelIndex &parent) const final - { - if (parent.isValid()) - return 0; - return m_columnCount; - } - - QModelIndex index(int row, int column, const QModelIndex &) const final - { - return createIndex(row, column, nullptr); - } - - QModelIndex parent(const QModelIndex &) const final - { - return QModelIndex(); - } - - // The items at the lower right of the grid might not correspond to source items, if - // source's row count is not N*columnCount - OptModelIndex mapToSource(const QModelIndex &proxyIndex) const - { - if (!proxyIndex.isValid()) - return QModelIndex(); - int sourceRow = proxyIndex.row() * m_columnCount + proxyIndex.column(); - if (sourceRow < sourceModel()->rowCount()) - return sourceModel()->index(sourceRow, 0); - return OptModelIndex(); - } - - QModelIndex mapFromSource(const QModelIndex &sourceIndex) const - { - if (!sourceIndex.isValid()) - return QModelIndex(); - QTC_CHECK(sourceIndex.column() == 0); - int proxyRow = sourceIndex.row() / m_columnCount; - int proxyColumn = sourceIndex.row() % m_columnCount; - return index(proxyRow, proxyColumn, QModelIndex()); - } - -private: - QAbstractItemModel *m_sourceModel = nullptr; - int m_columnCount = 1; -}; - class ExampleDelegate : public QStyledItemDelegate { Q_OBJECT @@ -436,13 +245,13 @@ public: const int d = 10; const int x = rc.x() + d; const int y = rc.y() + d; - const int w = rc.width() - 2 * d - itemGap; + const int w = rc.width() - 2 * d - GridProxyModel::GridItemGap; const int h = rc.height() - 2 * d; const bool hovered = option.state & QStyle::State_MouseOver; - const int tagsBase = tagsSeparatorY + 10; - const int shiftY = tagsSeparatorY - 20; - const int nameY = tagsSeparatorY - 20; + const int tagsBase = GridProxyModel::TagsSeparatorY + 10; + const int shiftY = GridProxyModel::TagsSeparatorY - 20; + const int nameY = GridProxyModel::TagsSeparatorY - 20; const QRect textRect = QRect(x, y + nameY, w, h); @@ -457,7 +266,7 @@ public: m_currentWidget = qobject_cast( const_cast(option.widget)); } - offset = m_startTime.elapsed() * itemHeight / 200; // Duration 200 ms. + offset = m_startTime.elapsed() * GridProxyModel::GridItemHeight / 200; // Duration 200 ms. if (offset < shiftY) QTimer::singleShot(5, this, &ExampleDelegate::goon); else if (offset > shiftY) @@ -530,7 +339,8 @@ public: // Separator line between text and 'Tags:' section painter->setPen(lightColor); - painter->drawLine(x, y + tagsSeparatorY, x + w, y + tagsSeparatorY); + painter->drawLine(x, y + GridProxyModel::TagsSeparatorY, + x + w, y + GridProxyModel::TagsSeparatorY); // The 'Tags:' section const int tagsHeight = h - tagsBase; @@ -578,7 +388,7 @@ public: auto mev = static_cast(ev); if (idx.isValid()) { const QPoint pos = mev->pos(); - if (pos.y() > option.rect.y() + tagsSeparatorY) { + if (pos.y() > option.rect.y() + GridProxyModel::TagsSeparatorY) { //const QStringList tags = idx.data(Tags).toStringList(); for (const auto &it : m_currentTagRects) { if (it.second.contains(pos)) @@ -641,8 +451,8 @@ public: m_searcher->setPlaceholderText(ExamplesWelcomePage::tr("Search in Examples...")); auto exampleSetSelector = new QComboBox(this); - exampleSetSelector->setMinimumWidth(itemWidth); - exampleSetSelector->setMaximumWidth(itemWidth); + exampleSetSelector->setMinimumWidth(GridProxyModel::GridItemWidth); + exampleSetSelector->setMaximumWidth(GridProxyModel::GridItemWidth); ExampleSetModel *exampleSetModel = m_examplesModel->exampleSetModel(); exampleSetSelector->setModel(exampleSetModel); exampleSetSelector->setCurrentIndex(exampleSetModel->selectedExampleSet()); @@ -675,7 +485,7 @@ public: int bestColumnCount() const { - return qMax(1, width() / (itemWidth + itemGap)); + return qMax(1, width() / (GridProxyModel::GridItemWidth + GridProxyModel::GridItemGap)); } void resizeEvent(QResizeEvent *ev) final