diff --git a/src/kpageview_p.cpp b/src/kpageview_p.cpp index a14932a..9e9a79a 100644 --- a/src/kpageview_p.cpp +++ b/src/kpageview_p.cpp @@ -1,630 +1,639 @@ /* This file is part of the KDE Libraries Copyright (C) 2006 Tobias Koenig (tokoe@kde.org) Copyright (C) 2007 Rafael Fernández López (ereslibre@kde.org) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kpageview_p.h" #include #include #include #include #include #include #include "kpagemodel.h" #include "loggingcategory.h" using namespace KDEPrivate; /** * KPagePlainView */ KPagePlainView::KPagePlainView(QWidget *parent) : QAbstractItemView(parent) { hide(); } QModelIndex KPagePlainView::indexAt(const QPoint &) const { return QModelIndex(); } void KPagePlainView::scrollTo(const QModelIndex &, ScrollHint) { } QRect KPagePlainView::visualRect(const QModelIndex &) const { return QRect(); } QModelIndex KPagePlainView::moveCursor(QAbstractItemView::CursorAction, Qt::KeyboardModifiers) { return QModelIndex(); } int KPagePlainView::horizontalOffset() const { return 0; } int KPagePlainView::verticalOffset() const { return 0; } bool KPagePlainView::isIndexHidden(const QModelIndex &) const { return false; } void KPagePlainView::setSelection(const QRect &, QFlags) { } QRegion KPagePlainView::visualRegionForSelection(const QItemSelection &) const { return QRegion(); } /** * KPageListView */ KPageListView::KPageListView(QWidget *parent) : QListView(parent) { setViewMode(QListView::ListMode); setMovement(QListView::Static); setVerticalScrollMode(QListView::ScrollPerPixel); QFont boldFont(font()); boldFont.setBold(true); setFont(boldFont); setItemDelegate(new KPageListViewDelegate(this)); } KPageListView::~KPageListView() { } void KPageListView::setModel(QAbstractItemModel *model) { /* KPageListViewProxy *proxy = new KPageListViewProxy( this ); proxy->setSourceModel( model ); proxy->rebuildMap(); connect( model, SIGNAL(layoutChanged()), proxy, SLOT(rebuildMap()) ); */ connect(model, &QAbstractItemModel::layoutChanged, this, &KPageListView::updateWidth); // QListView::setModel( proxy ); QListView::setModel(model); // Set our own selection model, which won't allow our current selection to be cleared setSelectionModel(new KDEPrivate::SelectionModel(model, this)); updateWidth(); } +void KPageListView::changeEvent(QEvent *event) +{ + QListView::changeEvent(event); + + if (event->type() == QEvent::FontChange) { + updateWidth(); + } +} + void KPageListView::updateWidth() { if (!model()) { return; } setFixedWidth(sizeHintForColumn(0) + verticalScrollBar()->sizeHint().width() + 5); } /** * KPageTreeView */ KPageTreeView::KPageTreeView(QWidget *parent) : QTreeView(parent) { header()->hide(); } void KPageTreeView::setModel(QAbstractItemModel *model) { connect(model, &QAbstractItemModel::layoutChanged, this, &KPageTreeView::updateWidth); QTreeView::setModel(model); // Set our own selection model, which won't allow our current selection to be cleared setSelectionModel(new KDEPrivate::SelectionModel(model, this)); updateWidth(); } void KPageTreeView::updateWidth() { if (!model()) { return; } int columns = model()->columnCount(); expandItems(); int width = 0; for (int i = 0; i < columns; ++i) { resizeColumnToContents(i); width = qMax(width, sizeHintForColumn(i)); } setFixedWidth(width + 25); } void KPageTreeView::expandItems(const QModelIndex &index) { setExpanded(index, true); const int count = model()->rowCount(index); for (int i = 0; i < count; ++i) { expandItems(model()->index(i, 0, index)); } } /** * KPageTabbedView */ KPageTabbedView::KPageTabbedView(QWidget *parent) : QAbstractItemView(parent) { // hide the viewport of the QAbstractScrollArea const QList list = findChildren(); for (int i = 0; i < list.count(); ++i) { list[ i ]->hide(); } setFrameShape(NoFrame); QVBoxLayout *layout = new QVBoxLayout(this); layout->setMargin(0); mTabWidget = new QTabWidget(this); connect(mTabWidget, &QTabWidget::currentChanged, this, &KPageTabbedView::currentPageChanged); layout->addWidget(mTabWidget); } KPageTabbedView::~KPageTabbedView() { if (model()) { for (int i = 0; i < mTabWidget->count(); ++i) { QWidget *page = qvariant_cast(model()->data(model()->index(i, 0), KPageModel::WidgetRole)); if (page) { page->setVisible(false); page->setParent(nullptr); // reparent our children before they are deleted } } } } void KPageTabbedView::setModel(QAbstractItemModel *model) { QAbstractItemView::setModel(model); connect(model, &QAbstractItemModel::layoutChanged, this, &KPageTabbedView::layoutChanged); layoutChanged(); } QModelIndex KPageTabbedView::indexAt(const QPoint &) const { if (model()) { return model()->index(0, 0); } else { return QModelIndex(); } } void KPageTabbedView::scrollTo(const QModelIndex &index, ScrollHint) { if (!index.isValid()) { return; } mTabWidget->setCurrentIndex(index.row()); } QRect KPageTabbedView::visualRect(const QModelIndex &) const { return QRect(); } QSize KPageTabbedView::minimumSizeHint() const { return mTabWidget->minimumSizeHint(); } QModelIndex KPageTabbedView::moveCursor(QAbstractItemView::CursorAction, Qt::KeyboardModifiers) { return QModelIndex(); } int KPageTabbedView::horizontalOffset() const { return 0; } int KPageTabbedView::verticalOffset() const { return 0; } bool KPageTabbedView::isIndexHidden(const QModelIndex &index) const { return (mTabWidget->currentIndex() != index.row()); } void KPageTabbedView::setSelection(const QRect &, QFlags) { } QRegion KPageTabbedView::visualRegionForSelection(const QItemSelection &) const { return QRegion(); } void KPageTabbedView::currentPageChanged(int index) { if (!model()) { return; } QModelIndex modelIndex = model()->index(index, 0); selectionModel()->setCurrentIndex(modelIndex, QItemSelectionModel::ClearAndSelect); } void KPageTabbedView::layoutChanged() { // save old position int pos = mTabWidget->currentIndex(); // clear tab bar int count = mTabWidget->count(); for (int i = 0; i < count; ++i) { mTabWidget->removeTab(0); } if (!model()) { return; } // add new tabs for (int i = 0; i < model()->rowCount(); ++i) { const QString title = model()->data(model()->index(i, 0)).toString(); const QIcon icon = model()->data(model()->index(i, 0), Qt::DecorationRole).value(); QWidget *page = qvariant_cast(model()->data(model()->index(i, 0), KPageModel::WidgetRole)); if (page) { QWidget *widget = new QWidget(this); QVBoxLayout *layout = new QVBoxLayout(widget); widget->setLayout(layout); layout->addWidget(page); page->setVisible(true); mTabWidget->addTab(widget, icon, title); } } mTabWidget->setCurrentIndex(pos); } void KPageTabbedView::dataChanged(const QModelIndex &index, const QModelIndex &, const QVector &roles) { if (!index.isValid()) { return; } if (index.row() < 0 || index.row() >= mTabWidget->count()) { return; } if (roles.isEmpty() || roles.contains(Qt::DisplayRole) || roles.contains(Qt::DecorationRole)) { const QString title = model()->data(index).toString(); const QIcon icon = model()->data(index, Qt::DecorationRole).value(); mTabWidget->setTabText(index.row(), title); mTabWidget->setTabIcon(index.row(), icon); } } /** * KPageListViewDelegate */ KPageListViewDelegate::KPageListViewDelegate(QObject *parent) : QAbstractItemDelegate(parent) { } static int layoutText(QTextLayout *layout, int maxWidth) { qreal height = 0; int textWidth = 0; layout->beginLayout(); while (true) { QTextLine line = layout->createLine(); if (!line.isValid()) { break; } line.setLineWidth(maxWidth); line.setPosition(QPointF(0, height)); height += line.height(); textWidth = qMax(textWidth, qRound(line.naturalTextWidth() + 0.5)); } layout->endLayout(); return textWidth; } void KPageListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { if (!index.isValid()) { return; } QStyleOptionViewItem opt(option); opt.showDecorationSelected = true; QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); const QIcon::Mode iconMode = (option.state & QStyle::State_Selected) && (option.state & QStyle::State_Active) ? QIcon::Selected : QIcon::Normal; int iconSize = style->pixelMetric(QStyle::PM_IconViewIconSize); const QString text = index.model()->data(index, Qt::DisplayRole).toString(); const QIcon icon = index.model()->data(index, Qt::DecorationRole).value(); const QPixmap pixmap = icon.pixmap(iconSize, iconSize, iconMode); QFontMetrics fm = painter->fontMetrics(); int wp = pixmap.width() / pixmap.devicePixelRatio(); int hp = pixmap.height() / pixmap.devicePixelRatio(); QTextLayout iconTextLayout(text, option.font); QTextOption textOption(Qt::AlignHCenter); iconTextLayout.setTextOption(textOption); int maxWidth = qMax(3 * wp, 8 * fm.height()); layoutText(&iconTextLayout, maxWidth); QPen pen = painter->pen(); QPalette::ColorGroup cg = option.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; if (cg == QPalette::Normal && !(option.state & QStyle::State_Active)) { cg = QPalette::Inactive; } style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget); if (option.state & QStyle::State_Selected) { painter->setPen(option.palette.color(cg, QPalette::HighlightedText)); } else { painter->setPen(option.palette.color(cg, QPalette::Text)); } painter->drawPixmap(option.rect.x() + (option.rect.width() / 2) - (wp / 2), option.rect.y() + 5, pixmap); if (!text.isEmpty()) { iconTextLayout.draw(painter, QPoint(option.rect.x() + (option.rect.width() / 2) - (maxWidth / 2), option.rect.y() + hp + 7)); } painter->setPen(pen); drawFocus(painter, option, option.rect); } QSize KPageListViewDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { if (!index.isValid()) { return QSize(0, 0); } QStyleOptionViewItem opt(option); opt.showDecorationSelected = true; QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); int iconSize = style->pixelMetric(QStyle::PM_IconViewIconSize); const QString text = index.model()->data(index, Qt::DisplayRole).toString(); const QIcon icon = index.model()->data(index, Qt::DecorationRole).value(); const QPixmap pixmap = icon.pixmap(iconSize, iconSize); QFontMetrics fm = option.fontMetrics; int gap = fm.height(); int wp = pixmap.width() / pixmap.devicePixelRatio(); int hp = pixmap.height() / pixmap.devicePixelRatio(); if (hp == 0) { /** * No pixmap loaded yet, we'll use the default icon size in this case. */ hp = iconSize; wp = iconSize; } QTextLayout iconTextLayout(text, option.font); int wt = layoutText(&iconTextLayout, qMax(3 * wp, 8 * fm.height())); int ht = iconTextLayout.boundingRect().height(); int width, height; if (text.isEmpty()) { height = hp; } else { height = hp + ht + 10; } width = qMax(wt, wp) + gap; return QSize(width, height); } void KPageListViewDelegate::drawFocus(QPainter *painter, const QStyleOptionViewItem &option, const QRect &rect) const { if (option.state & QStyle::State_HasFocus) { QStyleOptionFocusRect o; o.QStyleOption::operator=(option); o.rect = rect; o.state |= QStyle::State_KeyboardFocusChange; QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled) ? QPalette::Normal : QPalette::Disabled; o.backgroundColor = option.palette.color(cg, (option.state & QStyle::State_Selected) ? QPalette::Highlight : QPalette::Background); QStyle *style = option.widget ? option.widget->style() : QApplication::style(); style->drawPrimitive(QStyle::PE_FrameFocusRect, &o, painter, option.widget); } } /** * KPageListViewProxy */ KPageListViewProxy::KPageListViewProxy(QObject *parent) : QAbstractProxyModel(parent) { } KPageListViewProxy::~KPageListViewProxy() { } int KPageListViewProxy::rowCount(const QModelIndex &) const { return mList.count(); } int KPageListViewProxy::columnCount(const QModelIndex &) const { return 1; } QModelIndex KPageListViewProxy::index(int row, int column, const QModelIndex &) const { if (column > 1 || row >= mList.count()) { return QModelIndex(); } else { return createIndex(row, column, mList[ row ].internalPointer()); } } QModelIndex KPageListViewProxy::parent(const QModelIndex &) const { return QModelIndex(); } QVariant KPageListViewProxy::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } if (index.row() >= mList.count()) { return QVariant(); } return sourceModel()->data(mList[ index.row() ], role); } QModelIndex KPageListViewProxy::mapFromSource(const QModelIndex &index) const { if (!index.isValid()) { return QModelIndex(); } for (int i = 0; i < mList.count(); ++i) { if (mList[ i ] == index) { return createIndex(i, 0, index.internalPointer()); } } return QModelIndex(); } QModelIndex KPageListViewProxy::mapToSource(const QModelIndex &index) const { if (!index.isValid()) { return QModelIndex(); } return mList[ index.row() ]; } void KPageListViewProxy::rebuildMap() { mList.clear(); const QAbstractItemModel *model = sourceModel(); if (!model) { return; } for (int i = 0; i < model->rowCount(); ++i) { addMapEntry(model->index(i, 0)); } for (int i = 0; i < mList.count(); ++i) { qCDebug(KWidgetsAddonsLog, "%d:0 -> %d:%d", i, mList[ i ].row(), mList[ i ].column()); } emit layoutChanged(); } void KPageListViewProxy::addMapEntry(const QModelIndex &index) { if (sourceModel()->rowCount(index) == 0) { mList.append(index); } else { const int count = sourceModel()->rowCount(index); for (int i = 0; i < count; ++i) { addMapEntry(sourceModel()->index(i, 0, index)); } } } SelectionModel::SelectionModel(QAbstractItemModel *model, QObject *parent) : QItemSelectionModel(model, parent) { } void SelectionModel::clear() { // Don't allow the current selection to be cleared } void SelectionModel::select(const QModelIndex &index, QItemSelectionModel::SelectionFlags command) { // Don't allow the current selection to be cleared if (!index.isValid() && (command & QItemSelectionModel::Clear)) { return; } QItemSelectionModel::select(index, command); } void SelectionModel::select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command) { // Don't allow the current selection to be cleared if (!selection.count() && (command & QItemSelectionModel::Clear)) { return; } QItemSelectionModel::select(selection, command); } diff --git a/src/kpageview_p.h b/src/kpageview_p.h index 049b2c7..e265089 100644 --- a/src/kpageview_p.h +++ b/src/kpageview_p.h @@ -1,241 +1,244 @@ /* This file is part of the KDE Libraries Copyright (C) 2006 Tobias Koenig (tokoe@kde.org) Copyright (C) 2007 Rafael Fernández López (ereslibre@kde.org) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KPAGEVIEW_P_H #define KPAGEVIEW_P_H #include "kpageview.h" #include #include #include #include #include #include #include #include class KPageStackedWidget : public QStackedWidget { public: KPageStackedWidget(QWidget *parent = nullptr) : QStackedWidget(parent) { } void setMinimumSize(const QSize &size) { mMinimumSize = size; } QSize minimumSizeHint() const Q_DECL_OVERRIDE { return mMinimumSize.expandedTo(QStackedWidget::minimumSizeHint()); } private: QSize mMinimumSize; }; class KPageViewPrivate { Q_DECLARE_PUBLIC(KPageView) protected: KPageViewPrivate(KPageView *); KPageView *q_ptr; // data QAbstractItemModel *model; KPageView::FaceType faceType; // gui QGridLayout *layout; KPageStackedWidget *stack; KTitleWidget *titleWidget; QWidget *defaultWidget; QAbstractItemView *view; void updateTitleWidget(const QModelIndex &index); void updateSelection(); void cleanupPages(); QList collectPages(const QModelIndex &parent = QModelIndex()); KPageView::FaceType detectAutoFace() const; // private slots void _k_rebuildGui(); void _k_modelChanged(); void _k_dataChanged(const QModelIndex &, const QModelIndex &); void _k_pageSelected(const QItemSelection &, const QItemSelection &); private: void init(); }; namespace KDEPrivate { class KPageListViewDelegate; class KPageListViewProxy; class KPagePlainView : public QAbstractItemView { public: KPagePlainView(QWidget *parent = nullptr); QModelIndex indexAt(const QPoint &point) const Q_DECL_OVERRIDE; void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible) Q_DECL_OVERRIDE; QRect visualRect(const QModelIndex &index) const Q_DECL_OVERRIDE; protected: QModelIndex moveCursor(QAbstractItemView::CursorAction, Qt::KeyboardModifiers) Q_DECL_OVERRIDE; int horizontalOffset() const Q_DECL_OVERRIDE; int verticalOffset() const Q_DECL_OVERRIDE; bool isIndexHidden(const QModelIndex &) const Q_DECL_OVERRIDE; void setSelection(const QRect &, QFlags) Q_DECL_OVERRIDE; QRegion visualRegionForSelection(const QItemSelection &) const Q_DECL_OVERRIDE; }; class KPageListView : public QListView { Q_OBJECT public: KPageListView(QWidget *parent = nullptr); virtual ~KPageListView(); void setModel(QAbstractItemModel *model) Q_DECL_OVERRIDE; +protected: + void changeEvent(QEvent *event) Q_DECL_OVERRIDE; + private Q_SLOTS: void updateWidth(); }; class KPageTreeView : public QTreeView { Q_OBJECT public: KPageTreeView(QWidget *parent = nullptr); void setModel(QAbstractItemModel *model) Q_DECL_OVERRIDE; private Q_SLOTS: void updateWidth(); private: void expandItems(const QModelIndex &index = QModelIndex()); }; class KPageTabbedView : public QAbstractItemView { Q_OBJECT public: KPageTabbedView(QWidget *parent = nullptr); virtual ~KPageTabbedView(); void setModel(QAbstractItemModel *model) Q_DECL_OVERRIDE; QModelIndex indexAt(const QPoint &point) const Q_DECL_OVERRIDE; void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible) Q_DECL_OVERRIDE; QRect visualRect(const QModelIndex &index) const Q_DECL_OVERRIDE; QSize minimumSizeHint() const Q_DECL_OVERRIDE; protected: QModelIndex moveCursor(QAbstractItemView::CursorAction, Qt::KeyboardModifiers) Q_DECL_OVERRIDE; int horizontalOffset() const Q_DECL_OVERRIDE; int verticalOffset() const Q_DECL_OVERRIDE; bool isIndexHidden(const QModelIndex &) const Q_DECL_OVERRIDE; void setSelection(const QRect &, QFlags) Q_DECL_OVERRIDE; QRegion visualRegionForSelection(const QItemSelection &) const Q_DECL_OVERRIDE; private Q_SLOTS: void currentPageChanged(int); void layoutChanged(); void dataChanged(const QModelIndex &, const QModelIndex &, const QVector &roles) Q_DECL_OVERRIDE; private: QTabWidget *mTabWidget; }; class KPageListViewDelegate : public QAbstractItemDelegate { Q_OBJECT public: KPageListViewDelegate(QObject *parent = nullptr); void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE; private: void drawFocus(QPainter *, const QStyleOptionViewItem &, const QRect &) const; }; /** * We need this proxy model to map the leaves of a tree-like model * to a one-level list model. */ class KPageListViewProxy : public QAbstractProxyModel { Q_OBJECT public: KPageListViewProxy(QObject *parent = nullptr); virtual ~KPageListViewProxy(); int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; QModelIndex parent(const QModelIndex &) const Q_DECL_OVERRIDE; QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE; QModelIndex mapFromSource(const QModelIndex &index) const Q_DECL_OVERRIDE; QModelIndex mapToSource(const QModelIndex &index) const Q_DECL_OVERRIDE; public Q_SLOTS: void rebuildMap(); private: void addMapEntry(const QModelIndex &); QList mList; }; class SelectionModel : public QItemSelectionModel { Q_OBJECT public: SelectionModel(QAbstractItemModel *model, QObject *parent); public Q_SLOTS: void clear() Q_DECL_OVERRIDE; void select(const QModelIndex &index, QItemSelectionModel::SelectionFlags command) Q_DECL_OVERRIDE; void select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command) Q_DECL_OVERRIDE; }; } #endif