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 mutable QTreeWidgetItem *m_editedItem =
nullptr;
227 mutable QWidget *m_editedWidget =
nullptr;
228 mutable QtProperty *m_editedProperty =
nullptr;
233 if (m_editedWidget == object) {
234 m_editedWidget =
nullptr;
235 m_editedItem =
nullptr;
236 m_editedProperty =
nullptr;
242 if (property == m_editedProperty)
243 m_editedWidget->deleteLater();
247 const QStyleOptionViewItem &,
const QModelIndex &index)
const
249 if (index.column() == 1 && m_editorPrivate) {
250 QtProperty *property = m_editorPrivate->indexToProperty(index);
251 QTreeWidgetItem *item = m_editorPrivate->indexToItem(index);
252 if (property && item && (item->flags() & Qt::ItemIsEnabled)) {
253 QWidget *editor = m_editorPrivate->createEditor(property, parent);
255 editor->setAutoFillBackground(
true);
256 if (editor->palette().color(editor->backgroundRole()) == Qt::transparent)
257 editor->setBackgroundRole(QPalette::Window);
258 connect(editor, &QObject::destroyed,
259 this, &QtPropertyEditorDelegate::slotEditorDestroyed);
260 m_editedProperty = property;
262 m_editedWidget = editor;
273 const QStyleOptionViewItem &option,
const QModelIndex &index)
const
276 editor->setGeometry(option.rect.adjusted(0, 0, 0, -1));
280 const QModelIndex &index)
const
282 bool hasValue =
true;
283 if (m_editorPrivate) {
284 QtProperty *property = m_editorPrivate->indexToProperty(index);
288 QStyleOptionViewItem opt = option;
289 if ((m_editorPrivate && index.column() == 0) || !hasValue) {
290 QtProperty *property = m_editorPrivate->indexToProperty(index);
292 opt.font.setBold(
true);
293 opt.fontMetrics = QFontMetrics(opt.font);
298 c = opt.palette.color(QPalette::Dark);
301 ? QColor(Qt::white) : opt.palette.color(QPalette::BrightText);
302 opt.palette.setColor(QPalette::Text, textColor);
304 c = m_editorPrivate->calculatedBackgroundColor(m_editorPrivate->indexToBrowserItem(index));
305 if (c.isValid() && (opt.features & QStyleOptionViewItem::Alternate))
309 painter->fillRect(option.rect, c);
310 opt.state &= ~QStyle::State_HasFocus;
311 QStyledItemDelegate::paint(painter, opt, index);
313 opt.palette.setCurrentColorGroup(QPalette::Active);
314 QColor color =
static_cast<QRgb>(QApplication::style()->styleHint(QStyle::SH_Table_GridLineColor, &opt));
316 painter->setPen(QPen(color));
317 if (!m_editorPrivate || (!m_editorPrivate
->lastColumn(index.column()
) && hasValue)) {
318 int right = (option.direction == Qt::LeftToRight) ? option.rect.right() : option.rect.left();
319 painter->drawLine(right, option.rect.y(), right, option.rect.bottom());
325 const QModelIndex &index)
const
327 return QStyledItemDelegate::sizeHint(option, index) + QSize(3, 4);
336 pix.fill(Qt::transparent);
337 QStyleOption branchOption;
338 branchOption.rect = QRect(2, 2, 9, 9);
339 branchOption.palette = palette;
340 branchOption.state = QStyle::State_Children;
345 style->drawPrimitive(QStyle::PE_IndicatorBranch, &branchOption, &p);
348 rc.addPixmap(pix, QIcon::Selected, QIcon::Off);
350 branchOption.state |= QStyle::State_Open;
351 pix.fill(Qt::transparent);
353 style->drawPrimitive(QStyle::PE_IndicatorBranch, &branchOption, &p);
356 rc.addPixmap(pix, QIcon::Normal, QIcon::On);
357 rc.addPixmap(pix, QIcon::Selected, QIcon::On);
363 auto *layout =
new QHBoxLayout(parent);
364 layout->setContentsMargins(QMargins());
367 m_treeWidget->setIconSize(QtPropertyBrowserUtils::itemViewIconSize);
368 layout->addWidget(m_treeWidget);
370 m_treeWidget->setColumnCount(2);
372 labels.append(QCoreApplication::translate(
"QtTreePropertyBrowser",
"Property"));
373 labels.append(QCoreApplication::translate(
"QtTreePropertyBrowser",
"Value"));
374 m_treeWidget->setHeaderLabels(labels);
375 m_treeWidget->setAlternatingRowColors(
true);
376 m_treeWidget->setEditTriggers(QAbstractItemView::EditKeyPressed);
379 m_treeWidget->setItemDelegate(m_delegate);
380 m_treeWidget->header()->setSectionsMovable(
false);
381 m_treeWidget->header()->setSectionResizeMode(QHeaderView::Stretch);
383 m_expandIcon = drawIndicatorIcon(q_ptr->palette(), q_ptr->style());
385 QObject::connect(m_treeWidget, &QTreeView::collapsed,
386 q_ptr, [
this](
const QModelIndex &index) { slotCollapsed(index); });
387 QObject::connect(m_treeWidget, &QTreeView::expanded,
388 q_ptr, [
this](
const QModelIndex &index) { slotExpanded(index); });
389 QObject::connect(m_treeWidget, &QTreeWidget::currentItemChanged,
390 q_ptr, [
this](QTreeWidgetItem *current, QTreeWidgetItem *previous)
391 { slotCurrentTreeItemChanged(current, previous); });
396 if (QTreeWidgetItem *treeItem = m_treeWidget->currentItem())
397 return m_itemToIndex.value(treeItem);
403 const bool blocked = block ? m_treeWidget->blockSignals(
true) :
false;
404 if (browserItem ==
nullptr)
405 m_treeWidget->setCurrentItem(
nullptr);
407 m_treeWidget->setCurrentItem(m_indexToItem.value(browserItem));
409 m_treeWidget->blockSignals(blocked);
414 QTreeWidgetItem *item = m_treeWidget->indexToItem(index);
423 QTreeWidgetItem *item = m_treeWidget->indexToItem(index);
424 return m_itemToIndex.value(item);
429 return m_treeWidget->indexToItem(index);
434 return m_treeWidget->header()->visualIndex(column) == m_treeWidget->columnCount() - 1;
439 Qt::ItemFlags flags = item->flags();
440 if (flags & Qt::ItemIsEnabled) {
441 flags &= ~Qt::ItemIsEnabled;
442 item->setFlags(flags);
444 const int childCount = item->childCount();
445 for (
int i = 0; i < childCount; i++) {
446 QTreeWidgetItem *child = item->child(i);
454 Qt::ItemFlags flags = item->flags();
455 flags |= Qt::ItemIsEnabled;
456 item->setFlags(flags);
457 const int childCount = item->childCount();
458 for (
int i = 0; i < childCount; i++) {
459 QTreeWidgetItem *child = item->child(i);
460 QtProperty *property = m_itemToIndex[child]->property();
477 QTreeWidgetItem *afterItem = m_indexToItem.value(afterIndex);
478 QTreeWidgetItem *parentItem = m_indexToItem.value(index->parent());
480 QTreeWidgetItem *newItem =
nullptr;
482 newItem =
new QTreeWidgetItem(parentItem, afterItem);
484 newItem =
new QTreeWidgetItem(m_treeWidget, afterItem);
486 m_itemToIndex[newItem] = index;
487 m_indexToItem[index] = newItem;
489 newItem->setFlags(newItem->flags() | Qt::ItemIsEditable);
490 newItem->setExpanded(
true);
497 QTreeWidgetItem *item = m_indexToItem.value(index);
499 if (m_treeWidget->currentItem() == item) {
500 m_treeWidget->setCurrentItem(
nullptr);
505 m_indexToItem.remove(index);
506 m_itemToIndex.remove(item);
507 m_indexToBackgroundColor.remove(index);
512 QTreeWidgetItem *item = m_indexToItem.value(index);
519 QtProperty *property = m_itemToIndex[item]->property();
522 const QString valueToolTip = property->valueToolTip();
523 const QString valueText = property->valueText();
524 item->setToolTip(1, valueToolTip.isEmpty() ? valueText : valueToolTip);
525 item->setIcon(1, property->valueIcon());
526 item->setText(1, valueText);
528 expandIcon = m_expandIcon;
530 item->setIcon(0, expandIcon);
532 const QString descriptionToolTip = property->descriptionToolTip();
533 const QString propertyName = property->propertyName();
534 item->setToolTip(0, descriptionToolTip.isEmpty() ? propertyName : descriptionToolTip);
535 item->setStatusTip(0, property->statusTip());
536 item->setWhatsThis(0, property->whatsThis());
537 item->setText(0, propertyName);
538 bool wasEnabled = item->flags() & Qt::ItemIsEnabled;
539 bool isEnabled = wasEnabled;
541 QTreeWidgetItem *parent = item->parent();
542 isEnabled = !parent || (parent->flags() & Qt::ItemIsEnabled);
546 if (wasEnabled != isEnabled) {
552 m_treeWidget->viewport()->update();
558 const auto itEnd = m_indexToBackgroundColor.constEnd();
560 auto it = m_indexToBackgroundColor.constFind(i);
570 QTreeWidgetItem *item = indexToItem(index);
573 emit q_ptr->collapsed(idx);
578 QTreeWidgetItem *item = indexToItem(index);
581 emit q_ptr->expanded(idx);
592 QtBrowserItem *browserItem = newItem ? m_itemToIndex.value(newItem) : 0;
593 m_browserChangedBlocked =
true;
595 m_browserChangedBlocked =
false;
605 if (QTreeWidgetItem *treeItem = m_indexToItem.value(browserItem,
nullptr)) {
606 m_treeWidget->setCurrentItem (treeItem, 1);
607 m_treeWidget->editItem(treeItem, 1);
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
642
643
644
645
646
647
650
651
652
653
654
655
658
659
666 QObject::connect(
this, &QtAbstractPropertyBrowser::currentItemChanged,
667 this, [
this](QtBrowserItem *current)
668 { d_ptr->slotCurrentBrowserItemChanged(current); });
672
673
674
675
676
677
678
679
680
684
685
686
689 return d_ptr->m_treeWidget->indentation();
694 d_ptr->m_treeWidget->setIndentation(i);
698
699
700
703 return d_ptr->m_treeWidget->rootIsDecorated();
708 d_ptr->m_treeWidget->setRootIsDecorated(show);
709 for (
auto it = d_ptr->m_itemToIndex.cbegin(), end = d_ptr->m_itemToIndex.cend(); it != end; ++it) {
710 QtProperty *property = it.value()->property();
711 if (!property->hasValue())
712 d_ptr->updateItem(it.key());
717
718
719
720
723 return d_ptr->m_treeWidget->alternatingRowColors();
728 d_ptr->m_treeWidget->setAlternatingRowColors(enable);
732
733
734
737 return d_ptr->m_headerVisible;
742 if (d_ptr->m_headerVisible == visible)
745 d_ptr->m_headerVisible = visible;
746 d_ptr->m_treeWidget->header()->setVisible(visible);
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
771
772
773
777 return d_ptr->m_resizeMode;
782 if (d_ptr->m_resizeMode == mode)
785 d_ptr->m_resizeMode = mode;
786 QHeaderView::ResizeMode m = QHeaderView::Stretch;
788 case QtTreePropertyBrowser::Interactive: m = QHeaderView::Interactive;
break;
789 case QtTreePropertyBrowser::Fixed: m = QHeaderView::Fixed;
break;
790 case QtTreePropertyBrowser::ResizeToContents: m = QHeaderView::ResizeToContents;
break;
791 case QtTreePropertyBrowser::Stretch:
792 default: m = QHeaderView::Stretch;
break;
794 d_ptr->m_treeWidget->header()->setSectionResizeMode(m);
798
799
800
804 return d_ptr->m_treeWidget->header()->sectionSize(0);
809 d_ptr->m_treeWidget->header()->resizeSection(0, position);
813
814
815
816
820 QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item);
822 treeItem->setExpanded(expanded);
826
827
828
829
833 QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item);
835 return treeItem->isExpanded();
840
841
842
843
844
848 if (
const QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item))
849 return !treeItem->isHidden();
854
855
856
857
858
862 if (QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item))
863 treeItem->setHidden(!visible);
867
868
869
870
871
875 if (!d_ptr->m_indexToItem.contains(item))
878 d_ptr->m_indexToBackgroundColor[item] = color;
880 d_ptr->m_indexToBackgroundColor.remove(item);
881 d_ptr->m_treeWidget->viewport()->update();
885
886
887
888
892 return d_ptr->m_indexToBackgroundColor.value(item);
896
897
898
899
900
901
905 return d_ptr->calculatedBackgroundColor(item);
909
910
911
912
913
914
915
916
919 if (d_ptr->m_markPropertiesWithoutValue == mark)
922 d_ptr->m_markPropertiesWithoutValue = mark;
923 for (
auto it = d_ptr->m_itemToIndex.cbegin(), end = d_ptr->m_itemToIndex.cend(); it != end; ++it) {
924 QtProperty *property = it.value()->property();
925 if (!property->hasValue())
926 d_ptr->updateItem(it.key());
928 d_ptr->m_treeWidget->viewport()->update();
933 return d_ptr->m_markPropertiesWithoutValue;
937
938
941 d_ptr->propertyInserted(item, afterItem);
945
946
949 d_ptr->propertyRemoved(item);
953
954
957 d_ptr->propertyChanged(item);
961
962
965 d_ptr->editItem(item);
970#include "moc_qttreepropertybrowser_p.cpp"
971#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.
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