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
61 const QRect r = tabBar->tabRect(m_tabIdx);
84 p.setClipRegion(e->
region());
87 p.fillRect(
rect(), c);
93void KPageViewPrivate::rebuildGui()
99 if (view && view->selectionModel()) {
101 currentLastIndex = view->selectionModel()->currentIndex();
105 view = q->createView();
113 view->setModel(model);
117 if (view->selectionModel()) {
122 pageSelected(selected, deselected);
125 if (currentLastIndex.
isValid()) {
133 stack->setVisible(
false);
134 layout->removeWidget(stack);
136 layout->addWidget(stack, 3, 1);
137 stack->setVisible(
true);
140 titleWidget->setPalette(qApp->palette(titleWidget));
142 if (!hasSearchableView()) {
143 layout->removeWidget(searchLineEditContainer);
144 searchLineEditContainer->setVisible(
false);
145 titleWidget->setAutoFillBackground(
false);
146 layout->setSpacing(0);
147 separatorLine->setVisible(
false);
148 titleWidget->setObjectName(
"KPageView::TitleWidgetNonSearchable");
154 titleWidget->setObjectName(
"KPageView::TitleWidget");
155 searchLineEditContainer->setVisible(
true);
157 separatorLine->setVisible(
true);
160 searchLineEditContainer->setContentsMargins(4, 3, 4, 3);
161 titleWidget->setContentsMargins(5, 4, 4, 2);
164 layout->setSpacing(0);
165 layout->setContentsMargins({});
168 layout->removeWidget(titleWidget);
171 layout->removeWidget(pageHeader);
172 pageHeader->setVisible(q->showPageHeader());
173 titleWidget->setVisible(
false);
176 layout->addWidget(pageHeader, 1, 1);
178 layout->addWidget(pageHeader, 1, 1, 1, 2);
181 titleWidget->setVisible(q->showPageHeader());
183 layout->addWidget(titleWidget, 1, 1);
185 layout->addWidget(titleWidget, 1, 1, 1, 2);
191 layout->addWidget(view, 2, 1);
196 layout->addWidget(view, 3, 2, 3, 1);
198 layout->addWidget(view, 4, 1);
203 layout->addWidget(view, 3, 0, 3, 1);
207void KPageViewPrivate::updateSelection()
215 if (!view || !view->selectionModel()) {
219 const QModelIndex index = view->selectionModel()->currentIndex();
225void KPageViewPrivate::cleanupPages()
231 for (
int i = 0; i < stack->count(); ++i) {
232 QWidget *page = stack->widget(i);
235 for (
int j = 0; j < widgets.
count(); ++j) {
236 if (widgets[j] == page) {
242 stack->removeWidget(page);
253 int rows = model->rowCount(parentIndex);
254 for (
int j = 0; j < rows; ++j) {
255 const QModelIndex index = model->index(j, 0, parentIndex);
258 if (model->rowCount(index) > 0) {
259 retval += collectPages(index);
269 return detectAutoFace();
282 bool hasSubPages =
false;
283 const int count = model->rowCount();
284 for (
int i = 0; i < count; ++i) {
285 if (model->rowCount(model->index(i, 0)) > 0) {
295 if (model->rowCount() > 1) {
302void KPageViewPrivate::modelChanged()
317 QSize size = stack->size();
319 for (
int i = 0; i < widgets.
count(); ++i) {
320 const QWidget *widget = widgets[i];
325 stack->setMinimumSize(size);
337 if (index.
indexes().size() != 1) {
344 if (previous.
indexes().size() == 1) {
345 previousIndex = previous.
indexes().first();
352 if (stack->indexOf(widget) == -1) {
353 stack->addWidget(widget);
356 stack->setCurrentWidget(widget);
358 stack->setCurrentWidget(defaultWidget);
361 updateTitleWidget(currentIndex);
365 Q_EMIT q->currentPageChanged(currentIndex, previousIndex);
368void KPageViewPrivate::updateTitleWidget(
const QModelIndex &index)
373 if (!headerVisible) {
374 titleWidget->setVisible(
false);
382 titleWidget->setText(header);
384 titleWidget->setVisible(q->showPageHeader());
395 QModelIndex index = view->selectionModel()->currentIndex();
400 updateTitleWidget(index);
403KPageViewPrivate::KPageViewPrivate(
KPageView *_parent)
409 , titleWidget(nullptr)
410 , searchLineEditContainer(new
QWidget())
416void KPageViewPrivate::init()
420 stack =
new KPageStackedWidget(q);
422 titleWidget->setObjectName(
"KPageView::TitleWidget");
424 separatorLine =
new QFrame(q);
426 separatorLine->setFixedHeight(1);
430 layout->addWidget(titleWidget, 1, 1, 1, 2);
432 layout->addWidget(separatorLine, 2, 0, 1, 3);
434 layout->addWidget(stack, 3, 1);
436 defaultWidget =
new QWidget(q);
437 stack->addWidget(defaultWidget);
440 layout->setColumnStretch(1, 1);
441 layout->setRowStretch(3, 1);
443 searchTimer.setInterval(400);
444 searchTimer.setSingleShot(
true);
445 searchTimer.callOnTimeout(q, [
this] {
446 onSearchTextChanged();
448 q->setFocusProxy(searchLineEdit);
449 searchLineEdit->setPlaceholderText(
KPageView::tr(
"Search..."));
450 searchLineEdit->setClearButtonEnabled(
true);
451 searchLineEdit->setParent(defaultWidget);
456 auto containerLayout =
new QVBoxLayout(searchLineEditContainer);
457 containerLayout->setContentsMargins({});
458 containerLayout->setSpacing(0);
459 containerLayout->addWidget(searchLineEdit);
460 searchLineEditContainer->setObjectName(
"KPageView::Search");
465 const int rc = model->rowCount(parent);
467 for (
int i = 0; i < rc; ++i) {
468 auto child = model->index(i, 0, parent);
469 auto item = model->
item(child);
470 if (child.isValid() && item) {
472 ret << getAllPages(model, child);
478template<
typename W
idgetType>
482 const auto widgets = page->
findChildren<WidgetType *>();
483 for (
auto label : widgets) {
496 for (
auto cb : comboxBoxes) {
497 if (cb->findText(text, Qt::MatchFlag::MatchContains) != -1) {
505struct FindChildrenHelper {
512template<
typename First,
typename... Rest>
513struct FindChildrenHelper<First, Rest...> {
516 return hasMatchingText<First>(text, page) << FindChildrenHelper<Rest...>::hasMatchingTextForTypes(text, page);
523 auto model = tree->
model();
524 const int rows = model->rowCount(parent);
525 for (
int i = 0; i < rows; ++i) {
526 const auto index = model->index(i, 0, parent);
530 current = model->index(i, 0, parent);
532 auto curr = walkTreeAndHideItems(tree, searchText, pagesToHide, index);
540bool KPageViewPrivate::hasSearchableView()
const
543 return qobject_cast<KDEPrivate::KPageListView *>(view) || qobject_cast<KDEPrivate::KPageTreeView *>(view);
546void KPageViewPrivate::onSearchTextChanged()
548 if (!hasSearchableView()) {
552 const QString text = searchLineEdit->text();
554 std::vector<QWidget *> matchedWidgets;
557 for (
auto item : pages) {
558 const auto matchingWidgets = FindChildrenHelper<QLabel, QAbstractButton, QComboBox>::hasMatchingTextForTypes(text, item->widget());
559 if (matchingWidgets.isEmpty()) {
560 pagesToHide << item->name();
562 matchedWidgets.
insert(matchedWidgets.end(), matchingWidgets.begin(), matchingWidgets.end());
568 if (
auto list = qobject_cast<QListView *>(view)) {
569 for (
int i = 0; i < model->rowCount(); ++i) {
570 const auto itemName = model->index(i, 0).
data().
toString();
573 current = model->index(i, 0);
576 }
else if (
auto tree = qobject_cast<QTreeView *>(view)) {
577 current = walkTreeAndHideItems(tree, text, pagesToHide, {});
578 auto parent = current.
parent();
579 while (parent.isValid()) {
580 tree->
setRowHidden(parent.row(), parent.parent(),
false);
581 parent = parent.parent();
584 qWarning() <<
"Unreacheable, unknown view:" << view;
589 view->setCurrentIndex(current);
593 qDeleteAll(m_searchMatchOverlays);
594 m_searchMatchOverlays.clear();
596 using TabWidgetAndPage = QPair<QTabWidget *, QWidget *>;
597 auto tabWidgetParent = [](
QWidget *w) {
601 auto parent = w->parentWidget();
602 TabWidgetAndPage p = {
nullptr,
nullptr};
603 if (
auto tw = qobject_cast<QTabWidget *>(parent)) {
609 if (
auto tw = qobject_cast<QTabWidget *>(parent)) {
610 if (parentChain.
size() >= 3) {
614 p.second = parentChain.
value((parentChain.
size() - 1) - 2);
620 parent = parent->parentWidget();
621 parentChain << parent;
626 for (
auto w : matchedWidgets) {
628 m_searchMatchOverlays <<
new SearchMatchOverlay(w);
629 if (!w->isVisible()) {
630 const auto [tabWidget, page] = tabWidgetParent(w);
631 if (!tabWidget && !page) {
634 const int idx = tabWidget->indexOf(page);
639 m_searchMatchOverlays <<
new SearchMatchOverlay(tabWidget->tabBar(), idx);
647 :
KPageView(*new KPageViewPrivate(this), parent)
676 d->dataChanged(topLeft, bottomRight);
681 d->view->setModel(
model);
697 d->faceType = faceType;
711 if (!d->view || !d->view->selectionModel()) {
721 if (!d->view || !d->view->selectionModel()) {
725 return d->view->selectionModel()->currentIndex();
732 d->view->setItemDelegate(delegate);
740 return d->view->itemDelegate();
752 bool isCurrent = (d->stack->currentIndex() == d->stack->indexOf(d->defaultWidget));
755 d->stack->removeWidget(d->defaultWidget);
756 delete d->defaultWidget;
759 d->defaultWidget = widget;
760 d->stack->addWidget(d->defaultWidget);
763 d->stack->setCurrentWidget(d->defaultWidget);
770 if (d->pageHeader == header) {
775 d->layout->removeWidget(d->pageHeader);
777 d->layout->removeWidget(d->titleWidget);
779 d->pageHeader = header;
786 d->layout->addWidget(d->titleWidget, 1, 1, 1, 2);
794 if (!d->pageHeader) {
795 return d->titleWidget;
797 return d->pageHeader;
803 if (d->pageFooter == footer) {
808 d->layout->removeWidget(d->pageFooter);
811 d->pageFooter = footer;
815 d->layout->addWidget(d->pageFooter, 4, 1);
822 return d->pageFooter;
828 const FaceType faceType = d->effectiveFaceType();
830 if (faceType ==
Plain) {
831 return new KDEPrivate::KPagePlainView(
this);
834 return new KDEPrivate::KPageListView(
this);
836 if (faceType ==
List) {
837 auto view =
new KDEPrivate::KPageListView(
this);
838 view->setItemDelegate(
new KDEPrivate::KPageListViewDelegate(
this));
839 view->setFlexibleWidth(
true);
842 if (faceType ==
Tree) {
843 return new KDEPrivate::KPageTreeView(
this);
846 return new KDEPrivate::KPageTabbedView(
this);
855 const FaceType faceType = d->effectiveFaceType();
860 return d->pageHeader || !d->titleWidget->text().isEmpty();
867 const FaceType faceType = d->effectiveFaceType();
876#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)