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>
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>
20#include <QtCore/qhash.h>
21#include <QtCore/qoperatingsystemversion.h>
25using namespace Qt::StringLiterals;
27static constexpr bool isWindows = QOperatingSystemVersion::currentType() == QOperatingSystemVersion::Windows;
31 return QGuiApplication::styleHints()->colorScheme() != Qt::ColorScheme::Dark;
39 Q_DECLARE_PUBLIC(QtTreePropertyBrowser)
48 {
return q_ptr->createEditor(property, parent); }
75 void updateItem(QTreeWidgetItem *item);
77 QHash<QtBrowserItem *, QTreeWidgetItem *> m_indexToItem;
78 QHash<QTreeWidgetItem *, QtBrowserItem *> m_itemToIndex;
80 QHash<QtBrowserItem *, QColor> m_indexToBackgroundColor;
84 bool m_headerVisible =
true;
87 bool m_markPropertiesWithoutValue =
false;
88 bool m_browserChangedBlocked =
false;
100 { m_editorPrivate = editorPrivate; }
103 {
return itemFromIndex(index); }
108 void drawRow(QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index)
const override;
117 connect(header(), &QHeaderView::sectionDoubleClicked,
this, &QTreeView::resizeColumnToContents);
122 QStyleOptionViewItem opt = option;
123 bool hasValue =
true;
124 if (m_editorPrivate) {
125 QtProperty *property = m_editorPrivate->indexToProperty(index);
130 const QColor c = option.palette.color(QPalette::Dark);
131 painter->fillRect(option.rect, c);
132 opt.palette.setColor(QPalette::AlternateBase, c);
134 const QColor c = m_editorPrivate->calculatedBackgroundColor(m_editorPrivate->indexToBrowserItem(index));
136 painter->fillRect(option.rect, c);
137 opt.palette.setColor(QPalette::AlternateBase, c.lighter(112));
140 QTreeWidget::drawRow(painter, opt, index);
141 QColor color =
static_cast<QRgb>(QApplication::style()->styleHint(QStyle::SH_Table_GridLineColor, &opt));
143 painter->setPen(QPen(color));
144 painter->drawLine(opt.rect.x(), opt.rect.bottom(), opt.rect.right(), opt.rect.bottom());
150 switch (event->key()) {
155 if (
const QTreeWidgetItem *item = currentItem())
156 if (item->columnCount() >= 2 && ((item->flags() & (Qt::ItemIsEditable | Qt::ItemIsEnabled)) == (Qt::ItemIsEditable | Qt::ItemIsEnabled))) {
159 QModelIndex index = currentIndex();
160 if (index.column() == 0) {
161 index = index.sibling(index.row(), 1);
162 setCurrentIndex(index);
171 QTreeWidget::keyPressEvent(event);
176 QTreeWidget::mousePressEvent(event);
177 QTreeWidgetItem *item = itemAt(event->position().toPoint());
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))) {
185 if (event->position().toPoint().x() + header()->offset() < 20)
186 item->setExpanded(!item->isExpanded());
199 { m_editorPrivate = editorPrivate; }
202 const QModelIndex &index)
const override;
205 const QModelIndex &index)
const override;
207 void paint(QPainter *painter,
const QStyleOptionViewItem &option,
208 const QModelIndex &index)
const override;
210 QSize sizeHint(
const QStyleOptionViewItem &option,
const QModelIndex &index)
const override;
213 const QModelIndex &)
const override {}
226 int indentation(
const QModelIndex &index)
const;
229 mutable EditorToPropertyMap m_editorToProperty;
232 mutable PropertyToEditorMap m_propertyToEditor;
234 mutable QTreeWidgetItem *m_editedItem =
nullptr;
235 mutable QWidget *m_editedWidget =
nullptr;
240 if (!m_editorPrivate)
243 QTreeWidgetItem *item = m_editorPrivate->indexToItem(index);
245 while (item->parent()) {
246 item = item->parent();
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);
262 if (m_editedWidget == w) {
263 m_editedWidget =
nullptr;
264 m_editedItem =
nullptr;
271 if (QWidget *w = m_propertyToEditor.value(property,
nullptr))
276 const QStyleOptionViewItem &,
const QModelIndex &index)
const
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);
284 editor->setAutoFillBackground(
true);
285 if (editor->palette().color(editor->backgroundRole()) == Qt::transparent)
286 editor->setBackgroundRole(QPalette::Window);
288 connect(editor, &QObject::destroyed,
289 this, &QtPropertyEditorDelegate::slotEditorDestroyed);
290 m_propertyToEditor[property] = editor;
291 m_editorToProperty[editor] = property;
293 m_editedWidget = editor;
302 const QStyleOptionViewItem &option,
const QModelIndex &index)
const
305 editor->setGeometry(option.rect.adjusted(0, 0, 0, -1));
309 const QModelIndex &index)
const
311 bool hasValue =
true;
312 if (m_editorPrivate) {
313 QtProperty *property = m_editorPrivate->indexToProperty(index);
317 QStyleOptionViewItem opt = option;
318 if ((m_editorPrivate && index.column() == 0) || !hasValue) {
319 QtProperty *property = m_editorPrivate->indexToProperty(index);
321 opt.font.setBold(
true);
322 opt.fontMetrics = QFontMetrics(opt.font);
327 c = opt.palette.color(QPalette::Dark);
330 ? QColor(Qt::white) : opt.palette.color(QPalette::BrightText);
331 opt.palette.setColor(QPalette::Text, textColor);
333 c = m_editorPrivate->calculatedBackgroundColor(m_editorPrivate->indexToBrowserItem(index));
334 if (c.isValid() && (opt.features & QStyleOptionViewItem::Alternate))
338 painter->fillRect(option.rect, c);
339 opt.state &= ~QStyle::State_HasFocus;
340 QStyledItemDelegate::paint(painter, opt, index);
342 opt.palette.setCurrentColorGroup(QPalette::Active);
343 QColor color =
static_cast<QRgb>(QApplication::style()->styleHint(QStyle::SH_Table_GridLineColor, &opt));
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());
354 const QModelIndex &index)
const
356 return QStyledItemDelegate::sizeHint(option, index) + QSize(3, 4);
361 if (event->type() == QEvent::FocusOut) {
362 QFocusEvent *fe =
static_cast<QFocusEvent *>(event);
363 if (fe->reason() == Qt::ActiveWindowFocusReason)
366 return QStyledItemDelegate::eventFilter(object, event);
375 pix.fill(Qt::transparent);
376 QStyleOption branchOption;
377 branchOption.rect = QRect(2, 2, 9, 9);
378 branchOption.palette = palette;
379 branchOption.state = QStyle::State_Children;
384 style->drawPrimitive(QStyle::PE_IndicatorBranch, &branchOption, &p);
387 rc.addPixmap(pix, QIcon::Selected, QIcon::Off);
389 branchOption.state |= QStyle::State_Open;
390 pix.fill(Qt::transparent);
392 style->drawPrimitive(QStyle::PE_IndicatorBranch, &branchOption, &p);
395 rc.addPixmap(pix, QIcon::Normal, QIcon::On);
396 rc.addPixmap(pix, QIcon::Selected, QIcon::On);
402 auto *layout =
new QHBoxLayout(parent);
403 layout->setContentsMargins(QMargins());
406 m_treeWidget->setIconSize(QtPropertyBrowserUtils::itemViewIconSize);
407 layout->addWidget(m_treeWidget);
409 m_treeWidget->setColumnCount(2);
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);
418 m_treeWidget->setItemDelegate(m_delegate);
419 m_treeWidget->header()->setSectionsMovable(
false);
420 m_treeWidget->header()->setSectionResizeMode(QHeaderView::Stretch);
422 m_expandIcon = drawIndicatorIcon(q_ptr->palette(), q_ptr->style());
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); });
435 if (QTreeWidgetItem *treeItem = m_treeWidget->currentItem())
436 return m_itemToIndex.value(treeItem);
442 const bool blocked = block ? m_treeWidget->blockSignals(
true) :
false;
443 if (browserItem ==
nullptr)
444 m_treeWidget->setCurrentItem(
nullptr);
446 m_treeWidget->setCurrentItem(m_indexToItem.value(browserItem));
448 m_treeWidget->blockSignals(blocked);
453 QTreeWidgetItem *item = m_treeWidget->indexToItem(index);
462 QTreeWidgetItem *item = m_treeWidget->indexToItem(index);
463 return m_itemToIndex.value(item);
468 return m_treeWidget->indexToItem(index);
473 return m_treeWidget->header()->visualIndex(column) == m_treeWidget->columnCount() - 1;
478 Qt::ItemFlags flags = item->flags();
479 if (flags & Qt::ItemIsEnabled) {
480 flags &= ~Qt::ItemIsEnabled;
481 item->setFlags(flags);
483 const int childCount = item->childCount();
484 for (
int i = 0; i < childCount; i++) {
485 QTreeWidgetItem *child = item->child(i);
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();
516 QTreeWidgetItem *afterItem = m_indexToItem.value(afterIndex);
517 QTreeWidgetItem *parentItem = m_indexToItem.value(index->parent());
519 QTreeWidgetItem *newItem =
nullptr;
521 newItem =
new QTreeWidgetItem(parentItem, afterItem);
523 newItem =
new QTreeWidgetItem(m_treeWidget, afterItem);
525 m_itemToIndex[newItem] = index;
526 m_indexToItem[index] = newItem;
528 newItem->setFlags(newItem->flags() | Qt::ItemIsEditable);
529 newItem->setExpanded(
true);
536 QTreeWidgetItem *item = m_indexToItem.value(index);
538 if (m_treeWidget->currentItem() == item) {
539 m_treeWidget->setCurrentItem(
nullptr);
544 m_indexToItem.remove(index);
545 m_itemToIndex.remove(item);
546 m_indexToBackgroundColor.remove(index);
551 QTreeWidgetItem *item = m_indexToItem.value(index);
558 QtProperty *property = m_itemToIndex[item]->property();
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);
567 expandIcon = m_expandIcon;
569 item->setIcon(0, expandIcon);
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;
580 QTreeWidgetItem *parent = item->parent();
581 isEnabled = !parent || (parent->flags() & Qt::ItemIsEnabled);
585 if (wasEnabled != isEnabled) {
591 m_treeWidget->viewport()->update();
597 const auto itEnd = m_indexToBackgroundColor.constEnd();
599 auto it = m_indexToBackgroundColor.constFind(i);
609 QTreeWidgetItem *item = indexToItem(index);
612 emit q_ptr->collapsed(idx);
617 QTreeWidgetItem *item = indexToItem(index);
620 emit q_ptr->expanded(idx);
631 QtBrowserItem *browserItem = newItem ? m_itemToIndex.value(newItem) : 0;
632 m_browserChangedBlocked =
true;
634 m_browserChangedBlocked =
false;
644 if (QTreeWidgetItem *treeItem = m_indexToItem.value(browserItem,
nullptr)) {
645 m_treeWidget->setCurrentItem (treeItem, 1);
646 m_treeWidget->editItem(treeItem, 1);
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
681
682
683
684
685
686
689
690
691
692
693
694
697
698
705 QObject::connect(
this, &QtAbstractPropertyBrowser::currentItemChanged,
706 this, [
this](QtBrowserItem *current)
707 { d_ptr->slotCurrentBrowserItemChanged(current); });
711
712
713
714
715
716
717
718
719
723
724
725
728 return d_ptr->m_treeWidget->indentation();
733 d_ptr->m_treeWidget->setIndentation(i);
737
738
739
742 return d_ptr->m_treeWidget->rootIsDecorated();
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());
756
757
758
759
762 return d_ptr->m_treeWidget->alternatingRowColors();
767 d_ptr->m_treeWidget->setAlternatingRowColors(enable);
771
772
773
776 return d_ptr->m_headerVisible;
781 if (d_ptr->m_headerVisible == visible)
784 d_ptr->m_headerVisible = visible;
785 d_ptr->m_treeWidget->header()->setVisible(visible);
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
810
811
812
816 return d_ptr->m_resizeMode;
821 if (d_ptr->m_resizeMode == mode)
824 d_ptr->m_resizeMode = mode;
825 QHeaderView::ResizeMode m = QHeaderView::Stretch;
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;
833 d_ptr->m_treeWidget->header()->setSectionResizeMode(m);
837
838
839
843 return d_ptr->m_treeWidget->header()->sectionSize(0);
848 d_ptr->m_treeWidget->header()->resizeSection(0, position);
852
853
854
855
859 QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item);
861 treeItem->setExpanded(expanded);
865
866
867
868
872 QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item);
874 return treeItem->isExpanded();
879
880
881
882
883
887 if (
const QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item))
888 return !treeItem->isHidden();
893
894
895
896
897
901 if (QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item))
902 treeItem->setHidden(!visible);
906
907
908
909
910
914 if (!d_ptr->m_indexToItem.contains(item))
917 d_ptr->m_indexToBackgroundColor[item] = color;
919 d_ptr->m_indexToBackgroundColor.remove(item);
920 d_ptr->m_treeWidget->viewport()->update();
924
925
926
927
931 return d_ptr->m_indexToBackgroundColor.value(item);
935
936
937
938
939
940
944 return d_ptr->calculatedBackgroundColor(item);
948
949
950
951
952
953
954
955
958 if (d_ptr->m_markPropertiesWithoutValue == mark)
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());
967 d_ptr->m_treeWidget->viewport()->update();
972 return d_ptr->m_markPropertiesWithoutValue;
976
977
980 d_ptr->propertyInserted(item, afterItem);
984
985
988 d_ptr->propertyRemoved(item);
992
993
996 d_ptr->propertyChanged(item);
1000
1001
1004 d_ptr->editItem(item);
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)
bool markPropertiesWithoutValue() const
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)
bool lastColumn(int column) const
QTreeWidgetItem * editedItem() const
void slotCollapsed(const QModelIndex &index)
QtBrowserItem * indexToBrowserItem(const QModelIndex &index) const
QtBrowserItem * currentItem() 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 setRootIsDecorated(bool show)
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.
bool alternatingRowColors() const
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.
int splitterPosition() const
bool propertiesWithoutValueMarked() const
bool rootIsDecorated() const
void setHeaderVisible(bool visible)
bool isHeaderVisible() const
void setAlternatingRowColors(bool enable)
bool isItemVisible(QtBrowserItem *item) const
Returns true if the item is visible; otherwise returns false.
void setSplitterPosition(int position)
ResizeMode resizeMode() const
void setPropertiesWithoutValueMarked(bool mark)
void setIndentation(int i)
Combined button and popup list for selecting options.
static QIcon drawIndicatorIcon(const QPalette &palette, QStyle *style)
static bool isLightTheme()
static constexpr bool isWindows