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 {}
227 mutable QTreeWidgetItem *m_editedItem =
nullptr;
228 mutable QWidget *m_editedWidget =
nullptr;
229 mutable QtProperty *m_editedProperty =
nullptr;
234 if (m_editedWidget == object) {
235 m_editedWidget =
nullptr;
236 m_editedItem =
nullptr;
237 m_editedProperty =
nullptr;
243 if (property == m_editedProperty)
244 m_editedWidget->deleteLater();
248 const QStyleOptionViewItem &,
const QModelIndex &index)
const
250 if (index.column() == 1 && m_editorPrivate) {
251 QtProperty *property = m_editorPrivate->indexToProperty(index);
252 QTreeWidgetItem *item = m_editorPrivate->indexToItem(index);
253 if (property && item && (item->flags() & Qt::ItemIsEnabled)) {
254 QWidget *editor = m_editorPrivate->createEditor(property, parent);
256 editor->setAutoFillBackground(
true);
257 if (editor->palette().color(editor->backgroundRole()) == Qt::transparent)
258 editor->setBackgroundRole(QPalette::Window);
260 connect(editor, &QObject::destroyed,
261 this, &QtPropertyEditorDelegate::slotEditorDestroyed);
262 m_editedProperty = property;
264 m_editedWidget = editor;
275 const QStyleOptionViewItem &option,
const QModelIndex &index)
const
278 editor->setGeometry(option.rect.adjusted(0, 0, 0, -1));
282 const QModelIndex &index)
const
284 bool hasValue =
true;
285 if (m_editorPrivate) {
286 QtProperty *property = m_editorPrivate->indexToProperty(index);
290 QStyleOptionViewItem opt = option;
291 if ((m_editorPrivate && index.column() == 0) || !hasValue) {
292 QtProperty *property = m_editorPrivate->indexToProperty(index);
294 opt.font.setBold(
true);
295 opt.fontMetrics = QFontMetrics(opt.font);
300 c = opt.palette.color(QPalette::Dark);
303 ? QColor(Qt::white) : opt.palette.color(QPalette::BrightText);
304 opt.palette.setColor(QPalette::Text, textColor);
306 c = m_editorPrivate->calculatedBackgroundColor(m_editorPrivate->indexToBrowserItem(index));
307 if (c.isValid() && (opt.features & QStyleOptionViewItem::Alternate))
311 painter->fillRect(option.rect, c);
312 opt.state &= ~QStyle::State_HasFocus;
313 QStyledItemDelegate::paint(painter, opt, index);
315 opt.palette.setCurrentColorGroup(QPalette::Active);
316 QColor color =
static_cast<QRgb>(QApplication::style()->styleHint(QStyle::SH_Table_GridLineColor, &opt));
318 painter->setPen(QPen(color));
319 if (!m_editorPrivate || (!m_editorPrivate
->lastColumn(index.column()
) && hasValue)) {
320 int right = (option.direction == Qt::LeftToRight) ? option.rect.right() : option.rect.left();
321 painter->drawLine(right, option.rect.y(), right, option.rect.bottom());
327 const QModelIndex &index)
const
329 return QStyledItemDelegate::sizeHint(option, index) + QSize(3, 4);
334 if (event->type() == QEvent::FocusOut) {
335 QFocusEvent *fe =
static_cast<QFocusEvent *>(event);
336 if (fe->reason() == Qt::ActiveWindowFocusReason)
339 return QStyledItemDelegate::eventFilter(object, event);
348 pix.fill(Qt::transparent);
349 QStyleOption branchOption;
350 branchOption.rect = QRect(2, 2, 9, 9);
351 branchOption.palette = palette;
352 branchOption.state = QStyle::State_Children;
357 style->drawPrimitive(QStyle::PE_IndicatorBranch, &branchOption, &p);
360 rc.addPixmap(pix, QIcon::Selected, QIcon::Off);
362 branchOption.state |= QStyle::State_Open;
363 pix.fill(Qt::transparent);
365 style->drawPrimitive(QStyle::PE_IndicatorBranch, &branchOption, &p);
368 rc.addPixmap(pix, QIcon::Normal, QIcon::On);
369 rc.addPixmap(pix, QIcon::Selected, QIcon::On);
375 auto *layout =
new QHBoxLayout(parent);
376 layout->setContentsMargins(QMargins());
379 m_treeWidget->setIconSize(QtPropertyBrowserUtils::itemViewIconSize);
380 layout->addWidget(m_treeWidget);
382 m_treeWidget->setColumnCount(2);
384 labels.append(QCoreApplication::translate(
"QtTreePropertyBrowser",
"Property"));
385 labels.append(QCoreApplication::translate(
"QtTreePropertyBrowser",
"Value"));
386 m_treeWidget->setHeaderLabels(labels);
387 m_treeWidget->setAlternatingRowColors(
true);
388 m_treeWidget->setEditTriggers(QAbstractItemView::EditKeyPressed);
391 m_treeWidget->setItemDelegate(m_delegate);
392 m_treeWidget->header()->setSectionsMovable(
false);
393 m_treeWidget->header()->setSectionResizeMode(QHeaderView::Stretch);
395 m_expandIcon = drawIndicatorIcon(q_ptr->palette(), q_ptr->style());
397 QObject::connect(m_treeWidget, &QTreeView::collapsed,
398 q_ptr, [
this](
const QModelIndex &index) { slotCollapsed(index); });
399 QObject::connect(m_treeWidget, &QTreeView::expanded,
400 q_ptr, [
this](
const QModelIndex &index) { slotExpanded(index); });
401 QObject::connect(m_treeWidget, &QTreeWidget::currentItemChanged,
402 q_ptr, [
this](QTreeWidgetItem *current, QTreeWidgetItem *previous)
403 { slotCurrentTreeItemChanged(current, previous); });
408 if (QTreeWidgetItem *treeItem = m_treeWidget->currentItem())
409 return m_itemToIndex.value(treeItem);
415 const bool blocked = block ? m_treeWidget->blockSignals(
true) :
false;
416 if (browserItem ==
nullptr)
417 m_treeWidget->setCurrentItem(
nullptr);
419 m_treeWidget->setCurrentItem(m_indexToItem.value(browserItem));
421 m_treeWidget->blockSignals(blocked);
426 QTreeWidgetItem *item = m_treeWidget->indexToItem(index);
435 QTreeWidgetItem *item = m_treeWidget->indexToItem(index);
436 return m_itemToIndex.value(item);
441 return m_treeWidget->indexToItem(index);
446 return m_treeWidget->header()->visualIndex(column) == m_treeWidget->columnCount() - 1;
451 Qt::ItemFlags flags = item->flags();
452 if (flags & Qt::ItemIsEnabled) {
453 flags &= ~Qt::ItemIsEnabled;
454 item->setFlags(flags);
456 const int childCount = item->childCount();
457 for (
int i = 0; i < childCount; i++) {
458 QTreeWidgetItem *child = item->child(i);
466 Qt::ItemFlags flags = item->flags();
467 flags |= Qt::ItemIsEnabled;
468 item->setFlags(flags);
469 const int childCount = item->childCount();
470 for (
int i = 0; i < childCount; i++) {
471 QTreeWidgetItem *child = item->child(i);
472 QtProperty *property = m_itemToIndex[child]->property();
489 QTreeWidgetItem *afterItem = m_indexToItem.value(afterIndex);
490 QTreeWidgetItem *parentItem = m_indexToItem.value(index->parent());
492 QTreeWidgetItem *newItem =
nullptr;
494 newItem =
new QTreeWidgetItem(parentItem, afterItem);
496 newItem =
new QTreeWidgetItem(m_treeWidget, afterItem);
498 m_itemToIndex[newItem] = index;
499 m_indexToItem[index] = newItem;
501 newItem->setFlags(newItem->flags() | Qt::ItemIsEditable);
502 newItem->setExpanded(
true);
509 QTreeWidgetItem *item = m_indexToItem.value(index);
511 if (m_treeWidget->currentItem() == item) {
512 m_treeWidget->setCurrentItem(
nullptr);
517 m_indexToItem.remove(index);
518 m_itemToIndex.remove(item);
519 m_indexToBackgroundColor.remove(index);
524 QTreeWidgetItem *item = m_indexToItem.value(index);
531 QtProperty *property = m_itemToIndex[item]->property();
534 const QString valueToolTip = property->valueToolTip();
535 const QString valueText = property->valueText();
536 item->setToolTip(1, valueToolTip.isEmpty() ? valueText : valueToolTip);
537 item->setIcon(1, property->valueIcon());
538 item->setText(1, valueText);
540 expandIcon = m_expandIcon;
542 item->setIcon(0, expandIcon);
544 const QString descriptionToolTip = property->descriptionToolTip();
545 const QString propertyName = property->propertyName();
546 item->setToolTip(0, descriptionToolTip.isEmpty() ? propertyName : descriptionToolTip);
547 item->setStatusTip(0, property->statusTip());
548 item->setWhatsThis(0, property->whatsThis());
549 item->setText(0, propertyName);
550 bool wasEnabled = item->flags() & Qt::ItemIsEnabled;
551 bool isEnabled = wasEnabled;
553 QTreeWidgetItem *parent = item->parent();
554 isEnabled = !parent || (parent->flags() & Qt::ItemIsEnabled);
558 if (wasEnabled != isEnabled) {
564 m_treeWidget->viewport()->update();
570 const auto itEnd = m_indexToBackgroundColor.constEnd();
572 auto it = m_indexToBackgroundColor.constFind(i);
582 QTreeWidgetItem *item = indexToItem(index);
585 emit q_ptr->collapsed(idx);
590 QTreeWidgetItem *item = indexToItem(index);
593 emit q_ptr->expanded(idx);
604 QtBrowserItem *browserItem = newItem ? m_itemToIndex.value(newItem) : 0;
605 m_browserChangedBlocked =
true;
607 m_browserChangedBlocked =
false;
617 if (QTreeWidgetItem *treeItem = m_indexToItem.value(browserItem,
nullptr)) {
618 m_treeWidget->setCurrentItem (treeItem, 1);
619 m_treeWidget->editItem(treeItem, 1);
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
654
655
656
657
658
659
662
663
664
665
666
667
670
671
678 QObject::connect(
this, &QtAbstractPropertyBrowser::currentItemChanged,
679 this, [
this](QtBrowserItem *current)
680 { d_ptr->slotCurrentBrowserItemChanged(current); });
684
685
686
687
688
689
690
691
692
696
697
698
701 return d_ptr->m_treeWidget->indentation();
706 d_ptr->m_treeWidget->setIndentation(i);
710
711
712
715 return d_ptr->m_treeWidget->rootIsDecorated();
720 d_ptr->m_treeWidget->setRootIsDecorated(show);
721 for (
auto it = d_ptr->m_itemToIndex.cbegin(), end = d_ptr->m_itemToIndex.cend(); it != end; ++it) {
722 QtProperty *property = it.value()->property();
723 if (!property->hasValue())
724 d_ptr->updateItem(it.key());
729
730
731
732
735 return d_ptr->m_treeWidget->alternatingRowColors();
740 d_ptr->m_treeWidget->setAlternatingRowColors(enable);
744
745
746
749 return d_ptr->m_headerVisible;
754 if (d_ptr->m_headerVisible == visible)
757 d_ptr->m_headerVisible = visible;
758 d_ptr->m_treeWidget->header()->setVisible(visible);
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
783
784
785
789 return d_ptr->m_resizeMode;
794 if (d_ptr->m_resizeMode == mode)
797 d_ptr->m_resizeMode = mode;
798 QHeaderView::ResizeMode m = QHeaderView::Stretch;
800 case QtTreePropertyBrowser::Interactive: m = QHeaderView::Interactive;
break;
801 case QtTreePropertyBrowser::Fixed: m = QHeaderView::Fixed;
break;
802 case QtTreePropertyBrowser::ResizeToContents: m = QHeaderView::ResizeToContents;
break;
803 case QtTreePropertyBrowser::Stretch:
804 default: m = QHeaderView::Stretch;
break;
806 d_ptr->m_treeWidget->header()->setSectionResizeMode(m);
810
811
812
816 return d_ptr->m_treeWidget->header()->sectionSize(0);
821 d_ptr->m_treeWidget->header()->resizeSection(0, position);
825
826
827
828
832 QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item);
834 treeItem->setExpanded(expanded);
838
839
840
841
845 QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item);
847 return treeItem->isExpanded();
852
853
854
855
856
860 if (
const QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item))
861 return !treeItem->isHidden();
866
867
868
869
870
874 if (QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item))
875 treeItem->setHidden(!visible);
879
880
881
882
883
887 if (!d_ptr->m_indexToItem.contains(item))
890 d_ptr->m_indexToBackgroundColor[item] = color;
892 d_ptr->m_indexToBackgroundColor.remove(item);
893 d_ptr->m_treeWidget->viewport()->update();
897
898
899
900
904 return d_ptr->m_indexToBackgroundColor.value(item);
908
909
910
911
912
913
917 return d_ptr->calculatedBackgroundColor(item);
921
922
923
924
925
926
927
928
931 if (d_ptr->m_markPropertiesWithoutValue == mark)
934 d_ptr->m_markPropertiesWithoutValue = mark;
935 for (
auto it = d_ptr->m_itemToIndex.cbegin(), end = d_ptr->m_itemToIndex.cend(); it != end; ++it) {
936 QtProperty *property = it.value()->property();
937 if (!property->hasValue())
938 d_ptr->updateItem(it.key());
940 d_ptr->m_treeWidget->viewport()->update();
945 return d_ptr->m_markPropertiesWithoutValue;
949
950
953 d_ptr->propertyInserted(item, afterItem);
957
958
961 d_ptr->propertyRemoved(item);
965
966
969 d_ptr->propertyChanged(item);
973
974
977 d_ptr->editItem(item);
982#include "moc_qttreepropertybrowser_p.cpp"
983#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