6#include <QtCore/QOperatingSystemVersion>
8#include <QtGui/QFocusEvent>
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>
22using namespace Qt::StringLiterals;
24static constexpr bool isWindows = QOperatingSystemVersion::currentType() == QOperatingSystemVersion::Windows;
28 return QGuiApplication::styleHints()->colorScheme() != Qt::ColorScheme::Dark;
36 Q_DECLARE_PUBLIC(QtTreePropertyBrowser)
40 void init(QWidget *parent);
46 {
return q_ptr->createEditor(property, parent); }
73 void updateItem(QTreeWidgetItem *item);
75 QHash<QtBrowserItem *, QTreeWidgetItem *> m_indexToItem;
76 QHash<QTreeWidgetItem *, QtBrowserItem *> m_itemToIndex;
78 QHash<QtBrowserItem *, QColor> m_indexToBackgroundColor;
83 QtTreePropertyBrowser::ResizeMode m_resizeMode;
85 bool m_markPropertiesWithoutValue;
86 bool m_browserChangedBlocked;
98 { m_editorPrivate = editorPrivate; }
101 {
return itemFromIndex(index); }
106 void drawRow(QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index)
const override;
116 connect(header(), &QHeaderView::sectionDoubleClicked,
this, &QTreeView::resizeColumnToContents);
121 QStyleOptionViewItem opt = option;
122 bool hasValue =
true;
123 if (m_editorPrivate) {
124 QtProperty *property = m_editorPrivate->indexToProperty(index);
129 const QColor c = option.palette.color(QPalette::Dark);
130 painter->fillRect(option.rect, c);
131 opt.palette.setColor(QPalette::AlternateBase, c);
133 const QColor c = m_editorPrivate->calculatedBackgroundColor(m_editorPrivate->indexToBrowserItem(index));
135 painter->fillRect(option.rect, c);
136 opt.palette.setColor(QPalette::AlternateBase, c.lighter(112));
139 QTreeWidget::drawRow(painter, opt, index);
140 QColor color =
static_cast<QRgb>(QApplication::style()->styleHint(QStyle::SH_Table_GridLineColor, &opt));
142 painter->setPen(QPen(color));
143 painter->drawLine(opt.rect.x(), opt.rect.bottom(), opt.rect.right(), opt.rect.bottom());
149 switch (event->key()) {
154 if (
const QTreeWidgetItem *item = currentItem())
155 if (item->columnCount() >= 2 && ((item->flags() & (Qt::ItemIsEditable | Qt::ItemIsEnabled)) == (Qt::ItemIsEditable | Qt::ItemIsEnabled))) {
158 QModelIndex index = currentIndex();
159 if (index.column() == 0) {
160 index = index.sibling(index.row(), 1);
161 setCurrentIndex(index);
170 QTreeWidget::keyPressEvent(event);
175 QTreeWidget::mousePressEvent(event);
176 QTreeWidgetItem *item = itemAt(event->position().toPoint());
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))) {
184 if (event->position().toPoint().x() + header()->offset() < 20)
185 item->setExpanded(!item->isExpanded());
206 const QModelIndex &index)
const override;
208 void paint(QPainter *painter,
const QStyleOptionViewItem &option,
209 const QModelIndex &index)
const override;
211 QSize sizeHint(
const QStyleOptionViewItem &option,
const QModelIndex &index)
const override;
214 const QModelIndex &)
const override {}
227 int indentation(
const QModelIndex &index)
const;
230 mutable EditorToPropertyMap m_editorToProperty;
233 mutable PropertyToEditorMap m_propertyToEditor;
235 mutable QTreeWidgetItem *m_editedItem;
236 mutable QWidget *m_editedWidget;
241 if (!m_editorPrivate)
244 QTreeWidgetItem *item = m_editorPrivate->indexToItem(index);
246 while (item->parent()) {
247 item = item->parent();
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);
263 if (m_editedWidget == w) {
264 m_editedWidget =
nullptr;
265 m_editedItem =
nullptr;
272 if (QWidget *w = m_propertyToEditor.value(property,
nullptr))
277 const QStyleOptionViewItem &,
const QModelIndex &index)
const
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);
285 editor->setAutoFillBackground(
true);
286 if (editor->palette().color(editor->backgroundRole()) == Qt::transparent)
287 editor->setBackgroundRole(QPalette::Window);
289 connect(editor, &QObject::destroyed,
290 this, &QtPropertyEditorDelegate::slotEditorDestroyed);
291 m_propertyToEditor[property] = editor;
292 m_editorToProperty[editor] = property;
294 m_editedWidget = editor;
303 const QStyleOptionViewItem &option,
const QModelIndex &index)
const
306 editor->setGeometry(option.rect.adjusted(0, 0, 0, -1));
310 const QModelIndex &index)
const
312 bool hasValue =
true;
313 if (m_editorPrivate) {
314 QtProperty *property = m_editorPrivate->indexToProperty(index);
318 QStyleOptionViewItem opt = option;
319 if ((m_editorPrivate && index.column() == 0) || !hasValue) {
320 QtProperty *property = m_editorPrivate->indexToProperty(index);
322 opt.font.setBold(
true);
323 opt.fontMetrics = QFontMetrics(opt.font);
328 c = opt.palette.color(QPalette::Dark);
330 const QColor textColor = isWindows && isLightTheme()
331 ? QColor(Qt::white) : opt.palette.color(QPalette::BrightText);
332 opt.palette.setColor(QPalette::Text, textColor);
334 c = m_editorPrivate->calculatedBackgroundColor(m_editorPrivate->indexToBrowserItem(index));
335 if (c.isValid() && (opt.features & QStyleOptionViewItem::Alternate))
339 painter->fillRect(option.rect, c);
340 opt.state &= ~QStyle::State_HasFocus;
341 QItemDelegate::paint(painter, opt, index);
343 opt.palette.setCurrentColorGroup(QPalette::Active);
344 QColor color =
static_cast<QRgb>(QApplication::style()->styleHint(QStyle::SH_Table_GridLineColor, &opt));
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());
355 const QModelIndex &index)
const
357 return QItemDelegate::sizeHint(option, index) + QSize(3, 4);
362 if (event->type() == QEvent::FocusOut) {
363 QFocusEvent *fe =
static_cast<QFocusEvent *>(event);
364 if (fe->reason() == Qt::ActiveWindowFocusReason)
367 return QItemDelegate::eventFilter(object, event);
373 m_headerVisible(
true),
374 m_resizeMode(QtTreePropertyBrowser::Stretch),
376 m_markPropertiesWithoutValue(
false),
377 m_browserChangedBlocked(
false)
385 pix.fill(Qt::transparent);
386 QStyleOption branchOption;
387 branchOption.rect = QRect(2, 2, 9, 9);
388 branchOption.palette = palette;
389 branchOption.state = QStyle::State_Children;
394 style->drawPrimitive(QStyle::PE_IndicatorBranch, &branchOption, &p);
397 rc.addPixmap(pix, QIcon::Selected, QIcon::Off);
399 branchOption.state |= QStyle::State_Open;
400 pix.fill(Qt::transparent);
402 style->drawPrimitive(QStyle::PE_IndicatorBranch, &branchOption, &p);
405 rc.addPixmap(pix, QIcon::Normal, QIcon::On);
406 rc.addPixmap(pix, QIcon::Selected, QIcon::On);
412 auto *layout =
new QHBoxLayout(parent);
413 layout->setContentsMargins(QMargins());
416 m_treeWidget->setIconSize(QSize(18, 18));
417 layout->addWidget(m_treeWidget);
419 m_treeWidget->setColumnCount(2);
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);
427 m_delegate->setEditorPrivate(
this);
428 m_treeWidget->setItemDelegate(m_delegate);
429 m_treeWidget->header()->setSectionsMovable(
false);
430 m_treeWidget->header()->setSectionResizeMode(QHeaderView::Stretch);
432 m_expandIcon = drawIndicatorIcon(q_ptr->palette(), q_ptr->style());
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); });
445 if (QTreeWidgetItem *treeItem = m_treeWidget->currentItem())
446 return m_itemToIndex.value(treeItem);
452 const bool blocked = block ? m_treeWidget->blockSignals(
true) :
false;
453 if (browserItem ==
nullptr)
454 m_treeWidget->setCurrentItem(
nullptr);
456 m_treeWidget->setCurrentItem(m_indexToItem.value(browserItem));
458 m_treeWidget->blockSignals(blocked);
463 QTreeWidgetItem *item = m_treeWidget->indexToItem(index);
472 QTreeWidgetItem *item = m_treeWidget->indexToItem(index);
473 return m_itemToIndex.value(item);
478 return m_treeWidget->indexToItem(index);
483 return m_treeWidget->header()->visualIndex(column) == m_treeWidget->columnCount() - 1;
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);
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();
526 QTreeWidgetItem *afterItem = m_indexToItem.value(afterIndex);
527 QTreeWidgetItem *parentItem = m_indexToItem.value(index->parent());
529 QTreeWidgetItem *newItem =
nullptr;
531 newItem =
new QTreeWidgetItem(parentItem, afterItem);
533 newItem =
new QTreeWidgetItem(m_treeWidget, afterItem);
535 m_itemToIndex[newItem] = index;
536 m_indexToItem[index] = newItem;
538 newItem->setFlags(newItem->flags() | Qt::ItemIsEditable);
539 newItem->setExpanded(
true);
546 QTreeWidgetItem *item = m_indexToItem.value(index);
548 if (m_treeWidget->currentItem() == item) {
549 m_treeWidget->setCurrentItem(0);
554 m_indexToItem.remove(index);
555 m_itemToIndex.remove(item);
556 m_indexToBackgroundColor.remove(index);
561 QTreeWidgetItem *item = m_indexToItem.value(index);
568 QtProperty *property = m_itemToIndex[item]->property();
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);
577 expandIcon = m_expandIcon;
579 item->setIcon(0, expandIcon);
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;
590 QTreeWidgetItem *parent = item->parent();
591 if (!parent || (parent->flags() & Qt::ItemIsEnabled))
598 if (wasEnabled != isEnabled) {
604 m_treeWidget->viewport()->update();
610 const auto itEnd = m_indexToBackgroundColor.constEnd();
612 auto it = m_indexToBackgroundColor.constFind(i);
622 QTreeWidgetItem *item = indexToItem(index);
625 emit q_ptr->collapsed(idx);
630 QTreeWidgetItem *item = indexToItem(index);
633 emit q_ptr->expanded(idx);
644 QtBrowserItem *browserItem = newItem ? m_itemToIndex.value(newItem) : 0;
645 m_browserChangedBlocked =
true;
647 m_browserChangedBlocked =
false;
657 if (QTreeWidgetItem *treeItem = m_indexToItem.value(browserItem,
nullptr)) {
658 m_treeWidget->setCurrentItem (treeItem, 1);
659 m_treeWidget->editItem(treeItem, 1);
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
694
695
696
697
698
699
702
703
704
705
706
707
710
711
718 QObject::connect(
this, &QtAbstractPropertyBrowser::currentItemChanged,
719 this, [
this](QtBrowserItem *current)
720 { d_ptr->slotCurrentBrowserItemChanged(current); });
724
725
726
727
728
729
730
731
732
738
739
740
743 return d_ptr->m_treeWidget->indentation();
748 d_ptr->m_treeWidget->setIndentation(i);
752
753
754
757 return d_ptr->m_treeWidget->rootIsDecorated();
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());
771
772
773
774
777 return d_ptr->m_treeWidget->alternatingRowColors();
782 d_ptr->m_treeWidget->setAlternatingRowColors(enable);
786
787
788
791 return d_ptr->m_headerVisible;
796 if (d_ptr->m_headerVisible == visible)
799 d_ptr->m_headerVisible = visible;
800 d_ptr->m_treeWidget->header()->setVisible(visible);
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
825
826
827
831 return d_ptr->m_resizeMode;
836 if (d_ptr->m_resizeMode == mode)
839 d_ptr->m_resizeMode = mode;
840 QHeaderView::ResizeMode m = QHeaderView::Stretch;
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;
848 d_ptr->m_treeWidget->header()->setSectionResizeMode(m);
852
853
854
858 return d_ptr->m_treeWidget->header()->sectionSize(0);
863 d_ptr->m_treeWidget->header()->resizeSection(0, position);
867
868
869
870
874 QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item);
876 treeItem->setExpanded(expanded);
880
881
882
883
887 QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item);
889 return treeItem->isExpanded();
894
895
896
897
898
902 if (
const QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item))
903 return !treeItem->isHidden();
908
909
910
911
912
916 if (QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item))
917 treeItem->setHidden(!visible);
921
922
923
924
925
929 if (!d_ptr->m_indexToItem.contains(item))
932 d_ptr->m_indexToBackgroundColor[item] = color;
934 d_ptr->m_indexToBackgroundColor.remove(item);
935 d_ptr->m_treeWidget->viewport()->update();
939
940
941
942
946 return d_ptr->m_indexToBackgroundColor.value(item);
950
951
952
953
954
955
959 return d_ptr->calculatedBackgroundColor(item);
963
964
965
966
967
968
969
970
973 if (d_ptr->m_markPropertiesWithoutValue == mark)
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());
982 d_ptr->m_treeWidget->viewport()->update();
987 return d_ptr->m_markPropertiesWithoutValue;
991
992
995 d_ptr->propertyInserted(item, afterItem);
999
1000
1003 d_ptr->propertyRemoved(item);
1007
1008
1011 d_ptr->propertyChanged(item);
1015
1016
1019 d_ptr->editItem(item);
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)
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 init(QWidget *parent)
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()
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