Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qttreepropertybrowser.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#include <QtCore/QOperatingSystemVersion>
7#include <QtCore/QHash>
8#include <QtGui/QFocusEvent>
9#include <QtGui/QIcon>
10#include <QtGui/QPainter>
11#include <QtGui/QPalette>
12#include <QtGui/QStyleHints>
13#include <QtWidgets/QApplication>
14#include <QtWidgets/QHBoxLayout>
15#include <QtWidgets/QHeaderView>
16#include <QtWidgets/QItemDelegate>
17#include <QtWidgets/QStyle>
18#include <QtWidgets/QTreeWidget>
19
21
22using namespace Qt::StringLiterals;
23
24static constexpr bool isWindows = QOperatingSystemVersion::currentType() == QOperatingSystemVersion::Windows;
25
26static inline bool isLightTheme()
27{
28 return QGuiApplication::styleHints()->colorScheme() != Qt::ColorScheme::Dark;
29}
30
32
34{
35 QtTreePropertyBrowser *q_ptr = nullptr;
36 Q_DECLARE_PUBLIC(QtTreePropertyBrowser)
37
38public:
40
41 void propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex);
44 QWidget *createEditor(QtProperty *property, QWidget *parent) const
45 { return q_ptr->createEditor(property, parent); }
46 QtProperty *indexToProperty(const QModelIndex &index) const;
47 QTreeWidgetItem *indexToItem(const QModelIndex &index) const;
48 QtBrowserItem *indexToBrowserItem(const QModelIndex &index) const;
49 bool lastColumn(int column) const;
50 void disableItem(QTreeWidgetItem *item) const;
51 void enableItem(QTreeWidgetItem *item) const;
52 bool hasValue(QTreeWidgetItem *item) const;
53
54 void slotCollapsed(const QModelIndex &index);
55 void slotExpanded(const QModelIndex &index);
56
58
59 QtPropertyEditorView *treeWidget() const { return m_treeWidget; }
60 bool markPropertiesWithoutValue() const { return m_markPropertiesWithoutValue; }
61
63 void setCurrentItem(QtBrowserItem *browserItem, bool block);
64 void editItem(QtBrowserItem *browserItem);
65
67 void slotCurrentTreeItemChanged(QTreeWidgetItem *newItem, QTreeWidgetItem *);
68
69 QTreeWidgetItem *editedItem() const;
70
71private:
72 void updateItem(QTreeWidgetItem *item);
73
74 QHash<QtBrowserItem *, QTreeWidgetItem *> m_indexToItem;
75 QHash<QTreeWidgetItem *, QtBrowserItem *> m_itemToIndex;
76
77 QHash<QtBrowserItem *, QColor> m_indexToBackgroundColor;
78
79 QtPropertyEditorView *m_treeWidget = nullptr;
80
81 bool m_headerVisible = true;
82 QtTreePropertyBrowser::ResizeMode m_resizeMode = QtTreePropertyBrowser::Stretch;
83 class QtPropertyEditorDelegate *m_delegate = nullptr;
84 bool m_markPropertiesWithoutValue = false;
85 bool m_browserChangedBlocked = false;
86 QIcon m_expandIcon;
87};
88
89// ------------ QtPropertyEditorView
91{
93public:
95
97 { m_editorPrivate = editorPrivate; }
98
99 QTreeWidgetItem *indexToItem(const QModelIndex &index) const
100 { return itemFromIndex(index); }
101
102protected:
103 void keyPressEvent(QKeyEvent *event) override;
104 void mousePressEvent(QMouseEvent *event) override;
105 void drawRow(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
106
107private:
108 QtTreePropertyBrowserPrivate *m_editorPrivate = nullptr;
109};
110
111QtPropertyEditorView::QtPropertyEditorView(QWidget *parent) :
112 QTreeWidget(parent)
113{
114 connect(header(), &QHeaderView::sectionDoubleClicked, this, &QTreeView::resizeColumnToContents);
115}
116
117void QtPropertyEditorView::drawRow(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
118{
119 QStyleOptionViewItem opt = option;
120 bool hasValue = true;
121 if (m_editorPrivate) {
122 QtProperty *property = m_editorPrivate->indexToProperty(index);
123 if (property)
124 hasValue = property->hasValue();
125 }
126 if (!hasValue && m_editorPrivate->markPropertiesWithoutValue()) {
127 const QColor c = option.palette.color(QPalette::Dark);
128 painter->fillRect(option.rect, c);
129 opt.palette.setColor(QPalette::AlternateBase, c);
130 } else {
131 const QColor c = m_editorPrivate->calculatedBackgroundColor(m_editorPrivate->indexToBrowserItem(index));
132 if (c.isValid()) {
133 painter->fillRect(option.rect, c);
134 opt.palette.setColor(QPalette::AlternateBase, c.lighter(112));
135 }
136 }
137 QTreeWidget::drawRow(painter, opt, index);
138 QColor color = static_cast<QRgb>(QApplication::style()->styleHint(QStyle::SH_Table_GridLineColor, &opt));
139 painter->save();
140 painter->setPen(QPen(color));
141 painter->drawLine(opt.rect.x(), opt.rect.bottom(), opt.rect.right(), opt.rect.bottom());
142 painter->restore();
143}
144
145void QtPropertyEditorView::keyPressEvent(QKeyEvent *event)
146{
147 switch (event->key()) {
148 case Qt::Key_Return:
149 case Qt::Key_Enter:
150 case Qt::Key_Space: // Trigger Edit
151 if (!m_editorPrivate->editedItem())
152 if (const QTreeWidgetItem *item = currentItem())
153 if (item->columnCount() >= 2 && ((item->flags() & (Qt::ItemIsEditable | Qt::ItemIsEnabled)) == (Qt::ItemIsEditable | Qt::ItemIsEnabled))) {
154 event->accept();
155 // If the current position is at column 0, move to 1.
156 QModelIndex index = currentIndex();
157 if (index.column() == 0) {
158 index = index.sibling(index.row(), 1);
159 setCurrentIndex(index);
160 }
161 edit(index);
162 return;
163 }
164 break;
165 default:
166 break;
167 }
168 QTreeWidget::keyPressEvent(event);
169}
170
171void QtPropertyEditorView::mousePressEvent(QMouseEvent *event)
172{
173 QTreeWidget::mousePressEvent(event);
174 QTreeWidgetItem *item = itemAt(event->position().toPoint());
175
176 if (item) {
177 if ((item != m_editorPrivate->editedItem()) && (event->button() == Qt::LeftButton)
178 && (header()->logicalIndexAt(event->position().toPoint().x()) == 1)
179 && ((item->flags() & (Qt::ItemIsEditable | Qt::ItemIsEnabled)) == (Qt::ItemIsEditable | Qt::ItemIsEnabled))) {
180 editItem(item, 1);
181 } else if (!m_editorPrivate->hasValue(item) && m_editorPrivate->markPropertiesWithoutValue() && !rootIsDecorated()) {
182 if (event->position().toPoint().x() + header()->offset() < 20)
183 item->setExpanded(!item->isExpanded());
184 }
185 }
186}
187
188// ------------ QtPropertyEditorDelegate
190{
192public:
194
196 { m_editorPrivate = editorPrivate; }
197
198 QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
199 const QModelIndex &index) const override;
200
201 void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
202 const QModelIndex &index) const override;
203
204 void paint(QPainter *painter, const QStyleOptionViewItem &option,
205 const QModelIndex &index) const override;
206
207 QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
208
209 void setModelData(QWidget *, QAbstractItemModel *,
210 const QModelIndex &) const override {}
211
212 void setEditorData(QWidget *, const QModelIndex &) const override {}
213
214 bool eventFilter(QObject *object, QEvent *event) override;
215 void closeEditor(QtProperty *property);
216
217 QTreeWidgetItem *editedItem() const { return m_editedItem; }
218
219private slots:
221
222private:
223 int indentation(const QModelIndex &index) const;
224
226 mutable EditorToPropertyMap m_editorToProperty;
227
229 mutable PropertyToEditorMap m_propertyToEditor;
230 QtTreePropertyBrowserPrivate *m_editorPrivate = nullptr;
231 mutable QTreeWidgetItem *m_editedItem = nullptr;
232 mutable QWidget *m_editedWidget = nullptr;
233};
234
235int QtPropertyEditorDelegate::indentation(const QModelIndex &index) const
236{
237 if (!m_editorPrivate)
238 return 0;
239
240 QTreeWidgetItem *item = m_editorPrivate->indexToItem(index);
241 int indent = 0;
242 while (item->parent()) {
243 item = item->parent();
244 ++indent;
245 }
246 if (m_editorPrivate->treeWidget()->rootIsDecorated())
247 ++indent;
248 return indent * m_editorPrivate->treeWidget()->indentation();
249}
250
251void QtPropertyEditorDelegate::slotEditorDestroyed(QObject *object)
252{
253 if (auto *w = qobject_cast<QWidget *>(object)) {
254 const auto it = m_editorToProperty.find(w);
255 if (it != m_editorToProperty.end()) {
256 m_propertyToEditor.remove(it.value());
257 m_editorToProperty.erase(it);
258 }
259 if (m_editedWidget == w) {
260 m_editedWidget = nullptr;
261 m_editedItem = nullptr;
262 }
263 }
264}
265
267{
268 if (QWidget *w = m_propertyToEditor.value(property, nullptr))
269 w->deleteLater();
270}
271
273 const QStyleOptionViewItem &, const QModelIndex &index) const
274{
275 if (index.column() == 1 && m_editorPrivate) {
276 QtProperty *property = m_editorPrivate->indexToProperty(index);
277 QTreeWidgetItem *item = m_editorPrivate->indexToItem(index);
278 if (property && item && (item->flags() & Qt::ItemIsEnabled)) {
279 QWidget *editor = m_editorPrivate->createEditor(property, parent);
280 if (editor) {
281 editor->setAutoFillBackground(true);
282 if (editor->palette().color(editor->backgroundRole()) == Qt::transparent)
283 editor->setBackgroundRole(QPalette::Window);
284 editor->installEventFilter(const_cast<QtPropertyEditorDelegate *>(this));
285 connect(editor, &QObject::destroyed,
286 this, &QtPropertyEditorDelegate::slotEditorDestroyed);
287 m_propertyToEditor[property] = editor;
288 m_editorToProperty[editor] = property;
289 m_editedItem = item;
290 m_editedWidget = editor;
291 }
292 return editor;
293 }
294 }
295 return nullptr;
296}
297
299 const QStyleOptionViewItem &option, const QModelIndex &index) const
300{
301 Q_UNUSED(index);
302 editor->setGeometry(option.rect.adjusted(0, 0, 0, -1));
303}
304
305void QtPropertyEditorDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
306 const QModelIndex &index) const
307{
308 bool hasValue = true;
309 if (m_editorPrivate) {
310 QtProperty *property = m_editorPrivate->indexToProperty(index);
311 if (property)
312 hasValue = property->hasValue();
313 }
314 QStyleOptionViewItem opt = option;
315 if ((m_editorPrivate && index.column() == 0) || !hasValue) {
316 QtProperty *property = m_editorPrivate->indexToProperty(index);
317 if (property && property->isModified()) {
318 opt.font.setBold(true);
319 opt.fontMetrics = QFontMetrics(opt.font);
320 }
321 }
322 QColor c;
323 if (!hasValue && m_editorPrivate->markPropertiesWithoutValue()) {
324 c = opt.palette.color(QPalette::Dark);
325 // Hardcode "white" for Windows/light which is otherwise blue
326 const QColor textColor = isWindows && isLightTheme()
327 ? QColor(Qt::white) : opt.palette.color(QPalette::BrightText);
328 opt.palette.setColor(QPalette::Text, textColor);
329 } else {
330 c = m_editorPrivate->calculatedBackgroundColor(m_editorPrivate->indexToBrowserItem(index));
331 if (c.isValid() && (opt.features & QStyleOptionViewItem::Alternate))
332 c = c.lighter(112);
333 }
334 if (c.isValid())
335 painter->fillRect(option.rect, c);
336 opt.state &= ~QStyle::State_HasFocus;
337 QItemDelegate::paint(painter, opt, index);
338
339 opt.palette.setCurrentColorGroup(QPalette::Active);
340 QColor color = static_cast<QRgb>(QApplication::style()->styleHint(QStyle::SH_Table_GridLineColor, &opt));
341 painter->save();
342 painter->setPen(QPen(color));
343 if (!m_editorPrivate || (!m_editorPrivate->lastColumn(index.column()) && hasValue)) {
344 int right = (option.direction == Qt::LeftToRight) ? option.rect.right() : option.rect.left();
345 painter->drawLine(right, option.rect.y(), right, option.rect.bottom());
346 }
347 painter->restore();
348}
349
350QSize QtPropertyEditorDelegate::sizeHint(const QStyleOptionViewItem &option,
351 const QModelIndex &index) const
352{
353 return QItemDelegate::sizeHint(option, index) + QSize(3, 4);
354}
355
356bool QtPropertyEditorDelegate::eventFilter(QObject *object, QEvent *event)
357{
358 if (event->type() == QEvent::FocusOut) {
359 QFocusEvent *fe = static_cast<QFocusEvent *>(event);
360 if (fe->reason() == Qt::ActiveWindowFocusReason)
361 return false;
362 }
363 return QItemDelegate::eventFilter(object, event);
364}
365
366// -------- QtTreePropertyBrowserPrivate implementation
367
368// Draw an icon indicating opened/closing branches
369static QIcon drawIndicatorIcon(const QPalette &palette, QStyle *style)
370{
371 QPixmap pix(14, 14);
372 pix.fill(Qt::transparent);
373 QStyleOption branchOption;
374 branchOption.rect = QRect(2, 2, 9, 9); // ### hardcoded in qcommonstyle.cpp
375 branchOption.palette = palette;
376 branchOption.state = QStyle::State_Children;
377
378 QPainter p;
379 // Draw closed state
380 p.begin(&pix);
381 style->drawPrimitive(QStyle::PE_IndicatorBranch, &branchOption, &p);
382 p.end();
383 QIcon rc = pix;
384 rc.addPixmap(pix, QIcon::Selected, QIcon::Off);
385 // Draw opened state
386 branchOption.state |= QStyle::State_Open;
387 pix.fill(Qt::transparent);
388 p.begin(&pix);
389 style->drawPrimitive(QStyle::PE_IndicatorBranch, &branchOption, &p);
390 p.end();
391
392 rc.addPixmap(pix, QIcon::Normal, QIcon::On);
393 rc.addPixmap(pix, QIcon::Selected, QIcon::On);
394 return rc;
395}
396
397void QtTreePropertyBrowserPrivate::init(QWidget *parent)
398{
399 auto *layout = new QHBoxLayout(parent);
400 layout->setContentsMargins(QMargins());
401 m_treeWidget = new QtPropertyEditorView(parent);
402 m_treeWidget->setEditorPrivate(this);
403 m_treeWidget->setIconSize(QSize(18, 18));
404 layout->addWidget(m_treeWidget);
405
406 m_treeWidget->setColumnCount(2);
407 QStringList labels;
408 labels.append(QCoreApplication::translate("QtTreePropertyBrowser", "Property"));
409 labels.append(QCoreApplication::translate("QtTreePropertyBrowser", "Value"));
410 m_treeWidget->setHeaderLabels(labels);
411 m_treeWidget->setAlternatingRowColors(true);
412 m_treeWidget->setEditTriggers(QAbstractItemView::EditKeyPressed);
413 m_delegate = new QtPropertyEditorDelegate(parent);
414 m_delegate->setEditorPrivate(this);
415 m_treeWidget->setItemDelegate(m_delegate);
416 m_treeWidget->header()->setSectionsMovable(false);
417 m_treeWidget->header()->setSectionResizeMode(QHeaderView::Stretch);
418
419 m_expandIcon = drawIndicatorIcon(q_ptr->palette(), q_ptr->style());
420
421 QObject::connect(m_treeWidget, &QTreeView::collapsed,
422 q_ptr, [this](const QModelIndex &index) { slotCollapsed(index); });
423 QObject::connect(m_treeWidget, &QTreeView::expanded,
424 q_ptr, [this](const QModelIndex &index) { slotExpanded(index); });
425 QObject::connect(m_treeWidget, &QTreeWidget::currentItemChanged,
426 q_ptr, [this](QTreeWidgetItem *current, QTreeWidgetItem *previous)
427 { slotCurrentTreeItemChanged(current, previous); });
428}
429
431{
432 if (QTreeWidgetItem *treeItem = m_treeWidget->currentItem())
433 return m_itemToIndex.value(treeItem);
434 return nullptr;
435}
436
438{
439 const bool blocked = block ? m_treeWidget->blockSignals(true) : false;
440 if (browserItem == nullptr)
441 m_treeWidget->setCurrentItem(nullptr);
442 else
443 m_treeWidget->setCurrentItem(m_indexToItem.value(browserItem));
444 if (block)
445 m_treeWidget->blockSignals(blocked);
446}
447
449{
450 QTreeWidgetItem *item = m_treeWidget->indexToItem(index);
451 QtBrowserItem *idx = m_itemToIndex.value(item);
452 if (idx)
453 return idx->property();
454 return nullptr;
455}
456
458{
459 QTreeWidgetItem *item = m_treeWidget->indexToItem(index);
460 return m_itemToIndex.value(item);
461}
462
463QTreeWidgetItem *QtTreePropertyBrowserPrivate::indexToItem(const QModelIndex &index) const
464{
465 return m_treeWidget->indexToItem(index);
466}
467
469{
470 return m_treeWidget->header()->visualIndex(column) == m_treeWidget->columnCount() - 1;
471}
472
473void QtTreePropertyBrowserPrivate::disableItem(QTreeWidgetItem *item) const
474{
475 Qt::ItemFlags flags = item->flags();
476 if (flags & Qt::ItemIsEnabled) {
477 flags &= ~Qt::ItemIsEnabled;
478 item->setFlags(flags);
479 m_delegate->closeEditor(m_itemToIndex[item]->property());
480 const int childCount = item->childCount();
481 for (int i = 0; i < childCount; i++) {
482 QTreeWidgetItem *child = item->child(i);
483 disableItem(child);
484 }
485 }
486}
487
488void QtTreePropertyBrowserPrivate::enableItem(QTreeWidgetItem *item) const
489{
490 Qt::ItemFlags flags = item->flags();
491 flags |= Qt::ItemIsEnabled;
492 item->setFlags(flags);
493 const int childCount = item->childCount();
494 for (int i = 0; i < childCount; i++) {
495 QTreeWidgetItem *child = item->child(i);
496 QtProperty *property = m_itemToIndex[child]->property();
497 if (property->isEnabled()) {
498 enableItem(child);
499 }
500 }
501}
502
503bool QtTreePropertyBrowserPrivate::hasValue(QTreeWidgetItem *item) const
504{
505 QtBrowserItem *browserItem = m_itemToIndex.value(item);
506 if (browserItem)
507 return browserItem->property()->hasValue();
508 return false;
509}
510
512{
513 QTreeWidgetItem *afterItem = m_indexToItem.value(afterIndex);
514 QTreeWidgetItem *parentItem = m_indexToItem.value(index->parent());
515
516 QTreeWidgetItem *newItem = nullptr;
517 if (parentItem) {
518 newItem = new QTreeWidgetItem(parentItem, afterItem);
519 } else {
520 newItem = new QTreeWidgetItem(m_treeWidget, afterItem);
521 }
522 m_itemToIndex[newItem] = index;
523 m_indexToItem[index] = newItem;
524
525 newItem->setFlags(newItem->flags() | Qt::ItemIsEditable);
526 newItem->setExpanded(true);
527
528 updateItem(newItem);
529}
530
532{
533 QTreeWidgetItem *item = m_indexToItem.value(index);
534
535 if (m_treeWidget->currentItem() == item) {
536 m_treeWidget->setCurrentItem(nullptr);
537 }
538
539 delete item;
540
541 m_indexToItem.remove(index);
542 m_itemToIndex.remove(item);
543 m_indexToBackgroundColor.remove(index);
544}
545
547{
548 QTreeWidgetItem *item = m_indexToItem.value(index);
549
550 updateItem(item);
551}
552
553void QtTreePropertyBrowserPrivate::updateItem(QTreeWidgetItem *item)
554{
555 QtProperty *property = m_itemToIndex[item]->property();
556 QIcon expandIcon;
557 if (property->hasValue()) {
558 const QString valueToolTip = property->valueToolTip();
559 const QString valueText = property->valueText();
560 item->setToolTip(1, valueToolTip.isEmpty() ? valueText : valueToolTip);
561 item->setIcon(1, property->valueIcon());
562 item->setText(1, valueText);
563 } else if (markPropertiesWithoutValue() && !m_treeWidget->rootIsDecorated()) {
564 expandIcon = m_expandIcon;
565 }
566 item->setIcon(0, expandIcon);
567 item->setFirstColumnSpanned(!property->hasValue());
568 const QString descriptionToolTip = property->descriptionToolTip();
569 const QString propertyName = property->propertyName();
570 item->setToolTip(0, descriptionToolTip.isEmpty() ? propertyName : descriptionToolTip);
571 item->setStatusTip(0, property->statusTip());
572 item->setWhatsThis(0, property->whatsThis());
573 item->setText(0, propertyName);
574 bool wasEnabled = item->flags() & Qt::ItemIsEnabled;
575 bool isEnabled = wasEnabled;
576 if (property->isEnabled()) {
577 QTreeWidgetItem *parent = item->parent();
578 isEnabled = !parent || (parent->flags() & Qt::ItemIsEnabled);
579 } else {
580 isEnabled = false;
581 }
582 if (wasEnabled != isEnabled) {
583 if (isEnabled)
584 enableItem(item);
585 else
586 disableItem(item);
587 }
588 m_treeWidget->viewport()->update();
589}
590
592{
593 QtBrowserItem *i = item;
594 const auto itEnd = m_indexToBackgroundColor.constEnd();
595 while (i) {
596 auto it = m_indexToBackgroundColor.constFind(i);
597 if (it != itEnd)
598 return it.value();
599 i = i->parent();
600 }
601 return {};
602}
603
604void QtTreePropertyBrowserPrivate::slotCollapsed(const QModelIndex &index)
605{
606 QTreeWidgetItem *item = indexToItem(index);
607 QtBrowserItem *idx = m_itemToIndex.value(item);
608 if (item)
609 emit q_ptr->collapsed(idx);
610}
611
612void QtTreePropertyBrowserPrivate::slotExpanded(const QModelIndex &index)
613{
614 QTreeWidgetItem *item = indexToItem(index);
615 QtBrowserItem *idx = m_itemToIndex.value(item);
616 if (item)
617 emit q_ptr->expanded(idx);
618}
619
621{
622 if (!m_browserChangedBlocked && item != currentItem())
623 setCurrentItem(item, true);
624}
625
626void QtTreePropertyBrowserPrivate::slotCurrentTreeItemChanged(QTreeWidgetItem *newItem, QTreeWidgetItem *)
627{
628 QtBrowserItem *browserItem = newItem ? m_itemToIndex.value(newItem) : 0;
629 m_browserChangedBlocked = true;
630 q_ptr->setCurrentItem(browserItem);
631 m_browserChangedBlocked = false;
632}
633
635{
636 return m_delegate->editedItem();
637}
638
640{
641 if (QTreeWidgetItem *treeItem = m_indexToItem.value(browserItem, nullptr)) {
642 m_treeWidget->setCurrentItem (treeItem, 1);
643 m_treeWidget->editItem(treeItem, 1);
644 }
645}
646
647/*!
648 \class QtTreePropertyBrowser
649 \internal
650 \inmodule QtDesigner
651 \since 4.4
652
653 \brief The QtTreePropertyBrowser class provides QTreeWidget based
654 property browser.
655
656 A property browser is a widget that enables the user to edit a
657 given set of properties. Each property is represented by a label
658 specifying the property's name, and an editing widget (e.g. a line
659 edit or a combobox) holding its value. A property can have zero or
660 more subproperties.
661
662 QtTreePropertyBrowser provides a tree based view for all nested
663 properties, i.e. properties that have subproperties can be in an
664 expanded (subproperties are visible) or collapsed (subproperties
665 are hidden) state. For example:
666
667 \image qttreepropertybrowser.png
668
669 Use the QtAbstractPropertyBrowser API to add, insert and remove
670 properties from an instance of the QtTreePropertyBrowser class.
671 The properties themselves are created and managed by
672 implementations of the QtAbstractPropertyManager class.
673
674 \sa QtGroupBoxPropertyBrowser, QtAbstractPropertyBrowser
675*/
676
677/*!
678 \fn void QtTreePropertyBrowser::collapsed(QtBrowserItem *item)
679
680 This signal is emitted when the \a item is collapsed.
681
682 \sa expanded(), setExpanded()
683*/
684
685/*!
686 \fn void QtTreePropertyBrowser::expanded(QtBrowserItem *item)
687
688 This signal is emitted when the \a item is expanded.
689
690 \sa collapsed(), setExpanded()
691*/
692
693/*!
694 Creates a property browser with the given \a parent.
695*/
696QtTreePropertyBrowser::QtTreePropertyBrowser(QWidget *parent)
697 : QtAbstractPropertyBrowser(parent), d_ptr(new QtTreePropertyBrowserPrivate)
698{
699 d_ptr->q_ptr = this;
700
701 d_ptr->init(this);
702 QObject::connect(this, &QtAbstractPropertyBrowser::currentItemChanged,
703 this, [this](QtBrowserItem *current)
704 { d_ptr->slotCurrentBrowserItemChanged(current); });
705}
706
707/*!
708 Destroys this property browser.
709
710 Note that the properties that were inserted into this browser are
711 \e not destroyed since they may still be used in other
712 browsers. The properties are owned by the manager that created
713 them.
714
715 \sa QtProperty, QtAbstractPropertyManager
716*/
718
719/*!
720 \property QtTreePropertyBrowser::indentation
721 \brief indentation of the items in the tree view.
722*/
724{
725 return d_ptr->m_treeWidget->indentation();
726}
727
729{
730 d_ptr->m_treeWidget->setIndentation(i);
731}
732
733/*!
734 \property QtTreePropertyBrowser::rootIsDecorated
735 \brief whether to show controls for expanding and collapsing root items.
736*/
738{
739 return d_ptr->m_treeWidget->rootIsDecorated();
740}
741
743{
744 d_ptr->m_treeWidget->setRootIsDecorated(show);
745 for (auto it = d_ptr->m_itemToIndex.cbegin(), end = d_ptr->m_itemToIndex.cend(); it != end; ++it) {
746 QtProperty *property = it.value()->property();
747 if (!property->hasValue())
748 d_ptr->updateItem(it.key());
749 }
750}
751
752/*!
753 \property QtTreePropertyBrowser::alternatingRowColors
754 \brief whether to draw the background using alternating colors.
755 By default this property is set to true.
756*/
758{
759 return d_ptr->m_treeWidget->alternatingRowColors();
760}
761
763{
764 d_ptr->m_treeWidget->setAlternatingRowColors(enable);
765}
766
767/*!
768 \property QtTreePropertyBrowser::headerVisible
769 \brief whether to show the header.
770*/
772{
773 return d_ptr->m_headerVisible;
774}
775
777{
778 if (d_ptr->m_headerVisible == visible)
779 return;
780
781 d_ptr->m_headerVisible = visible;
782 d_ptr->m_treeWidget->header()->setVisible(visible);
783}
784
785/*!
786 \enum QtTreePropertyBrowser::ResizeMode
787
788 The resize mode specifies the behavior of the header sections.
789
790 \value Interactive The user can resize the sections.
791 The sections can also be resized programmatically using setSplitterPosition().
792
793 \value Fixed The user cannot resize the section.
794 The section can only be resized programmatically using setSplitterPosition().
795
796 \value Stretch QHeaderView will automatically resize the section to fill the available space.
797 The size cannot be changed by the user or programmatically.
798
799 \value ResizeToContents QHeaderView will automatically resize the section to its optimal
800 size based on the contents of the entire column.
801 The size cannot be changed by the user or programmatically.
802
803 \sa setResizeMode()
804*/
805
806/*!
807 \property QtTreePropertyBrowser::resizeMode
808 \brief the resize mode of setions in the header.
809*/
810
812{
813 return d_ptr->m_resizeMode;
814}
815
816void QtTreePropertyBrowser::setResizeMode(QtTreePropertyBrowser::ResizeMode mode)
817{
818 if (d_ptr->m_resizeMode == mode)
819 return;
820
821 d_ptr->m_resizeMode = mode;
822 QHeaderView::ResizeMode m = QHeaderView::Stretch;
823 switch (mode) {
824 case QtTreePropertyBrowser::Interactive: m = QHeaderView::Interactive; break;
825 case QtTreePropertyBrowser::Fixed: m = QHeaderView::Fixed; break;
826 case QtTreePropertyBrowser::ResizeToContents: m = QHeaderView::ResizeToContents; break;
827 case QtTreePropertyBrowser::Stretch:
828 default: m = QHeaderView::Stretch; break;
829 }
830 d_ptr->m_treeWidget->header()->setSectionResizeMode(m);
831}
832
833/*!
834 \property QtTreePropertyBrowser::splitterPosition
835 \brief the position of the splitter between the colunms.
836*/
837
839{
840 return d_ptr->m_treeWidget->header()->sectionSize(0);
841}
842
844{
845 d_ptr->m_treeWidget->header()->resizeSection(0, position);
846}
847
848/*!
849 Sets the \a item to either collapse or expanded, depending on the value of \a expanded.
850
851 \sa isExpanded(), expanded(), collapsed()
852*/
853
855{
856 QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item);
857 if (treeItem)
858 treeItem->setExpanded(expanded);
859}
860
861/*!
862 Returns true if the \a item is expanded; otherwise returns false.
863
864 \sa setExpanded()
865*/
866
868{
869 QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item);
870 if (treeItem)
871 return treeItem->isExpanded();
872 return false;
873}
874
875/*!
876 Returns true if the \a item is visible; otherwise returns false.
877
878 \sa setItemVisible()
879 \since 4.5
880*/
881
883{
884 if (const QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item))
885 return !treeItem->isHidden();
886 return false;
887}
888
889/*!
890 Sets the \a item to be visible, depending on the value of \a visible.
891
892 \sa isItemVisible()
893 \since 4.5
894*/
895
897{
898 if (QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item))
899 treeItem->setHidden(!visible);
900}
901
902/*!
903 Sets the \a item's background color to \a color. Note that while item's background
904 is rendered every second row is being drawn with alternate color (which is a bit lighter than items \a color)
905
906 \sa backgroundColor(), calculatedBackgroundColor()
907*/
908
910{
911 if (!d_ptr->m_indexToItem.contains(item))
912 return;
913 if (color.isValid())
914 d_ptr->m_indexToBackgroundColor[item] = color;
915 else
916 d_ptr->m_indexToBackgroundColor.remove(item);
917 d_ptr->m_treeWidget->viewport()->update();
918}
919
920/*!
921 Returns the \a item's color. If there is no color set for item it returns invalid color.
922
923 \sa calculatedBackgroundColor(), setBackgroundColor()
924*/
925
927{
928 return d_ptr->m_indexToBackgroundColor.value(item);
929}
930
931/*!
932 Returns the \a item's color. If there is no color set for item it returns parent \a item's
933 color (if there is no color set for parent it returns grandparent's color and so on). In case
934 the color is not set for \a item and it's top level item it returns invalid color.
935
936 \sa backgroundColor(), setBackgroundColor()
937*/
938
940{
941 return d_ptr->calculatedBackgroundColor(item);
942}
943
944/*!
945 \property QtTreePropertyBrowser::propertiesWithoutValueMarked
946 \brief whether to enable or disable marking properties without value.
947
948 When marking is enabled the item's background is rendered in dark color and item's
949 foreground is rendered with light color.
950
951 \sa propertiesWithoutValueMarked()
952*/
954{
955 if (d_ptr->m_markPropertiesWithoutValue == mark)
956 return;
957
958 d_ptr->m_markPropertiesWithoutValue = mark;
959 for (auto it = d_ptr->m_itemToIndex.cbegin(), end = d_ptr->m_itemToIndex.cend(); it != end; ++it) {
960 QtProperty *property = it.value()->property();
961 if (!property->hasValue())
962 d_ptr->updateItem(it.key());
963 }
964 d_ptr->m_treeWidget->viewport()->update();
965}
966
968{
969 return d_ptr->m_markPropertiesWithoutValue;
970}
971
972/*!
973 \reimp
974*/
976{
977 d_ptr->propertyInserted(item, afterItem);
978}
979
980/*!
981 \reimp
982*/
984{
985 d_ptr->propertyRemoved(item);
986}
987
988/*!
989 \reimp
990*/
992{
993 d_ptr->propertyChanged(item);
994}
995
996/*!
997 Sets the current item to \a item and opens the relevant editor for it.
998*/
1000{
1001 d_ptr->editItem(item);
1002}
1003
1004QT_END_NAMESPACE
1005
1006#include "moc_qttreepropertybrowser_p.cpp"
1007#include "qttreepropertybrowser.moc"
QtAbstractPropertyBrowser provides a base class for implementing property browsers.
void setCurrentItem(QtBrowserItem *)
Sets the current item in the property browser to item.
The QtBrowserItem class represents a property in a property browser instance.
QtProperty * property() const
Returns the property which is accosiated with this item.
QtBrowserItem * parent() const
Returns the parent item of this item.
void closeEditor(QtProperty *property)
void setEditorData(QWidget *, const QModelIndex &) const override
Sets the data to be displayed and edited by the editor from the data model item specified by the mode...
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override
Updates the editor for the item specified by index according to the style option given.
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override
Returns the size needed by the delegate to display the item specified by index, taking into account t...
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
Renders the delegate using the given painter and style option for the item specified by index.
void setModelData(QWidget *, QAbstractItemModel *, const QModelIndex &) const override
Gets data from the editor widget and stores it in the specified model at the item index.
bool eventFilter(QObject *object, QEvent *event) override
\reimp
QWidget * createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override
Returns the widget used to edit the item specified by index for editing.
void setEditorPrivate(QtTreePropertyBrowserPrivate *editorPrivate)
QTreeWidgetItem * editedItem() const
QTreeWidgetItem * indexToItem(const QModelIndex &index) const
void keyPressEvent(QKeyEvent *event) override
void mousePressEvent(QMouseEvent *event) override
void drawRow(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
Draws the row in the tree view that contains the model item index, using the painter given.
void setEditorPrivate(QtTreePropertyBrowserPrivate *editorPrivate)
The QtProperty class encapsulates an instance of a property.
bool isEnabled() const
Returns whether the property is enabled.
bool hasValue() const
Returns whether the property has a value.
bool isModified() const
Returns whether the property is modified.
void enableItem(QTreeWidgetItem *item) const
QWidget * createEditor(QtProperty *property, QWidget *parent) const
void setCurrentItem(QtBrowserItem *browserItem, bool block)
QColor calculatedBackgroundColor(QtBrowserItem *item) const
QtProperty * indexToProperty(const QModelIndex &index) const
void slotCurrentBrowserItemChanged(QtBrowserItem *item)
void propertyChanged(QtBrowserItem *index)
bool hasValue(QTreeWidgetItem *item) const
void slotCurrentTreeItemChanged(QTreeWidgetItem *newItem, QTreeWidgetItem *)
QTreeWidgetItem * indexToItem(const QModelIndex &index) const
void propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex)
void editItem(QtBrowserItem *browserItem)
QTreeWidgetItem * editedItem() const
void slotCollapsed(const QModelIndex &index)
QtBrowserItem * indexToBrowserItem(const QModelIndex &index) const
void slotExpanded(const QModelIndex &index)
void disableItem(QTreeWidgetItem *item) const
QtPropertyEditorView * treeWidget() const
void propertyRemoved(QtBrowserItem *index)
The QtTreePropertyBrowser class provides QTreeWidget based property browser.
QColor calculatedBackgroundColor(QtBrowserItem *item) const
Returns the item's color.
~QtTreePropertyBrowser() override
Destroys this property browser.
void itemInserted(QtBrowserItem *item, QtBrowserItem *afterItem) override
\reimp
bool isExpanded(QtBrowserItem *item) const
Returns true if the item is expanded; otherwise returns false.
void editItem(QtBrowserItem *item)
Sets the current item to item and opens the relevant editor for it.
void setItemVisible(QtBrowserItem *item, bool visible)
Sets the item to be visible, depending on the value of visible.
void setExpanded(QtBrowserItem *item, bool expanded)
Sets the item to either collapse or expanded, depending on the value of expanded.
void setBackgroundColor(QtBrowserItem *item, QColor color)
Sets the item's background color to color.
void itemChanged(QtBrowserItem *item) override
\reimp
void itemRemoved(QtBrowserItem *item) override
\reimp
QColor backgroundColor(QtBrowserItem *item) const
Returns the item's color.
void setHeaderVisible(bool visible)
void setAlternatingRowColors(bool enable)
bool isItemVisible(QtBrowserItem *item) const
Returns true if the item is visible; otherwise returns false.
void setSplitterPosition(int position)
void setPropertiesWithoutValueMarked(bool mark)
static QIcon drawIndicatorIcon(const QPalette &palette, QStyle *style)
static bool isLightTheme()
static constexpr bool isWindows