Files
qglengine/widgets/propertyeditor.cpp
2020-08-25 21:57:31 +03:00

427 lines
19 KiB
C++

/*
QGL PropertyEditor
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "propertyeditor.h"
QWidget * Delegate::widgetForProperty(QWidget * parent, const QModelIndex & index) const {
QWidget * w = 0;
int type = 0;
QVariant value = index.data(Qt::UserRole);
if (index.data(Qt::UserRole + 2).toString() == "__flags") return 0;
if (index.data(Qt::UserRole + 1).toString() == "__flag") {
qulonglong key = index.data(Qt::UserRole).toULongLong();
value = index.parent().data(Qt::UserRole);
//QMetaProperty prop = index.parent().data(Qt::UserRole + 1).value<QMetaProperty>();
w = new QCheckBox(parent); type = 14; ((QCheckBox*)w)->setChecked(((value.toULongLong() & key) == key && key != 0) || (value.toULongLong() == 0 && key == 0));
((QCheckBox*)w)->setText("0x" + QString::number(key, 16).toUpper());
connect((QCheckBox*)w, SIGNAL(clicked(bool)), this, SLOT(changedFlag()));
//qDebug() << prop.enumerator().name();
} else {
if (value.canConvert<PropertyValuePair>()) {
PropertyValuePair prop = value.value<PropertyValuePair>();
if (prop.first.isEnumType()) {
w = new QComboBox(parent); type = 13; ((QComboBox*)w)->setCurrentIndex(value.toInt());
w->setProperty("__prop", QVariant::fromValue<QMetaProperty>(prop.first));
QMetaEnum menum = prop.first.enumerator();
for (int i = 0; i < menum.keyCount(); ++i) {
((QComboBox*)w)->addItem(QString(menum.key(i)) + " (0x" + QString::number(menum.value(i), 16).toUpper() + ")", menum.value(i));
if (menum.value(i) == prop.second.toInt())
((QComboBox*)w)->setCurrentIndex(i);
}
connect((QComboBox*)w, SIGNAL(currentIndexChanged(int)), this, SLOT(changed()));
}
} else {
switch (value.type()) {
case QVariant::Int: w = new QSpinBox(parent); type = 2; ((QSpinBox*)w)->setRange(-0x7FFFFFFF, 0x7FFFFFFF); connect((QSpinBox*)w, SIGNAL(valueChanged(int)), this, SLOT(changed())); break;
case QVariant::UInt: w = new QSpinBox(parent); type = 3; ((QSpinBox*)w)->setRange(0, 0xFFFFFFFF); connect((QSpinBox*)w, SIGNAL(valueChanged(int)), this, SLOT(changed())); break;
case QVariant::LongLong: w = new QSpinBox(parent); type = 4; ((QSpinBox*)w)->setRange(-0x7FFFFFFF, 0x7FFFFFFF); connect((QSpinBox*)w, SIGNAL(valueChanged(int)), this, SLOT(changed())); break;
case QVariant::ULongLong: w = new QSpinBox(parent); type = 5; ((QSpinBox*)w)->setRange(0, 0xFFFFFFFF); connect((QSpinBox*)w, SIGNAL(valueChanged(int)), this, SLOT(changed())); break;
case QVariant::Double: w = new QDoubleSpinBox(parent); type = 6; ((QDoubleSpinBox*)w)->setRange(-999999999, 999999999); ((QDoubleSpinBox*)w)->setDecimals(3); connect((QDoubleSpinBox*)w, SIGNAL(valueChanged(double)), this, SLOT(changed())); break;
case QVariant::Bool: w = new QCheckBox(parent); type = 7; ((QCheckBox*)w)->setChecked(value.toBool()); connect((QCheckBox*)w, SIGNAL(toggled(bool)), this, SLOT(changed())); break;
case QVariant::Color: w = new ColorButton(parent); type = 8; ((ColorButton*)w)->setUseAlphaChannel(true); ((ColorButton*)w)->setColor(value.value<QColor>()); connect((ColorButton*)w, SIGNAL(colorChanged(QColor)), this, SLOT(changed())); break;
case QVariant::Point: w = new QPointEdit(parent); type = 9; ((QPointEdit*)w)->setDecimals(0); ((QPointEdit*)w)->setValue(QPointF(value.toPoint())); connect((QPointEdit*)w, SIGNAL(valueChanged(QPointF)), this, SLOT(changed())); break;
case QVariant::PointF: w = new QPointEdit(parent); type = 10; ((QPointEdit*)w)->setDecimals(3); ((QPointEdit*)w)->setValue(value.toPointF()); connect((QPointEdit*)w, SIGNAL(valueChanged(QPointF)), this, SLOT(changed())); break;
case QVariant::Rect: w = new QRectEdit(parent); type = 11; ((QRectEdit*)w)->setDecimals(0); ((QRectEdit*)w)->setValue(QRectF(value.toRect())); connect((QRectEdit*)w, SIGNAL(valueChanged(QRectF)), this, SLOT(changed())); break;
case QVariant::RectF: w = new QRectEdit(parent); type = 12; ((QRectEdit*)w)->setDecimals(3); ((QRectEdit*)w)->setValue(value.toRectF()); connect((QRectEdit*)w, SIGNAL(valueChanged(QRectF)), this, SLOT(changed())); break;
case QVariant::String: default: w = new CLineEdit(parent); type = 1; ((CLineEdit*)w)->setDefaultText(value.toString()); connect((CLineEdit*)w, SIGNAL(textChanged(QString)), this, SLOT(changed())); break;
}
}
}
if (w == 0) return 0;
/*QPalette pal = w->palette();
pal.setColor(QPalette::Window, Qt::white);
w->setPalette(pal);*/
w->setAutoFillBackground(true);
w->setProperty("__type", type);
return w;
}
void Delegate::setWidgetProperty(QWidget * w, const QVariant & value) const {
if (w == 0) return;
switch (w->property("__type").toInt()) {
case 1: ((CLineEdit*)w)->setText(value.toString()); break;
case 2: case 3: case 4: case 5: ((QSpinBox*)w)->setValue(value.toInt()); break;
case 6: ((QDoubleSpinBox*)w)->setValue(value.toDouble()); break;
case 7: ((QCheckBox*)w)->setChecked(value.toBool()); break;
case 8: ((ColorButton*)w)->setColor(value.value<QColor>()); break;
case 9: ((QPointEdit*)w)->setValue(value.value<QPoint>()); break;
case 10: ((QPointEdit*)w)->setValue(value.value<QPointF>()); break;
case 11: ((QRectEdit*)w)->setValue(value.value<QRect>()); break;
case 12: ((QRectEdit*)w)->setValue(value.value<QRectF>()); break;
}
}
const QVariant Delegate::widgetProperty(QWidget * w) const {
if (w == 0) return QVariant();
switch (w->property("__type").toInt()) {
case 1: return QVariant::fromValue<QString>(((CLineEdit*)w)->text()); break;
case 2: return QVariant::fromValue<int>(((QSpinBox*)w)->value()); break;
case 3: return QVariant::fromValue<uint>(((QSpinBox*)w)->value()); break;
case 4: return QVariant::fromValue<qlonglong>(((QSpinBox*)w)->value()); break;
case 5: return QVariant::fromValue<qulonglong>(((QSpinBox*)w)->value()); break;
case 6: return QVariant::fromValue<double>(((QDoubleSpinBox*)w)->value()); break;
case 7: return QVariant::fromValue<bool>(((QCheckBox*)w)->isChecked()); break;
case 8: return QVariant::fromValue<QColor>(((ColorButton*)w)->color()); break;
case 9: return QVariant::fromValue<QPoint>(((QPointEdit*)w)->value().toPoint()); break;
case 10: return QVariant::fromValue<QPointF>(((QPointEdit*)w)->value()); break;
case 11: return QVariant::fromValue<QRect>(((QRectEdit*)w)->value().toRect()); break;
case 12: return QVariant::fromValue<QRectF>(((QRectEdit*)w)->value()); break;
case 13: return QVariant::fromValue<PropertyValuePair>(PropertyValuePair(w->property("__prop").value<QMetaProperty>(), ((QComboBox*)w)->itemData(((QComboBox*)w)->currentIndex()))); break;
default: return QVariant(); break;
}
return QVariant();
}
void Delegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const {
if (index.data(Qt::UserRole + 1).toString() != "__flag")
model->setData(index, widgetProperty(editor), Qt::UserRole);
}
void Delegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const {
QStyledItemDelegate::paint(painter, option, index);
QVariant value = index.data(Qt::UserRole);
QStyle * style = QApplication::style();
QStyleOption * so = 0;
QStyleOptionComplex * soc = 0;
QString text;
QRect rect;
QPalette::ColorRole role = (option.state.testFlag(QStyle::State_Selected) && option.state.testFlag(QStyle::State_Active) ? QPalette::HighlightedText : QPalette::WindowText);
if (index.data(Qt::UserRole + 2).toString() == "__flags") {
text = "0x" + QString::number(value.toInt(), 16).toUpper();
style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text),
Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role);
return;
}
if (index.data(Qt::UserRole + 1) == "__flag") {
qulonglong key = index.data(Qt::UserRole).toULongLong();
value = index.parent().data(Qt::UserRole);
so = new QStyleOptionButton();
so->rect = option.rect;
so->palette = option.palette;
so->fontMetrics = option.fontMetrics;
((QStyleOptionButton*)so)->state = (((value.toULongLong() & key) == key && key != 0) || (value.toULongLong() == 0 && key == 0) ? QStyle::State_On : QStyle::State_Off) | option.state;
((QStyleOptionButton*)so)->text = "0x" + QString::number(key, 16).toUpper();
if (option.state.testFlag(QStyle::State_Selected))
so->palette.setColor(QPalette::WindowText, so->palette.color(QPalette::HighlightedText));
style->drawControl(QStyle::CE_CheckBox, so, painter);
} else {
if (value.canConvert<PropertyValuePair>()) {
PropertyValuePair prop = value.value<PropertyValuePair>();
if (prop.first.isEnumType()) {
QMetaEnum menum = prop.first.enumerator();
for (int i = 0; i < menum.keyCount(); ++i) {
if (menum.value(i) == prop.second.toInt()) {
text = QString(menum.key(i)) + " (0x" + QString::number(menum.value(i), 16).toUpper() + ")";
break;
}
}
style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text),
Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role);
}
} else {
switch (value.type()) {
case QVariant::Int:
text.setNum(value.toInt());
style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text),
Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role);
break;
case QVariant::UInt:
text.setNum(value.toUInt());
style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text),
Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role);
break;
case QVariant::LongLong:
text.setNum(value.toLongLong());
style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text),
Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role);
break;
case QVariant::ULongLong:
text.setNum(value.toULongLong());
style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text),
Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role);
break;
case QVariant::Double:
text.setNum(value.toDouble(), 'f', 3);
style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text),
Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role);
break;
case QVariant::Bool:
so = new QStyleOptionButton();
so->rect = option.rect;
so->state = option.state;
so->palette = option.palette;
so->fontMetrics = option.fontMetrics;
((QStyleOptionButton*)so)->state = (value.toBool() ? QStyle::State_On : QStyle::State_Off) | option.state;
style->drawControl(QStyle::CE_CheckBox, so, painter);
break;
case QVariant::Color:
rect = option.rect;//style->subElementRect(QStyle::QStyle::SE_FrameContents, so);
rect.setRect(rect.x() + 3, rect.y() + 3, rect.width() - 6, rect.height() - 6);
painter->fillRect(rect, ab);
painter->fillRect(rect, value.value<QColor>());
break;
case QVariant::Point:
text = pointString(value.toPoint());
style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text),
Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role);
break;
case QVariant::PointF:
text = pointString(value.toPointF());
style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text),
Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role);
break;
case QVariant::Rect:
text = rectString(value.toRect());
style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text),
Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role);
break;
case QVariant::RectF:
text = rectString(value.toRectF());
style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, text),
Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, text, role);
break;
case QVariant::String: default:
style->drawItemText(painter, style->itemTextRect(option.fontMetrics, option.rect, Qt::AlignLeft | Qt::AlignVCenter, true, value.toString()),
Qt::AlignLeft | Qt::AlignVCenter, option.palette, true, value.toString(), role);
break;
}
}
}
/*so = new QStyleOptionFrame();
so->rect = option.rect;
so->state = option.state;
so->palette = option.palette;
so->fontMetrics = option.fontMetrics;
((QStyleOptionFrame*)so)->state = (value.toBool() ? QStyle::State_On : QStyle::State_Off);
style->drawPrimitive(QStyle::PE_PanelLineEdit, so, painter);
style->drawPrimitive(QStyle::PE_FrameLineEdit, so, painter);
break;*/
if (so != 0) delete so;
if (soc != 0) delete soc;
}
void Delegate::changedFlag() {
QAbstractItemModel * model = const_cast<QAbstractItemModel * >(cmi.model());
model->setData(cmi, qobject_cast<QCheckBox * >(sender())->isChecked(), Qt::UserRole + 3);
QModelIndex p = cmi.parent(), mi;
int row = 0;
qulonglong val = 0;
QList<QModelIndex> chldr;
mi = p.child(row, 1);
while (mi.isValid()) {
chldr << mi;
model->setData(mi, !mi.data(Qt::UserRole + 4).toBool(), Qt::UserRole + 4);
mi = p.child(++row, 1);
}
bool cc = cmi.data(Qt::UserRole + 3).toBool();
qulonglong cv = cmi.data(Qt::UserRole).toULongLong();
//qDebug() << "*****";
if (cc && cv == 0) {
val = 0;
//qDebug() << "null" << cv;
} else {
if (!cc && cv != 0) {
//qDebug() << "uncheck" << cv;
for (int i = 0; i < chldr.size(); ++i) {
if (chldr[i] == cmi) continue;
//qDebug() << (chldr[i].data(Qt::UserRole).toULongLong() & cv);
if (chldr[i].data(Qt::UserRole).toULongLong() & cv)
model->setData(chldr[i], false, Qt::UserRole + 3);
}
}
for (int i = 0; i < chldr.size(); ++i) {
//qDebug() << chldr[i].data(Qt::UserRole + 3).toBool();
if (chldr[i].data(Qt::UserRole + 3).toBool())
val |= chldr[i].data(Qt::UserRole).toULongLong();
}
}
for (int i = 0; i < chldr.size(); ++i) {
if (chldr[i] == cmi) continue;
cv = chldr[i].data(Qt::UserRole).toULongLong();
model->setData(chldr[i], ((val & cv) == cv && cv != 0) || (val == 0 && cv == 0), Qt::UserRole + 3);
}
//qDebug() << val;
model->setData(p, val, Qt::UserRole);
model->setData(p.sibling(p.row(), 1), val, Qt::UserRole);
}
PropertyEditor::PropertyEditor(QWidget * parent): QTreeWidget(parent) {
object = 0;
active_ = false;
configTree();
connect(this, SIGNAL(itemClicked(QTreeWidgetItem * , int)), this, SLOT(itemClicked(QTreeWidgetItem * , int)));
connect(this, SIGNAL(itemChanged(QTreeWidgetItem * , int)), this, SLOT(itemChanged(QTreeWidgetItem * , int)));
}
PropertyEditor::~PropertyEditor() {
}
void PropertyEditor::changeEvent(QEvent * e) {
QTreeWidget::changeEvent(e);
if (e->type() == QEvent::LanguageChange) {
configTree();
return;
}
}
void PropertyEditor::configTree() {
setColumnCount(2);
setRootIsDecorated(false);
setColumnWidth(0, 170);
setColumnWidth(1, 10);
header()->setStretchLastSection(true);
QStringList lbls;
lbls << tr("Property") << tr("Value");
setHeaderLabels(lbls);
setAlternatingRowColors(true);
setItemDelegateForColumn(1, new Delegate());
}
void PropertyEditor::itemClicked(QTreeWidgetItem * item, int column) {
if (column == 0)
item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
else {
item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);
editItem(item, 1);
}
}
void PropertyEditor::itemChanged(QTreeWidgetItem * item, int column) {
if (!active_) return;
if (column != 1) return;
QVariant value = item->data(1, Qt::UserRole);
if (value.canConvert<PropertyValuePair>()) {
value = value.value<PropertyValuePair>().second;
}
object->setProperty(item->text(0).toLatin1(), value);
}
void PropertyEditor::rebuild() {
clear();
configTree();
if (object == 0) return;
active_ = false;
const QMetaObject * mo = object->metaObject();
QList<const QMetaObject * > mol;
while (mo != 0) {
mol.push_front(mo);
mo = mo->superClass();
}
int ps, pe;
QTreeWidgetItem * ti, * tli, * tfi;
QVariant value;
// QWidget * pw = 0;
int chue = 0;
QColor bc;
font_b = font();
font_b.setBold(true);
foreach (const QMetaObject * o, mol) {
ps = o->propertyOffset();
pe = o->propertyCount();// - ps;
//qDebug() << i->className() << ps << pe;
tli = new QTreeWidgetItem();
tli->setText(0, o->className());
tli->setFont(0, font_b);
setItemBackColor(tli, Qt::darkGray);
setItemForeColor(tli, Qt::white);
addTopLevelItem(tli);
setFirstItemColumnSpanned(tli, true);
tli->setExpanded(true);
for (int i = ps; i < pe; ++i) {
props << o->property(i);
value = o->property(i).read(object);
ti = new QTreeWidgetItem();
ti->setSizeHint(1, QSize(20, 20));
bc.setHsv(chue, 60, 245 + (i % 2) * 20 - 10);
setItemBackColor(ti, bc);
ti->setText(0, o->property(i).name());
if (props.back().isFlagType()) {
QMetaEnum menum = props.back().enumerator();
for (int j = 0; j < menum.keyCount(); ++j) {
tfi = new QTreeWidgetItem();
tfi->setText(0, menum.key(j));
tfi->setData(1, Qt::UserRole, menum.value(j));
tfi->setData(1, Qt::UserRole + 1, "__flag");
tfi->setData(1, Qt::UserRole + 2, value.toULongLong());
tfi->setData(1, Qt::UserRole + 3, (value.toULongLong() & menum.value(j)) > 0);
tfi->setSizeHint(1, QSize(20, 20));
bc.setHsv(chue, 60, 245 + ((i + j + 1) % 2) * 20 - 10);
setItemBackColor(tfi, bc);
ti->addChild(tfi);
}
ti->setData(0, Qt::UserRole, value);
ti->setData(1, Qt::UserRole, value);
ti->setData(1, Qt::UserRole + 2, "__flags");
ti->setData(0, Qt::UserRole + 1, QVariant::fromValue<QMetaProperty>(props.back()));
}
else if (props.back().isEnumType())
value.setValue<PropertyValuePair>(PropertyValuePair(props.back(), value));
//ti->setText(1, value.toString());
ti->setData(1, Qt::UserRole, value);
tli->addChild(ti);
//const_cast<QModelIndex & >(indexFromItem(ti, 1)).;
//if (pw != 0) setItemWidget(ti, 1, pw);
}
chue += 60;
chue %= 360;
}
active_ = true;
}
void PropertyEditor::refresh() {
}