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