拖动构建行为树

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;
}
std::shared_ptr<LogicalNode> LogicalNode::bindMap() const
std::shared_ptr<BehaviorMapNode> LogicalNode::bindMap() const
{
auto node_temp = this->parent().lock();
if (!node_temp) return nullptr;
@ -195,7 +195,8 @@ std::shared_ptr<LogicalNode> LogicalNode::bindMap() const
while (node_temp->nodeKind() != NodeKind::MAPNODE) {
node_temp = node_temp->parent().lock();
}
return node_temp;
return std::dynamic_pointer_cast<BehaviorMapNode>(node_temp);
}
void LogicalNode::setID(int unique_id)
@ -222,6 +223,11 @@ BehaviorMapNode::BehaviorMapNode(std::shared_ptr<MapKernal> 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)
{
if (_variables.contains(key))

View File

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

View File

@ -1,6 +1,6 @@
#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) {
this->setAcceptDrops(true);
}
@ -11,16 +11,63 @@ QRectF NodePresent::contentMeasure() const
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
{
return AcceptType::PREV_LEVEL;
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;
// 当前节点存在父节点且父节点可以存在多个节点
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 <QMimeData>
#include <QDebug>
void NodePresent::dragEnterEvent(QGraphicsSceneDragDropEvent* e)
{
QGraphicsItem::dragEnterEvent(e);
if (e->mimeData()->hasFormat("application/drag-node")) {
e->acceptProposedAction();
}
@ -30,27 +77,65 @@ void NodePresent::dragLeaveEvent(QGraphicsSceneDragDropEvent* event)
{
QGraphicsItem::dragLeaveEvent(event);
this->_drop_target = AcceptType::NONE;
this->update();
}
#include <QApplication>
void NodePresent::dragMoveEvent(QGraphicsSceneDragDropEvent* e)
{
QGraphicsItem::dragMoveEvent(e);
this->update();
if (e->mimeData()->hasFormat("application/drag-node")) {
auto local_pos = this->mapToItem(this, e->pos());
this->_drop_target = this->testAccept(local_pos);
if (_drop_target != AcceptType::NONE)
auto kind_string = QString::fromUtf8(e->mimeData()->data("application/drag-node"));
this->_drop_target = this->testAccept(e->pos(), kind_string);
if (_drop_target != AcceptType::NONE) {
e->acceptProposedAction();
return;
}
}
e->ignore();
}
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->update();
}
QRectF NodePresent::boundingRect() const
@ -65,12 +150,44 @@ QRectF NodePresent::boundingRect() const
#include <QStyleOptionGraphicsItem>
void NodePresent::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
painter->save();
painter->drawRect(boundingRect());
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));
auto rect = contentMeasure();
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*/)
@ -209,7 +326,9 @@ QPointF BehaviorsPresent::nodeRelayout(QHash<std::shared_ptr<LogicalNode>, std::
default:
// 布局子节点集合位置,根节点必须是顶端根节点
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);
}
}break;
@ -297,7 +416,6 @@ NodeTypesView::NodeTypesView(QWidget* parent /*= nullptr*/)
this->setSelectionMode(QAbstractItemView::SingleSelection);
}
#include <QMimeData>
#include <QVariant>
#include <QDrag>
#include <QPainter>

View File

@ -7,24 +7,26 @@
uint qHash(const std::shared_ptr<LogicalNode> data, uint seed) noexcept;
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>
class NodePresent : public QGraphicsItem {
private:
QWidget* const _widget_p;
BehaviorsPresent* const _widget_p;
std::shared_ptr<LogicalNode> _node_bind;
double& _column_width;
AcceptType _drop_target = AcceptType::NONE;
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;
AcceptType testAccept(const QPointF& local_pos) const;
AcceptType testAccept(const QPointF& local_pos, const QString &kind_str) const;
// ͨ¹ý QGraphicsItem ¼Ì³Ð
QRectF boundingRect() const override;
@ -56,6 +58,7 @@ private:
std::shared_ptr<BehaviorMapNode> _bind_maproot = nullptr;
public:
uint64_t _node_id_max = 0;
BehaviorsPresent(QWidget* parent = nullptr);
/// <summary>