10#include "kpageview_p.h"
12#include "kpagemodel.h"
13#include "kpagewidgetmodel.h"
14#include "loggingcategory.h"
16#include <ktitlewidget.h>
18#include <QAbstractButton>
19#include <QAbstractItemView>
20#include <QApplication>
32class SearchMatchOverlay :
public QWidget
66 const QRect r = tabBar->tabRect(m_tabIdx);
89 p.setClipRegion(e->
region());
92 p.fillRect(
rect(), c);
98void KPageViewPrivate::rebuildGui()
104 if (view && view->selectionModel()) {
106 currentLastIndex = view->selectionModel()->currentIndex();
110 view = q->createView();
118 view->setModel(model);
122 if (view->selectionModel()) {
127 pageSelected(selected, deselected);
130 if (currentLastIndex.
isValid()) {
138 stack->setVisible(
false);
139 layout->removeWidget(stack);
141 layout->addWidget(stack, 3, 1);
142 stack->setVisible(
true);
145 titleWidget->setPalette(qApp->palette(titleWidget));
147 if (!hasSearchableView()) {
148 layout->removeWidget(searchLineEditContainer);
149 searchLineEditContainer->setVisible(
false);
150 titleWidget->setAutoFillBackground(
false);
151 layout->setSpacing(0);
152 separatorLine->setVisible(
false);
153 titleWidget->setObjectName(
"KPageView::TitleWidgetNonSearchable");
159 titleWidget->setObjectName(
"KPageView::TitleWidget");
160 searchLineEditContainer->setVisible(
true);
162 separatorLine->setVisible(
true);
165 searchLineEditContainer->setContentsMargins(4, 3, 4, 3);
166 titleWidget->setContentsMargins(5, 4, 4, 2);
169 layout->setSpacing(0);
170 layout->setContentsMargins({});
173 layout->removeWidget(titleWidget);
176 layout->removeWidget(pageHeader);
177 pageHeader->setVisible(q->showPageHeader());
178 titleWidget->setVisible(
false);
181 layout->addWidget(pageHeader, 1, 1);
183 layout->addWidget(pageHeader, 1, 1, 1, 2);
186 titleWidget->setVisible(q->showPageHeader());
188 layout->addWidget(titleWidget, 1, 1);
190 layout->addWidget(titleWidget, 1, 1, 1, 2);
196 layout->addWidget(view, 2, 1);
201 layout->addWidget(view, 3, 2, 3, 1);
203 layout->addWidget(view, 4, 1);
208 layout->addWidget(view, 3, 0, 3, 1);
212void KPageViewPrivate::updateSelection()
220 if (!view || !view->selectionModel()) {
224 const QModelIndex index = view->selectionModel()->currentIndex();
230void KPageViewPrivate::cleanupPages()
236 for (
int i = 0; i < stack->count(); ++i) {
237 QWidget *page = stack->widget(i);
240 for (
int j = 0; j < widgets.
count(); ++j) {
241 if (widgets[j] == page) {
247 stack->removeWidget(page);
258 int rows = model->rowCount(parentIndex);
259 for (
int j = 0; j < rows; ++j) {
260 const QModelIndex index = model->index(j, 0, parentIndex);
263 if (model->rowCount(index) > 0) {
264 retval += collectPages(index);
274 return detectAutoFace();
287 bool hasSubPages =
false;
288 const int count = model->rowCount();
289 for (
int i = 0; i < count; ++i) {
290 if (model->rowCount(model->index(i, 0)) > 0) {
300 if (model->rowCount() > 1) {
307void KPageViewPrivate::modelChanged()
322 QSize size = stack->size();
324 for (
int i = 0; i < widgets.
count(); ++i) {
325 const QWidget *widget = widgets[i];
330 stack->setMinimumSize(size);
342 if (index.
indexes().size() != 1) {
349 if (previous.
indexes().size() == 1) {
350 previousIndex = previous.
indexes().first();
357 if (stack->indexOf(widget) == -1) {
358 stack->addWidget(widget);
361 stack->setCurrentWidget(widget);
363 stack->setCurrentWidget(defaultWidget);
366 updateTitleWidget(currentIndex);
370 Q_EMIT q->currentPageChanged(currentIndex, previousIndex);
373void KPageViewPrivate::updateTitleWidget(
const QModelIndex &index)
378 if (!headerVisible) {
379 titleWidget->setVisible(
false);
387 titleWidget->setText(header);
389 titleWidget->setVisible(q->showPageHeader());
400 QModelIndex index = view->selectionModel()->currentIndex();
405 updateTitleWidget(index);
408KPageViewPrivate::KPageViewPrivate(
KPageView *_parent)
414 , titleWidget(nullptr)
415 , searchLineEditContainer(new
QWidget())
421void KPageViewPrivate::init()
425 stack =
new KPageStackedWidget(q);
427 titleWidget->setObjectName(
"KPageView::TitleWidget");
429 separatorLine =
new QFrame(q);
431 separatorLine->setFixedHeight(1);
435 layout->addWidget(titleWidget, 1, 1, 1, 2);
437 layout->addWidget(separatorLine, 2, 0, 1, 3);
439 layout->addWidget(stack, 3, 1);
441 defaultWidget =
new QWidget(q);
442 stack->addWidget(defaultWidget);
445 layout->setColumnStretch(1, 1);
446 layout->setRowStretch(3, 1);
448 searchTimer.setInterval(400);
449 searchTimer.setSingleShot(
true);
450 searchTimer.callOnTimeout(q, [
this] {
451 onSearchTextChanged();
453 q->setFocusProxy(searchLineEdit);
454 searchLineEdit->setPlaceholderText(
KPageView::tr(
"Search…",
"@info:placeholder"));
455 searchLineEdit->setClearButtonEnabled(
true);
456 searchLineEdit->setParent(defaultWidget);
461 auto containerLayout =
new QVBoxLayout(searchLineEditContainer);
462 containerLayout->setContentsMargins({});
463 containerLayout->setSpacing(0);
464 containerLayout->addWidget(searchLineEdit);
465 searchLineEditContainer->setObjectName(
"KPageView::Search");
470 const int rc = model->rowCount(parent);
472 for (
int i = 0; i < rc; ++i) {
473 auto child = model->index(i, 0, parent);
474 auto item = model->
item(child);
475 if (child.isValid() && item) {
477 ret << getAllPages(model, child);
483template<
typename W
idgetType>
487 const auto widgets = page->
findChildren<WidgetType *>();
488 for (
auto label : widgets) {
501 for (
auto cb : comboxBoxes) {
502 if (cb->findText(text, Qt::MatchFlag::MatchContains) != -1) {
510struct FindChildrenHelper {
517template<
typename First,
typename... Rest>
518struct FindChildrenHelper<First, Rest...> {
521 return hasMatchingText<First>(text, page) << FindChildrenHelper<Rest...>::hasMatchingTextForTypes(text, page);
528 auto model = tree->
model();
529 const int rows = model->rowCount(parent);
530 for (
int i = 0; i < rows; ++i) {
531 const auto index = model->index(i, 0, parent);
535 current = model->index(i, 0, parent);
537 auto curr = walkTreeAndHideItems(tree, searchText, pagesToHide, index);
545bool KPageViewPrivate::hasSearchableView()
const
548 return qobject_cast<KDEPrivate::KPageListView *>(view) || qobject_cast<KDEPrivate::KPageTreeView *>(view);
551void KPageViewPrivate::onSearchTextChanged()
553 if (!hasSearchableView()) {
557 const QString text = searchLineEdit->text();
559 std::vector<QWidget *> matchedWidgets;
562 for (
auto item : pages) {
563 const auto matchingWidgets = FindChildrenHelper<QLabel, QAbstractButton, QComboBox>::hasMatchingTextForTypes(text, item->widget());
564 if (matchingWidgets.isEmpty()) {
565 pagesToHide << item->name();
567 matchedWidgets.
insert(matchedWidgets.end(), matchingWidgets.begin(), matchingWidgets.end());
573 if (
auto list = qobject_cast<QListView *>(view)) {
574 for (
int i = 0; i < model->rowCount(); ++i) {
575 const auto itemName = model->index(i, 0).
data().
toString();
578 current = model->index(i, 0);
581 }
else if (
auto tree = qobject_cast<QTreeView *>(view)) {
582 current = walkTreeAndHideItems(tree, text, pagesToHide, {});
583 auto parent = current.
parent();
584 while (parent.isValid()) {
585 tree->
setRowHidden(parent.row(), parent.parent(),
false);
586 parent = parent.parent();
589 qWarning() <<
"Unreacheable, unknown view:" << view;
594 view->setCurrentIndex(current);
598 qDeleteAll(m_searchMatchOverlays);
599 m_searchMatchOverlays.clear();
601 using TabWidgetAndPage = QPair<QTabWidget *, QWidget *>;
602 auto tabWidgetParent = [](
QWidget *w) {
606 auto parent = w->parentWidget();
607 TabWidgetAndPage p = {
nullptr,
nullptr};
609 parentChain << parent;
611 if (
auto tw = qobject_cast<QTabWidget *>(parent)) {
612 if (parentChain.
size() >= 3) {
616 p.second = parentChain.
value((parentChain.
size() - 1) - 2);
621 parent = parent->parentWidget();
622 parentChain << parent;
627 for (
auto w : matchedWidgets) {
629 m_searchMatchOverlays <<
new SearchMatchOverlay(w);
630 if (!w->isVisible()) {
631 const auto [tabWidget, page] = tabWidgetParent(w);
632 if (!tabWidget && !page) {
635 const int idx = tabWidget->indexOf(page);
641 const bool alreadyOverlayed =
642 std::any_of(m_searchMatchOverlays.cbegin(), m_searchMatchOverlays.cend(), [tabbar = tabWidget->tabBar(), idx](SearchMatchOverlay *overlay) {
643 return idx == overlay->tabIndex() && tabbar == overlay->parentWidget();
645 if (!alreadyOverlayed) {
646 m_searchMatchOverlays <<
new SearchMatchOverlay(tabWidget->tabBar(), idx);
655 :
KPageView(*new KPageViewPrivate(this), parent)
684 d->dataChanged(topLeft, bottomRight);
689 d->view->setModel(
model);
705 d->faceType = faceType;
719 if (!d->view || !d->view->selectionModel()) {
729 if (!d->view || !d->view->selectionModel()) {
733 return d->view->selectionModel()->currentIndex();
740 d->view->setItemDelegate(delegate);
748 return d->view->itemDelegate();
760 bool isCurrent = (d->stack->currentIndex() == d->stack->indexOf(d->defaultWidget));
763 d->stack->removeWidget(d->defaultWidget);
764 delete d->defaultWidget;
767 d->defaultWidget = widget;
768 d->stack->addWidget(d->defaultWidget);
771 d->stack->setCurrentWidget(d->defaultWidget);
778 if (d->pageHeader == header) {
783 d->layout->removeWidget(d->pageHeader);
785 d->layout->removeWidget(d->titleWidget);
787 d->pageHeader = header;
794 d->layout->addWidget(d->titleWidget, 1, 1, 1, 2);
802 if (!d->pageHeader) {
803 return d->titleWidget;
805 return d->pageHeader;
811 if (d->pageFooter == footer) {
816 d->layout->removeWidget(d->pageFooter);
819 d->pageFooter = footer;
823 d->layout->addWidget(d->pageFooter, 4, 1);
830 return d->pageFooter;
836 const FaceType faceType = d->effectiveFaceType();
838 if (faceType ==
Plain) {
839 return new KDEPrivate::KPagePlainView(
this);
842 return new KDEPrivate::KPageListView(
this);
844 if (faceType ==
List) {
845 auto view =
new KDEPrivate::KPageListView(
this);
846 view->setItemDelegate(
new KDEPrivate::KPageListViewDelegate(
this));
847 view->setFlexibleWidth(
true);
850 if (faceType ==
Tree) {
851 return new KDEPrivate::KPageTreeView(
this);
854 return new KDEPrivate::KPageTabbedView(
this);
863 const FaceType faceType = d->effectiveFaceType();
868 return d->pageHeader || !d->titleWidget->text().isEmpty();
875 const FaceType faceType = d->effectiveFaceType();
884#include "moc_kpageview.cpp"
@ WidgetRole
A pointer to the page widget.
@ HeaderVisibleRole
when true, show the page header, if false don't
@ HeaderRole
A string to be rendered as page header.
A base class which can handle multiple pages.
KPageView(QWidget *parent=nullptr)
Creates a page view with given parent.
void setCurrentPage(const QModelIndex &index)
Sets the page with.
QModelIndex currentPage() const
Returns the index for the current page or an invalid index if no current page exists.
virtual QAbstractItemView * createView()
Returns the navigation view, depending on the current face type.
void setPageFooter(QWidget *footer)
Set a widget as the footer for this Page view.
virtual Qt::Alignment viewPosition() const
Returns the position where the navigation view should be located according to the page stack.
void setFaceType(FaceType faceType)
Sets the face type of the page view.
void setModel(QAbstractItemModel *model)
Sets the model of the page view.
QAbstractItemDelegate * itemDelegate() const
Returns the item delegate of the page view.
void setPageHeader(QWidget *header)
Set a widget as the header for this Page view It will replace the standard page title.
FaceType
This enum is used to decide which type of navigation view shall be used in the page view.
@ List
An icon list is used as navigation view.
@ Auto
Depending on the number of pages in the model, the Plain (one page), the List (several pages) or the ...
@ Tree
A tree list is used as navigation view.
@ Plain
No navigation view will be visible and only the first page of the model will be shown.
@ FlatList
A flat list with small icons is used as navigation view.
@ Tabbed
A tab widget is used as navigation view.
virtual bool showPageHeader() const
Returns whether the page header should be visible.
void setDefaultWidget(QWidget *widget)
Sets the widget which will be shown when a page is selected that has no own widget set.
~KPageView() override
Destroys the page view.
void setItemDelegate(QAbstractItemDelegate *delegate)
Sets the item.
QAbstractItemModel * model() const
Returns the model of the page view.
This page model is used by KPageWidget to provide a hierarchical layout of pages.
KPageWidgetItem * item(const QModelIndex &index) const
Returns the KPageWidgetItem for a given index or a null pointer if the index is invalid.
KIOCORE_EXPORT QStringList list(const QString &fileClass)
QString label(StandardShortcut id)
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList< int > &roles)
void layoutChanged(const QList< QPersistentModelIndex > &parents, QAbstractItemModel::LayoutChangeHint hint)
QAbstractItemModel * model() const const
QIcon fromTheme(const QString &name)
QModelIndexList indexes() const const
void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
void textChanged(const QString &text)
void append(QList< T > &&value)
qsizetype count() const const
QVariant data(int role) const const
bool isValid() const const
QModelIndex parent() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
virtual bool eventFilter(QObject *watched, QEvent *event)
QList< T > findChildren(Qt::FindChildOptions options) const const
void installEventFilter(QObject *filterObj)
QObject * parent() const const
T qobject_cast(QObject *object)
QString tr(const char *sourceText, const char *disambiguation, int n)
const QRegion & region() const const
bool contains(const QSet< T > &other) const const
iterator insert(const T &value)
QSize expandedTo(const QSize &otherSize) const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
bool isRowHidden(int row, const QModelIndex &parent) const const
void setRowHidden(int row, const QModelIndex &parent, bool hide)
QString toString() const const
qsizetype size() const const
T value(qsizetype i) const const
void setContentsMargins(const QMargins &margins)