blockview small code clean add PIValueTree to BLockItem, BlockItemPin and BlockBusItem add QAD::CursorOverrider::restore() fix QCodeEdit escape key while block selection
578 lines
16 KiB
C++
578 lines
16 KiB
C++
#include "blockview.h"
|
|
|
|
#include <QApplication>
|
|
#include <piqt.h>
|
|
|
|
|
|
#define BLOCKITEM_DEFAULT_PIN_MARGIN 20
|
|
|
|
BlockItem::BlockItem(QGraphicsItem * parent): QGraphicsObject(parent), PropertyStorage(), g_main(this), g_selection(this) {
|
|
setData(bvidType, bvitBlock);
|
|
setZValue(2.);
|
|
setAcceptHoverEvents(true);
|
|
setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable);
|
|
g_main.setData(bvidMoveParent, true);
|
|
g_selection.setData(bvidItemSelection, true);
|
|
g_selection.setAcceptedMouseButtons(Qt::MouseButtons());
|
|
g_selection.setZValue(10.);
|
|
g_selection.hide();
|
|
g_selection.setData(bvidVisualizeSelection, true);
|
|
col = Qt::lightGray;
|
|
_resize(QSizeF(100., 60.));
|
|
QPen p(QColor(128, 128, 255), lineThickness(), Qt::DotLine);
|
|
p.setCosmetic(true);
|
|
g_selection.setPen(p);
|
|
g_selection.setBrush(QColor(128, 128, 255, 32));
|
|
pins_margin = BLOCKITEM_DEFAULT_PIN_MARGIN;
|
|
anim_thick.setTargetObject(this);
|
|
anim_thick.setPropertyName("_thickness");
|
|
anim_thick.setEasingCurve(QEasingCurve::OutQuad);
|
|
anim_thick.setDuration(300);
|
|
anim_sel.setTargetObject(this);
|
|
anim_sel.setPropertyName("_selRect");
|
|
anim_sel.setEasingCurve(QEasingCurve::OutCubic);
|
|
anim_sel.setDuration(400);
|
|
t_sel.start();
|
|
}
|
|
|
|
|
|
BlockItem::~BlockItem() {
|
|
clearDecors();
|
|
}
|
|
|
|
|
|
void BlockItem::_resize(QSizeF s) {
|
|
g_main.setRect(QRectF(QPointF(), s));
|
|
g_main.setPos(QPointF(-g_main.rect().center().x(), -10.));
|
|
g_selection.setRect(enlargedRect(g_main.rect(), 0, 0, 8));
|
|
g_selection.setPos(g_main.pos());
|
|
QRadialGradient g(g_main.rect().width() / 2., 0, qMax<qreal>(g_main.rect().width() / 2., g_main.rect().height()));
|
|
g.setSpread(QGradient::PadSpread);
|
|
g.setCoordinateMode(QGradient::LogicalMode);
|
|
g.setColorAt(0., col.darker(120.));
|
|
g.setColorAt(1., col.lighter(120.));
|
|
g_main.setBrush(QBrush(g));
|
|
prepareGeometryChange();
|
|
arrangePins();
|
|
}
|
|
|
|
|
|
void BlockItem::_moveToTop(bool only_decors) {
|
|
qreal dy = -g_main.rect().center().y() + 10;
|
|
if (!only_decors) moveBy(0., dy);
|
|
for (QGraphicsItem * d: decors_)
|
|
d->moveBy(0., -dy);
|
|
}
|
|
|
|
|
|
BlockItemPin * BlockItem::addPin(BlockItemPin * pin, bool update_) {
|
|
pin->setParentItem(this);
|
|
if (!pins_[pin->alignment()].contains(pin)) pins_[pin->alignment()] << pin;
|
|
pin->parent_ = this;
|
|
if (update_) arrangePins();
|
|
return pin;
|
|
}
|
|
|
|
|
|
BlockItemPin * BlockItem::addPin(Qt::Alignment align, int bus_type, const QString & text, bool update_) {
|
|
BlockItemPin * pin = new BlockItemPin(align, bus_type, text, this);
|
|
pin->parent_ = this;
|
|
pins_[pin->alignment()] << pin;
|
|
if (update_) arrangePins();
|
|
return pin;
|
|
}
|
|
|
|
|
|
void BlockItem::removePin(BlockItemPin * pin) {
|
|
if (!pin) return;
|
|
QMutableMapIterator<Qt::Alignment, QVector<BlockItemPin *>> it(pins_);
|
|
while (it.hasNext()) {
|
|
it.next();
|
|
QVector<BlockItemPin *> & pv(it.value());
|
|
if (pv.contains(pin)) pv.remove(it.value().indexOf(pin));
|
|
}
|
|
delete pin;
|
|
arrangePins();
|
|
}
|
|
|
|
|
|
void BlockItem::addDecor(QGraphicsItem * item) {
|
|
if (!item) return;
|
|
if (decors_.contains(item)) return;
|
|
if (qgraphicsitem_cast<QGraphicsPixmapItem *>(item))
|
|
qgraphicsitem_cast<QGraphicsPixmapItem *>(item)->setTransformationMode(Qt::SmoothTransformation);
|
|
if (qgraphicsitem_cast<QGraphicsSimpleTextItem *>(item))
|
|
qgraphicsitem_cast<QGraphicsSimpleTextItem *>(item)->setData(bvidDecorText,
|
|
qgraphicsitem_cast<QGraphicsSimpleTextItem *>(item)->text());
|
|
if (qgraphicsitem_cast<AlignedTextItem *>(item))
|
|
qgraphicsitem_cast<AlignedTextItem *>(item)->setData(bvidDecorText, qgraphicsitem_cast<AlignedTextItem *>(item)->text());
|
|
item->setData(bvidMoveParent, true);
|
|
item->setData(bvidBlockDecor, true);
|
|
decors_ << item;
|
|
item->setParentItem(this);
|
|
}
|
|
|
|
|
|
void BlockItem::addDecor(QGraphicsItem & item) {
|
|
if (decors_.contains(&item)) return;
|
|
if (qgraphicsitem_cast<QGraphicsPixmapItem *>(&item))
|
|
qgraphicsitem_cast<QGraphicsPixmapItem *>(&item)->setTransformationMode(Qt::SmoothTransformation);
|
|
if (qgraphicsitem_cast<QGraphicsSimpleTextItem *>(&item))
|
|
qgraphicsitem_cast<QGraphicsSimpleTextItem *>(&item)->setData(bvidDecorText,
|
|
qgraphicsitem_cast<QGraphicsSimpleTextItem *>(&item)->text());
|
|
if (qgraphicsitem_cast<AlignedTextItem *>(&item))
|
|
qgraphicsitem_cast<AlignedTextItem *>(&item)->setData(bvidDecorText, qgraphicsitem_cast<AlignedTextItem *>(&item)->text());
|
|
item.setData(bvidMoveParent, true);
|
|
item.setData(bvidBlockDecor, true);
|
|
item.setParentItem(this);
|
|
}
|
|
|
|
|
|
void BlockItem::removeDecor(QGraphicsItem * item) {
|
|
if (scene() && item) scene()->sendEvent(item, new QGraphicsSceneEvent(QEvent::Close));
|
|
decors_.removeAll(item);
|
|
delete item;
|
|
}
|
|
|
|
|
|
QVector<BlockItemPin *> BlockItem::takePins() {
|
|
QVector<BlockItemPin *> ret = pins();
|
|
pins_.clear();
|
|
return ret;
|
|
}
|
|
|
|
|
|
void BlockItem::clearPins() {
|
|
QList<QVector<BlockItemPin *>> mp = pins_.values();
|
|
for (int i = 0; i < mp.size(); ++i)
|
|
qDeleteAll(mp[i]);
|
|
pins_.clear();
|
|
}
|
|
|
|
|
|
void BlockItem::clearDecors() {
|
|
bool pbs = false;
|
|
if (scene()) pbs = scene()->blockSignals(true);
|
|
if (scene()) {
|
|
for (QGraphicsItem * i: decors_)
|
|
scene()->sendEvent(i, new QGraphicsSceneEvent(QEvent::Close));
|
|
}
|
|
qDeleteAll(decors_);
|
|
decors_.clear();
|
|
if (scene()) {
|
|
scene()->blockSignals(pbs);
|
|
scene()->selectionChanged();
|
|
}
|
|
}
|
|
|
|
|
|
QVector<BlockItemPin *> BlockItem::pins() const {
|
|
QList<QVector<BlockItemPin *>> mp = pins_.values();
|
|
QVector<BlockItemPin *> ret;
|
|
for (int i = 0; i < mp.size(); ++i)
|
|
ret << mp[i];
|
|
return ret;
|
|
}
|
|
|
|
|
|
QVector<BlockItemPin *> BlockItem::pinsOnSide(Qt::Alignment al) const {
|
|
return pins_.value(al);
|
|
}
|
|
|
|
|
|
QByteArray BlockItem::saveModel() {
|
|
QVector<uint32_t> decor_vis_flags;
|
|
for (auto i: decors_)
|
|
decor_vis_flags << i->data(bvidDecorShowLogic).toUInt();
|
|
ChunkStream cs;
|
|
cs.add(1, pos())
|
|
.add(2, rotation())
|
|
.add(3, size())
|
|
.add(4, color())
|
|
.add(5, pins())
|
|
.add(6, decors_)
|
|
.add(7, pins_margin)
|
|
.add(8, decor_vis_flags)
|
|
.add(0xFF, _blockitem_current_version_);
|
|
return cs.data();
|
|
}
|
|
|
|
|
|
void BlockItem::loadModel(const QByteArray & data) {
|
|
// qDebug() << "load from" << data.size() << "bytes";
|
|
clearPins();
|
|
clearDecors();
|
|
col = Qt::lightGray;
|
|
_resize(QSizeF(100., 60.));
|
|
if (data.isEmpty()) return;
|
|
ChunkStream cs(data);
|
|
QVector<BlockItemPin *> tp;
|
|
QList<QGraphicsItem *> dl;
|
|
QVector<uint32_t> decor_vis_flags;
|
|
int version = -1;
|
|
while (!cs.atEnd()) {
|
|
switch (cs.read()) {
|
|
case 1: /*setPos(cs.getData<QPointF>());*/ break;
|
|
case 2: /*setRotation(cs.getData<qreal>());*/ break;
|
|
case 3: setSize(cs.getData<QSizeF>()); break;
|
|
case 4: setColor(cs.getData<QColor>()); break;
|
|
case 5:
|
|
cs.get(tp);
|
|
for (BlockItemPin * p: tp)
|
|
addPin(p);
|
|
break;
|
|
case 6:
|
|
cs.get(dl);
|
|
for (QGraphicsItem * d: dl)
|
|
addDecor(d);
|
|
break;
|
|
case 7: setPinsMargin(cs.getData<int>()); break;
|
|
case 8: cs.get(decor_vis_flags); break;
|
|
case 0xFF: cs.get(version); break;
|
|
}
|
|
}
|
|
for (int i = 0; i < qMin(decors_.size(), decor_vis_flags.size()); ++i)
|
|
decors_[i]->setData(bvidDecorShowLogic, decor_vis_flags[i]);
|
|
if (version <= 0) _moveToTop(true);
|
|
procDecorShowLogic();
|
|
}
|
|
|
|
|
|
QByteArray BlockItem::save() const {
|
|
ChunkStream cs;
|
|
QMap<QString, QList<BlockItem::Property>> pp;
|
|
for (BlockItemPin * p: pins()) {
|
|
// qDebug() << "save pin" << p->text() << "->" << p->properties().size();
|
|
pp[p->text()] = p->properties();
|
|
}
|
|
cs.add(1, pos()).add(2, rotation()).add(3, props).add(5, pp).add(6, size());
|
|
cs.add(10, data(2000)).add(11, data(2001)).add(12, prop_bindings).add(0xFF, _blockitem_current_version_);
|
|
cs.add(13, piqSerialize(value_tree));
|
|
return cs.data();
|
|
}
|
|
|
|
|
|
void BlockItem::load(const QByteArray & data) {
|
|
if (data.isEmpty()) return;
|
|
ChunkStream cs(data);
|
|
QMap<QString, QList<BlockItem::Property>> _p;
|
|
value_tree = {};
|
|
int version = -1;
|
|
while (!cs.atEnd()) {
|
|
switch (cs.read()) {
|
|
case 1: setPos(cs.getData<QPointF>()); break;
|
|
case 2: setRotation(cs.getData<qreal>()); break;
|
|
case 3: cs.get(props); break;
|
|
case 5:
|
|
cs.get(_p);
|
|
// qDebug() << "load pins" << _p.size();
|
|
for (BlockItemPin * p: pins()) {
|
|
// qDebug() << "load pin" << p->text() << "->" << _p.contains(p->text());
|
|
if (_p.contains(p->text())) p->properties() = _p[p->text()];
|
|
}
|
|
break;
|
|
case 6: setSize(cs.getData<QSizeF>()); break;
|
|
case 10: setData(2000, cs.getData<QVariant>()); break;
|
|
case 11: setData(2001, cs.getData<QVariant>()); break;
|
|
case 12: prop_bindings = cs.getData<QList<QPair<QString, QString>>>(); break;
|
|
case 13: value_tree = piqDeserialize<PIValueTree>(cs.getData<QByteArray>()); break;
|
|
case 0xFF: cs.get(version); break;
|
|
}
|
|
}
|
|
if (version <= 0) {
|
|
_moveToTop();
|
|
}
|
|
}
|
|
|
|
|
|
BlockItem * BlockItem::copy() const {
|
|
BlockItem * ret = new BlockItem();
|
|
ret->setPos(pos());
|
|
ret->setSize(size());
|
|
ret->setColor(color());
|
|
ret->setSelected(false);
|
|
ret->props = props;
|
|
ret->setPinsMargin(pinsMargin());
|
|
QVector<BlockItemPin *> mp = pins();
|
|
for (BlockItemPin * p: mp) {
|
|
BlockItemPin * np = new BlockItemPin();
|
|
np->setBusType(p->busType());
|
|
np->setAlignment(p->alignment());
|
|
np->setText(p->text());
|
|
np->setToolTip(p->toolTip());
|
|
np->properties() = p->properties();
|
|
ret->addPin(np);
|
|
}
|
|
for (auto i: decors_) {
|
|
ret->addDecor(qDeserialize<QGraphicsItem *>(qSerialize(i)));
|
|
}
|
|
ret->procDecorShowLogic();
|
|
return ret;
|
|
}
|
|
|
|
|
|
QList<BlockBusItem *> BlockItem::connectedBuses() const {
|
|
QList<BlockBusItem *> ret;
|
|
for (BlockItemPin * p: pins())
|
|
ret << p->connectedBuses();
|
|
return ret;
|
|
}
|
|
|
|
|
|
BlockItemPin * BlockItem::pinByText(const QString & t) const {
|
|
for (BlockItemPin * p: pins())
|
|
if (p->text() == t) return p;
|
|
return 0;
|
|
}
|
|
|
|
|
|
BlockItemPin * BlockItem::pinAtBus(BlockBusItem * bus) const {
|
|
if (bus == 0) return 0;
|
|
for (BlockItemPin * p: pins())
|
|
if (p->connectedBuses().contains(bus)) return p;
|
|
return 0;
|
|
}
|
|
|
|
void BlockItem::setColor(QColor c) {
|
|
col = c;
|
|
_resize(size());
|
|
}
|
|
|
|
|
|
QRectF BlockItem::sceneRect() const {
|
|
return g_main.mapRectToScene(g_main.boundingRect());
|
|
}
|
|
|
|
|
|
void BlockItem::setWidth(qreal w) {
|
|
setSize(QSizeF(w, size().height()));
|
|
}
|
|
|
|
|
|
void BlockItem::setHeight(qreal h) {
|
|
setSize(QSizeF(size().width(), h));
|
|
}
|
|
|
|
|
|
void BlockItem::setPinsMargin(int marg) {
|
|
if (marg > 1 && marg < 256) pins_margin = marg;
|
|
arrangePins();
|
|
}
|
|
|
|
|
|
QRectF BlockItem::boundingRect() const {
|
|
return g_main.mapRectToParent(g_main.boundingRect());
|
|
}
|
|
|
|
|
|
void BlockItem::hoverEnterEvent(QGraphicsSceneHoverEvent * e) {
|
|
setHovered(true);
|
|
emit blockHoverEnter(this);
|
|
}
|
|
|
|
|
|
void BlockItem::hoverLeaveEvent(QGraphicsSceneHoverEvent * e) {
|
|
setHovered(false);
|
|
emit blockHoverLeave(this);
|
|
}
|
|
|
|
|
|
#define _POS(m) (i - ((cp.size() - 1) / 2)) * m
|
|
|
|
void BlockItem::arrangePins() {
|
|
QVector<BlockItemPin *> pl = pins();
|
|
pins_.clear();
|
|
for (BlockItemPin * p: pl)
|
|
pins_[p->alignment()] << p;
|
|
QVector<BlockItemPin *> cp = pins_.value(Qt::AlignBottom);
|
|
for (int i = 0; i < cp.size(); ++i)
|
|
cp[i]->setPos(_POS(pins_margin), bottom());
|
|
cp = pins_.value(Qt::AlignTop);
|
|
for (int i = 0; i < cp.size(); ++i)
|
|
cp[i]->setPos(_POS(pins_margin), top());
|
|
cp = pins_.value(Qt::AlignLeft);
|
|
for (int i = 0; i < cp.size(); ++i)
|
|
cp[i]->setPos(left(), i * pins_margin);
|
|
cp = pins_.value(Qt::AlignRight);
|
|
for (int i = 0; i < cp.size(); ++i)
|
|
cp[i]->setPos(right(), i * pins_margin);
|
|
}
|
|
|
|
#undef _POS
|
|
|
|
|
|
void BlockItem::removeBindings(const QString & bind_name) {
|
|
for (int i = 0; i < prop_bindings.size(); ++i) {
|
|
if (prop_bindings[i].second == bind_name) {
|
|
prop_bindings.removeAt(i);
|
|
i--;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void BlockItem::removeBindingByProperty(const QString & prop_name) {
|
|
for (int i = 0; i < prop_bindings.size(); ++i) {
|
|
if (prop_bindings[i].first == prop_name) {
|
|
prop_bindings.removeAt(i);
|
|
i--;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void BlockItem::addBinding(const QString & prop_name, const QString & bind_name) {
|
|
QPair<QString, QString> pb(prop_name, bind_name);
|
|
prop_bindings.removeAll(pb);
|
|
prop_bindings << pb;
|
|
}
|
|
|
|
|
|
void BlockItem::applyBinding(const QString & bind_name, const QVariant & bind_value) {
|
|
for (int i = 0; i < prop_bindings.size(); ++i) {
|
|
if (prop_bindings[i].second == bind_name) {
|
|
setPropertyValue(prop_bindings[i].first, bind_value);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void BlockItem::applyBindings(const PropertyStorage & bindings) {
|
|
for (int i = 0; i < prop_bindings.size(); ++i) {
|
|
if (bindings.isPropertyExists(prop_bindings[i].second)) {
|
|
setPropertyValue(prop_bindings[i].first, bindings.propertyValueByName(prop_bindings[i].second));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void BlockItem::setBindings(const QList<QPair<QString, QString>> & bindings) {
|
|
prop_bindings = bindings;
|
|
}
|
|
|
|
|
|
const QList<QPair<QString, QString>> & BlockItem::getBindings() const {
|
|
return prop_bindings;
|
|
}
|
|
|
|
|
|
QString BlockItem::getBindName(const QString & prop_name) const {
|
|
for (int i = 0; i < prop_bindings.size(); ++i) {
|
|
if (prop_bindings[i].first == prop_name) {
|
|
return prop_bindings[i].second;
|
|
}
|
|
}
|
|
return QString();
|
|
}
|
|
|
|
|
|
QStringList BlockItem::getBindNames() const {
|
|
QStringList ret;
|
|
for (int i = 0; i < prop_bindings.size(); ++i)
|
|
if (!ret.contains(prop_bindings[i].second)) ret << prop_bindings[i].second;
|
|
return ret;
|
|
}
|
|
|
|
QStringList BlockItem::getBindProps() const {
|
|
QStringList ret;
|
|
for (int i = 0; i < prop_bindings.size(); ++i)
|
|
ret << prop_bindings[i].first;
|
|
return ret;
|
|
}
|
|
|
|
|
|
QVariant BlockItem::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant & value) {
|
|
if (change == QGraphicsItem::ItemSelectedChange) {
|
|
// qDebug() << "select" << value.toBool();
|
|
if (value.toBool() && !isSelected() && ((BlockView *)scene()->views().back())->isBlockAnimationEnabled() && t_sel.elapsed() > 50) {
|
|
g_selection.setRect(enlargedRect(g_main.rect(), 0, 0, 16));
|
|
anim_sel.setStartValue(selectionRect());
|
|
anim_sel.setEndValue(enlargedRect(g_main.rect(), 0, 0, 8));
|
|
anim_sel.start();
|
|
}
|
|
t_sel.restart();
|
|
g_selection.setVisible(value.toBool());
|
|
}
|
|
auto ret = QGraphicsItem::itemChange(change, value);
|
|
if (change == QGraphicsItem::ItemSelectedChange) procDecorShowLogic();
|
|
return ret;
|
|
}
|
|
|
|
|
|
void BlockItem::setHovered(bool yes) {
|
|
is_hovered = yes;
|
|
double thick = yes ? 2.5 : 1.;
|
|
if (((BlockView *)scene()->views().back())->isBlockAnimationEnabled()) {
|
|
anim_thick.stop();
|
|
anim_thick.setStartValue(thickness());
|
|
anim_thick.setEndValue(thick);
|
|
anim_thick.start();
|
|
} else
|
|
setThickness(thick);
|
|
procDecorShowLogic();
|
|
}
|
|
|
|
|
|
void BlockItem::procDecorShowLogic() {
|
|
if (is_blockeditor) return;
|
|
bool is_selected = g_selection.isVisible();
|
|
// qDebug() << "procDecorShowLogic" << is_selected << is_hovered;
|
|
for (auto i: decors_) {
|
|
BlockviewDecorShowLogics slf(i->data(bvidDecorShowLogic).toUInt());
|
|
if (slf == bvdslAlways) continue;
|
|
bool vis = false;
|
|
if (isEnabled() && slf.testFlag(bvdslEnabled)) vis = true;
|
|
if (is_hovered && slf.testFlag(bvdslHovered)) vis = true;
|
|
if (is_selected && slf.testFlag(bvdslSelected)) vis = true;
|
|
if (slf.testFlag(bvdslInverse)) vis = !vis;
|
|
i->setVisible(vis);
|
|
}
|
|
}
|
|
|
|
|
|
double BlockItem::thickness() const {
|
|
return g_main.pen().widthF();
|
|
}
|
|
|
|
|
|
void BlockItem::setThickness(double w) {
|
|
QPen p = g_main.pen();
|
|
p.setWidthF(w);
|
|
g_main.setPen(p);
|
|
}
|
|
|
|
|
|
QRectF BlockItem::selectionRect() const {
|
|
return g_selection.rect();
|
|
}
|
|
|
|
|
|
void BlockItem::setSelectionRect(const QRectF & r) {
|
|
g_selection.setRect(r);
|
|
}
|
|
|
|
|
|
QDataStream & operator<<(QDataStream & s, const BlockItemPin * p) {
|
|
ChunkStream cs;
|
|
cs.add(1, int(p->alignment())).add(2, p->busType()).add(3, p->text()).add(4, p->toolTip());
|
|
cs.add(5, piqSerialize(p->value_tree));
|
|
s << cs.data();
|
|
return s;
|
|
}
|
|
|
|
|
|
QDataStream & operator>>(QDataStream & s, BlockItemPin *& p) {
|
|
ChunkStream cs(s);
|
|
p = new BlockItemPin();
|
|
while (!cs.atEnd()) {
|
|
switch (cs.read()) {
|
|
case 1: p->setAlignment((Qt::Alignment)cs.getData<int>()); break;
|
|
case 2: p->setBusType(cs.getData<int>()); break;
|
|
case 3: p->setText(cs.getData<QString>()); break;
|
|
case 4: p->setToolTip(cs.getData<QString>()); break;
|
|
case 5: p->value_tree = piqDeserialize<PIValueTree>(cs.getData<QByteArray>()); break;
|
|
}
|
|
}
|
|
return s;
|
|
}
|