implemented arrays and reimplemented expanding

This commit is contained in:
itsmattkc
2021-12-01 12:14:26 -08:00
parent 239a019690
commit 2473bccec3
4 changed files with 266 additions and 104 deletions
+74 -10
View File
@@ -49,7 +49,6 @@ NodeView::NodeView(QWidget *parent) :
create_edge_(nullptr),
create_edge_output_item_(nullptr),
create_edge_input_item_(nullptr),
create_edge_expand_item_(nullptr),
paste_command_(nullptr),
scale_(1.0)
{
@@ -500,11 +499,11 @@ void NodeView::mouseReleaseEvent(QMouseEvent *event)
// Clear highlight if we set one
create_edge_input_item_->SetHighlighted(false);
// Collapse if we expanded it
if (create_edge_expand_item_) {
create_edge_expand_item_->SetExpanded(false);
create_edge_expand_item_->setZValue(0);
// Collapse any items we expanded
for (auto it=create_edge_expanded_items_.crbegin(); it!=create_edge_expanded_items_.crend(); it++) {
CollapseItem(*it);
}
create_edge_expanded_items_.clear();
NodeInput &creating_input = create_edge_input_;
if (creating_input.IsValid()) {
@@ -526,6 +525,13 @@ void NodeView::mouseReleaseEvent(QMouseEvent *event)
}
command->add_child(new NodeEdgeAddCommand(creating_output, creating_input));
// If the output is not in the input's context, add it now. We check the item rather than
// the node itself, because sometimes a node may not be in the context but another node
// representing it will be (e.g. groups)
if (!scene_.context_map().value(create_edge_input_item_->GetContext())->GetItemFromMap(creating_output)) {
command->add_child(new NodeSetPositionCommand(creating_output, create_edge_input_item_->GetContext(), scene_.context_map().value(create_edge_input_item_->GetContext())->MapScenePosToNodePosInContext(create_edge_output_item_->scenePos())));
}
}
creating_input.Reset();
@@ -624,6 +630,18 @@ void NodeView::mouseReleaseEvent(QMouseEvent *event)
super::mouseReleaseEvent(event);
}
void NodeView::mouseDoubleClickEvent(QMouseEvent *event)
{
super::mouseDoubleClickEvent(event);
if (!(event->modifiers() & Qt::ControlModifier)) {
NodeViewItem *item_at_cursor = dynamic_cast<NodeViewItem*>(itemAt(event->pos()));
if (item_at_cursor) {
item_at_cursor->ToggleExpanded();
}
}
}
void NodeView::resizeEvent(QResizeEvent *event)
{
super::resizeEvent(event);
@@ -1022,6 +1040,13 @@ void NodeView::ZoomFromKeyboard(double multiplier)
ZoomIntoCursorPosition(nullptr, multiplier, cursor_pos);
}
void NodeView::ClearCreateEdgeInputIfNecessary()
{
if (create_edge_from_output_ && create_edge_input_.IsValid()) {
create_edge_input_.Reset();
}
}
QPointF NodeView::GetEstimatedPositionForContext(NodeViewItem *item, Node *context) const
{
return item->GetNodePosition() - context_offsets_.value(context);
@@ -1051,6 +1076,37 @@ void NodeView::PositionNewEdge(const QPoint &pos)
item_at_cursor = nullptr;
}
// Collapse any items that the cursor is no longer inside
int i=create_edge_expanded_items_.size() - 1;
for ( ; i>=0; i--) {
NodeViewItem* nvi = create_edge_expanded_items_.at(i);
QPointF local_pt = nvi->mapFromScene(scene_pt);
if (nvi->contains(local_pt) || (!nvi->IsOutputItem() && nvi->parentItem()->contains(nvi->parentItem()->mapFromScene(scene_pt)) && local_pt.y() > nvi->rect().bottom())) {
break;
} else {
// Collapsing an item will destroy its children, so if the cursor item happens to be a child
// of the item we're about to collapse, set it to null
if (item_at_cursor && item_at_cursor->parentItem() == nvi) {
item_at_cursor = nullptr;
}
if (opposing_item && opposing_item->parentItem() == nvi) {
opposing_item = nullptr;
ClearCreateEdgeInputIfNecessary();
}
CollapseItem(nvi);
}
}
create_edge_expanded_items_.resize(i + 1);
// Expand item if possible
if (item_at_cursor && item_at_cursor->CanBeExpanded() && !item_at_cursor->IsExpanded()) {
ExpandItem(item_at_cursor);
create_edge_expanded_items_.append(item_at_cursor);
}
// Filter out connecting to a node that connects to us or an item of the same type
if (item_at_cursor
&& ((create_edge_from_output_ && item_at_cursor->GetNode()->OutputsTo(source_item->GetNode(), true))
@@ -1068,11 +1124,7 @@ void NodeView::PositionNewEdge(const QPoint &pos)
}
// Clear cached input
if (create_edge_from_output_) {
if (create_edge_input_.IsValid()) {
create_edge_input_.Reset();
}
}
ClearCreateEdgeInputIfNecessary();
// If this is an input and we're
opposing_item = item_at_cursor;
@@ -1293,4 +1345,16 @@ bool NodeView::IsItemAttachedToCursor(NodeViewItem *item) const
return false;
}
void NodeView::ExpandItem(NodeViewItem *item)
{
item->SetExpanded(true);
item->setZValue(100);
}
void NodeView::CollapseItem(NodeViewItem *item)
{
item->SetExpanded(false);
item->setZValue(0);
}
}
+9 -1
View File
@@ -107,6 +107,7 @@ protected:
virtual void mousePressEvent(QMouseEvent *event) override;
virtual void mouseMoveEvent(QMouseEvent *event) override;
virtual void mouseReleaseEvent(QMouseEvent* event) override;
virtual void mouseDoubleClickEvent(QMouseEvent* event) override;
virtual void resizeEvent(QResizeEvent *event) override;
@@ -135,6 +136,8 @@ private:
void ZoomFromKeyboard(double multiplier);
void ClearCreateEdgeInputIfNecessary();
QPointF GetEstimatedPositionForContext(NodeViewItem *item, Node *context) const;
Menu *CreateAddMenu(Menu *parent);
@@ -149,6 +152,10 @@ private:
bool IsItemAttachedToCursor(NodeViewItem *item) const;
void ExpandItem(NodeViewItem *item);
void CollapseItem(NodeViewItem *item);
NodeViewMiniMap *minimap_;
struct AttachedItem {
@@ -164,11 +171,12 @@ private:
NodeViewEdge* create_edge_;
NodeViewItem* create_edge_output_item_;
NodeViewItem* create_edge_input_item_;
NodeViewItem *create_edge_expand_item_;
NodeInput create_edge_input_;
bool create_edge_already_exists_;
bool create_edge_from_output_;
QVector<NodeViewItem*> create_edge_expanded_items_;
NodeViewScene scene_;
MultiUndoCommand* paste_command_;
+171 -91
View File
@@ -48,6 +48,7 @@ NodeViewItem::NodeViewItem(Node *node, const QString &input, int element, Node *
expanded_(false),
highlighted_(false),
flow_dir_(NodeViewCommon::kInvalidDirection),
arrow_click_(false),
label_as_output_(false)
{
//
@@ -235,9 +236,7 @@ void NodeViewItem::RemoveEdge(NodeViewEdge *edge)
void NodeViewItem::SetExpanded(bool e, bool hide_titlebar)
{
if ((IsOutputItem() && !has_connectable_inputs_)
|| (!IsOutputItem() && !(node_->GetInputFlags(input_) & kInputFlagArray))
|| (expanded_ == e)) {
if (!CanBeExpanded() || (expanded_ == e)) {
return;
}
@@ -253,13 +252,14 @@ void NodeViewItem::SetExpanded(bool e, bool hide_titlebar)
}
if (expanded_) {
node_->Retranslate();
if (IsOutputItem()) {
// Create items for each input of the node
int i = 1;
foreach (const QString &input, node_->inputs()) {
if (IsInputValid(input)) {
NodeViewItem *item = new NodeViewItem(node_, input, -1, context_, this);
item->setPos(QPointF(0, i * item->rect().height()));
children_.append(item);
i++;
}
@@ -271,15 +271,12 @@ void NodeViewItem::SetExpanded(bool e, bool hide_titlebar)
(*it)->set_to_item(GetItemForInput((*it)->input()));
}
}
SetRectSize(i);
} else {
// Create items for each element of the input array
int arr_sz = node_->InputArraySize(input_);
children_.resize(arr_sz);
for (int i=0; i<arr_sz; i++) {
NodeViewItem *item = new NodeViewItem(node_, input_, i, context_, this);
item->setPos(pos() + QPointF(0, (i+1) * item->rect().height()));
children_[i] = item;
}
@@ -299,10 +296,10 @@ void NodeViewItem::SetExpanded(bool e, bool hide_titlebar)
delete child;
}
children_.clear();
SetRectSize(1);
}
UpdateChildrenPositions();
if (flow_dir_ == NodeViewCommon::kTopToBottom) {
UpdateOutputConnectorPosition();
}
@@ -325,47 +322,81 @@ void NodeViewItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *opti
// has been slightly modified
QPalette app_pal = Core::instance()->main_window()->palette();
// Draw the titlebar
if (IsOutputItem()) {
QRectF single_unit_rect = rect();
single_unit_rect.setHeight(DefaultItemHeight());
// We only draw a single unit's worth
QRectF single_unit_rect = rect();
single_unit_rect.setHeight(DefaultItemHeight());
// Output item drawing code
if (IsOutputItem()) {
// Set output item colors
painter->setPen(Qt::black);
painter->setBrush(node_->brush(single_unit_rect.top(), single_unit_rect.bottom()));
} else {
// Set input item colors
painter->setPen(Qt::NoPen);
painter->setBrush(element_ == -1 ? app_pal.color(QPalette::Window) : app_pal.color(QPalette::Base));
}
painter->drawRect(single_unit_rect);
painter->drawRect(single_unit_rect);
painter->setPen(app_pal.color(QPalette::Text));
// Draw highlight if applicable
if (highlighted_) {
QColor highlight_col = app_pal.color(QPalette::Text);
highlight_col.setAlpha(64);
painter->setBrush(highlight_col);
painter->drawRect(rect());
}
QString node_label, node_shortname;
// Determine what text to draw and whether to draw an arrow
QString node_label, node_name;
if (IsOutputItem()) {
if (label_as_output_) {
node_shortname = QCoreApplication::translate("NodeViewItem", "Output");
node_name = QCoreApplication::translate("NodeViewItem", "Output");
} else {
node_label = node_->GetLabel();
node_shortname = node_->ShortName();
node_name = node_->ShortName();
}
int icon_size = painter->fontMetrics().height()/2;
if (node_label.isEmpty()) {
// Draw shortname only
DrawNodeTitle(painter, node_shortname, single_unit_rect, Qt::AlignVCenter, icon_size, has_connectable_inputs_);
} else {
if (element_ == -1) {
node_name = node_->GetInputName(input_);
} else {
int text_pad = DefaultTextPadding()/2;
QRectF safe_label_bounds = single_unit_rect.adjusted(text_pad, text_pad, -text_pad, -text_pad);
QFont f;
qreal font_sz = f.pointSizeF();
f.setPointSizeF(font_sz * 0.8);
painter->setFont(f);
DrawNodeTitle(painter, node_label, safe_label_bounds, Qt::AlignTop, icon_size, has_connectable_inputs_);
f.setPointSizeF(font_sz * 0.6);
painter->setFont(f);
DrawNodeTitle(painter, node_shortname, safe_label_bounds, Qt::AlignBottom, icon_size, false);
node_name = QString::number(element_);
}
}
// Draw final border
// Draw arrow if necessary
int arrow_size = CanBeExpanded() ? DrawExpandArrow(painter) : 0;
if (IsOutputItem()) {
// Determine the text color (automatically calculate from node background color)
painter->setPen(ColorCoding::GetUISelectorColor(node_->color()));
} else {
// Just use text item
painter->setPen(app_pal.text().color());
}
if (node_label.isEmpty()) {
// Draw name only
DrawNodeTitle(painter, node_name, single_unit_rect, Qt::AlignVCenter, arrow_size);
} else {
int text_pad = DefaultTextPadding()/2;
QRectF safe_label_bounds = single_unit_rect.adjusted(text_pad, text_pad, -text_pad, -text_pad);
QFont f;
qreal font_sz = f.pointSizeF();
// Draw label as larger/upper text
f.setPointSizeF(font_sz * 0.8);
painter->setFont(f);
DrawNodeTitle(painter, node_label, safe_label_bounds, Qt::AlignTop, arrow_size);
// Draw node name as smaller/lower text
f.setPointSizeF(font_sz * 0.6);
painter->setFont(f);
DrawNodeTitle(painter, node_name, safe_label_bounds, Qt::AlignBottom, arrow_size);
}
// Draw final border (output only)
if (IsOutputItem()) {
QPen border_pen;
border_pen.setWidth(node_border_width_);
@@ -379,28 +410,17 @@ void NodeViewItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *opti
painter->setBrush(Qt::NoBrush);
painter->drawRect(rect());
} else {
// Input item drawing code
painter->setPen(Qt::NoPen);
painter->setBrush(app_pal.color(QPalette::Window));
painter->drawRect(rect());
if (highlighted_) {
QColor highlight_col = app_pal.color(QPalette::Text);
highlight_col.setAlpha(64);
painter->setBrush(highlight_col);
painter->drawRect(rect());
}
painter->setPen(app_pal.color(QPalette::Text));
painter->drawText(rect(), Qt::AlignCenter, node_->GetInputName(input_));
}
}
void NodeViewItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
if (last_arrow_rect_.contains(event->pos().toPoint())) {
arrow_click_ = true;
ToggleExpanded();
return;
}
event->setModifiers(FlipControlAndShiftModifiers(event->modifiers()));
QGraphicsRectItem::mousePressEvent(event);
@@ -408,6 +428,10 @@ void NodeViewItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
void NodeViewItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if (arrow_click_) {
return;
}
event->setModifiers(FlipControlAndShiftModifiers(event->modifiers()));
QGraphicsRectItem::mouseMoveEvent(event);
@@ -415,20 +439,16 @@ void NodeViewItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
void NodeViewItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
if (arrow_click_) {
arrow_click_ = false;
return;
}
event->setModifiers(FlipControlAndShiftModifiers(event->modifiers()));
QGraphicsRectItem::mouseReleaseEvent(event);
}
void NodeViewItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
QGraphicsRectItem::mouseDoubleClickEvent(event);
if (!(event->modifiers() & Qt::ControlModifier)) {
SetExpanded(!IsExpanded());
}
}
QVariant NodeViewItem::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value)
{
if (change == ItemPositionHasChanged && node_) {
@@ -456,29 +476,22 @@ void NodeViewItem::ReadjustAllEdges()
void NodeViewItem::UpdateContextRect()
{
if (NodeViewContext *ctx = dynamic_cast<NodeViewContext*>(parentItem())) {
ctx->UpdateRect();
QGraphicsItem *item = parentItem();
while (item) {
if (NodeViewContext *ctx = dynamic_cast<NodeViewContext*>(item)) {
ctx->UpdateRect();
break;
}
item = item->parentItem();
}
}
void NodeViewItem::DrawNodeTitle(QPainter* painter, QString text, const QRectF& rect, Qt::Alignment vertical_align, int icon_size, bool draw_arrow)
void NodeViewItem::DrawNodeTitle(QPainter* painter, QString text, const QRectF& rect, Qt::Alignment vertical_align, int icon_full_size)
{
QFontMetrics fm = painter->fontMetrics();
painter->setRenderHint(QPainter::SmoothPixmapTransform);
// Draw right or down arrow based on expanded state
int icon_padding = DefaultItemHeight() / 2 - icon_size / 2;
int icon_full_size = icon_size + icon_padding * 2;
if (draw_arrow) {
const QIcon& expand_icon = IsExpanded() ? icon::TriDown : icon::TriRight;
int icon_size_scaled = icon_size * painter->transform().m11();
painter->drawPixmap(QRect(this->rect().x() + icon_padding,
this->rect().y() + icon_padding,
icon_size,
icon_size), expand_icon.pixmap(QSize(icon_size_scaled, icon_size_scaled)));
}
// Calculate how much space we have for text
int item_width = this->rect().width();
int max_text_width = item_width - DefaultTextPadding() * 2 - icon_full_size;
@@ -497,9 +510,6 @@ void NodeViewItem::DrawNodeTitle(QPainter* painter, QString text, const QRectF&
text = concatenated;
}
// Determine the text color (automatically calculate from node background color)
painter->setPen(ColorCoding::GetUISelectorColor(node_->color()));
// Determine X position (favors horizontal centering unless it'll overrun the arrow)
QRectF text_rect = rect;
Qt::Alignment text_align = Qt::AlignHCenter | vertical_align;
@@ -515,6 +525,28 @@ void NodeViewItem::DrawNodeTitle(QPainter* painter, QString text, const QRectF&
text);
}
int NodeViewItem::DrawExpandArrow(QPainter *painter)
{
// Draw right or down arrow based on expanded state
int icon_size = painter->fontMetrics().height()/2;
int icon_padding = DefaultItemHeight() / 2 - icon_size / 2;
int icon_full_size = icon_size + icon_padding * 2;
painter->setRenderHint(QPainter::SmoothPixmapTransform);
const QIcon& expand_icon = IsExpanded() ? icon::TriDown : icon::TriRight;
int icon_size_scaled = icon_size * painter->transform().m11();
last_arrow_rect_ = QRect(this->rect().x() + icon_padding,
this->rect().y() + icon_padding,
icon_size,
icon_size);
painter->drawPixmap(last_arrow_rect_, expand_icon.pixmap(QSize(icon_size_scaled, icon_size_scaled)));
return icon_full_size;
}
void NodeViewItem::SetLabelAsOutput(bool e)
{
label_as_output_ = e;
@@ -642,6 +674,44 @@ void NodeViewItem::SetRectSize(int height_units)
setRect(QRectF(-widget_width/2, -widget_height/2, widget_width, widget_height * height_units));
}
bool NodeViewItem::CanBeExpanded() const
{
if (IsOutputItem()) {
return has_connectable_inputs_;
} else {
return node_->GetInputFlags(input_) & kInputFlagArray && element_ == -1 && !node_->IsInputConnected(input_);
}
}
void NodeViewItem::UpdateChildrenPositions()
{
int y = 1;
int h = DefaultItemHeight();
foreach (NodeViewItem *c, children_) {
c->setPos(QPointF(0, y * h));
y += c->GetLogicalHeightWithChildren();
}
SetRectSize(y);
if (NodeViewItem *p = dynamic_cast<NodeViewItem*>(parentItem())) {
p->UpdateChildrenPositions();
}
}
int NodeViewItem::GetLogicalHeightWithChildren() const
{
int h = 1;
foreach (NodeViewItem *c, children_) {
h += c->GetLogicalHeightWithChildren();
}
return h;
}
void NodeViewItem::UpdateFlowDirectionOfInputItem(NodeViewItem *child)
{
if (!child->IsOutputItem()) {
@@ -659,16 +729,26 @@ void NodeViewItem::UpdateFlowDirectionOfInputItem(NodeViewItem *child)
void NodeViewItem::RepopulateInputs()
{
has_connectable_inputs_ = false;
if (IsOutputItem()) {
has_connectable_inputs_ = false;
foreach (const QString& input, node_->inputs()) {
if (IsInputValid(input)) {
has_connectable_inputs_ = true;
break;
foreach (const QString& input, node_->inputs()) {
if (IsInputValid(input)) {
has_connectable_inputs_ = true;
break;
}
}
input_connector_->setVisible(has_connectable_inputs_);
if (IsExpanded()) {
// Create or remove inputs when necessary
}
} else {
if (IsExpanded() && element_ == -1) {
// Create or remove array elements when necessary
}
}
input_connector_->setVisible(has_connectable_inputs_);
}
void NodeViewItem::NodeAppearanceChanged()
@@ -698,13 +778,13 @@ NodeViewItem *NodeViewItem::GetItemForInput(NodeInput input)
// Look for the input in our children
foreach (NodeViewItem *i, children_) {
if (i->input_ == input.input()) {
return i;
return i->GetItemForInput(input);
}
}
} else {
// Look for element in our children
if (input.element() >= 0 && input.element() < children_.size()) {
return children_.at(input.element());
return children_.at(input.element())->GetItemForInput(input);
}
}
}
+12 -2
View File
@@ -143,20 +143,23 @@ public:
void UpdateFlowDirectionOfInputItem(NodeViewItem *child);
bool CanBeExpanded() const;
protected:
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
virtual void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override;
virtual QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) override;
private:
void UpdateContextRect();
void DrawNodeTitle(QPainter *painter, QString text, const QRectF &rect, Qt::Alignment vertical_align, int icon_size, bool draw_arrow);
void DrawNodeTitle(QPainter *painter, QString text, const QRectF &rect, Qt::Alignment vertical_align, int icon_full_size);
int DrawExpandArrow(QPainter *painter);
/**
* @brief Internal update function when logical position changes
@@ -170,6 +173,10 @@ private:
void SetRectSize(int height_units = 1);
void UpdateChildrenPositions();
int GetLogicalHeightWithChildren() const;
/**
* @brief Reference to attached Node
*/
@@ -200,6 +207,9 @@ private:
QPointF cached_node_pos_;
QRect last_arrow_rect_;
bool arrow_click_;
NodeViewItemConnector *input_connector_;
NodeViewItemConnector *output_connector_;