8#include <QtWidgets/qapplication.h>
9#include <QtWidgets/qboxlayout.h>
10#include <QtWidgets/qheaderview.h>
11#include <QtWidgets/qstyle.h>
12#include <QtWidgets/qstyleditemdelegate.h>
13#include <QtWidgets/qtreewidget.h>
15#include <QtGui/qevent.h>
16#include <QtGui/qicon.h>
17#include <QtGui/qpainter.h>
18#include <QtGui/qpalette.h>
19#include <QtGui/qstylehints.h>
21#include <QtCore/qhash.h>
22#include <QtCore/qoperatingsystemversion.h>
26using namespace Qt::StringLiterals;
28static constexpr bool isWindows = QOperatingSystemVersion::currentType() == QOperatingSystemVersion::Windows;
32 return QGuiApplication::styleHints()->colorScheme() != Qt::ColorScheme::Dark;
40 Q_DECLARE_PUBLIC(QtTreePropertyBrowser)
49 {
return q_ptr->createEditor(property, parent); }
76 void updateItem(QTreeWidgetItem *item);
78 QHash<QtBrowserItem *, QTreeWidgetItem *> m_indexToItem;
79 QHash<QTreeWidgetItem *, QtBrowserItem *> m_itemToIndex;
81 QHash<QtBrowserItem *, QColor> m_indexToBackgroundColor;
85 bool m_headerVisible =
true;
88 bool m_markPropertiesWithoutValue =
false;
89 bool m_browserChangedBlocked =
false;
101 { m_editorPrivate = editorPrivate; }
104 {
return itemFromIndex(index); }
109 void drawRow(QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index)
const override;
118 connect(header(), &QHeaderView::sectionDoubleClicked,
this, &QTreeView::resizeColumnToContents);
123 QStyleOptionViewItem opt = option;
124 bool hasValue =
true;
125 if (m_editorPrivate) {
126 QtProperty *property = m_editorPrivate->indexToProperty(index);
131 const QColor c = option.palette.color(QPalette::Dark);
132 painter->fillRect(option.rect, c);
133 opt.palette.setColor(QPalette::AlternateBase, c);
135 const QColor c = m_editorPrivate->calculatedBackgroundColor(m_editorPrivate->indexToBrowserItem(index));
137 painter->fillRect(option.rect, c);
138 opt.palette.setColor(QPalette::AlternateBase, c.lighter(112));
141 QTreeWidget::drawRow(painter, opt, index);
142 QColor color =
static_cast<QRgb>(QApplication::style()->styleHint(QStyle::SH_Table_GridLineColor, &opt));
144 painter->setPen(QPen(color));
145 painter->drawLine(opt.rect.x(), opt.rect.bottom(), opt.rect.right(), opt.rect.bottom());
151 switch (event->key()) {
156 if (
const QTreeWidgetItem *item = currentItem())
157 if (item->columnCount() >= 2 && ((item->flags() & (Qt::ItemIsEditable | Qt::ItemIsEnabled)) == (Qt::ItemIsEditable | Qt::ItemIsEnabled))) {
160 QModelIndex index = currentIndex();
161 if (index.column() == 0) {
162 index = index.sibling(index.row(), 1);
163 setCurrentIndex(index);
172 QTreeWidget::keyPressEvent(event);
177 QTreeWidget::mousePressEvent(event);
178 QTreeWidgetItem *item = itemAt(event->position().toPoint());
181 if ((item != m_editorPrivate->editedItem()) && (event->button() == Qt::LeftButton)
182 && (header()->logicalIndexAt(event->position().toPoint().x()) == 1)
183 && ((item->flags() & (Qt::ItemIsEditable | Qt::ItemIsEnabled)) == (Qt::ItemIsEditable | Qt::ItemIsEnabled))) {
186 if (event->position().toPoint().x() + header()->offset() < 20)
187 item->setExpanded(!item->isExpanded());
200 { m_editorPrivate = editorPrivate; }
203 const QModelIndex &index)
const override;
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 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);
259 connect(editor, &QObject::destroyed,
260 this, &QtPropertyEditorDelegate::slotEditorDestroyed);
261 m_editedProperty = property;
263 m_editedWidget = editor;
274 const QStyleOptionViewItem &option,
const QModelIndex &index)
const
277 editor->setGeometry(option.rect.adjusted(0, 0, 0, -1));
281 const QModelIndex &index)
const
283 bool hasValue =
true;
284 if (m_editorPrivate) {
285 QtProperty *property = m_editorPrivate->indexToProperty(index);
289 QStyleOptionViewItem opt = option;
290 if ((m_editorPrivate && index.column() == 0) || !hasValue) {
291 QtProperty *property = m_editorPrivate->indexToProperty(index);
293 opt.font.setBold(
true);
294 opt.fontMetrics = QFontMetrics(opt.font);
299 c = opt.palette.color(QPalette::Dark);
302 ? QColor(Qt::white) : opt.palette.color(QPalette::BrightText);
303 opt.palette.setColor(QPalette::Text, textColor);
305 c = m_editorPrivate->calculatedBackgroundColor(m_editorPrivate->indexToBrowserItem(index));
306 if (c.isValid() && (opt.features & QStyleOptionViewItem::Alternate))
310 painter->fillRect(option.rect, c);
311 opt.state &= ~QStyle::State_HasFocus;
312 QStyledItemDelegate::paint(painter, opt, index);
314 opt.palette.setCurrentColorGroup(QPalette::Active);
315 QColor color =
static_cast<QRgb>(QApplication::style()->styleHint(QStyle::SH_Table_GridLineColor, &opt));
317 painter->setPen(QPen(color));
318 if (!m_editorPrivate || (!m_editorPrivate
->lastColumn(index.column()
) && hasValue)) {
319 int right = (option.direction == Qt::LeftToRight) ? option.rect.right() : option.rect.left();
320 painter->drawLine(right, option.rect.y(), right, option.rect.bottom());
326 const QModelIndex &index)
const
328 return QStyledItemDelegate::sizeHint(option, index) + QSize(3, 4);
337 pix.fill(Qt::transparent);
338 QStyleOption branchOption;
339 branchOption.rect = QRect(2, 2, 9, 9);
340 branchOption.palette = palette;
341 branchOption.state = QStyle::State_Children;
346 style->drawPrimitive(QStyle::PE_IndicatorBranch, &branchOption, &p);
349 rc.addPixmap(pix, QIcon::Selected, QIcon::Off);
351 branchOption.state |= QStyle::State_Open;
352 pix.fill(Qt::transparent);
354 style->drawPrimitive(QStyle::PE_IndicatorBranch, &branchOption, &p);
357 rc.addPixmap(pix, QIcon::Normal, QIcon::On);
358 rc.addPixmap(pix, QIcon::Selected, QIcon::On);
364 auto *layout =
new QHBoxLayout(parent);
365 layout->setContentsMargins(QMargins());
368 m_treeWidget->setIconSize(QtPropertyBrowserUtils::itemViewIconSize);
369 layout->addWidget(m_treeWidget);
371 m_treeWidget->setColumnCount(2);
373 labels.append(QCoreApplication::translate(
"QtTreePropertyBrowser",
"Property"));
374 labels.append(QCoreApplication::translate(
"QtTreePropertyBrowser",
"Value"));
375 m_treeWidget->setHeaderLabels(labels);
376 m_treeWidget->setAlternatingRowColors(
true);
377 m_treeWidget->setEditTriggers(QAbstractItemView::EditKeyPressed);
380 m_treeWidget->setItemDelegate(m_delegate);
381 m_treeWidget->header()->setSectionsMovable(
false);
382 m_treeWidget->header()->setSectionResizeMode(QHeaderView::Stretch);
384 m_expandIcon = drawIndicatorIcon(q_ptr->palette(), q_ptr->style());
386 QObject::connect(m_treeWidget, &QTreeView::collapsed,
387 q_ptr, [
this](
const QModelIndex &index) { slotCollapsed(index); });
388 QObject::connect(m_treeWidget, &QTreeView::expanded,
389 q_ptr, [
this](
const QModelIndex &index) { slotExpanded(index); });
390 QObject::connect(m_treeWidget, &QTreeWidget::currentItemChanged,
391 q_ptr, [
this](QTreeWidgetItem *current, QTreeWidgetItem *previous)
392 { slotCurrentTreeItemChanged(current, previous); });
397 if (QTreeWidgetItem *treeItem = m_treeWidget->currentItem())
398 return m_itemToIndex.value(treeItem);
404 const bool blocked = block ? m_treeWidget->blockSignals(
true) :
false;
405 if (browserItem ==
nullptr)
406 m_treeWidget->setCurrentItem(
nullptr);
408 m_treeWidget->setCurrentItem(m_indexToItem.value(browserItem));
410 m_treeWidget->blockSignals(blocked);
415 QTreeWidgetItem *item = m_treeWidget->indexToItem(index);
424 QTreeWidgetItem *item = m_treeWidget->indexToItem(index);
425 return m_itemToIndex.value(item);
430 return m_treeWidget->indexToItem(index);
435 return m_treeWidget->header()->visualIndex(column) == m_treeWidget->columnCount() - 1;
440 Qt::ItemFlags flags = item->flags();
441 if (flags & Qt::ItemIsEnabled) {
442 flags &= ~Qt::ItemIsEnabled;
443 item->setFlags(flags);
445 const int childCount = item->childCount();
446 for (
int i = 0; i < childCount; i++) {
447 QTreeWidgetItem *child = item->child(i);
455 Qt::ItemFlags flags = item->flags();
456 flags |= Qt::ItemIsEnabled;
457 item->setFlags(flags);
458 const int childCount = item->childCount();
459 for (
int i = 0; i < childCount; i++) {
460 QTreeWidgetItem *child = item->child(i);
461 QtProperty *property = m_itemToIndex[child]->property();
478 QTreeWidgetItem *afterItem = m_indexToItem.value(afterIndex);
479 QTreeWidgetItem *parentItem = m_indexToItem.value(index->parent());
481 QTreeWidgetItem *newItem =
nullptr;
483 newItem =
new QTreeWidgetItem(parentItem, afterItem);
485 newItem =
new QTreeWidgetItem(m_treeWidget, afterItem);
487 m_itemToIndex[newItem] = index;
488 m_indexToItem[index] = newItem;
490 newItem->setFlags(newItem->flags() | Qt::ItemIsEditable);
491 newItem->setExpanded(
true);
498 QTreeWidgetItem *item = m_indexToItem.value(index);
500 if (m_treeWidget->currentItem() == item) {
501 m_treeWidget->setCurrentItem(
nullptr);
506 m_indexToItem.remove(index);
507 m_itemToIndex.remove(item);
508 m_indexToBackgroundColor.remove(index);
513 QTreeWidgetItem *item = m_indexToItem.value(index);
520 QtProperty *property = m_itemToIndex[item]->property();
523 const QString valueToolTip = property->valueToolTip();
524 const QString valueText = property->valueText();
525 item->setToolTip(1, valueToolTip.isEmpty() ? valueText : valueToolTip);
526 item->setIcon(1, property->valueIcon());
527 item->setText(1, valueText);
529 expandIcon = m_expandIcon;
531 item->setIcon(0, expandIcon);
533 const QString descriptionToolTip = property->descriptionToolTip();
534 const QString propertyName = property->propertyName();
535 item->setToolTip(0, descriptionToolTip.isEmpty() ? propertyName : descriptionToolTip);
536 item->setStatusTip(0, property->statusTip());
537 item->setWhatsThis(0, property->whatsThis());
538 item->setText(0, propertyName);
539 bool wasEnabled = item->flags() & Qt::ItemIsEnabled;
540 bool isEnabled = wasEnabled;
542 QTreeWidgetItem *parent = item->parent();
543 isEnabled = !parent || (parent->flags() & Qt::ItemIsEnabled);
547 if (wasEnabled != isEnabled) {
553 m_treeWidget->viewport()->update();
559 const auto itEnd = m_indexToBackgroundColor.constEnd();
561 auto it = m_indexToBackgroundColor.constFind(i);
571 QTreeWidgetItem *item = indexToItem(index);
574 emit q_ptr->collapsed(idx);
579 QTreeWidgetItem *item = indexToItem(index);
582 emit q_ptr->expanded(idx);
593 QtBrowserItem *browserItem = newItem ? m_itemToIndex.value(newItem) : 0;
594 m_browserChangedBlocked =
true;
596 m_browserChangedBlocked =
false;
606 if (QTreeWidgetItem *treeItem = m_indexToItem.value(browserItem,
nullptr)) {
607 m_treeWidget->setCurrentItem (treeItem, 1);
608 m_treeWidget->editItem(treeItem, 1);
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
640
643
644
645
646
647
648
651
652
653
654
655
656
659
660
667 QObject::connect(
this, &QtAbstractPropertyBrowser::currentItemChanged,
668 this, [
this](QtBrowserItem *current)
669 { d_ptr->slotCurrentBrowserItemChanged(current); });
673
674
675
676
677
678
679
680
681
685
686
687
690 return d_ptr->m_treeWidget->indentation();
695 d_ptr->m_treeWidget->setIndentation(i);
699
700
701
704 return d_ptr->m_treeWidget->rootIsDecorated();
709 d_ptr->m_treeWidget->setRootIsDecorated(show);
710 for (
auto it = d_ptr->m_itemToIndex.cbegin(), end = d_ptr->m_itemToIndex.cend(); it != end; ++it) {
711 QtProperty *property = it.value()->property();
712 if (!property->hasValue())
713 d_ptr->updateItem(it.key());
718
719
720
721
724 return d_ptr->m_treeWidget->alternatingRowColors();
729 d_ptr->m_treeWidget->setAlternatingRowColors(enable);
733
734
735
738 return d_ptr->m_headerVisible;
743 if (d_ptr->m_headerVisible == visible)
746 d_ptr->m_headerVisible = visible;
747 d_ptr->m_treeWidget->header()->setVisible(visible);
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
772
773
774
778 return d_ptr->m_resizeMode;
783 if (d_ptr->m_resizeMode == mode)
786 d_ptr->m_resizeMode = mode;
787 QHeaderView::ResizeMode m = QHeaderView::Stretch;
789 case QtTreePropertyBrowser::Interactive: m = QHeaderView::Interactive;
break;
790 case QtTreePropertyBrowser::Fixed: m = QHeaderView::Fixed;
break;
791 case QtTreePropertyBrowser::ResizeToContents: m = QHeaderView::ResizeToContents;
break;
792 case QtTreePropertyBrowser::Stretch:
793 default: m = QHeaderView::Stretch;
break;
795 d_ptr->m_treeWidget->header()->setSectionResizeMode(m);
799
800
801
805 return d_ptr->m_treeWidget->header()->sectionSize(0);
810 d_ptr->m_treeWidget->header()->resizeSection(0, position);
814
815
816
817
821 QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item);
823 treeItem->setExpanded(expanded);
827
828
829
830
834 QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item);
836 return treeItem->isExpanded();
841
842
843
844
845
849 if (
const QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item))
850 return !treeItem->isHidden();
855
856
857
858
859
863 if (QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item))
864 treeItem->setHidden(!visible);
868
869
870
871
872
876 if (!d_ptr->m_indexToItem.contains(item))
879 d_ptr->m_indexToBackgroundColor[item] = color;
881 d_ptr->m_indexToBackgroundColor.remove(item);
882 d_ptr->m_treeWidget->viewport()->update();
886
887
888
889
893 return d_ptr->m_indexToBackgroundColor.value(item);
897
898
899
900
901
902
906 return d_ptr->calculatedBackgroundColor(item);
910
911
912
913
914
915
916
917
920 if (d_ptr->m_markPropertiesWithoutValue == mark)
923 d_ptr->m_markPropertiesWithoutValue = mark;
924 for (
auto it = d_ptr->m_itemToIndex.cbegin(), end = d_ptr->m_itemToIndex.cend(); it != end; ++it) {
925 QtProperty *property = it.value()->property();
926 if (!property->hasValue())
927 d_ptr->updateItem(it.key());
929 d_ptr->m_treeWidget->viewport()->update();
934 return d_ptr->m_markPropertiesWithoutValue;
938
939
942 d_ptr->propertyInserted(item, afterItem);
946
947
950 d_ptr->propertyRemoved(item);
954
955
958 d_ptr->propertyChanged(item);
962
963
966 d_ptr->editItem(item);
971#include "moc_qttreepropertybrowser_p.cpp"
972#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