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)
45 {
return q_ptr->createEditor(property, parent); }
72 void updateItem(QTreeWidgetItem *item);
74 QHash<QtBrowserItem *, QTreeWidgetItem *> m_indexToItem;
75 QHash<QTreeWidgetItem *, QtBrowserItem *> m_itemToIndex;
77 QHash<QtBrowserItem *, QColor> m_indexToBackgroundColor;
81 bool m_headerVisible =
true;
84 bool m_markPropertiesWithoutValue =
false;
85 bool m_browserChangedBlocked =
false;
97 { m_editorPrivate = editorPrivate; }
100 {
return itemFromIndex(index); }
105 void drawRow(QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index)
const override;
114 connect(header(), &QHeaderView::sectionDoubleClicked,
this, &QTreeView::resizeColumnToContents);
119 QStyleOptionViewItem opt = option;
120 bool hasValue =
true;
121 if (m_editorPrivate) {
122 QtProperty *property = m_editorPrivate->indexToProperty(index);
127 const QColor c = option.palette.color(QPalette::Dark);
128 painter->fillRect(option.rect, c);
129 opt.palette.setColor(QPalette::AlternateBase, c);
131 const QColor c = m_editorPrivate->calculatedBackgroundColor(m_editorPrivate->indexToBrowserItem(index));
133 painter->fillRect(option.rect, c);
134 opt.palette.setColor(QPalette::AlternateBase, c.lighter(112));
137 QTreeWidget::drawRow(painter, opt, index);
138 QColor color =
static_cast<QRgb>(QApplication::style()->styleHint(QStyle::SH_Table_GridLineColor, &opt));
140 painter->setPen(QPen(color));
141 painter->drawLine(opt.rect.x(), opt.rect.bottom(), opt.rect.right(), opt.rect.bottom());
147 switch (event->key()) {
152 if (
const QTreeWidgetItem *item = currentItem())
153 if (item->columnCount() >= 2 && ((item->flags() & (Qt::ItemIsEditable | Qt::ItemIsEnabled)) == (Qt::ItemIsEditable | Qt::ItemIsEnabled))) {
156 QModelIndex index = currentIndex();
157 if (index.column() == 0) {
158 index = index.sibling(index.row(), 1);
159 setCurrentIndex(index);
168 QTreeWidget::keyPressEvent(event);
173 QTreeWidget::mousePressEvent(event);
174 QTreeWidgetItem *item = itemAt(event->position().toPoint());
177 if ((item != m_editorPrivate->editedItem()) && (event->button() == Qt::LeftButton)
178 && (header()->logicalIndexAt(event->position().toPoint().x()) == 1)
179 && ((item->flags() & (Qt::ItemIsEditable | Qt::ItemIsEnabled)) == (Qt::ItemIsEditable | Qt::ItemIsEnabled))) {
182 if (event->position().toPoint().x() + header()->offset() < 20)
183 item->setExpanded(!item->isExpanded());
196 { m_editorPrivate = editorPrivate; }
199 const QModelIndex &index)
const override;
202 const QModelIndex &index)
const override;
204 void paint(QPainter *painter,
const QStyleOptionViewItem &option,
205 const QModelIndex &index)
const override;
207 QSize sizeHint(
const QStyleOptionViewItem &option,
const QModelIndex &index)
const override;
210 const QModelIndex &)
const override {}
223 int indentation(
const QModelIndex &index)
const;
226 mutable EditorToPropertyMap m_editorToProperty;
229 mutable PropertyToEditorMap m_propertyToEditor;
231 mutable QTreeWidgetItem *m_editedItem =
nullptr;
232 mutable QWidget *m_editedWidget =
nullptr;
237 if (!m_editorPrivate)
240 QTreeWidgetItem *item = m_editorPrivate->indexToItem(index);
242 while (item->parent()) {
243 item = item->parent();
253 if (
auto *w = qobject_cast<QWidget *>(object)) {
254 const auto it = m_editorToProperty.find(w);
255 if (it != m_editorToProperty.end()) {
256 m_propertyToEditor.remove(it.value());
257 m_editorToProperty.erase(it);
259 if (m_editedWidget == w) {
260 m_editedWidget =
nullptr;
261 m_editedItem =
nullptr;
268 if (QWidget *w = m_propertyToEditor.value(property,
nullptr))
273 const QStyleOptionViewItem &,
const QModelIndex &index)
const
275 if (index.column() == 1 && m_editorPrivate) {
276 QtProperty *property = m_editorPrivate->indexToProperty(index);
277 QTreeWidgetItem *item = m_editorPrivate->indexToItem(index);
278 if (property && item && (item->flags() & Qt::ItemIsEnabled)) {
279 QWidget *editor = m_editorPrivate->createEditor(property, parent);
281 editor->setAutoFillBackground(
true);
282 if (editor->palette().color(editor->backgroundRole()) == Qt::transparent)
283 editor->setBackgroundRole(QPalette::Window);
285 connect(editor, &QObject::destroyed,
286 this, &QtPropertyEditorDelegate::slotEditorDestroyed);
287 m_propertyToEditor[property] = editor;
288 m_editorToProperty[editor] = property;
290 m_editedWidget = editor;
299 const QStyleOptionViewItem &option,
const QModelIndex &index)
const
302 editor->setGeometry(option.rect.adjusted(0, 0, 0, -1));
306 const QModelIndex &index)
const
308 bool hasValue =
true;
309 if (m_editorPrivate) {
310 QtProperty *property = m_editorPrivate->indexToProperty(index);
314 QStyleOptionViewItem opt = option;
315 if ((m_editorPrivate && index.column() == 0) || !hasValue) {
316 QtProperty *property = m_editorPrivate->indexToProperty(index);
318 opt.font.setBold(
true);
319 opt.fontMetrics = QFontMetrics(opt.font);
324 c = opt.palette.color(QPalette::Dark);
326 const QColor textColor = isWindows && isLightTheme()
327 ? QColor(Qt::white) : opt.palette.color(QPalette::BrightText);
328 opt.palette.setColor(QPalette::Text, textColor);
330 c = m_editorPrivate->calculatedBackgroundColor(m_editorPrivate->indexToBrowserItem(index));
331 if (c.isValid() && (opt.features & QStyleOptionViewItem::Alternate))
335 painter->fillRect(option.rect, c);
336 opt.state &= ~QStyle::State_HasFocus;
337 QItemDelegate::paint(painter, opt, index);
339 opt.palette.setCurrentColorGroup(QPalette::Active);
340 QColor color =
static_cast<QRgb>(QApplication::style()->styleHint(QStyle::SH_Table_GridLineColor, &opt));
342 painter->setPen(QPen(color));
343 if (!m_editorPrivate || (!m_editorPrivate
->lastColumn(index.column()
) && hasValue)) {
344 int right = (option.direction == Qt::LeftToRight) ? option.rect.right() : option.rect.left();
345 painter->drawLine(right, option.rect.y(), right, option.rect.bottom());
351 const QModelIndex &index)
const
353 return QItemDelegate::sizeHint(option, index) + QSize(3, 4);
358 if (event->type() == QEvent::FocusOut) {
359 QFocusEvent *fe =
static_cast<QFocusEvent *>(event);
360 if (fe->reason() == Qt::ActiveWindowFocusReason)
363 return QItemDelegate::eventFilter(object, event);
372 pix.fill(Qt::transparent);
373 QStyleOption branchOption;
374 branchOption.rect = QRect(2, 2, 9, 9);
375 branchOption.palette = palette;
376 branchOption.state = QStyle::State_Children;
381 style->drawPrimitive(QStyle::PE_IndicatorBranch, &branchOption, &p);
384 rc.addPixmap(pix, QIcon::Selected, QIcon::Off);
386 branchOption.state |= QStyle::State_Open;
387 pix.fill(Qt::transparent);
389 style->drawPrimitive(QStyle::PE_IndicatorBranch, &branchOption, &p);
392 rc.addPixmap(pix, QIcon::Normal, QIcon::On);
393 rc.addPixmap(pix, QIcon::Selected, QIcon::On);
399 auto *layout =
new QHBoxLayout(parent);
400 layout->setContentsMargins(QMargins());
403 m_treeWidget->setIconSize(QSize(18, 18));
404 layout->addWidget(m_treeWidget);
406 m_treeWidget->setColumnCount(2);
408 labels.append(QCoreApplication::translate(
"QtTreePropertyBrowser",
"Property"));
409 labels.append(QCoreApplication::translate(
"QtTreePropertyBrowser",
"Value"));
410 m_treeWidget->setHeaderLabels(labels);
411 m_treeWidget->setAlternatingRowColors(
true);
412 m_treeWidget->setEditTriggers(QAbstractItemView::EditKeyPressed);
415 m_treeWidget->setItemDelegate(m_delegate);
416 m_treeWidget->header()->setSectionsMovable(
false);
417 m_treeWidget->header()->setSectionResizeMode(QHeaderView::Stretch);
419 m_expandIcon = drawIndicatorIcon(q_ptr->palette(), q_ptr->style());
421 QObject::connect(m_treeWidget, &QTreeView::collapsed,
422 q_ptr, [
this](
const QModelIndex &index) { slotCollapsed(index); });
423 QObject::connect(m_treeWidget, &QTreeView::expanded,
424 q_ptr, [
this](
const QModelIndex &index) { slotExpanded(index); });
425 QObject::connect(m_treeWidget, &QTreeWidget::currentItemChanged,
426 q_ptr, [
this](QTreeWidgetItem *current, QTreeWidgetItem *previous)
427 { slotCurrentTreeItemChanged(current, previous); });
432 if (QTreeWidgetItem *treeItem = m_treeWidget->currentItem())
433 return m_itemToIndex.value(treeItem);
439 const bool blocked = block ? m_treeWidget->blockSignals(
true) :
false;
440 if (browserItem ==
nullptr)
441 m_treeWidget->setCurrentItem(
nullptr);
443 m_treeWidget->setCurrentItem(m_indexToItem.value(browserItem));
445 m_treeWidget->blockSignals(blocked);
450 QTreeWidgetItem *item = m_treeWidget->indexToItem(index);
459 QTreeWidgetItem *item = m_treeWidget->indexToItem(index);
460 return m_itemToIndex.value(item);
465 return m_treeWidget->indexToItem(index);
470 return m_treeWidget->header()->visualIndex(column) == m_treeWidget->columnCount() - 1;
475 Qt::ItemFlags flags = item->flags();
476 if (flags & Qt::ItemIsEnabled) {
477 flags &= ~Qt::ItemIsEnabled;
478 item->setFlags(flags);
479 m_delegate->closeEditor(m_itemToIndex[item]->property());
480 const int childCount = item->childCount();
481 for (
int i = 0; i < childCount; i++) {
482 QTreeWidgetItem *child = item->child(i);
490 Qt::ItemFlags flags = item->flags();
491 flags |= Qt::ItemIsEnabled;
492 item->setFlags(flags);
493 const int childCount = item->childCount();
494 for (
int i = 0; i < childCount; i++) {
495 QTreeWidgetItem *child = item->child(i);
496 QtProperty *property = m_itemToIndex[child]->property();
513 QTreeWidgetItem *afterItem = m_indexToItem.value(afterIndex);
514 QTreeWidgetItem *parentItem = m_indexToItem.value(index->parent());
516 QTreeWidgetItem *newItem =
nullptr;
518 newItem =
new QTreeWidgetItem(parentItem, afterItem);
520 newItem =
new QTreeWidgetItem(m_treeWidget, afterItem);
522 m_itemToIndex[newItem] = index;
523 m_indexToItem[index] = newItem;
525 newItem->setFlags(newItem->flags() | Qt::ItemIsEditable);
526 newItem->setExpanded(
true);
533 QTreeWidgetItem *item = m_indexToItem.value(index);
535 if (m_treeWidget->currentItem() == item) {
536 m_treeWidget->setCurrentItem(
nullptr);
541 m_indexToItem.remove(index);
542 m_itemToIndex.remove(item);
543 m_indexToBackgroundColor.remove(index);
548 QTreeWidgetItem *item = m_indexToItem.value(index);
555 QtProperty *property = m_itemToIndex[item]->property();
558 const QString valueToolTip = property->valueToolTip();
559 const QString valueText = property->valueText();
560 item->setToolTip(1, valueToolTip.isEmpty() ? valueText : valueToolTip);
561 item->setIcon(1, property->valueIcon());
562 item->setText(1, valueText);
564 expandIcon = m_expandIcon;
566 item->setIcon(0, expandIcon);
568 const QString descriptionToolTip = property->descriptionToolTip();
569 const QString propertyName = property->propertyName();
570 item->setToolTip(0, descriptionToolTip.isEmpty() ? propertyName : descriptionToolTip);
571 item->setStatusTip(0, property->statusTip());
572 item->setWhatsThis(0, property->whatsThis());
573 item->setText(0, propertyName);
574 bool wasEnabled = item->flags() & Qt::ItemIsEnabled;
575 bool isEnabled = wasEnabled;
577 QTreeWidgetItem *parent = item->parent();
578 isEnabled = !parent || (parent->flags() & Qt::ItemIsEnabled);
582 if (wasEnabled != isEnabled) {
588 m_treeWidget->viewport()->update();
594 const auto itEnd = m_indexToBackgroundColor.constEnd();
596 auto it = m_indexToBackgroundColor.constFind(i);
606 QTreeWidgetItem *item = indexToItem(index);
609 emit q_ptr->collapsed(idx);
614 QTreeWidgetItem *item = indexToItem(index);
617 emit q_ptr->expanded(idx);
628 QtBrowserItem *browserItem = newItem ? m_itemToIndex.value(newItem) : 0;
629 m_browserChangedBlocked =
true;
631 m_browserChangedBlocked =
false;
641 if (QTreeWidgetItem *treeItem = m_indexToItem.value(browserItem,
nullptr)) {
642 m_treeWidget->setCurrentItem (treeItem, 1);
643 m_treeWidget->editItem(treeItem, 1);
648
649
650
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
678
679
680
681
682
683
686
687
688
689
690
691
694
695
702 QObject::connect(
this, &QtAbstractPropertyBrowser::currentItemChanged,
703 this, [
this](QtBrowserItem *current)
704 { d_ptr->slotCurrentBrowserItemChanged(current); });
708
709
710
711
712
713
714
715
716
720
721
722
725 return d_ptr->m_treeWidget->indentation();
730 d_ptr->m_treeWidget->setIndentation(i);
734
735
736
739 return d_ptr->m_treeWidget->rootIsDecorated();
744 d_ptr->m_treeWidget->setRootIsDecorated(show);
745 for (
auto it = d_ptr->m_itemToIndex.cbegin(), end = d_ptr->m_itemToIndex.cend(); it != end; ++it) {
746 QtProperty *property = it.value()->property();
747 if (!property->hasValue())
748 d_ptr->updateItem(it.key());
753
754
755
756
759 return d_ptr->m_treeWidget->alternatingRowColors();
764 d_ptr->m_treeWidget->setAlternatingRowColors(enable);
768
769
770
773 return d_ptr->m_headerVisible;
778 if (d_ptr->m_headerVisible == visible)
781 d_ptr->m_headerVisible = visible;
782 d_ptr->m_treeWidget->header()->setVisible(visible);
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
807
808
809
813 return d_ptr->m_resizeMode;
818 if (d_ptr->m_resizeMode == mode)
821 d_ptr->m_resizeMode = mode;
822 QHeaderView::ResizeMode m = QHeaderView::Stretch;
824 case QtTreePropertyBrowser::Interactive: m = QHeaderView::Interactive;
break;
825 case QtTreePropertyBrowser::Fixed: m = QHeaderView::Fixed;
break;
826 case QtTreePropertyBrowser::ResizeToContents: m = QHeaderView::ResizeToContents;
break;
827 case QtTreePropertyBrowser::Stretch:
828 default: m = QHeaderView::Stretch;
break;
830 d_ptr->m_treeWidget->header()->setSectionResizeMode(m);
834
835
836
840 return d_ptr->m_treeWidget->header()->sectionSize(0);
845 d_ptr->m_treeWidget->header()->resizeSection(0, position);
849
850
851
852
856 QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item);
858 treeItem->setExpanded(expanded);
862
863
864
865
869 QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item);
871 return treeItem->isExpanded();
876
877
878
879
880
884 if (
const QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item))
885 return !treeItem->isHidden();
890
891
892
893
894
898 if (QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item))
899 treeItem->setHidden(!visible);
903
904
905
906
907
911 if (!d_ptr->m_indexToItem.contains(item))
914 d_ptr->m_indexToBackgroundColor[item] = color;
916 d_ptr->m_indexToBackgroundColor.remove(item);
917 d_ptr->m_treeWidget->viewport()->update();
921
922
923
924
928 return d_ptr->m_indexToBackgroundColor.value(item);
932
933
934
935
936
937
941 return d_ptr->calculatedBackgroundColor(item);
945
946
947
948
949
950
951
952
955 if (d_ptr->m_markPropertiesWithoutValue == mark)
958 d_ptr->m_markPropertiesWithoutValue = mark;
959 for (
auto it = d_ptr->m_itemToIndex.cbegin(), end = d_ptr->m_itemToIndex.cend(); it != end; ++it) {
960 QtProperty *property = it.value()->property();
961 if (!property->hasValue())
962 d_ptr->updateItem(it.key());
964 d_ptr->m_treeWidget->viewport()->update();
969 return d_ptr->m_markPropertiesWithoutValue;
973
974
977 d_ptr->propertyInserted(item, afterItem);
981
982
985 d_ptr->propertyRemoved(item);
989
990
993 d_ptr->propertyChanged(item);
997
998
1001 d_ptr->editItem(item);
1006#include "moc_qttreepropertybrowser_p.cpp"
1007#include "qttreepropertybrowser.moc"
QtAbstractPropertyBrowser provides a base class for implementing property browsers.
void setCurrentItem(QtBrowserItem *)
Sets the current item in the property browser to item.
The QtBrowserItem class represents a property in a property browser instance.
QtProperty * property() const
Returns the property which is accosiated with this item.
QtBrowserItem * parent() const
Returns the parent item of this item.
void closeEditor(QtProperty *property)
void setEditorData(QWidget *, const QModelIndex &) const override
Sets the data to be displayed and edited by the editor from the data model item specified by the mode...
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override
Updates the editor for the item specified by index according to the style option given.
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override
Returns the size needed by the delegate to display the item specified by index, taking into account t...
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
Renders the delegate using the given painter and style option for the item specified by index.
void setModelData(QWidget *, QAbstractItemModel *, const QModelIndex &) const override
Gets data from the editor widget and stores it in the specified model at the item index.
bool eventFilter(QObject *object, QEvent *event) override
\reimp
QWidget * createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override
Returns the widget used to edit the item specified by index for editing.
void setEditorPrivate(QtTreePropertyBrowserPrivate *editorPrivate)
QTreeWidgetItem * editedItem() const
QTreeWidgetItem * indexToItem(const QModelIndex &index) const
void keyPressEvent(QKeyEvent *event) override
void mousePressEvent(QMouseEvent *event) override
void drawRow(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
Draws the row in the tree view that contains the model item index, using the painter given.
void setEditorPrivate(QtTreePropertyBrowserPrivate *editorPrivate)
The QtProperty class encapsulates an instance of a property.
bool isEnabled() const
Returns whether the property is enabled.
bool hasValue() const
Returns whether the property has a value.
bool isModified() const
Returns whether the property is modified.
void enableItem(QTreeWidgetItem *item) const
QWidget * createEditor(QtProperty *property, QWidget *parent) const
void setCurrentItem(QtBrowserItem *browserItem, bool block)
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)
static QIcon drawIndicatorIcon(const QPalette &palette, QStyle *style)
static bool isLightTheme()
static constexpr bool isWindows