拖动构建行为树

This commit is contained in:
codeboss 2025-07-03 01:11:48 +08:00
parent 3fcf823369
commit 8a27ca60e5
4 changed files with 148 additions and 19 deletions

View File

@ -187,7 +187,7 @@ int LogicalNode::depth() const
return this->parent().lock()->depth() + 1; return this->parent().lock()->depth() + 1;
} }
std::shared_ptr<LogicalNode> LogicalNode::bindMap() const std::shared_ptr<BehaviorMapNode> LogicalNode::bindMap() const
{ {
auto node_temp = this->parent().lock(); auto node_temp = this->parent().lock();
if (!node_temp) return nullptr; if (!node_temp) return nullptr;
@ -195,7 +195,8 @@ std::shared_ptr<LogicalNode> LogicalNode::bindMap() const
while (node_temp->nodeKind() != NodeKind::MAPNODE) { while (node_temp->nodeKind() != NodeKind::MAPNODE) {
node_temp = node_temp->parent().lock(); node_temp = node_temp->parent().lock();
} }
return node_temp;
return std::dynamic_pointer_cast<BehaviorMapNode>(node_temp);
} }
void LogicalNode::setID(int unique_id) void LogicalNode::setID(int unique_id)
@ -222,6 +223,11 @@ BehaviorMapNode::BehaviorMapNode(std::shared_ptr<MapKernal> core)
:LogicalNode(NodeKind::MAPNODE), _bind_kernal(core) { :LogicalNode(NodeKind::MAPNODE), _bind_kernal(core) {
} }
std::shared_ptr<MapKernal> BehaviorMapNode::getKernal() const
{
return _bind_kernal;
}
void BehaviorMapNode::setVariable(const QString& key, IO_TYPE t, std::shared_ptr<TopicData> ins) void BehaviorMapNode::setVariable(const QString& key, IO_TYPE t, std::shared_ptr<TopicData> ins)
{ {
if (_variables.contains(key)) if (_variables.contains(key))

View File

@ -324,6 +324,7 @@ enum class NodeKind {
COMPARENODE, // 比较节点 COMPARENODE, // 比较节点
ACTIONNODE, // 动作节点 ACTIONNODE, // 动作节点
}; };
class BehaviorMapNode;
/// <summary> /// <summary>
/// 所有逻辑节点的基类 /// 所有逻辑节点的基类
/// </summary> /// </summary>
@ -360,7 +361,7 @@ public:
/// 获取包含树图节点 /// 获取包含树图节点
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
std::shared_ptr<LogicalNode> bindMap() const; std::shared_ptr<BehaviorMapNode> bindMap() const;
/// <summary> /// <summary>
/// 回退节点 /// 回退节点
@ -475,6 +476,7 @@ private:
public: public:
BehaviorMapNode(std::shared_ptr<MapKernal> kernal); BehaviorMapNode(std::shared_ptr<MapKernal> kernal);
std::shared_ptr<MapKernal> getKernal() const;
/// <summary> /// <summary>
/// 设置变量如果类型错误会抛出UniException异常 /// 设置变量如果类型错误会抛出UniException异常

View File

@ -1,6 +1,6 @@
#include "BehaviorEditor.h" #include "BehaviorEditor.h"
NodePresent::NodePresent(QWidget* pwidget, double& width_bind, std::shared_ptr<LogicalNode> bind) NodePresent::NodePresent(BehaviorsPresent* pwidget, double& width_bind, std::shared_ptr<LogicalNode> bind)
: _widget_p(pwidget), _node_bind(bind), _column_width(width_bind) { : _widget_p(pwidget), _node_bind(bind), _column_width(width_bind) {
this->setAcceptDrops(true); this->setAcceptDrops(true);
} }
@ -11,16 +11,63 @@ QRectF NodePresent::contentMeasure() const
return metrics.boundingRect(this->_node_bind->rtName()); return metrics.boundingRect(this->_node_bind->rtName());
} }
AcceptType NodePresent::testAccept(const QPointF& local_pos) const #include <QDebug>
AcceptType NodePresent::testAccept(const QPointF& local_pos, const QString& kind_str) const
{ {
auto outline = this->boundingRect();
auto width = outline.width();
auto origin = outline.topLeft();
auto left_rect = QRectF(origin + QPointF(0, 0), QSizeF(width / 3, outline.height()));
auto right_rect = QRectF(origin + QPointF(width * 2 / 3, 0), QSizeF(width / 3, outline.height()));
auto top_rect = QRectF(origin + QPointF(width / 3, outline.height() / 2), QSizeF(width / 3, outline.height() / 2));
auto bottom_rect = QRectF(origin + QPointF(width / 3, 0), QSizeF(width / 3, outline.height() / 2));
QList<NodeKind> no_child{ NodeKind::ACTIONNODE, NodeKind::COMPARENODE };
auto pnode = this->_node_bind->parent().lock();
decltype(pnode) new_type = nullptr;
if (this->_node_bind->bindMap())
new_type = this->_node_bind->bindMap()->getKernal()->getNode(kind_str);
else
new_type = std::dynamic_pointer_cast<BehaviorMapNode>(this->_node_bind)->getKernal()->getNode(kind_str);
// 当前节点存在父节点且不属于Action和Compare
if (left_rect.contains(local_pos) && pnode && !no_child.contains(new_type->nodeKind()))
return AcceptType::PREV_LEVEL; return AcceptType::PREV_LEVEL;
// 当前节点存在父节点且父节点可以存在多个节点
if (pnode && pnode->nodeKind() != NodeKind::MAPNODE) {
if (top_rect.contains(local_pos))
return AcceptType::PREV_SIBLING;
if (bottom_rect.contains(local_pos))
return AcceptType::NEXT_SIBLING;
}
// 分辨当前节点的是否可以插入新节点
if (right_rect.contains(local_pos))
switch (this->_node_bind->nodeKind()) {
case NodeKind::MAPNODE: // 根节点只能容纳一个节点
if (!this->_node_bind->children().size() && !this->_node_bind->parent().lock())
return AcceptType::NEXT_LEVEL;
break;
case NodeKind::PARALLELNODE:
case NodeKind::SELECTORNODE:
case NodeKind::SEQUENCENODE:
return AcceptType::NEXT_LEVEL;
default: // Action和Compare节点不能容纳节点
break;
}
return AcceptType::NONE;
} }
#include <QGraphicsSceneDragDropEvent> #include <QGraphicsSceneDragDropEvent>
#include <QMimeData> #include <QMimeData>
#include <QDebug>
void NodePresent::dragEnterEvent(QGraphicsSceneDragDropEvent* e) void NodePresent::dragEnterEvent(QGraphicsSceneDragDropEvent* e)
{ {
QGraphicsItem::dragEnterEvent(e);
if (e->mimeData()->hasFormat("application/drag-node")) { if (e->mimeData()->hasFormat("application/drag-node")) {
e->acceptProposedAction(); e->acceptProposedAction();
} }
@ -30,27 +77,65 @@ void NodePresent::dragLeaveEvent(QGraphicsSceneDragDropEvent* event)
{ {
QGraphicsItem::dragLeaveEvent(event); QGraphicsItem::dragLeaveEvent(event);
this->_drop_target = AcceptType::NONE; this->_drop_target = AcceptType::NONE;
this->update();
} }
#include <QApplication>
void NodePresent::dragMoveEvent(QGraphicsSceneDragDropEvent* e) void NodePresent::dragMoveEvent(QGraphicsSceneDragDropEvent* e)
{ {
QGraphicsItem::dragMoveEvent(e);
this->update();
if (e->mimeData()->hasFormat("application/drag-node")) { if (e->mimeData()->hasFormat("application/drag-node")) {
auto local_pos = this->mapToItem(this, e->pos()); auto kind_string = QString::fromUtf8(e->mimeData()->data("application/drag-node"));
this->_drop_target = this->testAccept(local_pos); this->_drop_target = this->testAccept(e->pos(), kind_string);
if (_drop_target != AcceptType::NONE) if (_drop_target != AcceptType::NONE) {
e->acceptProposedAction(); e->acceptProposedAction();
return;
} }
} }
e->ignore();
}
void NodePresent::dropEvent(QGraphicsSceneDragDropEvent* e) void NodePresent::dropEvent(QGraphicsSceneDragDropEvent* e)
{ {
qDebug() << QString::fromUtf8(e->mimeData()->data("application/drag-node")); auto kind_string = QString::fromUtf8(e->mimeData()->data("application/drag-node"));
auto parent_node = this->_node_bind->parent().lock();
decltype(parent_node) new_type = nullptr;
if (this->_node_bind->bindMap())
new_type = this->_node_bind->bindMap()->getKernal()->getNode(kind_string);
else
new_type = std::dynamic_pointer_cast<BehaviorMapNode>(this->_node_bind)->getKernal()->getNode(kind_string);
auto new_node = std::dynamic_pointer_cast<LogicalNode>(new_type->newDefault());
new_node->setID(++_widget_p->_node_id_max);
auto this_node = this->_node_bind;
auto appoint_index = 0;
if(parent_node)
appoint_index = parent_node->children().indexOf(this_node);
switch (_drop_target) {
case AcceptType::NONE:
break;
case AcceptType::PREV_LEVEL: {
parent_node->remove(this_node);
parent_node->insert(new_node, appoint_index);
new_node->insert(this_node);
}break;
case AcceptType::NEXT_LEVEL:
this_node->insert(new_node);
break;
case AcceptType::PREV_SIBLING:
parent_node->insert(new_node, appoint_index);
break;
case AcceptType::NEXT_SIBLING:
parent_node->insert(new_node, appoint_index + 1);
break;
}
_widget_p->relayout();
this->_drop_target = AcceptType::NONE; this->_drop_target = AcceptType::NONE;
this->update();
} }
QRectF NodePresent::boundingRect() const QRectF NodePresent::boundingRect() const
@ -65,12 +150,44 @@ QRectF NodePresent::boundingRect() const
#include <QStyleOptionGraphicsItem> #include <QStyleOptionGraphicsItem>
void NodePresent::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) void NodePresent::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{ {
painter->save();
painter->drawRect(boundingRect()); painter->drawRect(boundingRect());
painter->fillRect(option->rect - QMargins(8, 8, 8, 8), Qt::gray); painter->fillRect(option->rect - QMargins(8, 8, 8, 8), Qt::gray);
auto outline = this->boundingRect();
auto width = outline.width();
auto origin = outline.topLeft();
painter->save();
painter->translate(QPointF(15, 15)); painter->translate(QPointF(15, 15));
auto rect = contentMeasure(); auto rect = contentMeasure();
painter->drawText(rect, 0, this->_node_bind->rtName()); painter->drawText(rect, 0, this->_node_bind->rtName());
painter->restore();
QRectF paint_target_rect;
switch (_drop_target) {
case AcceptType::NONE:
break;
case AcceptType::PREV_LEVEL:
paint_target_rect = QRectF(origin + QPointF(0, 0), QSizeF(width / 3, outline.height()));;
break;
case AcceptType::NEXT_LEVEL:
paint_target_rect = QRectF(origin + QPointF(width * 2 / 3, 0), QSizeF(width / 3, outline.height()));;
break;
case AcceptType::PREV_SIBLING:
paint_target_rect = QRectF(origin + QPointF(width / 3, outline.height() / 2), QSizeF(width / 3, outline.height() / 2));;
break;
case AcceptType::NEXT_SIBLING:
paint_target_rect = QRectF(origin + QPointF(width / 3, 0), QSizeF(width / 3, outline.height() / 2));;
break;
}
painter->fillRect(paint_target_rect, Qt::green);
painter->restore();
} }
BehaviorsPresent::BehaviorsPresent(QWidget* parent /*= nullptr*/) BehaviorsPresent::BehaviorsPresent(QWidget* parent /*= nullptr*/)
@ -209,7 +326,9 @@ QPointF BehaviorsPresent::nodeRelayout(QHash<std::shared_ptr<LogicalNode>, std::
default: default:
// 布局子节点集合位置,根节点必须是顶端根节点 // 布局子节点集合位置,根节点必须是顶端根节点
if (ins->nodeKind() != NodeKind::MAPNODE || !ins->bindMap()) { if (ins->nodeKind() != NodeKind::MAPNODE || !ins->bindMap()) {
for (auto item : ins->children()) { auto child_list = ins->children();
for (auto iter = child_list.rbegin(); iter != child_list.rend(); ++iter) {
auto item = *iter;
child_origin = nodeRelayout(_outline_occupy, item, child_origin); child_origin = nodeRelayout(_outline_occupy, item, child_origin);
} }
}break; }break;
@ -297,7 +416,6 @@ NodeTypesView::NodeTypesView(QWidget* parent /*= nullptr*/)
this->setSelectionMode(QAbstractItemView::SingleSelection); this->setSelectionMode(QAbstractItemView::SingleSelection);
} }
#include <QMimeData>
#include <QVariant> #include <QVariant>
#include <QDrag> #include <QDrag>
#include <QPainter> #include <QPainter>

View File

@ -7,24 +7,26 @@
uint qHash(const std::shared_ptr<LogicalNode> data, uint seed) noexcept; uint qHash(const std::shared_ptr<LogicalNode> data, uint seed) noexcept;
enum class AcceptType { enum class AcceptType {
NONE, PREV_LEVEL, NEXT_LEVEL, PREV_SIBLING, NEXT_SIBLING NONE = 0, PREV_LEVEL, NEXT_LEVEL, PREV_SIBLING, NEXT_SIBLING
}; };
class BehaviorsPresent;
/// <summary> /// <summary>
/// ½ÚµãÏÔʾ´úÀí /// ½ÚµãÏÔʾ´úÀí
/// </summary> /// </summary>
class NodePresent : public QGraphicsItem { class NodePresent : public QGraphicsItem {
private: private:
QWidget* const _widget_p; BehaviorsPresent* const _widget_p;
std::shared_ptr<LogicalNode> _node_bind; std::shared_ptr<LogicalNode> _node_bind;
double& _column_width; double& _column_width;
AcceptType _drop_target = AcceptType::NONE; AcceptType _drop_target = AcceptType::NONE;
public: public:
NodePresent(QWidget* pwidget, double& width_bind, std::shared_ptr<LogicalNode> bind); NodePresent(BehaviorsPresent* pwidget, double& width_bind, std::shared_ptr<LogicalNode> bind);
QRectF contentMeasure() const; QRectF contentMeasure() const;
AcceptType testAccept(const QPointF& local_pos) const; AcceptType testAccept(const QPointF& local_pos, const QString &kind_str) const;
// ͨ¹ý QGraphicsItem ¼Ì³Ð // ͨ¹ý QGraphicsItem ¼Ì³Ð
QRectF boundingRect() const override; QRectF boundingRect() const override;
@ -56,6 +58,7 @@ private:
std::shared_ptr<BehaviorMapNode> _bind_maproot = nullptr; std::shared_ptr<BehaviorMapNode> _bind_maproot = nullptr;
public: public:
uint64_t _node_id_max = 0;
BehaviorsPresent(QWidget* parent = nullptr); BehaviorsPresent(QWidget* parent = nullptr);
/// <summary> /// <summary>