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

KDE's Doxygen guidelines are available online.