721 lines
21 KiB
C++
721 lines
21 KiB
C++
#include "BehaviorEditor.h"
|
||
#include <QDebug>
|
||
#include <QGraphicsSceneDragDropEvent>
|
||
#include <QMimeData>
|
||
#include <QApplication>
|
||
#include <QSplitter>
|
||
#include <QMenuBar>
|
||
#include <QMenu>
|
||
#include <QFileDialog>
|
||
#include <QVariant>
|
||
#include <QDrag>
|
||
#include <QPainter>
|
||
#include <QStyleOptionGraphicsItem>
|
||
#include <QStandardItem>
|
||
|
||
uint qHash(const std::shared_ptr<LogicalNode> data, uint seed) noexcept
|
||
{
|
||
return qHash((void*)data.get(), seed);
|
||
}
|
||
|
||
NodePresent::NodePresent(BehaviorsPresent* pwidget, QVector<double>& columns_set, std::shared_ptr<LogicalNode> bind)
|
||
: _widget_p(pwidget), _columns_width_seqs(columns_set), _node_bind(bind) {
|
||
this->setAcceptDrops(true);
|
||
this->setCacheMode(QGraphicsItem::NoCache);
|
||
}
|
||
|
||
std::shared_ptr<LogicalNode> NodePresent::logicalBind() const
|
||
{
|
||
return _node_bind;
|
||
}
|
||
|
||
QRectF NodePresent::contentMeasure() const
|
||
{
|
||
auto metrics = this->_widget_p->fontMetrics();
|
||
auto rect = metrics.boundingRect(this->_node_bind->rtName());
|
||
rect.moveTopLeft(QPoint(0, 0));
|
||
return rect;
|
||
}
|
||
|
||
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 = 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;
|
||
|
||
QList<NodeKind> single_child{ NodeKind::MAPNODE, NodeKind::MODIFYNODE };
|
||
// 当前节点存在父节点且父节点可以存在多个节点
|
||
if (pnode && !single_child.contains(pnode->nodeKind())) {
|
||
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::MODIFYNODE: // 修饰节点只能容纳一个子节点
|
||
if (!this->_node_bind->children().size())
|
||
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;
|
||
}
|
||
|
||
void NodePresent::dragEnterEvent(QGraphicsSceneDragDropEvent* e)
|
||
{
|
||
QGraphicsItem::dragEnterEvent(e);
|
||
if (e->mimeData()->hasFormat("application/drag-node")) {
|
||
e->acceptProposedAction();
|
||
}
|
||
}
|
||
|
||
void NodePresent::dragLeaveEvent(QGraphicsSceneDragDropEvent* event)
|
||
{
|
||
QGraphicsItem::dragLeaveEvent(event);
|
||
this->_drop_target = AcceptType::NONE;
|
||
this->update();
|
||
}
|
||
|
||
void NodePresent::dragMoveEvent(QGraphicsSceneDragDropEvent* e)
|
||
{
|
||
QGraphicsItem::dragMoveEvent(e);
|
||
this->update();
|
||
if (e->mimeData()->hasFormat("application/drag-node")) {
|
||
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)
|
||
{
|
||
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;
|
||
}
|
||
|
||
this->_drop_target = AcceptType::NONE;
|
||
_widget_p->relayout();
|
||
this->update();
|
||
}
|
||
|
||
QRectF NodePresent::boundingRect() const
|
||
{
|
||
auto rect = contentMeasure();
|
||
auto depth = this->_node_bind->depth();
|
||
if (_columns_width_seqs.size() <= depth)
|
||
_columns_width_seqs.resize(depth + 1);
|
||
|
||
auto width_col = _columns_width_seqs[depth];
|
||
rect.setWidth(std::max(width_col, rect.width()));
|
||
_columns_width_seqs[depth] = rect.width();
|
||
|
||
return rect + QMargins(0, 0, 30, 30);
|
||
}
|
||
|
||
void NodePresent::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
|
||
{
|
||
painter->save();
|
||
|
||
//painter->drawRect(boundingRect());
|
||
painter->fillRect(option->rect - QMargins(padding, padding, padding, padding), 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*/)
|
||
: QGraphicsView(parent) {
|
||
this->setScene(&_bind_scene);
|
||
|
||
this->setAcceptDrops(true);
|
||
this->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
|
||
|
||
auto font = this->font();
|
||
font.setPixelSize(20);
|
||
this->setFont(font);
|
||
}
|
||
|
||
void BehaviorsPresent::setRoot(std::shared_ptr<BehaviorMapNode> root)
|
||
{
|
||
this->_bind_maproot = root;
|
||
|
||
// 清除显示节点
|
||
qDeleteAll(_present_peers);
|
||
_present_peers.clear();
|
||
|
||
// 清空分支
|
||
qDeleteAll(this->_branch_list);
|
||
this->_branch_list.clear();
|
||
|
||
relayout();
|
||
}
|
||
|
||
NodePresent* BehaviorsPresent::presentAllocate(std::shared_ptr<LogicalNode> ins)
|
||
{
|
||
for (auto idx = _column_aligns.size(); idx < ins->depth() + 1; ++idx)
|
||
_column_aligns.append(0);
|
||
|
||
QList<NodePresent*> _children_set;
|
||
switch (ins->nodeKind()) {
|
||
case NodeKind::MAPNODE:
|
||
case NodeKind::MODIFYNODE:
|
||
case NodeKind::PARALLELNODE:
|
||
case NodeKind::SEQUENCENODE:
|
||
case NodeKind::SELECTORNODE:
|
||
if (ins->nodeKind() != NodeKind::MAPNODE || !ins->bindMap()) {
|
||
for (auto it : ins->children()) {
|
||
auto child_graph = presentAllocate(it);
|
||
if (child_graph) _children_set << child_graph;
|
||
}
|
||
}
|
||
|
||
default:
|
||
if (_present_peers.contains(ins)) {
|
||
for (auto child : _children_set) {
|
||
_branch_list << new BranchPresent(_present_peers, child);
|
||
this->_bind_scene.addItem(_branch_list.last());
|
||
}
|
||
return nullptr;
|
||
}
|
||
|
||
_present_peers[ins] = new NodePresent(this, _column_aligns, ins);
|
||
this->_bind_scene.addItem(_present_peers[ins]);
|
||
|
||
return _present_peers[ins];
|
||
}
|
||
}
|
||
|
||
//void BehaviorsPresent::presentRelease(std::shared_ptr<LogicalNode> ins)
|
||
//{
|
||
// switch (ins->nodeKind())
|
||
// {
|
||
// case NodeKind::MAPNODE:
|
||
// case NodeKind::PARALLELNODE:
|
||
// case NodeKind::SELECTORNODE:
|
||
// case NodeKind::SEQUENCENODE: {
|
||
// for (auto item : ins->children()) {
|
||
// presentRelease(item);
|
||
// }
|
||
// }
|
||
// default:
|
||
// if (this->_present_peers.contains(ins))
|
||
// delete this->_present_peers[ins];
|
||
// this->_present_peers.remove(ins);
|
||
//
|
||
// // 清除BranchPresent
|
||
// BranchPresent *_target = nullptr;
|
||
// for(auto node : this->_branch_list)
|
||
// if (node->headNode() == ins) {
|
||
// _target = node;
|
||
// delete node;
|
||
// }
|
||
// this->_branch_list.removeAll(_target);
|
||
// break;
|
||
// }
|
||
//}
|
||
|
||
const double NodePresent::padding = 8;
|
||
void BehaviorsPresent::relayout()
|
||
{
|
||
if (!_bind_maproot)
|
||
return;
|
||
|
||
// 初始化显示节点
|
||
presentAllocate(_bind_maproot);
|
||
|
||
QHash<std::shared_ptr<LogicalNode>, std::pair<QSizeF, QSizeF>> results;
|
||
// 尺寸测量
|
||
outlineMeasure(_bind_maproot, results);
|
||
|
||
// 元素排布
|
||
nodeRelayout(results, _bind_maproot, QPointF());
|
||
|
||
// 调整分支图形位置
|
||
for (auto ins : this->_branch_list) {
|
||
auto rect_s = ins->startOutline();
|
||
auto rect_e = ins->endOutline();
|
||
|
||
qDebug() << __FILE__ << __LINE__ << rect_s << rect_e;
|
||
|
||
auto start_pos = rect_s.bottomRight() - QPoint(NodePresent::padding, rect_s.height() / 2);
|
||
auto end_pos = rect_e.topLeft() + QPoint(NodePresent::padding, rect_e.height() / 2);
|
||
|
||
auto left_val = std::min(start_pos.x(), end_pos.x());
|
||
auto top_val = std::min(start_pos.y(), end_pos.y());
|
||
|
||
ins->setPos(left_val, top_val);
|
||
ins->resetArrow(start_pos, end_pos);
|
||
}
|
||
|
||
this->update();
|
||
}
|
||
|
||
QSizeF BehaviorsPresent::outlineMeasure(std::shared_ptr<LogicalNode> ins, QHash<std::shared_ptr<LogicalNode>, std::pair<QSizeF, QSizeF>>& _outline_occupy)
|
||
{
|
||
QSizeF outline_box(0, 0);
|
||
|
||
switch (ins->nodeKind()) {
|
||
case NodeKind::MAPNODE:
|
||
case NodeKind::MODIFYNODE:
|
||
case NodeKind::PARALLELNODE:
|
||
case NodeKind::SELECTORNODE:
|
||
case NodeKind::SEQUENCENODE:
|
||
// 测量子节点集合尺寸,根节点必须是行为树顶部根节点
|
||
if (ins->nodeKind() != NodeKind::MAPNODE || !ins->bindMap()) {
|
||
for (auto nit : ins->children()) {
|
||
auto nit_size = outlineMeasure(nit, _outline_occupy);
|
||
outline_box.setWidth(std::max(outline_box.width(), nit_size.width() + _space_h));
|
||
outline_box += QSizeF(0, nit_size.height());
|
||
}
|
||
}
|
||
default: {
|
||
auto root_peer = _present_peers[ins];
|
||
auto root_box = root_peer->boundingRect();
|
||
outline_box += QSizeF(root_box.width(), 0);
|
||
outline_box.setHeight(std::max(outline_box.height(), root_box.height()));
|
||
|
||
_outline_occupy[ins] = std::make_pair(outline_box, root_box.size());
|
||
return outline_box;
|
||
}break;
|
||
}
|
||
}
|
||
|
||
QPointF BehaviorsPresent::nodeRelayout(QHash<std::shared_ptr<LogicalNode>, std::pair<QSizeF, QSizeF>>& _outline_occupy,
|
||
std::shared_ptr<LogicalNode> ins, const QPointF& origin_offset) {
|
||
// 提取尺寸记录
|
||
auto outline_occupy = _outline_occupy[ins];
|
||
auto node_outline = outline_occupy.first;
|
||
auto node_occupy = outline_occupy.second;
|
||
|
||
// 提取显示节点
|
||
auto present_node = _present_peers[ins];
|
||
|
||
// 设置节点位置
|
||
QPointF local_origin_offset(0, (node_outline.height() - node_occupy.height()) / 2);
|
||
present_node->setPos(origin_offset + local_origin_offset);
|
||
|
||
// 计算子节点位置
|
||
QPointF child_origin = origin_offset + QPointF(node_occupy.width() + _space_h, 0);
|
||
switch (ins->nodeKind())
|
||
{
|
||
case NodeKind::ACTIONNODE:
|
||
case NodeKind::COMPARENODE:
|
||
break;
|
||
default:
|
||
// 布局子节点集合位置,根节点必须是顶端根节点
|
||
if (ins->nodeKind() != NodeKind::MAPNODE || !ins->bindMap()) {
|
||
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;
|
||
}
|
||
|
||
// 计算左下角位置
|
||
return origin_offset + QPointF(0, node_outline.height());
|
||
}
|
||
|
||
#include <QJsonDocument>
|
||
void BehaviorEditor::open_behavior_map()
|
||
{
|
||
_current_fileurl = QFileDialog::getOpenFileUrl(this, u8"打开行为树文件", QUrl(), "*.behw");
|
||
if (!_current_fileurl.isValid())
|
||
return;
|
||
|
||
QFile data_file(_current_fileurl.toLocalFile());
|
||
data_file.open(QIODevice::ReadOnly);
|
||
auto json = QJsonDocument::fromJson(data_file.readAll());
|
||
|
||
// 载入数据,回复节点内容
|
||
_map_root->recoveryFrom(json.object());
|
||
this->_logical_present->setRoot(_map_root);
|
||
}
|
||
|
||
void BehaviorEditor::new_behavior_map()
|
||
{
|
||
_current_fileurl = QFileDialog::getSaveFileUrl(this, u8"创建行为树文件", QUrl(), "*.behw");
|
||
if (!_current_fileurl.isValid())
|
||
return;
|
||
|
||
qDebug() << _current_fileurl;
|
||
|
||
// 清空所有子节点内容
|
||
auto childs = this->_map_root->children();
|
||
if (childs.size())
|
||
this->_map_root->remove(childs.first());
|
||
|
||
// 清空行为树节点变量列表
|
||
auto variable_key_set = this->_map_root->inputVariableKeys();
|
||
variable_key_set << this->_map_root->outputVariableKeys();
|
||
for (auto key : variable_key_set)
|
||
this->_map_root->removeVariable(key);
|
||
|
||
// 重置地图节点
|
||
this->_logical_present->setRoot(_map_root);
|
||
}
|
||
|
||
#include <QLineEdit>
|
||
#include <QLabel>
|
||
#include <QGridLayout>
|
||
#include <QComboBox>
|
||
QWidget* BehaviorEditor::newModifyNodeConfigration(QWidget* pwidget)
|
||
{
|
||
auto panel = new QFrame(this);
|
||
panel->setFrameStyle(QFrame::WinPanel | QFrame::Sunken);
|
||
auto p_layout = new QGridLayout(panel);
|
||
|
||
p_layout->addWidget(new QLabel(u8"修饰类型:", this));
|
||
p_layout->addWidget(new QComboBox(this), 0, 1, 1, 3);
|
||
p_layout->addWidget(new QWidget(this), 1, 0, 1, 4);
|
||
|
||
p_layout->setRowStretch(1, 1);
|
||
p_layout->setColumnStretch(1, 1);
|
||
return panel;
|
||
}
|
||
|
||
BehaviorEditor::BehaviorEditor(QWidget* parent /*= nullptr*/)
|
||
: QMainWindow(parent),
|
||
_type_view(new NodeTypesView(this)),
|
||
_type_model(new QStandardItemModel(this)),
|
||
_logical_present(new BehaviorsPresent(this)),
|
||
_message_panel(new QTabWidget(this)),
|
||
_stacked_panel(new QStackedWidget(this)),
|
||
_logs_present(new QTextBrowser(this)),
|
||
_map_configuration(new BehaviorMapConfigurationPanel(this))
|
||
{
|
||
_global_loader = std::make_shared<MessageLoader>();
|
||
_global_kernal = std::make_shared<MapKernel>(_global_loader);
|
||
_global_kernal->initial();
|
||
|
||
this->_map_root = std::make_shared<BehaviorMapNode>(_global_kernal);
|
||
|
||
auto mbar = this->menuBar();
|
||
auto _file = mbar->addMenu(u8"文件");
|
||
_file->addAction(u8"打开行为树", this, &BehaviorEditor::open_behavior_map);
|
||
_file->addAction(u8"新建行为树", this, &BehaviorEditor::new_behavior_map);
|
||
|
||
auto font = this->font();
|
||
font.setPixelSize(20);
|
||
_type_view->setFont(font);
|
||
|
||
auto split_v = new QSplitter(Qt::Vertical, this);
|
||
this->setCentralWidget(split_v);
|
||
|
||
auto split_h = new QSplitter(Qt::Horizontal, this);
|
||
split_v->addWidget(split_h);
|
||
split_v->addWidget(_message_panel);
|
||
split_v->setStretchFactor(0, 1);
|
||
split_v->setStretchFactor(1, 0);
|
||
|
||
split_h->addWidget(_logical_present);
|
||
split_h->addWidget(_type_view);
|
||
split_h->setStretchFactor(0, 1);
|
||
split_h->setStretchFactor(1, 0);
|
||
|
||
_type_view->setModel(_type_model);
|
||
nodeTypesViewInit(_type_model);
|
||
|
||
|
||
// 下方堆叠面板
|
||
_message_panel->addTab(_logs_present, u8"控制台");
|
||
_message_panel->addTab(_stacked_panel, u8"属性配置");
|
||
_stacked_panel->addWidget(new BehaviorMapConfigurationPanel(this));
|
||
_stacked_panel->addWidget(newModifyNodeConfigration(this));
|
||
_stacked_panel->addWidget(newActionNodeConfigration(this));
|
||
_stacked_panel->addWidget(newCompareNodeConfigration(this));
|
||
_stacked_panel->addWidget(newDefaultConfigration(this));
|
||
}
|
||
|
||
void BehaviorEditor::nodeTypesViewInit(QStandardItemModel* m)
|
||
{
|
||
auto types = this->_global_kernal->nodeTypes();
|
||
std::sort(types.begin(), types.end());
|
||
for (auto type : types) {
|
||
auto row_item = new QStandardItem(type);
|
||
row_item->setEditable(false);
|
||
m->appendRow(row_item);
|
||
}
|
||
}
|
||
|
||
#include <QGridLayout>
|
||
#include <QLineEdit>
|
||
#include <QLabel>
|
||
#include <QComboBox>
|
||
#include <QPushButton>
|
||
|
||
QWidget* BehaviorEditor::newDefaultConfigration(QWidget* pwidget)
|
||
{
|
||
auto ins = new QLabel(u8"该结点无需配置", pwidget);
|
||
ins->setAlignment(Qt::AlignCenter);
|
||
ins->setFrameStyle(QFrame::WinPanel | QFrame::Sunken);
|
||
return ins;
|
||
}
|
||
|
||
QWidget* BehaviorEditor::newCompareNodeConfigration(QWidget* pwidget)
|
||
{
|
||
auto panel = new QFrame(this);
|
||
panel->setFrameStyle(QFrame::WinPanel | QFrame::Sunken);
|
||
auto p_layout = new QGridLayout(panel);
|
||
|
||
auto compare_types = new QComboBox(this);
|
||
p_layout->addWidget(new QLabel(u8"类型筛选:", this), 0, 0);
|
||
p_layout->addWidget(new QComboBox(this), 0, 1, 1, 5);
|
||
p_layout->addWidget(new QLabel(u8"比较器类型:"), 1, 0);
|
||
p_layout->addWidget(compare_types, 1, 1, 1, 5);
|
||
p_layout->addWidget(new QLabel(u8"输入变量A:", this), 2, 0);
|
||
p_layout->addWidget(new QComboBox(this), 2, 1, 1, 5);
|
||
p_layout->addWidget(new QLabel(u8"输入变量B:", this), 3, 0);
|
||
p_layout->addWidget(new QComboBox(this), 3, 1, 1, 5);
|
||
|
||
p_layout->setColumnStretch(1, 1);
|
||
return panel;
|
||
}
|
||
|
||
QWidget* BehaviorEditor::newActionNodeConfigration(QWidget* pwidget)
|
||
{
|
||
auto panel = new QFrame(this);
|
||
panel->setFrameStyle(QFrame::WinPanel | QFrame::Sunken);
|
||
auto p_layout = new QGridLayout(panel);
|
||
|
||
p_layout->addWidget(new QLabel(u8"执行器类型:", this));
|
||
p_layout->addWidget(new QComboBox(this), 0, 1, 1, 4);
|
||
auto vars_tabw = new QTabWidget(this);
|
||
p_layout->addWidget(vars_tabw, 1, 0, 3, 5);
|
||
vars_tabw->setTabPosition(QTabWidget::West);
|
||
|
||
vars_tabw->addTab(new QTableView(this), u8"输入变量");
|
||
vars_tabw->addTab(new QTableView(this), u8"输出变量");
|
||
|
||
p_layout->setColumnStretch(1, 1);
|
||
return panel;
|
||
}
|
||
|
||
|
||
|
||
NodeTypesView::NodeTypesView(QWidget* parent /*= nullptr*/)
|
||
: QListView(parent)
|
||
{
|
||
this->setDragEnabled(true);
|
||
this->setDragDropMode(QAbstractItemView::DragOnly);
|
||
this->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||
this->setSelectionMode(QAbstractItemView::SingleSelection);
|
||
}
|
||
|
||
void NodeTypesView::startDrag(Qt::DropActions supported)
|
||
{
|
||
auto selected = this->selectedIndexes();
|
||
if (selected.size() && selected.first().isValid()) {
|
||
auto val = this->model()->data(selected.first(), Qt::DisplayRole);
|
||
auto type_name = val.toString();
|
||
|
||
// 创建自定义MIME数据
|
||
auto mime_data = new QMimeData();
|
||
mime_data->setData("application/drag-node", type_name.toUtf8());
|
||
|
||
auto view_rect = this->visualRect(selected.first());
|
||
QPixmap pixmap(view_rect.size());
|
||
pixmap.fill(Qt::transparent);
|
||
QPainter p(&pixmap);
|
||
this->render(&p, QPoint(), QRegion(view_rect));
|
||
p.end();
|
||
|
||
// 创建拖动对象
|
||
auto drag = new QDrag(this);
|
||
drag->setMimeData(mime_data);
|
||
drag->setPixmap(pixmap);
|
||
|
||
auto result = drag->exec(supported);
|
||
if (result == Qt::CopyAction) {
|
||
qDebug() << __FILE__ << __LINE__;
|
||
}
|
||
}
|
||
}
|
||
|
||
BranchPresent::BranchPresent(const QHash<std::shared_ptr<LogicalNode>, NodePresent*>& present_set, NodePresent* head_anchor)
|
||
:_present_set_bind(present_set), _head_node(head_anchor) {
|
||
}
|
||
|
||
std::shared_ptr<LogicalNode> BranchPresent::headNode() const
|
||
{
|
||
return _head_node->logicalBind();
|
||
}
|
||
|
||
QRectF BranchPresent::startOutline() const
|
||
{
|
||
auto parent_node = _head_node->logicalBind()->parent().lock();
|
||
if (!parent_node)
|
||
return QRectF();
|
||
|
||
auto _start_node = _present_set_bind[parent_node];
|
||
auto _start_pos = _start_node->pos();
|
||
auto _s_rect = _start_node->boundingRect();
|
||
_s_rect.moveTopLeft(_start_pos);
|
||
|
||
return _s_rect;
|
||
}
|
||
|
||
QRectF BranchPresent::endOutline() const
|
||
{
|
||
auto parent_node = _head_node->logicalBind()->parent().lock();
|
||
if (!parent_node)
|
||
return QRectF();
|
||
|
||
auto _end_node = this->_head_node;
|
||
auto _end_pos = _end_node->pos();
|
||
auto _e_rect = _end_node->boundingRect();
|
||
_e_rect.moveTopLeft(_end_pos);
|
||
|
||
return _e_rect;
|
||
}
|
||
|
||
void BranchPresent::resetArrow(const QPointF& start, const QPointF& end)
|
||
{
|
||
this->_arrow_start = start;
|
||
this->_arrow_end = end;
|
||
qDebug() << __FILE__ << __LINE__ << _arrow_start << _arrow_end;
|
||
this->update();
|
||
}
|
||
|
||
QRectF BranchPresent::boundingRect() const
|
||
{
|
||
auto leftv = std::min(_arrow_start.x(), _arrow_end.x());
|
||
auto topv = std::min(_arrow_start.y(), _arrow_end.y());
|
||
auto width = std::abs(_arrow_start.x() - _arrow_end.x());
|
||
auto height = std::abs(_arrow_start.y() - _arrow_end.y());
|
||
|
||
return QRectF(0, 0, width, height);
|
||
}
|
||
|
||
void BranchPresent::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
|
||
{
|
||
painter->save();
|
||
painter->setRenderHint(QPainter::Antialiasing, true);
|
||
|
||
QPointF start(0, 0);
|
||
auto vtarget = _arrow_end - _arrow_start;
|
||
if (vtarget.y() < 0) {
|
||
start -= QPointF(0, vtarget.y());
|
||
vtarget -= QPointF(0, vtarget.y());
|
||
}
|
||
|
||
auto pt1 = start + QPointF(vtarget.x() / 2, 0);
|
||
auto pt2 = vtarget - QPointF(vtarget.x() / 2, 0);
|
||
|
||
QPainterPath bezier_curve;
|
||
bezier_curve.moveTo(start);
|
||
bezier_curve.cubicTo(pt1, pt2, vtarget);
|
||
|
||
QPen p(Qt::black);
|
||
p.setWidth(2);
|
||
painter->setPen(p);
|
||
|
||
painter->drawPath(bezier_curve);
|
||
//p.setColor(Qt::red);
|
||
//painter->setPen(p);
|
||
//painter->drawLine(start, pt1);
|
||
//painter->drawLine(pt1, pt2);
|
||
//painter->drawLine(pt1, vtarget);
|
||
|
||
//p.setColor(Qt::green);
|
||
//painter->setPen(p);
|
||
//painter->drawRect(option->rect);
|
||
|
||
painter->restore();
|
||
}
|