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