KWidgetsAddons

kpageview.cpp
1/*
2 This file is part of the KDE Libraries
3 SPDX-FileCopyrightText: 2006 Tobias Koenig <tokoe@kde.org>
4 SPDX-FileCopyrightText: 2007 Rafael Fernández López <ereslibre@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7*/
8
9#include "kpageview.h"
10#include "kpageview_p.h"
11
12#include "kpagemodel.h"
13#include "kpagewidgetmodel.h"
14#include "loggingcategory.h"
15
16#include <ktitlewidget.h>
17
18#include <QAbstractButton>
19#include <QAbstractItemView>
20#include <QApplication>
21#include <QCheckBox>
22#include <QComboBox>
23#include <QEvent>
24#include <QGridLayout>
25#include <QLabel>
26#include <QPaintEvent>
27#include <QPainter>
28#include <QSize>
29#include <QTimer>
30
31// Helper class that draws a rect over a matched widget
32class SearchMatchOverlay : public QWidget
33{
34public:
35 SearchMatchOverlay(QWidget *parent, int tabIdx = -1)
37 , m_tabIdx(tabIdx)
38 {
40 resize_impl();
42
43 show();
44 raise();
45 }
46
47private:
48 void doResize()
49 {
50 QMetaObject::invokeMethod(this, &SearchMatchOverlay::resize_impl, Qt::QueuedConnection);
51 }
52
53 void resize_impl()
54 {
55 if (m_tabIdx >= 0) {
57 if (!tabBar) {
58 setVisible(false);
59 return;
60 }
61 const QRect r = tabBar->tabRect(m_tabIdx);
62 if (geometry() != r) {
64 }
65 return;
66 }
67
68 if (parentWidget() && size() != parentWidget()->size()) {
70 }
71 }
72
73 bool eventFilter(QObject *o, QEvent *e) override
74 {
75 if (parentWidget() && o == parentWidget() && (e->type() == QEvent::Resize || e->type() == QEvent::Show)) {
76 doResize();
77 }
78 return QWidget::eventFilter(o, e);
79 }
80
81 void paintEvent(QPaintEvent *e) override
82 {
83 QPainter p(this);
84 p.setClipRegion(e->region());
86 c.setAlpha(110);
87 p.fillRect(rect(), c);
88 }
89
90 int m_tabIdx = -1;
91};
92
93void KPageViewPrivate::rebuildGui()
94{
95 // clean up old view
96 Q_Q(KPageView);
97
98 QModelIndex currentLastIndex;
99 if (view && view->selectionModel()) {
100 QObject::disconnect(m_selectionChangedConnection);
101 currentLastIndex = view->selectionModel()->currentIndex();
102 }
103
104 delete view;
105 view = q->createView();
106
107 Q_ASSERT(view);
108
109 view->setSelectionBehavior(QAbstractItemView::SelectItems);
110 view->setSelectionMode(QAbstractItemView::SingleSelection);
111
112 if (model) {
113 view->setModel(model);
114 }
115
116 // setup new view
117 if (view->selectionModel()) {
118 m_selectionChangedConnection = QObject::connect(view->selectionModel(),
120 q,
121 [this](const QItemSelection &selected, const QItemSelection &deselected) {
122 pageSelected(selected, deselected);
123 });
124
125 if (currentLastIndex.isValid()) {
126 view->selectionModel()->setCurrentIndex(currentLastIndex, QItemSelectionModel::Select);
127 } else if (model) {
128 view->selectionModel()->setCurrentIndex(model->index(0, 0), QItemSelectionModel::Select);
129 }
130 }
131
132 if (faceType == KPageView::Tabbed) {
133 stack->setVisible(false);
134 layout->removeWidget(stack);
135 } else {
136 layout->addWidget(stack, 3, 1);
137 stack->setVisible(true);
138 }
139
140 titleWidget->setPalette(qApp->palette(titleWidget));
141
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");
149 titleWidget->setContentsMargins(q_ptr->style()->pixelMetric(QStyle::PM_LayoutLeftMargin),
150 q_ptr->style()->pixelMetric(QStyle::PM_LayoutTopMargin),
151 q_ptr->style()->pixelMetric(QStyle::PM_LayoutRightMargin),
152 q_ptr->style()->pixelMetric(QStyle::PM_LayoutBottomMargin));
153 } else {
154 titleWidget->setObjectName("KPageView::TitleWidget");
155 searchLineEditContainer->setVisible(true);
156 searchLineEditContainer->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
157 separatorLine->setVisible(true);
158
159 // Adjust margins for a better alignment
160 searchLineEditContainer->setContentsMargins(4, 3, 4, 3);
161 titleWidget->setContentsMargins(5, 4, 4, 2);
162
163 // Adjust the search + title background so that it merges into the titlebar
164 layout->setSpacing(0);
165 layout->setContentsMargins({});
166 }
167
168 layout->removeWidget(titleWidget);
169
170 if (pageHeader) {
171 layout->removeWidget(pageHeader);
172 pageHeader->setVisible(q->showPageHeader());
173 titleWidget->setVisible(false);
174
175 if (faceType == KPageView::Tabbed) {
176 layout->addWidget(pageHeader, 1, 1);
177 } else {
178 layout->addWidget(pageHeader, 1, 1, 1, 2);
179 }
180 } else {
181 titleWidget->setVisible(q->showPageHeader());
182 if (faceType == KPageView::Tabbed) {
183 layout->addWidget(titleWidget, 1, 1);
184 } else {
185 layout->addWidget(titleWidget, 1, 1, 1, 2);
186 }
187 }
188
189 Qt::Alignment alignment = q->viewPosition();
190 if (alignment & Qt::AlignTop) {
191 layout->addWidget(view, 2, 1);
192 } else if (alignment & Qt::AlignRight) {
193 // search line
194 layout->addWidget(searchLineEditContainer, 1, 2, Qt::AlignVCenter);
195 // item view below the search line
196 layout->addWidget(view, 3, 2, 3, 1);
197 } else if (alignment & Qt::AlignBottom) {
198 layout->addWidget(view, 4, 1);
199 } else if (alignment & Qt::AlignLeft) {
200 // search line
201 layout->addWidget(searchLineEditContainer, 1, 0, Qt::AlignVCenter);
202 // item view below the search line
203 layout->addWidget(view, 3, 0, 3, 1);
204 }
205}
206
207void KPageViewPrivate::updateSelection()
208{
209 // Select the first item in the view if not done yet.
210
211 if (!model) {
212 return;
213 }
214
215 if (!view || !view->selectionModel()) {
216 return;
217 }
218
219 const QModelIndex index = view->selectionModel()->currentIndex();
220 if (!index.isValid()) {
221 view->selectionModel()->setCurrentIndex(model->index(0, 0), QItemSelectionModel::Select);
222 }
223}
224
225void KPageViewPrivate::cleanupPages()
226{
227 // Remove all orphan pages from the stacked widget.
228
229 const QList<QWidget *> widgets = collectPages();
230
231 for (int i = 0; i < stack->count(); ++i) {
232 QWidget *page = stack->widget(i);
233
234 bool found = false;
235 for (int j = 0; j < widgets.count(); ++j) {
236 if (widgets[j] == page) {
237 found = true;
238 }
239 }
240
241 if (!found) {
242 stack->removeWidget(page);
243 }
244 }
245}
246
247QList<QWidget *> KPageViewPrivate::collectPages(const QModelIndex &parentIndex)
248{
249 // Traverse through the model recursive and collect all widgets in
250 // a list.
251 QList<QWidget *> retval;
252
253 int rows = model->rowCount(parentIndex);
254 for (int j = 0; j < rows; ++j) {
255 const QModelIndex index = model->index(j, 0, parentIndex);
256 retval.append(qvariant_cast<QWidget *>(model->data(index, KPageModel::WidgetRole)));
257
258 if (model->rowCount(index) > 0) {
259 retval += collectPages(index);
260 }
261 }
262
263 return retval;
264}
265
266KPageView::FaceType KPageViewPrivate::effectiveFaceType() const
267{
268 if (faceType == KPageView::Auto) {
269 return detectAutoFace();
270 }
271
272 return faceType;
273}
274
275KPageView::FaceType KPageViewPrivate::detectAutoFace() const
276{
277 if (!model) {
278 return KPageView::Plain;
279 }
280
281 // Check whether the model has sub pages.
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) {
286 hasSubPages = true;
287 break;
288 }
289 }
290
291 if (hasSubPages) {
292 return KPageView::Tree;
293 }
294
295 if (model->rowCount() > 1) {
296 return KPageView::List;
297 }
298
299 return KPageView::Plain;
300}
301
302void KPageViewPrivate::modelChanged()
303{
304 if (!model) {
305 return;
306 }
307
308 // If the face type is Auto, we rebuild the GUI whenever the layout
309 // of the model changes.
310 if (faceType == KPageView::Auto) {
311 rebuildGui();
312 // If you discover some crashes use the line below instead...
313 // QTimer::singleShot(0, q, SLOT(rebuildGui()));
314 }
315
316 // Set the stack to the minimum size of the largest widget.
317 QSize size = stack->size();
318 const QList<QWidget *> widgets = collectPages();
319 for (int i = 0; i < widgets.count(); ++i) {
320 const QWidget *widget = widgets[i];
321 if (widget) {
322 size = size.expandedTo(widget->minimumSizeHint());
323 }
324 }
325 stack->setMinimumSize(size);
326
327 updateSelection();
328}
329
330void KPageViewPrivate::pageSelected(const QItemSelection &index, const QItemSelection &previous)
331{
332 if (!model) {
333 return;
334 }
335
336 // Return if the current Index is not valid
337 if (index.indexes().size() != 1) {
338 return;
339 }
340 QModelIndex currentIndex = index.indexes().first();
341
342 QModelIndex previousIndex;
343 // The previous index can be invalid
344 if (previous.indexes().size() == 1) {
345 previousIndex = previous.indexes().first();
346 }
347
348 if (faceType != KPageView::Tabbed) {
349 QWidget *widget = qvariant_cast<QWidget *>(model->data(currentIndex, KPageModel::WidgetRole));
350
351 if (widget) {
352 if (stack->indexOf(widget) == -1) { // not included yet
353 stack->addWidget(widget);
354 }
355
356 stack->setCurrentWidget(widget);
357 } else {
358 stack->setCurrentWidget(defaultWidget);
359 }
360
361 updateTitleWidget(currentIndex);
362 }
363
364 Q_Q(KPageView);
365 Q_EMIT q->currentPageChanged(currentIndex, previousIndex);
366}
367
368void KPageViewPrivate::updateTitleWidget(const QModelIndex &index)
369{
370 Q_Q(KPageView);
371
372 const bool headerVisible = model->data(index, KPageModel::HeaderVisibleRole).toBool();
373 if (!headerVisible) {
374 titleWidget->setVisible(false);
375 return;
376 }
377 QString header = model->data(index, KPageModel::HeaderRole).toString();
378 if (header.isEmpty()) {
379 header = model->data(index, Qt::DisplayRole).toString();
380 }
381
382 titleWidget->setText(header);
383
384 titleWidget->setVisible(q->showPageHeader());
385}
386
387void KPageViewPrivate::dataChanged(const QModelIndex &, const QModelIndex &)
388{
389 // When data has changed we update the header and icon for the currently selected
390 // page.
391 if (!view) {
392 return;
393 }
394
395 QModelIndex index = view->selectionModel()->currentIndex();
396 if (!index.isValid()) {
397 return;
398 }
399
400 updateTitleWidget(index);
401}
402
403KPageViewPrivate::KPageViewPrivate(KPageView *_parent)
404 : q_ptr(_parent)
405 , model(nullptr)
406 , faceType(KPageView::Auto)
407 , layout(nullptr)
408 , stack(nullptr)
409 , titleWidget(nullptr)
410 , searchLineEditContainer(new QWidget())
411 , searchLineEdit(new QLineEdit())
412 , view(nullptr)
413{
414}
415
416void KPageViewPrivate::init()
417{
418 Q_Q(KPageView);
419 layout = new QGridLayout(q);
420 stack = new KPageStackedWidget(q);
421 titleWidget = new KTitleWidget(q);
422 titleWidget->setObjectName("KPageView::TitleWidget");
423
424 separatorLine = new QFrame(q);
425 separatorLine->setFrameShape(QFrame::HLine);
426 separatorLine->setFixedHeight(1);
427 separatorLine->setFrameShadow(QFrame::Sunken);
428
429 // list view under it to the left
430 layout->addWidget(titleWidget, 1, 1, 1, 2);
431 // separator
432 layout->addWidget(separatorLine, 2, 0, 1, 3);
433 // and then the actual page on the right
434 layout->addWidget(stack, 3, 1);
435
436 defaultWidget = new QWidget(q);
437 stack->addWidget(defaultWidget);
438
439 // stack should use most space
440 layout->setColumnStretch(1, 1);
441 layout->setRowStretch(3, 1);
442
443 searchTimer.setInterval(400);
444 searchTimer.setSingleShot(true);
445 searchTimer.callOnTimeout(q, [this] {
446 onSearchTextChanged();
447 });
448 q->setFocusProxy(searchLineEdit);
449 searchLineEdit->setPlaceholderText(KPageView::tr("Search..."));
450 searchLineEdit->setClearButtonEnabled(true);
451 searchLineEdit->setParent(defaultWidget);
452 auto a = new QAction(q);
453 a->setIcon(QIcon::fromTheme(QStringLiteral("search")));
454 searchLineEdit->addAction(a, QLineEdit::LeadingPosition);
455 q->connect(searchLineEdit, &QLineEdit::textChanged, &searchTimer, QOverload<>::of(&QTimer::start));
456 auto containerLayout = new QVBoxLayout(searchLineEditContainer);
457 containerLayout->setContentsMargins({});
458 containerLayout->setSpacing(0);
459 containerLayout->addWidget(searchLineEdit);
460 searchLineEditContainer->setObjectName("KPageView::Search");
461}
462
463static QList<KPageWidgetItem *> getAllPages(KPageWidgetModel *model, const QModelIndex &parent)
464{
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) {
471 ret << item;
472 ret << getAllPages(model, child);
473 }
474 }
475 return ret;
476}
477
478template<typename WidgetType>
479static QList<QWidget *> hasMatchingText(const QString &text, QWidget *page)
480{
482 const auto widgets = page->findChildren<WidgetType *>();
483 for (auto label : widgets) {
484 if (label->text().contains(text, Qt::CaseInsensitive)) {
485 ret << label;
486 }
487 }
488 return ret;
489}
490
491template<>
492QList<QWidget *> hasMatchingText<QComboBox>(const QString &text, QWidget *page)
493{
495 const auto comboxBoxes = page->findChildren<QComboBox *>();
496 for (auto cb : comboxBoxes) {
497 if (cb->findText(text, Qt::MatchFlag::MatchContains) != -1) {
498 ret << cb;
499 }
500 }
501 return ret;
502}
503
504template<typename...>
505struct FindChildrenHelper {
506 static QList<QWidget *> hasMatchingTextForTypes(const QString &, QWidget *)
507 {
508 return {};
509 }
510};
511
512template<typename First, typename... Rest>
513struct FindChildrenHelper<First, Rest...> {
514 static QList<QWidget *> hasMatchingTextForTypes(const QString &text, QWidget *page)
515 {
516 return hasMatchingText<First>(text, page) << FindChildrenHelper<Rest...>::hasMatchingTextForTypes(text, page);
517 }
518};
519
520static QModelIndex walkTreeAndHideItems(QTreeView *tree, const QString &searchText, const QSet<QString> &pagesToHide, const QModelIndex &parent)
521{
522 QModelIndex current;
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);
527 const auto itemName = index.data().toString();
528 tree->setRowHidden(i, parent, pagesToHide.contains(itemName) && !itemName.contains(searchText, Qt::CaseInsensitive));
529 if (!searchText.isEmpty() && !tree->isRowHidden(i, parent) && !current.isValid()) {
530 current = model->index(i, 0, parent);
531 }
532 auto curr = walkTreeAndHideItems(tree, searchText, pagesToHide, index);
533 if (!current.isValid()) {
534 current = curr;
535 }
536 }
537 return current;
538}
539
540bool KPageViewPrivate::hasSearchableView() const
541{
542 // We support search for only these two types as they can hide rows
543 return qobject_cast<KDEPrivate::KPageListView *>(view) || qobject_cast<KDEPrivate::KPageTreeView *>(view);
544}
545
546void KPageViewPrivate::onSearchTextChanged()
547{
548 if (!hasSearchableView()) {
549 return;
550 }
551
552 const QString text = searchLineEdit->text();
553 QSet<QString> pagesToHide;
554 std::vector<QWidget *> matchedWidgets;
555 if (!text.isEmpty()) {
556 const auto pages = getAllPages(static_cast<KPageWidgetModel *>(model), {});
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();
561 }
562 matchedWidgets.insert(matchedWidgets.end(), matchingWidgets.begin(), matchingWidgets.end());
563 }
564 }
565
566 if (model) {
567 QModelIndex current;
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();
571 list->setRowHidden(i, pagesToHide.contains(itemName) && !itemName.contains(text, Qt::CaseInsensitive));
572 if (!text.isEmpty() && !list->isRowHidden(i) && !current.isValid()) {
573 current = model->index(i, 0);
574 }
575 }
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();
582 }
583 } else {
584 qWarning() << "Unreacheable, unknown view:" << view;
585 Q_UNREACHABLE();
586 }
587
588 if (current.isValid()) {
589 view->setCurrentIndex(current);
590 }
591 }
592
593 qDeleteAll(m_searchMatchOverlays);
594 m_searchMatchOverlays.clear();
595
596 using TabWidgetAndPage = QPair<QTabWidget *, QWidget *>;
597 auto tabWidgetParent = [](QWidget *w) {
598 // Finds if @p w is in a QTabWidget and returns
599 // The QTabWidget + the widget in the stack where
600 // @p w lives
601 auto parent = w->parentWidget();
602 TabWidgetAndPage p = {nullptr, nullptr};
603 if (auto tw = qobject_cast<QTabWidget *>(parent)) {
604 p.first = tw;
605 }
607 while (parent) {
608 if (!p.first) {
609 if (auto tw = qobject_cast<QTabWidget *>(parent)) {
610 if (parentChain.size() >= 3) {
611 // last == QTabWidget
612 // second last == QStackedWidget of QTabWidget
613 // third last => the widget we want
614 p.second = parentChain.value((parentChain.size() - 1) - 2);
615 }
616 p.first = tw;
617 break;
618 }
619 }
620 parent = parent->parentWidget();
621 parentChain << parent;
622 }
623 return p;
624 };
625
626 for (auto w : matchedWidgets) {
627 if (w) {
628 m_searchMatchOverlays << new SearchMatchOverlay(w);
629 if (!w->isVisible()) {
630 const auto [tabWidget, page] = tabWidgetParent(w);
631 if (!tabWidget && !page) {
632 continue;
633 }
634 const int idx = tabWidget->indexOf(page);
635 if (idx < 0) {
636 // qDebug() << page << tabWidget << "not found" << w;
637 continue;
638 }
639 m_searchMatchOverlays << new SearchMatchOverlay(tabWidget->tabBar(), idx);
640 }
641 }
642 }
643}
644
645// KPageView Implementation
647 : KPageView(*new KPageViewPrivate(this), parent)
648{
649}
650
651KPageView::KPageView(KPageViewPrivate &dd, QWidget *parent)
652 : QWidget(parent)
653 , d_ptr(&dd)
654{
655 d_ptr->init();
656}
657
658KPageView::~KPageView() = default;
659
661{
662 Q_D(KPageView);
663 // clean up old model
664 if (d->model) {
665 disconnect(d->m_layoutChangedConnection);
666 disconnect(d->m_dataChangedConnection);
667 }
668
669 d->model = model;
670
671 if (d->model) {
672 d->m_layoutChangedConnection = connect(d->model, &QAbstractItemModel::layoutChanged, this, [d]() {
673 d->modelChanged();
674 });
675 d->m_dataChangedConnection = connect(d->model, &QAbstractItemModel::dataChanged, this, [d](const QModelIndex &topLeft, const QModelIndex &bottomRight) {
676 d->dataChanged(topLeft, bottomRight);
677 });
678
679 // set new model in navigation view
680 if (d->view) {
681 d->view->setModel(model);
682 }
683 }
684
685 d->rebuildGui();
686}
687
689{
690 Q_D(const KPageView);
691 return d->model;
692}
693
695{
696 Q_D(KPageView);
697 d->faceType = faceType;
698
699 d->rebuildGui();
700}
701
702KPageView::FaceType KPageView::faceType() const
703{
704 Q_D(const KPageView);
705 return d->faceType;
706}
707
709{
710 Q_D(KPageView);
711 if (!d->view || !d->view->selectionModel()) {
712 return;
713 }
714
715 d->view->selectionModel()->setCurrentIndex(index, QItemSelectionModel::SelectCurrent);
716}
717
719{
720 Q_D(const KPageView);
721 if (!d->view || !d->view->selectionModel()) {
722 return QModelIndex();
723 }
724
725 return d->view->selectionModel()->currentIndex();
726}
727
729{
730 Q_D(KPageView);
731 if (d->view) {
732 d->view->setItemDelegate(delegate);
733 }
734}
735
737{
738 Q_D(const KPageView);
739 if (d->view) {
740 return d->view->itemDelegate();
741 } else {
742 return nullptr;
743 }
744}
745
747{
748 Q_D(KPageView);
749
750 Q_ASSERT(widget);
751
752 bool isCurrent = (d->stack->currentIndex() == d->stack->indexOf(d->defaultWidget));
753
754 // remove old default widget
755 d->stack->removeWidget(d->defaultWidget);
756 delete d->defaultWidget;
757
758 // add new default widget
759 d->defaultWidget = widget;
760 d->stack->addWidget(d->defaultWidget);
761
762 if (isCurrent) {
763 d->stack->setCurrentWidget(d->defaultWidget);
764 }
765}
766
768{
769 Q_D(KPageView);
770 if (d->pageHeader == header) {
771 return;
772 }
773
774 if (d->pageHeader) {
775 d->layout->removeWidget(d->pageHeader);
776 }
777 d->layout->removeWidget(d->titleWidget);
778
779 d->pageHeader = header;
780
781 // Give it a colSpan of 2 to add a margin to the right
782 if (d->pageHeader) {
783 d->layout->addWidget(d->pageHeader, 1, 1, 1, 2);
784 d->pageHeader->setVisible(showPageHeader());
785 } else {
786 d->layout->addWidget(d->titleWidget, 1, 1, 1, 2);
787 d->titleWidget->setVisible(showPageHeader());
788 }
789}
790
791QWidget *KPageView::pageHeader() const
792{
793 Q_D(const KPageView);
794 if (!d->pageHeader) {
795 return d->titleWidget;
796 }
797 return d->pageHeader;
798}
799
801{
802 Q_D(KPageView);
803 if (d->pageFooter == footer) {
804 return;
805 }
806
807 if (d->pageFooter) {
808 d->layout->removeWidget(d->pageFooter);
809 }
810
811 d->pageFooter = footer;
812
813 if (footer) {
814 d->pageFooter->setContentsMargins(4, 4, 4, 4);
815 d->layout->addWidget(d->pageFooter, 4, 1);
816 }
817}
818
819QWidget *KPageView::pageFooter() const
820{
821 Q_D(const KPageView);
822 return d->pageFooter;
823}
824
826{
827 Q_D(KPageView);
828 const FaceType faceType = d->effectiveFaceType();
829
830 if (faceType == Plain) {
831 return new KDEPrivate::KPagePlainView(this);
832 }
833 if (faceType == FlatList) {
834 return new KDEPrivate::KPageListView(this);
835 }
836 if (faceType == List) {
837 auto view = new KDEPrivate::KPageListView(this);
838 view->setItemDelegate(new KDEPrivate::KPageListViewDelegate(this));
839 view->setFlexibleWidth(true);
840 return view;
841 }
842 if (faceType == Tree) {
843 return new KDEPrivate::KPageTreeView(this);
844 }
845 if (faceType == Tabbed) {
846 return new KDEPrivate::KPageTabbedView(this);
847 }
848
849 return nullptr;
850}
851
853{
854 Q_D(const KPageView);
855 const FaceType faceType = d->effectiveFaceType();
856
857 if (faceType == Tabbed) {
858 return false;
859 } else {
860 return d->pageHeader || !d->titleWidget->text().isEmpty();
861 }
862}
863
865{
866 Q_D(const KPageView);
867 const FaceType faceType = d->effectiveFaceType();
868
869 if (faceType == Plain || faceType == Tabbed) {
870 return Qt::AlignTop;
871 } else {
872 return Qt::AlignLeft;
873 }
874}
875
876#include "moc_kpageview.cpp"
@ WidgetRole
A pointer to the page widget.
Definition kpagemodel.h:70
@ HeaderVisibleRole
when true, show the page header, if false don't
Definition kpagemodel.h:75
@ HeaderRole
A string to be rendered as page header.
Definition kpagemodel.h:59
A base class which can handle multiple pages.
Definition kpageview.h:50
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.
Definition kpageview.h:62
@ List
An icon list is used as navigation view.
Definition kpageview.h:78
@ Auto
Depending on the number of pages in the model, the Plain (one page), the List (several pages) or the ...
Definition kpageview.h:69
@ Tree
A tree list is used as navigation view.
Definition kpageview.h:82
@ Plain
No navigation view will be visible and only the first page of the model will be shown.
Definition kpageview.h:74
@ FlatList
A flat list with small icons is used as navigation view.
Definition kpageview.h:90
@ Tabbed
A tab widget is used as navigation view.
Definition kpageview.h:86
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.
Standard title widget.
KIOCORE_EXPORT QStringList list(const QString &fileClass)
QString label(StandardShortcut id)
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector< int > &roles)
void layoutChanged(const QList< QPersistentModelIndex > &parents, QAbstractItemModel::LayoutChangeHint hint)
QAbstractItemModel * model() const const
void setAlpha(int alpha)
QEvent::Type type() const const
QIcon fromTheme(const QString &name)
QModelIndexList indexes() const const
void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
void addWidget(QWidget *w)
void textChanged(const QString &text)
void append(const T &value)
int count(const T &value) const const
bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, QGenericArgument val5, QGenericArgument val6, QGenericArgument val7, QGenericArgument val8, QGenericArgument val9)
QVariant data(int role) const const
bool isValid() const const
QModelIndex parent() const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
virtual bool eventFilter(QObject *watched, QEvent *event)
QList< T > findChildren(const QString &name, 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 T &value) const const
QSet::iterator insert(const T &value)
QSize expandedTo(const QSize &otherSize) const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
QChar * data()
bool isEmpty() const const
PM_LayoutLeftMargin
typedef Alignment
CaseInsensitive
QueuedConnection
DisplayRole
WA_TransparentForMouseEvents
void start()
bool isRowHidden(int row, const QModelIndex &parent) const const
void setRowHidden(int row, const QModelIndex &parent, bool hide)
QString toString() const const
int size() const const
T value(int i) const const
QLayout * layout() const const
QWidget * parentWidget() const const
void raise()
void resize(int w, int h)
void setAttribute(Qt::WidgetAttribute attribute, bool on)
void setContentsMargins(int left, int top, int right, int bottom)
void setGeometry(int x, int y, int w, int h)
void show()
virtual void setVisible(bool visible)
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sun Feb 25 2024 18:40:26 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.