mirror of
https://github.com/Palm1r/QodeAssist.git
synced 2025-11-22 02:22:44 -05:00
feat: Add basic task flow run and edit (#212)
This commit is contained in:
14
TaskFlow/CMakeLists.txt
Normal file
14
TaskFlow/CMakeLists.txt
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
add_subdirectory(core)
|
||||||
|
add_subdirectory(Editor)
|
||||||
|
# add_subdirectory(serialization)
|
||||||
|
# add_subdirectory(tasks)
|
||||||
|
|
||||||
|
qt_add_library(TaskFlow STATIC)
|
||||||
|
|
||||||
|
target_link_libraries(TaskFlow
|
||||||
|
PUBLIC
|
||||||
|
TaskFlowCore
|
||||||
|
TaskFlowEditorplugin
|
||||||
|
# TaskFlowSerialization
|
||||||
|
# TaskFlowTasks
|
||||||
|
)
|
||||||
41
TaskFlow/Editor/CMakeLists.txt
Normal file
41
TaskFlow/Editor/CMakeLists.txt
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
qt_add_library(TaskFlowEditor STATIC)
|
||||||
|
|
||||||
|
qt_policy(SET QTP0001 NEW)
|
||||||
|
qt_policy(SET QTP0004 NEW)
|
||||||
|
|
||||||
|
qt_add_qml_module(TaskFlowEditor
|
||||||
|
URI TaskFlow.Editor
|
||||||
|
VERSION 1.0
|
||||||
|
DEPENDENCIES QtQuick
|
||||||
|
RESOURCES
|
||||||
|
QML_FILES
|
||||||
|
qml/FlowEditorView.qml
|
||||||
|
qml/Flow.qml
|
||||||
|
qml/Task.qml
|
||||||
|
qml/TaskPort.qml
|
||||||
|
qml/TaskParameter.qml
|
||||||
|
qml/TaskConnection.qml
|
||||||
|
SOURCES
|
||||||
|
FlowEditor.hpp FlowEditor.cpp
|
||||||
|
FlowsModel.hpp FlowsModel.cpp
|
||||||
|
TaskItem.hpp TaskItem.cpp
|
||||||
|
FlowItem.hpp FlowItem.cpp
|
||||||
|
TaskModel.hpp TaskModel.cpp
|
||||||
|
TaskPortItem.hpp TaskPortItem.cpp
|
||||||
|
TaskPortModel.hpp TaskPortModel.cpp
|
||||||
|
TaskConnectionsModel.hpp TaskConnectionsModel.cpp
|
||||||
|
TaskConnectionItem.hpp TaskConnectionItem.cpp
|
||||||
|
GridBackground.hpp GridBackground.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(TaskFlowEditor
|
||||||
|
PUBLIC
|
||||||
|
Qt::Quick
|
||||||
|
PRIVATE
|
||||||
|
TaskFlowCore
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(TaskFlowEditor
|
||||||
|
PUBLIC
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}
|
||||||
|
)
|
||||||
120
TaskFlow/Editor/FlowEditor.cpp
Normal file
120
TaskFlow/Editor/FlowEditor.cpp
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https:
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "FlowEditor.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::TaskFlow {
|
||||||
|
|
||||||
|
FlowEditor::FlowEditor(QQuickItem *parent)
|
||||||
|
: QQuickItem(parent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void FlowEditor::initialize()
|
||||||
|
{
|
||||||
|
emit availableTaskTypesChanged();
|
||||||
|
emit availableFlowsChanged();
|
||||||
|
|
||||||
|
m_flowsModel = new FlowsModel(m_flowManager, this);
|
||||||
|
|
||||||
|
emit flowsModelChanged();
|
||||||
|
|
||||||
|
if (m_flowsModel->rowCount() > 0) {
|
||||||
|
setCurrentFlowIndex(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// setCurrentFlowId(m_flowManager->flows().begin().value()->flowId());
|
||||||
|
m_currentFlow = m_flowManager->getFlow();
|
||||||
|
emit currentFlowChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FlowEditor::currentFlowId() const
|
||||||
|
{
|
||||||
|
return m_currentFlowId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlowEditor::setCurrentFlowId(const QString &newCurrentFlowId)
|
||||||
|
{
|
||||||
|
if (m_currentFlowId == newCurrentFlowId)
|
||||||
|
return;
|
||||||
|
m_currentFlowId = newCurrentFlowId;
|
||||||
|
emit currentFlowIdChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList FlowEditor::availableTaskTypes() const
|
||||||
|
{
|
||||||
|
if (m_flowManager)
|
||||||
|
return m_flowManager->getAvailableTasksTypes();
|
||||||
|
else {
|
||||||
|
return {"No flow manager"};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList FlowEditor::availableFlows() const
|
||||||
|
{
|
||||||
|
if (m_flowManager) {
|
||||||
|
auto flows = m_flowManager->getAvailableFlows();
|
||||||
|
return flows.size() > 0 ? flows : QStringList{"No flows"};
|
||||||
|
} else {
|
||||||
|
return {"No flow manager"};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlowEditor::setFlowManager(FlowManager *newFlowManager)
|
||||||
|
{
|
||||||
|
if (m_flowManager == newFlowManager)
|
||||||
|
return;
|
||||||
|
m_flowManager = newFlowManager;
|
||||||
|
|
||||||
|
initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
FlowsModel *FlowEditor::flowsModel() const
|
||||||
|
{
|
||||||
|
return m_flowsModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FlowEditor::currentFlowIndex() const
|
||||||
|
{
|
||||||
|
return m_currentFlowIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlowEditor::setCurrentFlowIndex(int newCurrentFlowIndex)
|
||||||
|
{
|
||||||
|
if (m_currentFlowIndex == newCurrentFlowIndex)
|
||||||
|
return;
|
||||||
|
m_currentFlowIndex = newCurrentFlowIndex;
|
||||||
|
emit currentFlowIndexChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
Flow *FlowEditor::getFlow(const QString &flowName)
|
||||||
|
{
|
||||||
|
return m_flowManager->getFlow(flowName);
|
||||||
|
}
|
||||||
|
|
||||||
|
Flow *FlowEditor::getCurrentFlow()
|
||||||
|
{
|
||||||
|
return m_flowManager->getFlow(m_currentFlowId);
|
||||||
|
}
|
||||||
|
|
||||||
|
Flow *FlowEditor::currentFlow() const
|
||||||
|
{
|
||||||
|
return m_currentFlow;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QodeAssist::TaskFlow
|
||||||
86
TaskFlow/Editor/FlowEditor.hpp
Normal file
86
TaskFlow/Editor/FlowEditor.hpp
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https:
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QQuickItem>
|
||||||
|
|
||||||
|
#include "FlowsModel.hpp"
|
||||||
|
#include <FlowManager.hpp>
|
||||||
|
|
||||||
|
namespace QodeAssist::TaskFlow {
|
||||||
|
|
||||||
|
class FlowEditor : public QQuickItem
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QML_ELEMENT
|
||||||
|
|
||||||
|
Q_PROPERTY(
|
||||||
|
QString currentFlowId READ currentFlowId WRITE setCurrentFlowId NOTIFY currentFlowIdChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
QStringList availableTaskTypes READ availableTaskTypes NOTIFY availableTaskTypesChanged)
|
||||||
|
Q_PROPERTY(QStringList availableFlows READ availableFlows NOTIFY availableFlowsChanged)
|
||||||
|
Q_PROPERTY(FlowsModel *flowsModel READ flowsModel NOTIFY flowsModelChanged)
|
||||||
|
Q_PROPERTY(int currentFlowIndex READ currentFlowIndex WRITE setCurrentFlowIndex NOTIFY
|
||||||
|
currentFlowIndexChanged)
|
||||||
|
|
||||||
|
Q_PROPERTY(Flow *currentFlow READ currentFlow NOTIFY currentFlowChanged FINAL)
|
||||||
|
|
||||||
|
public:
|
||||||
|
FlowEditor(QQuickItem *parent = nullptr);
|
||||||
|
|
||||||
|
void initialize();
|
||||||
|
|
||||||
|
QString currentFlowId() const;
|
||||||
|
void setCurrentFlowId(const QString &newCurrentFlowId);
|
||||||
|
|
||||||
|
QStringList availableTaskTypes() const;
|
||||||
|
QStringList availableFlows() const;
|
||||||
|
|
||||||
|
void setFlowManager(FlowManager *newFlowManager);
|
||||||
|
|
||||||
|
FlowsModel *flowsModel() const;
|
||||||
|
|
||||||
|
int currentFlowIndex() const;
|
||||||
|
void setCurrentFlowIndex(int newCurrentFlowIndex);
|
||||||
|
|
||||||
|
Q_INVOKABLE Flow *getFlow(const QString &flowName);
|
||||||
|
Q_INVOKABLE Flow *getCurrentFlow();
|
||||||
|
|
||||||
|
Flow *currentFlow() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void currentFlowIdChanged();
|
||||||
|
void availableTaskTypesChanged();
|
||||||
|
void availableFlowsChanged();
|
||||||
|
void flowsModelChanged();
|
||||||
|
|
||||||
|
void currentFlowIndexChanged();
|
||||||
|
|
||||||
|
void currentFlowChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
FlowManager *m_flowManager = nullptr;
|
||||||
|
QString m_currentFlowId;
|
||||||
|
FlowsModel *m_flowsModel;
|
||||||
|
int m_currentFlowIndex;
|
||||||
|
Flow *m_currentFlow = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::TaskFlow
|
||||||
90
TaskFlow/Editor/FlowItem.cpp
Normal file
90
TaskFlow/Editor/FlowItem.cpp
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
#include "FlowItem.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::TaskFlow {
|
||||||
|
|
||||||
|
FlowItem::FlowItem(QQuickItem *parent)
|
||||||
|
: QQuickItem(parent)
|
||||||
|
{
|
||||||
|
connect(this, &QQuickItem::childrenChanged, this, [this]() { updateFlowLayout(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FlowItem::flowId() const
|
||||||
|
{
|
||||||
|
if (!m_flow)
|
||||||
|
return {"no flow"};
|
||||||
|
return m_flow->flowId();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlowItem::setFlowId(const QString &newFlowId)
|
||||||
|
{
|
||||||
|
if (m_flow->flowId() == newFlowId)
|
||||||
|
return;
|
||||||
|
m_flow->setFlowId(newFlowId);
|
||||||
|
emit flowIdChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
Flow *FlowItem::flow() const
|
||||||
|
{
|
||||||
|
return m_flow;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlowItem::setFlow(Flow *newFlow)
|
||||||
|
{
|
||||||
|
if (m_flow == newFlow)
|
||||||
|
return;
|
||||||
|
m_flow = newFlow;
|
||||||
|
emit flowChanged();
|
||||||
|
emit flowIdChanged();
|
||||||
|
qDebug() << "FlowItem::setFlow" << m_flow->flowId() << newFlow;
|
||||||
|
|
||||||
|
m_taskModel = new TaskModel(m_flow, this);
|
||||||
|
m_connectionsModel = new TaskConnectionsModel(m_flow, this);
|
||||||
|
|
||||||
|
emit taskModelChanged();
|
||||||
|
emit connectionsModelChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskModel *FlowItem::taskModel() const
|
||||||
|
{
|
||||||
|
return m_taskModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskConnectionsModel *FlowItem::connectionsModel() const
|
||||||
|
{
|
||||||
|
return m_connectionsModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantList FlowItem::taskItems() const
|
||||||
|
{
|
||||||
|
return m_taskItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlowItem::setTaskItems(const QVariantList &newTaskItems)
|
||||||
|
{
|
||||||
|
qDebug() << "FlowItem::setTaskItems" << newTaskItems;
|
||||||
|
if (m_taskItems == newTaskItems)
|
||||||
|
return;
|
||||||
|
m_taskItems = newTaskItems;
|
||||||
|
emit taskItemsChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlowItem::updateFlowLayout()
|
||||||
|
{
|
||||||
|
auto allItems = this->childItems();
|
||||||
|
|
||||||
|
for (auto child : allItems) {
|
||||||
|
if (child->objectName() == QString("TaskItem")) {
|
||||||
|
qDebug() << "Found TaskItem:" << child;
|
||||||
|
auto taskItem = qobject_cast<TaskItem *>(child);
|
||||||
|
m_taskItemsList.insert(taskItem, taskItem->task());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (child->objectName() == QString("TaskConnectionItem")) {
|
||||||
|
qDebug() << "Found TaskConnectionItem:" << child;
|
||||||
|
auto connectionItem = qobject_cast<TaskConnectionItem *>(child);
|
||||||
|
m_taskConnectionsList.insert(connectionItem, connectionItem->connection());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QodeAssist::TaskFlow
|
||||||
61
TaskFlow/Editor/FlowItem.hpp
Normal file
61
TaskFlow/Editor/FlowItem.hpp
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QQuickItem>
|
||||||
|
|
||||||
|
#include "TaskConnectionItem.hpp"
|
||||||
|
#include "TaskConnectionsModel.hpp"
|
||||||
|
#include "TaskItem.hpp"
|
||||||
|
#include "TaskModel.hpp"
|
||||||
|
#include <Flow.hpp>
|
||||||
|
#include <TaskConnection.hpp>
|
||||||
|
|
||||||
|
namespace QodeAssist::TaskFlow {
|
||||||
|
|
||||||
|
class FlowItem : public QQuickItem
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QML_ELEMENT
|
||||||
|
|
||||||
|
Q_PROPERTY(QString flowId READ flowId WRITE setFlowId NOTIFY flowIdChanged)
|
||||||
|
Q_PROPERTY(Flow *flow READ flow WRITE setFlow NOTIFY flowChanged)
|
||||||
|
Q_PROPERTY(TaskModel *taskModel READ taskModel NOTIFY taskModelChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
TaskConnectionsModel *connectionsModel READ connectionsModel NOTIFY connectionsModelChanged)
|
||||||
|
Q_PROPERTY(QVariantList taskItems READ taskItems WRITE setTaskItems NOTIFY taskItemsChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit FlowItem(QQuickItem *parent = nullptr);
|
||||||
|
|
||||||
|
QString flowId() const;
|
||||||
|
void setFlowId(const QString &newFlowId);
|
||||||
|
|
||||||
|
Flow *flow() const;
|
||||||
|
void setFlow(Flow *newFlow);
|
||||||
|
|
||||||
|
TaskModel *taskModel() const;
|
||||||
|
|
||||||
|
TaskConnectionsModel *connectionsModel() const;
|
||||||
|
|
||||||
|
QVariantList taskItems() const;
|
||||||
|
void setTaskItems(const QVariantList &newTaskItems);
|
||||||
|
|
||||||
|
void updateFlowLayout();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void flowIdChanged();
|
||||||
|
void flowChanged();
|
||||||
|
void taskModelChanged();
|
||||||
|
void connectionsModelChanged();
|
||||||
|
void taskItemsChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Flow *m_flow = nullptr;
|
||||||
|
TaskModel *m_taskModel = nullptr;
|
||||||
|
TaskConnectionsModel *m_connectionsModel = nullptr;
|
||||||
|
QVariantList m_taskItems;
|
||||||
|
|
||||||
|
QHash<TaskItem *, BaseTask *> m_taskItemsList;
|
||||||
|
QHash<TaskConnectionItem *, TaskConnection *> m_taskConnectionsList;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::TaskFlow
|
||||||
54
TaskFlow/Editor/FlowsModel.cpp
Normal file
54
TaskFlow/Editor/FlowsModel.cpp
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#include "FlowsModel.hpp"
|
||||||
|
|
||||||
|
#include "FlowManager.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::TaskFlow {
|
||||||
|
|
||||||
|
FlowsModel::FlowsModel(FlowManager *flowManager, QObject *parent)
|
||||||
|
: QAbstractListModel(parent)
|
||||||
|
, m_flowManager(flowManager)
|
||||||
|
{
|
||||||
|
connect(m_flowManager, &FlowManager::flowAdded, this, &FlowsModel::onFlowAdded);
|
||||||
|
}
|
||||||
|
|
||||||
|
int FlowsModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
return m_flowManager->flows().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant FlowsModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (!index.isValid() || !m_flowManager || index.row() >= m_flowManager->flows().size())
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
const auto flows = m_flowManager->flows().values();
|
||||||
|
|
||||||
|
switch (role) {
|
||||||
|
case FlowRoles::FlowIdRole:
|
||||||
|
return flows.at(index.row())->flowId();
|
||||||
|
case FlowRoles::FlowDataRole:
|
||||||
|
return QVariant::fromValue(flows.at(index.row()));
|
||||||
|
default:
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<int, QByteArray> FlowsModel::roleNames() const
|
||||||
|
{
|
||||||
|
QHash<int, QByteArray> roles;
|
||||||
|
roles[FlowRoles::FlowIdRole] = "flowId";
|
||||||
|
roles[FlowRoles::FlowDataRole] = "flowData";
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlowsModel::onFlowAdded(const QString &flowId)
|
||||||
|
{
|
||||||
|
// qDebug() << "FlowsModel::Flow added: " << flowId;
|
||||||
|
// int newIndex = m_flowManager->flows().size();
|
||||||
|
// beginInsertRows(QModelIndex(), newIndex, newIndex);
|
||||||
|
// endInsertRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlowsModel::onFlowRemoved(const QString &flowId) {}
|
||||||
|
|
||||||
|
} // namespace QodeAssist::TaskFlow
|
||||||
31
TaskFlow/Editor/FlowsModel.hpp
Normal file
31
TaskFlow/Editor/FlowsModel.hpp
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
// #include "tasks/Flow.hpp"
|
||||||
|
#include <FlowManager.hpp>
|
||||||
|
|
||||||
|
namespace QodeAssist::TaskFlow {
|
||||||
|
|
||||||
|
class FlowsModel : public QAbstractListModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
enum FlowRoles { FlowIdRole = Qt::UserRole, FlowDataRole };
|
||||||
|
|
||||||
|
FlowsModel(FlowManager *flowManager, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void onFlowAdded(const QString &flowId);
|
||||||
|
void onFlowRemoved(const QString &flowId);
|
||||||
|
|
||||||
|
private:
|
||||||
|
FlowManager *m_flowManager;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::TaskFlow
|
||||||
98
TaskFlow/Editor/GridBackground.cpp
Normal file
98
TaskFlow/Editor/GridBackground.cpp
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "GridBackground.hpp"
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QPixmap>
|
||||||
|
#include <QQuickWindow>
|
||||||
|
#include <QSGSimpleRectNode>
|
||||||
|
#include <QSGSimpleTextureNode>
|
||||||
|
|
||||||
|
namespace QodeAssist::TaskFlow {
|
||||||
|
|
||||||
|
GridBackground::GridBackground(QQuickItem *parent)
|
||||||
|
: QQuickItem(parent)
|
||||||
|
{
|
||||||
|
setFlag(QQuickItem::ItemHasContents, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
int GridBackground::gridSize() const
|
||||||
|
{
|
||||||
|
return m_gridSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GridBackground::setGridSize(int size)
|
||||||
|
{
|
||||||
|
if (m_gridSize != size) {
|
||||||
|
m_gridSize = size;
|
||||||
|
update();
|
||||||
|
emit gridSizeChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor GridBackground::gridColor() const
|
||||||
|
{
|
||||||
|
return m_gridColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GridBackground::setGridColor(const QColor &color)
|
||||||
|
{
|
||||||
|
if (m_gridColor != color) {
|
||||||
|
m_gridColor = color;
|
||||||
|
update();
|
||||||
|
emit gridColorChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QSGNode *GridBackground::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
|
||||||
|
{
|
||||||
|
QSGSimpleTextureNode *node = static_cast<QSGSimpleTextureNode *>(oldNode);
|
||||||
|
if (!node) {
|
||||||
|
node = new QSGSimpleTextureNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap pixmap(width(), height());
|
||||||
|
pixmap.fill(Qt::transparent);
|
||||||
|
|
||||||
|
QPainter painter(&pixmap);
|
||||||
|
painter.setRenderHint(QPainter::Antialiasing, false);
|
||||||
|
|
||||||
|
QPen pen(m_gridColor);
|
||||||
|
pen.setWidth(1);
|
||||||
|
painter.setPen(pen);
|
||||||
|
painter.setOpacity(this->opacity());
|
||||||
|
|
||||||
|
for (int x = 0; x < width(); x += m_gridSize) {
|
||||||
|
painter.drawLine(x, 0, x, height());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int y = 0; y < height(); y += m_gridSize) {
|
||||||
|
painter.drawLine(0, y, width(), y);
|
||||||
|
}
|
||||||
|
|
||||||
|
painter.end();
|
||||||
|
|
||||||
|
QSGTexture *texture = window()->createTextureFromImage(pixmap.toImage());
|
||||||
|
node->setTexture(texture);
|
||||||
|
node->setRect(boundingRect());
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QodeAssist::TaskFlow
|
||||||
57
TaskFlow/Editor/GridBackground.hpp
Normal file
57
TaskFlow/Editor/GridBackground.hpp
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QColor>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QQuickItem>
|
||||||
|
|
||||||
|
namespace QodeAssist::TaskFlow {
|
||||||
|
|
||||||
|
class GridBackground : public QQuickItem
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QML_ELEMENT
|
||||||
|
|
||||||
|
Q_PROPERTY(int gridSize READ gridSize WRITE setGridSize NOTIFY gridSizeChanged)
|
||||||
|
Q_PROPERTY(QColor gridColor READ gridColor WRITE setGridColor NOTIFY gridColorChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit GridBackground(QQuickItem *parent = nullptr);
|
||||||
|
|
||||||
|
int gridSize() const;
|
||||||
|
void setGridSize(int size);
|
||||||
|
|
||||||
|
QColor gridColor() const;
|
||||||
|
void setGridColor(const QColor &color);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void gridSizeChanged();
|
||||||
|
void gridColorChanged();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_gridSize = 20;
|
||||||
|
QColor m_gridColor = QColor(128, 128, 128);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::TaskFlow
|
||||||
153
TaskFlow/Editor/TaskConnectionItem.cpp
Normal file
153
TaskFlow/Editor/TaskConnectionItem.cpp
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
#include "TaskConnectionItem.hpp"
|
||||||
|
#include "TaskItem.hpp"
|
||||||
|
#include "TaskPortItem.hpp"
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
namespace QodeAssist::TaskFlow {
|
||||||
|
|
||||||
|
TaskConnectionItem::TaskConnectionItem(QQuickItem *parent)
|
||||||
|
: QQuickItem(parent)
|
||||||
|
{
|
||||||
|
setObjectName("TaskConnectionItem");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskConnectionItem::setConnection(TaskConnection *connection)
|
||||||
|
{
|
||||||
|
if (m_connection == connection)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_connection = connection;
|
||||||
|
emit connectionChanged();
|
||||||
|
|
||||||
|
calculatePositions();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskConnectionItem::updatePositions()
|
||||||
|
{
|
||||||
|
// calculatePositions();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskConnectionItem::calculatePositions()
|
||||||
|
{
|
||||||
|
if (!m_connection) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find source task item
|
||||||
|
QQuickItem *sourceTaskItem = findTaskItem(m_connection->sourceTask());
|
||||||
|
QQuickItem *targetTaskItem = findTaskItem(m_connection->targetTask());
|
||||||
|
|
||||||
|
if (!sourceTaskItem || !targetTaskItem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find port items within tasks
|
||||||
|
QQuickItem *sourcePortItem = findPortItem(sourceTaskItem, m_connection->sourcePort());
|
||||||
|
QQuickItem *targetPortItem = findPortItem(targetTaskItem, m_connection->targetPort());
|
||||||
|
|
||||||
|
if (!sourcePortItem || !targetPortItem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate global positions
|
||||||
|
QPointF sourceGlobal
|
||||||
|
= sourcePortItem
|
||||||
|
->mapToItem(parentItem(), sourcePortItem->width() / 2, sourcePortItem->height() / 2);
|
||||||
|
QPointF targetGlobal
|
||||||
|
= targetPortItem
|
||||||
|
->mapToItem(parentItem(), targetPortItem->width() / 2, targetPortItem->height() / 2);
|
||||||
|
|
||||||
|
if (m_startPoint != sourceGlobal) {
|
||||||
|
m_startPoint = sourceGlobal;
|
||||||
|
emit startPointChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_endPoint != targetGlobal) {
|
||||||
|
m_endPoint = targetGlobal;
|
||||||
|
emit endPointChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QQuickItem *TaskConnectionItem::findTaskItem(BaseTask *task)
|
||||||
|
{
|
||||||
|
for (const QVariant &item : m_taskItems) {
|
||||||
|
QQuickItem *taskItem = qvariant_cast<QQuickItem *>(item);
|
||||||
|
if (!taskItem)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
QVariant taskProp = taskItem->property("task");
|
||||||
|
if (taskProp.isValid() && taskProp.value<BaseTask *>() == task) {
|
||||||
|
return taskItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
QQuickItem *TaskConnectionItem::findTaskItemRecursive(QQuickItem *item, BaseTask *task)
|
||||||
|
{
|
||||||
|
// Проверяем objectName и task property
|
||||||
|
if (item->objectName() == "TaskItem") {
|
||||||
|
QVariant taskProp = item->property("task");
|
||||||
|
if (taskProp.isValid()) {
|
||||||
|
BaseTask *itemTask = taskProp.value<BaseTask *>();
|
||||||
|
if (itemTask == task) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Рекурсивно ищем в детях
|
||||||
|
auto children = item->childItems();
|
||||||
|
|
||||||
|
for (QQuickItem *child : children) {
|
||||||
|
if (QQuickItem *found = findTaskItemRecursive(child, task)) {
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
QQuickItem *TaskConnectionItem::findPortItem(QQuickItem *taskItem, TaskPort *port)
|
||||||
|
{
|
||||||
|
std::function<QQuickItem *(QQuickItem *)> findPortRecursive =
|
||||||
|
[&](QQuickItem *item) -> QQuickItem * {
|
||||||
|
// Проверяем objectName и port property
|
||||||
|
if (item->objectName() == "TaskPortItem") {
|
||||||
|
QVariant portProp = item->property("port");
|
||||||
|
if (portProp.isValid()) {
|
||||||
|
TaskPort *itemPort = portProp.value<TaskPort *>();
|
||||||
|
if (itemPort == port) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Рекурсивно ищем в детях
|
||||||
|
for (QQuickItem *child : item->childItems()) {
|
||||||
|
if (QQuickItem *found = findPortRecursive(child)) {
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
return findPortRecursive(taskItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantList TaskConnectionItem::taskItems() const
|
||||||
|
{
|
||||||
|
return m_taskItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskConnectionItem::setTaskItems(const QVariantList &newTaskItems)
|
||||||
|
{
|
||||||
|
if (m_taskItems == newTaskItems)
|
||||||
|
return;
|
||||||
|
m_taskItems = newTaskItems;
|
||||||
|
emit taskItemsChanged();
|
||||||
|
|
||||||
|
calculatePositions();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QodeAssist::TaskFlow
|
||||||
55
TaskFlow/Editor/TaskConnectionItem.hpp
Normal file
55
TaskFlow/Editor/TaskConnectionItem.hpp
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "TaskConnection.hpp"
|
||||||
|
#include <QPointF>
|
||||||
|
#include <QQuickItem>
|
||||||
|
|
||||||
|
namespace QodeAssist::TaskFlow {
|
||||||
|
|
||||||
|
class TaskConnectionItem : public QQuickItem
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QML_ELEMENT
|
||||||
|
|
||||||
|
Q_PROPERTY(QPointF startPoint READ startPoint NOTIFY startPointChanged)
|
||||||
|
Q_PROPERTY(QPointF endPoint READ endPoint NOTIFY endPointChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
TaskConnection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
|
||||||
|
|
||||||
|
Q_PROPERTY(QVariantList taskItems READ taskItems WRITE setTaskItems NOTIFY taskItemsChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
TaskConnectionItem(QQuickItem *parent = nullptr);
|
||||||
|
|
||||||
|
QPointF startPoint() const { return m_startPoint; }
|
||||||
|
QPointF endPoint() const { return m_endPoint; }
|
||||||
|
|
||||||
|
TaskConnection *connection() const { return m_connection; }
|
||||||
|
void setConnection(TaskConnection *connection);
|
||||||
|
|
||||||
|
Q_INVOKABLE void updatePositions();
|
||||||
|
|
||||||
|
QVariantList taskItems() const;
|
||||||
|
void setTaskItems(const QVariantList &newTaskItems);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void startPointChanged();
|
||||||
|
void endPointChanged();
|
||||||
|
void connectionChanged();
|
||||||
|
|
||||||
|
void taskItemsChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void calculatePositions();
|
||||||
|
QQuickItem *findTaskItem(BaseTask *task);
|
||||||
|
QQuickItem *findTaskItemRecursive(QQuickItem *item, BaseTask *task);
|
||||||
|
QQuickItem *findPortItem(QQuickItem *taskItem, TaskPort *port);
|
||||||
|
|
||||||
|
private:
|
||||||
|
TaskConnection *m_connection = nullptr;
|
||||||
|
QPointF m_startPoint;
|
||||||
|
QPointF m_endPoint;
|
||||||
|
QVariantList m_taskItems;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::TaskFlow
|
||||||
29
TaskFlow/Editor/TaskConnectionsModel.cpp
Normal file
29
TaskFlow/Editor/TaskConnectionsModel.cpp
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#include "TaskConnectionsModel.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::TaskFlow {
|
||||||
|
|
||||||
|
TaskConnectionsModel::TaskConnectionsModel(Flow *flow, QObject *parent)
|
||||||
|
: QAbstractListModel(parent)
|
||||||
|
, m_flow(flow)
|
||||||
|
{}
|
||||||
|
|
||||||
|
int TaskConnectionsModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
return m_flow->connections().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant TaskConnectionsModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (role == TaskConnectionsRoles::TaskConnectionsRole)
|
||||||
|
return QVariant::fromValue(m_flow->connections().at(index.row()));
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<int, QByteArray> TaskConnectionsModel::roleNames() const
|
||||||
|
{
|
||||||
|
QHash<int, QByteArray> roles;
|
||||||
|
roles[TaskConnectionsRoles::TaskConnectionsRole] = "connectionData";
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QodeAssist::TaskFlow
|
||||||
25
TaskFlow/Editor/TaskConnectionsModel.hpp
Normal file
25
TaskFlow/Editor/TaskConnectionsModel.hpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include <Flow.hpp>
|
||||||
|
|
||||||
|
namespace QodeAssist::TaskFlow {
|
||||||
|
|
||||||
|
class TaskConnectionsModel : public QAbstractListModel
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum TaskConnectionsRoles { TaskConnectionsRole = Qt::UserRole };
|
||||||
|
|
||||||
|
explicit TaskConnectionsModel(Flow *flow, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Flow *m_flow;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::TaskFlow
|
||||||
69
TaskFlow/Editor/TaskItem.cpp
Normal file
69
TaskFlow/Editor/TaskItem.cpp
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#include "TaskItem.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::TaskFlow {
|
||||||
|
|
||||||
|
TaskItem::TaskItem(QQuickItem *parent)
|
||||||
|
: QQuickItem(parent)
|
||||||
|
{
|
||||||
|
setObjectName("TaskItem");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TaskItem::taskId() const
|
||||||
|
{
|
||||||
|
return m_taskId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskItem::setTaskId(const QString &newTaskId)
|
||||||
|
{
|
||||||
|
if (m_taskId == newTaskId)
|
||||||
|
return;
|
||||||
|
m_taskId = newTaskId;
|
||||||
|
emit taskIdChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TaskItem::taskType() const
|
||||||
|
{
|
||||||
|
return m_task ? m_task->taskType() : QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseTask *TaskItem::task() const
|
||||||
|
{
|
||||||
|
return m_task;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskItem::setTask(BaseTask *newTask)
|
||||||
|
{
|
||||||
|
if (m_task == newTask)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_task = newTask;
|
||||||
|
|
||||||
|
if (m_task) {
|
||||||
|
m_taskId = m_task->taskId();
|
||||||
|
|
||||||
|
// Обновляем модели портов
|
||||||
|
m_inputPorts = new TaskPortModel(m_task->getInputPorts(), this);
|
||||||
|
m_outputPorts = new TaskPortModel(m_task->getOutputPorts(), this);
|
||||||
|
} else {
|
||||||
|
m_inputPorts = nullptr;
|
||||||
|
m_outputPorts = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit taskChanged();
|
||||||
|
emit inputPortsChanged();
|
||||||
|
emit outputPortsChanged();
|
||||||
|
emit taskIdChanged();
|
||||||
|
emit taskTypeChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskPortModel *TaskItem::inputPorts() const
|
||||||
|
{
|
||||||
|
return m_inputPorts;
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskPortModel *TaskItem::outputPorts() const
|
||||||
|
{
|
||||||
|
return m_outputPorts;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QodeAssist::TaskFlow
|
||||||
49
TaskFlow/Editor/TaskItem.hpp
Normal file
49
TaskFlow/Editor/TaskItem.hpp
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QQuickItem>
|
||||||
|
|
||||||
|
#include "TaskPortModel.hpp"
|
||||||
|
#include <BaseTask.hpp>
|
||||||
|
#include <TaskPort.hpp>
|
||||||
|
|
||||||
|
namespace QodeAssist::TaskFlow {
|
||||||
|
|
||||||
|
class TaskItem : public QQuickItem
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QML_ELEMENT
|
||||||
|
|
||||||
|
Q_PROPERTY(QString taskId READ taskId WRITE setTaskId NOTIFY taskIdChanged)
|
||||||
|
Q_PROPERTY(QString taskType READ taskType NOTIFY taskTypeChanged)
|
||||||
|
Q_PROPERTY(BaseTask *task READ task WRITE setTask NOTIFY taskChanged)
|
||||||
|
Q_PROPERTY(TaskPortModel *inputPorts READ inputPorts NOTIFY inputPortsChanged)
|
||||||
|
Q_PROPERTY(TaskPortModel *outputPorts READ outputPorts NOTIFY outputPortsChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
TaskItem(QQuickItem *parent = nullptr);
|
||||||
|
|
||||||
|
QString taskId() const;
|
||||||
|
void setTaskId(const QString &newTaskId);
|
||||||
|
QString taskType() const;
|
||||||
|
|
||||||
|
BaseTask *task() const;
|
||||||
|
void setTask(BaseTask *newTask);
|
||||||
|
|
||||||
|
TaskPortModel *inputPorts() const;
|
||||||
|
TaskPortModel *outputPorts() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void taskIdChanged();
|
||||||
|
void taskTypeChanged();
|
||||||
|
void taskChanged();
|
||||||
|
void inputPortsChanged();
|
||||||
|
void outputPortsChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_taskId;
|
||||||
|
BaseTask *m_task = nullptr;
|
||||||
|
TaskPortModel *m_inputPorts = nullptr;
|
||||||
|
TaskPortModel *m_outputPorts = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::TaskFlow
|
||||||
40
TaskFlow/Editor/TaskModel.cpp
Normal file
40
TaskFlow/Editor/TaskModel.cpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#include "TaskModel.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::TaskFlow {
|
||||||
|
|
||||||
|
TaskModel::TaskModel(Flow *flow, QObject *parent)
|
||||||
|
: QAbstractListModel(parent)
|
||||||
|
, m_flow(flow)
|
||||||
|
{}
|
||||||
|
|
||||||
|
int TaskModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
return m_flow->tasks().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant TaskModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (!index.isValid() || !m_flow || index.row() >= m_flow->tasks().size())
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
const auto &task = m_flow->tasks().values();
|
||||||
|
|
||||||
|
switch (role) {
|
||||||
|
case TaskRoles::TaskIdRole:
|
||||||
|
return task.at(index.row())->taskId();
|
||||||
|
case TaskRoles::TaskDataRole:
|
||||||
|
return QVariant::fromValue(task.at(index.row()));
|
||||||
|
default:
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<int, QByteArray> TaskModel::roleNames() const
|
||||||
|
{
|
||||||
|
QHash<int, QByteArray> roles;
|
||||||
|
roles[TaskRoles::TaskIdRole] = "taskId";
|
||||||
|
roles[TaskRoles::TaskDataRole] = "taskData";
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QodeAssist::TaskFlow
|
||||||
25
TaskFlow/Editor/TaskModel.hpp
Normal file
25
TaskFlow/Editor/TaskModel.hpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
|
||||||
|
#include <Flow.hpp>
|
||||||
|
|
||||||
|
namespace QodeAssist::TaskFlow {
|
||||||
|
|
||||||
|
class TaskModel : public QAbstractListModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
enum TaskRoles { TaskIdRole = Qt::UserRole, TaskDataRole };
|
||||||
|
|
||||||
|
TaskModel(Flow *flow, QObject *parent);
|
||||||
|
|
||||||
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Flow *m_flow;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::TaskFlow
|
||||||
29
TaskFlow/Editor/TaskPortItem.cpp
Normal file
29
TaskFlow/Editor/TaskPortItem.cpp
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#include "TaskPortItem.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::TaskFlow {
|
||||||
|
|
||||||
|
TaskPortItem::TaskPortItem(QQuickItem *parent)
|
||||||
|
: QQuickItem(parent)
|
||||||
|
{
|
||||||
|
setObjectName("TaskPortItem");
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskPort *TaskPortItem::port() const
|
||||||
|
{
|
||||||
|
return m_port;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskPortItem::setPort(TaskPort *newPort)
|
||||||
|
{
|
||||||
|
if (m_port == newPort)
|
||||||
|
return;
|
||||||
|
m_port = newPort;
|
||||||
|
emit portChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TaskPortItem::name() const
|
||||||
|
{
|
||||||
|
return m_port->name();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QodeAssist::TaskFlow
|
||||||
31
TaskFlow/Editor/TaskPortItem.hpp
Normal file
31
TaskFlow/Editor/TaskPortItem.hpp
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <TaskPort.hpp>
|
||||||
|
#include <QQuickItem>
|
||||||
|
|
||||||
|
namespace QodeAssist::TaskFlow {
|
||||||
|
|
||||||
|
class TaskPortItem : public QQuickItem
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QML_ELEMENT
|
||||||
|
|
||||||
|
Q_PROPERTY(TaskPort *port READ port WRITE setPort NOTIFY portChanged)
|
||||||
|
Q_PROPERTY(QString name READ name CONSTANT)
|
||||||
|
|
||||||
|
public:
|
||||||
|
TaskPortItem(QQuickItem *parent = nullptr);
|
||||||
|
|
||||||
|
TaskPort *port() const;
|
||||||
|
void setPort(TaskPort *newPort);
|
||||||
|
|
||||||
|
QString name() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void portChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
TaskPort *m_port = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::TaskFlow
|
||||||
39
TaskFlow/Editor/TaskPortModel.cpp
Normal file
39
TaskFlow/Editor/TaskPortModel.cpp
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#include "TaskPortModel.hpp"
|
||||||
|
#include "TaskPort.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::TaskFlow {
|
||||||
|
|
||||||
|
TaskPortModel::TaskPortModel(const QList<TaskPort *> &ports, QObject *parent)
|
||||||
|
: QAbstractListModel(parent)
|
||||||
|
, m_ports(ports)
|
||||||
|
{}
|
||||||
|
|
||||||
|
int TaskPortModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
return m_ports.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant TaskPortModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (!index.isValid() || index.row() >= m_ports.size())
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
switch (role) {
|
||||||
|
case TaskPortRoles::TaskPortNameRole:
|
||||||
|
return m_ports.at(index.row())->name();
|
||||||
|
case TaskPortRoles::TaskPortDataRole:
|
||||||
|
return QVariant::fromValue(m_ports.at(index.row()));
|
||||||
|
default:
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<int, QByteArray> TaskPortModel::roleNames() const
|
||||||
|
{
|
||||||
|
QHash<int, QByteArray> roles;
|
||||||
|
roles[TaskPortRoles::TaskPortNameRole] = "taskPortName";
|
||||||
|
roles[TaskPortRoles::TaskPortDataRole] = "taskPortData";
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QodeAssist::TaskFlow
|
||||||
25
TaskFlow/Editor/TaskPortModel.hpp
Normal file
25
TaskFlow/Editor/TaskPortModel.hpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
|
||||||
|
#include <BaseTask.hpp>
|
||||||
|
|
||||||
|
namespace QodeAssist::TaskFlow {
|
||||||
|
|
||||||
|
class TaskPortModel : public QAbstractListModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
enum TaskPortRoles { TaskPortNameRole = Qt::UserRole, TaskPortDataRole };
|
||||||
|
|
||||||
|
TaskPortModel(const QList<TaskPort *> &ports, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QList<TaskPort *> m_ports;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::TaskFlow
|
||||||
134
TaskFlow/Editor/qml/Flow.qml
Normal file
134
TaskFlow/Editor/qml/Flow.qml
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
import QtQuick
|
||||||
|
import TaskFlow.Editor
|
||||||
|
|
||||||
|
FlowItem {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: tasks
|
||||||
|
|
||||||
|
model: root.taskModel
|
||||||
|
delegate: Task {
|
||||||
|
// task: taskData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: connections
|
||||||
|
|
||||||
|
model: root.taskModel
|
||||||
|
delegate: TaskConnection {
|
||||||
|
// task: taskData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// property var qtaskItems: []
|
||||||
|
|
||||||
|
// // Flow container background
|
||||||
|
// Rectangle {
|
||||||
|
// anchors.fill: parent
|
||||||
|
// color: palette.alternateBase
|
||||||
|
// border.color: palette.mid
|
||||||
|
// border.width: 2
|
||||||
|
// radius: 8
|
||||||
|
|
||||||
|
// // Flow header
|
||||||
|
// Rectangle {
|
||||||
|
// id: flowHeader
|
||||||
|
// anchors.top: parent.top
|
||||||
|
// anchors.left: parent.left
|
||||||
|
// anchors.right: parent.right
|
||||||
|
// height: 40
|
||||||
|
// color: palette.button
|
||||||
|
// radius: 6
|
||||||
|
|
||||||
|
// Rectangle {
|
||||||
|
// anchors.bottom: parent.bottom
|
||||||
|
// anchors.left: parent.left
|
||||||
|
// anchors.right: parent.right
|
||||||
|
// height: parent.radius
|
||||||
|
// color: parent.color
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Text {
|
||||||
|
// anchors.centerIn: parent
|
||||||
|
// text: root.flowId
|
||||||
|
// color: palette.buttonText
|
||||||
|
// font.pixelSize: 14
|
||||||
|
// font.bold: true
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // // Tasks container
|
||||||
|
// // Row {
|
||||||
|
// // id: tasksRow
|
||||||
|
// // anchors.top: flowHeader.bottom
|
||||||
|
// // anchors.left: parent.left
|
||||||
|
// // anchors.margins: 25
|
||||||
|
// // anchors.topMargin: 25
|
||||||
|
// // objectName: "FlowTaskRow"
|
||||||
|
|
||||||
|
// // spacing: 40
|
||||||
|
|
||||||
|
// // Repeater {
|
||||||
|
// // model: root.taskModel
|
||||||
|
|
||||||
|
// // delegate: Task {
|
||||||
|
// // task: taskData
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// // onItemAdded: function(index, item){
|
||||||
|
// // console.log("task added", index, item)
|
||||||
|
// // qtaskItems.push(item)
|
||||||
|
// // root.insertTaskItem(index, item)
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// // onItemRemoved: function(index, item){
|
||||||
|
// // console.log("task added", index, item)
|
||||||
|
// // var idx = qtaskItems.indexOf(item)
|
||||||
|
// // if (idx !== -1) qtaskItems.splice(idx, 1)
|
||||||
|
// // }
|
||||||
|
// // }
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// // Repeater {
|
||||||
|
// // model: root.connectionsModel
|
||||||
|
|
||||||
|
// // delegate: TaskConnection {
|
||||||
|
// // connection: connectionData
|
||||||
|
// // }
|
||||||
|
// // }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Flow info tooltip
|
||||||
|
// Rectangle {
|
||||||
|
// id: infoTooltip
|
||||||
|
// anchors.top: parent.bottom
|
||||||
|
// anchors.left: parent.left
|
||||||
|
// anchors.topMargin: 5
|
||||||
|
// width: infoText.width + 20
|
||||||
|
// height: infoText.height + 10
|
||||||
|
// color: palette.base
|
||||||
|
// border.color: palette.shadow
|
||||||
|
// border.width: 1
|
||||||
|
// radius: 4
|
||||||
|
// visible: false
|
||||||
|
|
||||||
|
// Text {
|
||||||
|
// id: infoText
|
||||||
|
// anchors.centerIn: parent
|
||||||
|
// text: "Tasks: " + (root.taskModel ? root.taskModel.rowCount() : 0)
|
||||||
|
// color: palette.text
|
||||||
|
// font.pixelSize: 10
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// MouseArea {
|
||||||
|
// anchors.fill: parent
|
||||||
|
// hoverEnabled: true
|
||||||
|
|
||||||
|
// onEntered: infoTooltip.visible = true
|
||||||
|
// onExited: infoTooltip.visible = false
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
||||||
140
TaskFlow/Editor/qml/FlowEditorView.qml
Normal file
140
TaskFlow/Editor/qml/FlowEditorView.qml
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import TaskFlow.Editor
|
||||||
|
|
||||||
|
FlowEditor {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
width: 1200
|
||||||
|
height: 800
|
||||||
|
|
||||||
|
property SystemPalette sysPalette: SystemPalette {
|
||||||
|
colorGroup: SystemPalette.Active
|
||||||
|
}
|
||||||
|
palette {
|
||||||
|
window: sysPalette.window
|
||||||
|
windowText: sysPalette.windowText
|
||||||
|
base: sysPalette.base
|
||||||
|
alternateBase: sysPalette.alternateBase
|
||||||
|
text: sysPalette.text
|
||||||
|
button: sysPalette.button
|
||||||
|
buttonText: sysPalette.buttonText
|
||||||
|
highlight: sysPalette.highlight
|
||||||
|
highlightedText: sysPalette.highlightedText
|
||||||
|
light: sysPalette.light
|
||||||
|
mid: sysPalette.mid
|
||||||
|
dark: sysPalette.dark
|
||||||
|
shadow: sysPalette.shadow
|
||||||
|
brightText: sysPalette.brightText
|
||||||
|
}
|
||||||
|
|
||||||
|
// Background with grid pattern
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: palette.window
|
||||||
|
|
||||||
|
// Grid pattern using C++ implementation
|
||||||
|
GridBackground {
|
||||||
|
anchors.fill: parent
|
||||||
|
gridSize: 20
|
||||||
|
gridColor: palette.mid
|
||||||
|
opacity: 0.3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Header panel
|
||||||
|
Rectangle {
|
||||||
|
id: headerPanel
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
height: 60
|
||||||
|
color: palette.base
|
||||||
|
border.color: palette.mid
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.leftMargin: 20
|
||||||
|
spacing: 20
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "Flow Editor"
|
||||||
|
color: palette.windowText
|
||||||
|
font.pixelSize: 18
|
||||||
|
font.bold: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 2
|
||||||
|
height: 30
|
||||||
|
color: palette.mid
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "Flow:"
|
||||||
|
color: palette.text
|
||||||
|
font.pixelSize: 14
|
||||||
|
}
|
||||||
|
|
||||||
|
ComboBox {
|
||||||
|
id: flowComboBox
|
||||||
|
|
||||||
|
model: root.flowsModel
|
||||||
|
textRole: "flowId"
|
||||||
|
currentIndex: root.currentFlowIndex
|
||||||
|
|
||||||
|
onActivated: {
|
||||||
|
root.currentFlowIndex = currentIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "Available Tasks: " + root.availableTaskTypes.join(", ")
|
||||||
|
color: palette.text
|
||||||
|
font.pixelSize: 12
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main flow area
|
||||||
|
ScrollView {
|
||||||
|
id: scrollView
|
||||||
|
anchors.top: headerPanel.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
|
||||||
|
contentWidth: flow.width
|
||||||
|
contentHeight: flow.height
|
||||||
|
|
||||||
|
Flow {
|
||||||
|
id: flow
|
||||||
|
|
||||||
|
// flow: root.currentFlow
|
||||||
|
|
||||||
|
width: Math.max(root.width, 0)
|
||||||
|
height: Math.min(root.height, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
210
TaskFlow/Editor/qml/Task.qml
Normal file
210
TaskFlow/Editor/qml/Task.qml
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
import QtQuick
|
||||||
|
import TaskFlow.Editor
|
||||||
|
|
||||||
|
TaskItem{
|
||||||
|
id: root
|
||||||
|
|
||||||
|
width: 280
|
||||||
|
height: Math.max(200, contentColumn.height + 40)
|
||||||
|
|
||||||
|
DragHandler {
|
||||||
|
id: dragHandler
|
||||||
|
|
||||||
|
target: root
|
||||||
|
onActiveChanged: {
|
||||||
|
if (active) {
|
||||||
|
root.z = 1000; // Поднять над остальными
|
||||||
|
} else {
|
||||||
|
root.z = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Task node background
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: palette.window
|
||||||
|
border.color: palette.shadow
|
||||||
|
border.width: 1
|
||||||
|
radius: 6
|
||||||
|
|
||||||
|
// Task header
|
||||||
|
Rectangle {
|
||||||
|
id: taskHeader
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
height: 40
|
||||||
|
color: palette.button
|
||||||
|
radius: 6
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
height: parent.radius
|
||||||
|
color: parent.color
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
// text: root.taskType
|
||||||
|
color: palette.buttonText
|
||||||
|
font.pixelSize: 14
|
||||||
|
font.bold: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Task content
|
||||||
|
Column {
|
||||||
|
id: contentColumn
|
||||||
|
anchors.top: taskHeader.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.margins: 10
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
// Task ID
|
||||||
|
Text {
|
||||||
|
text: "ID: " + root.taskId
|
||||||
|
color: palette.text
|
||||||
|
font.pixelSize: 11
|
||||||
|
width: parent.width
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parameters section
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: paramColumn.height
|
||||||
|
// visible: root.parameters && root.parameters.rowCount() > 0
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: paramColumn
|
||||||
|
width: parent.width
|
||||||
|
spacing: 6
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "Parameters:"
|
||||||
|
color: palette.text
|
||||||
|
font.pixelSize: 10
|
||||||
|
font.bold: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: root.parameters
|
||||||
|
delegate: Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 24
|
||||||
|
color: palette.base
|
||||||
|
radius: 4
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.leftMargin: 8
|
||||||
|
spacing: 6
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: paramKey + ":"
|
||||||
|
color: palette.text
|
||||||
|
font.pixelSize: 9
|
||||||
|
font.bold: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: paramValue
|
||||||
|
color: palette.windowText
|
||||||
|
font.pixelSize: 9
|
||||||
|
width: Math.min(150, implicitWidth)
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input ports section (left side)
|
||||||
|
Column {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.leftMargin: -8
|
||||||
|
spacing: 6
|
||||||
|
// visible: root.inputPorts && root.inputPorts.rowCount() > 0
|
||||||
|
|
||||||
|
// Input label
|
||||||
|
Text {
|
||||||
|
text: "IN"
|
||||||
|
color: palette.highlight
|
||||||
|
font.pixelSize: 10
|
||||||
|
font.bold: true
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: -20
|
||||||
|
}
|
||||||
|
|
||||||
|
// Repeater {
|
||||||
|
// model: root.inputPorts
|
||||||
|
// delegate: Row {
|
||||||
|
// spacing: 6
|
||||||
|
|
||||||
|
// Text {
|
||||||
|
// text: taskPortName
|
||||||
|
// color: palette.text
|
||||||
|
// font.pixelSize: 9
|
||||||
|
// anchors.verticalCenter: parent.verticalCenter
|
||||||
|
// horizontalAlignment: Text.AlignRight
|
||||||
|
// width: 60
|
||||||
|
// elide: Text.ElideLeft
|
||||||
|
// }
|
||||||
|
|
||||||
|
// TaskPort {
|
||||||
|
// port: taskPortData
|
||||||
|
// isInput: true
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output ports section (right side)
|
||||||
|
Column {
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.rightMargin: -10
|
||||||
|
spacing: 8
|
||||||
|
// visible: root.outputPorts && root.outputPorts.rowCount() > 0
|
||||||
|
|
||||||
|
// Output label
|
||||||
|
Text {
|
||||||
|
text: "OUT"
|
||||||
|
color: palette.highlight
|
||||||
|
font.pixelSize: 10
|
||||||
|
font.bold: true
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: -24
|
||||||
|
}
|
||||||
|
|
||||||
|
// Repeater {
|
||||||
|
// model: root.outputPorts
|
||||||
|
// delegate: Row {
|
||||||
|
// spacing: 6
|
||||||
|
|
||||||
|
// TaskPort {
|
||||||
|
// port: taskPortData
|
||||||
|
// isInput: false
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Text {
|
||||||
|
// text: taskPortName
|
||||||
|
// color: palette.text
|
||||||
|
// font.pixelSize: 9
|
||||||
|
// anchors.verticalCenter: parent.verticalCenter
|
||||||
|
// width: 60
|
||||||
|
// elide: Text.ElideRight
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
68
TaskFlow/Editor/qml/TaskConnection.qml
Normal file
68
TaskFlow/Editor/qml/TaskConnection.qml
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Shapes
|
||||||
|
import TaskFlow.Editor
|
||||||
|
|
||||||
|
TaskConnectionItem {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property color connectionColor: "red"
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 10
|
||||||
|
height: 10
|
||||||
|
radius: width / 2
|
||||||
|
color: "blue"
|
||||||
|
}
|
||||||
|
|
||||||
|
// width: Math.abs(endPoint.x - startPoint.x) + 40
|
||||||
|
// height: Math.abs(endPoint.y - startPoint.y) + 40
|
||||||
|
// x: Math.min(startPoint.x, endPoint.x) - 20
|
||||||
|
// y: Math.min(startPoint.y, endPoint.y) - 20
|
||||||
|
|
||||||
|
// Shape {
|
||||||
|
// anchors.fill: parent
|
||||||
|
|
||||||
|
// ShapePath {
|
||||||
|
// strokeWidth: 2
|
||||||
|
// strokeColor: connectionColor
|
||||||
|
// fillColor: "transparent"
|
||||||
|
|
||||||
|
// property point localStart: Qt.point(
|
||||||
|
// root.startPoint.x - root.x,
|
||||||
|
// root.startPoint.y - root.y
|
||||||
|
// )
|
||||||
|
// property point localEnd: Qt.point(
|
||||||
|
// root.endPoint.x - root.x,
|
||||||
|
// root.endPoint.y - root.y
|
||||||
|
// )
|
||||||
|
|
||||||
|
// // Bezier curve
|
||||||
|
// property real controlOffset: Math.max(50, Math.abs(localEnd.x - localStart.x) * 0.4)
|
||||||
|
|
||||||
|
// startX: localStart.x
|
||||||
|
// startY: localStart.y
|
||||||
|
|
||||||
|
// PathCubic {
|
||||||
|
// x: parent.localEnd.x
|
||||||
|
// y: parent.localEnd.y
|
||||||
|
// control1X: parent.localStart.x + parent.controlOffset
|
||||||
|
// control1Y: parent.localStart.y
|
||||||
|
// control2X: parent.localEnd.x - parent.controlOffset
|
||||||
|
// control2Y: parent.localEnd.y
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Arrow head
|
||||||
|
// Rectangle {
|
||||||
|
// width: 8
|
||||||
|
// height: 8
|
||||||
|
// color: connectionColor
|
||||||
|
// rotation: 45
|
||||||
|
// x: root.endPoint.x - root.x - 4
|
||||||
|
// y: root.endPoint.y - root.y - 4
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Update positions when tasks might have moved
|
||||||
|
// Component.onCompleted: updatePositions()
|
||||||
|
}
|
||||||
6
TaskFlow/Editor/qml/TaskParameter.qml
Normal file
6
TaskFlow/Editor/qml/TaskParameter.qml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import QtQuick
|
||||||
|
import TaskFlow.Editor
|
||||||
|
|
||||||
|
Item {
|
||||||
|
|
||||||
|
}
|
||||||
63
TaskFlow/Editor/qml/TaskPort.qml
Normal file
63
TaskFlow/Editor/qml/TaskPort.qml
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import QtQuick
|
||||||
|
import TaskFlow.Editor
|
||||||
|
|
||||||
|
TaskPortItem {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool isInput: true
|
||||||
|
|
||||||
|
width: 20
|
||||||
|
height: 20
|
||||||
|
|
||||||
|
// Port circle
|
||||||
|
Rectangle {
|
||||||
|
id: portCircle
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: 16
|
||||||
|
height: 16
|
||||||
|
radius: 8
|
||||||
|
color: getPortColor()
|
||||||
|
border.color: palette.windowText
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
// Inner circle for connected state simulation
|
||||||
|
Rectangle {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: 8
|
||||||
|
height: 8
|
||||||
|
radius: 4
|
||||||
|
color: root.port ? palette.windowText : "transparent"
|
||||||
|
visible: root.port !== null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
|
||||||
|
onEntered: {
|
||||||
|
portCircle.scale = 1.3
|
||||||
|
portCircle.border.width = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
onExited: {
|
||||||
|
portCircle.scale = 1.0
|
||||||
|
portCircle.border.width = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPortColor() {
|
||||||
|
if (!root.port) return palette.mid
|
||||||
|
|
||||||
|
// Different colors for input/output using system palette
|
||||||
|
if (root.isInput) {
|
||||||
|
return palette.highlight // System highlight color for inputs
|
||||||
|
} else {
|
||||||
|
return Qt.lighter(palette.highlight, 1.3) // Lighter highlight for outputs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on scale {
|
||||||
|
NumberAnimation { duration: 100 }
|
||||||
|
}
|
||||||
|
}
|
||||||
115
TaskFlow/core/BaseTask.cpp
Normal file
115
TaskFlow/core/BaseTask.cpp
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "BaseTask.hpp"
|
||||||
|
#include "TaskPort.hpp"
|
||||||
|
#include <QUuid>
|
||||||
|
#include <QtConcurrent>
|
||||||
|
|
||||||
|
namespace QodeAssist::TaskFlow {
|
||||||
|
|
||||||
|
BaseTask::BaseTask(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_taskId("unknown" + QUuid::createUuid().toString())
|
||||||
|
{}
|
||||||
|
|
||||||
|
BaseTask::~BaseTask()
|
||||||
|
{
|
||||||
|
qDeleteAll(m_inputs);
|
||||||
|
qDeleteAll(m_outputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString BaseTask::taskId() const
|
||||||
|
{
|
||||||
|
return m_taskId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BaseTask::setTaskId(const QString &taskId)
|
||||||
|
{
|
||||||
|
m_taskId = taskId;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString BaseTask::taskType() const
|
||||||
|
{
|
||||||
|
return QString(metaObject()->className()).split("::").last();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BaseTask::addInputPort(const QString &name)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_tasksMutex);
|
||||||
|
m_inputs.append(new TaskPort(name, TaskPort::ValueType::Any, this));
|
||||||
|
}
|
||||||
|
|
||||||
|
void BaseTask::addOutputPort(const QString &name)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_tasksMutex);
|
||||||
|
m_outputs.append(new TaskPort(name, TaskPort::ValueType::Any, this));
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskPort *BaseTask::inputPort(const QString &name) const
|
||||||
|
{
|
||||||
|
for (TaskPort *port : m_inputs) {
|
||||||
|
if (port->name() == name) {
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskPort *BaseTask::outputPort(const QString &name) const
|
||||||
|
{
|
||||||
|
for (TaskPort *port : m_outputs) {
|
||||||
|
if (port->name() == name) {
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<TaskPort *> BaseTask::getInputPorts() const
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_tasksMutex);
|
||||||
|
return m_inputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<TaskPort *> BaseTask::getOutputPorts() const
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_tasksMutex);
|
||||||
|
return m_outputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFuture<TaskState> BaseTask::executeAsync()
|
||||||
|
{
|
||||||
|
return QtConcurrent::task([this]() -> TaskState { return execute(); }).spawn();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString BaseTask::taskStateAsString(TaskState state)
|
||||||
|
{
|
||||||
|
switch (state) {
|
||||||
|
case TaskState::Success:
|
||||||
|
return "Success";
|
||||||
|
case TaskState::Failed:
|
||||||
|
return "Failed";
|
||||||
|
case TaskState::Cancelled:
|
||||||
|
return "Cancelled";
|
||||||
|
}
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QodeAssist::TaskFlow
|
||||||
66
TaskFlow/core/BaseTask.hpp
Normal file
66
TaskFlow/core/BaseTask.hpp
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QFuture>
|
||||||
|
#include <QMetaType>
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
namespace QodeAssist::TaskFlow {
|
||||||
|
|
||||||
|
class TaskPort;
|
||||||
|
|
||||||
|
enum class TaskState { Success, Failed, Cancelled };
|
||||||
|
|
||||||
|
class BaseTask : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit BaseTask(QObject *parent = nullptr);
|
||||||
|
virtual ~BaseTask();
|
||||||
|
|
||||||
|
QString taskId() const;
|
||||||
|
void setTaskId(const QString &taskId);
|
||||||
|
QString taskType() const;
|
||||||
|
|
||||||
|
void addInputPort(const QString &name);
|
||||||
|
void addOutputPort(const QString &name);
|
||||||
|
|
||||||
|
TaskPort *inputPort(const QString &name) const;
|
||||||
|
TaskPort *outputPort(const QString &name) const;
|
||||||
|
|
||||||
|
QList<TaskPort *> getInputPorts() const;
|
||||||
|
QList<TaskPort *> getOutputPorts() const;
|
||||||
|
|
||||||
|
QFuture<TaskState> executeAsync();
|
||||||
|
virtual TaskState execute() = 0;
|
||||||
|
|
||||||
|
static QString taskStateAsString(TaskState state);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_taskId;
|
||||||
|
QList<TaskPort *> m_inputs;
|
||||||
|
QList<TaskPort *> m_outputs;
|
||||||
|
mutable QMutex m_tasksMutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::TaskFlow
|
||||||
22
TaskFlow/core/CMakeLists.txt
Normal file
22
TaskFlow/core/CMakeLists.txt
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
qt_add_library(TaskFlowCore STATIC
|
||||||
|
BaseTask.hpp BaseTask.cpp
|
||||||
|
TaskConnection.hpp TaskConnection.cpp
|
||||||
|
Flow.hpp Flow.cpp
|
||||||
|
TaskPort.hpp TaskPort.cpp
|
||||||
|
TaskRegistry.hpp TaskRegistry.cpp
|
||||||
|
FlowManager.hpp FlowManager.cpp
|
||||||
|
FlowRegistry.hpp FlowRegistry.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(TaskFlowCore
|
||||||
|
PUBLIC
|
||||||
|
Qt::Core
|
||||||
|
Qt::Concurrent
|
||||||
|
PRIVATE
|
||||||
|
QodeAssistLogger
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(TaskFlowCore
|
||||||
|
PUBLIC
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
)
|
||||||
355
TaskFlow/core/Flow.cpp
Normal file
355
TaskFlow/core/Flow.cpp
Normal file
@ -0,0 +1,355 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Flow.hpp"
|
||||||
|
#include "TaskPort.hpp"
|
||||||
|
#include <QUuid>
|
||||||
|
#include <QtConcurrent>
|
||||||
|
|
||||||
|
namespace QodeAssist::TaskFlow {
|
||||||
|
|
||||||
|
Flow::Flow(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_flowId("flow_" + QUuid::createUuid().toString())
|
||||||
|
{}
|
||||||
|
|
||||||
|
Flow::~Flow()
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_flowMutex);
|
||||||
|
qDeleteAll(m_connections);
|
||||||
|
qDeleteAll(m_tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Flow::flowId() const
|
||||||
|
{
|
||||||
|
return m_flowId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Flow::setFlowId(const QString &flowId)
|
||||||
|
{
|
||||||
|
if (m_flowId != flowId) {
|
||||||
|
m_flowId = flowId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Flow::addTask(BaseTask *task)
|
||||||
|
{
|
||||||
|
if (!task) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMutexLocker locker(&m_flowMutex);
|
||||||
|
|
||||||
|
QString taskId = task->taskId();
|
||||||
|
if (m_tasks.contains(taskId)) {
|
||||||
|
qWarning() << "Flow::addTask - Task with ID" << taskId << "already exists";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_tasks.insert(taskId, task);
|
||||||
|
task->setParent(this);
|
||||||
|
|
||||||
|
emit taskAdded(taskId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Flow::removeTask(const QString &taskId)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_flowMutex);
|
||||||
|
|
||||||
|
BaseTask *task = m_tasks.value(taskId);
|
||||||
|
if (!task) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = m_connections.begin();
|
||||||
|
while (it != m_connections.end()) {
|
||||||
|
TaskConnection *connection = *it;
|
||||||
|
if (connection->sourceTask() == task || connection->targetTask() == task) {
|
||||||
|
it = m_connections.erase(it);
|
||||||
|
emit connectionRemoved(connection);
|
||||||
|
delete connection;
|
||||||
|
} else {
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_tasks.remove(taskId);
|
||||||
|
emit taskRemoved(taskId);
|
||||||
|
delete task;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Flow::removeTask(BaseTask *task)
|
||||||
|
{
|
||||||
|
if (!task) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
removeTask(task->taskId());
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseTask *Flow::getTask(const QString &taskId) const
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_flowMutex);
|
||||||
|
return m_tasks.value(taskId);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Flow::hasTask(const QString &taskId) const
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_flowMutex);
|
||||||
|
return m_tasks.contains(taskId);
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<QString, BaseTask *> Flow::tasks() const
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_flowMutex);
|
||||||
|
return m_tasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskConnection *Flow::addConnection(TaskPort *sourcePort, TaskPort *targetPort)
|
||||||
|
{
|
||||||
|
if (!sourcePort || !targetPort) {
|
||||||
|
qWarning() << "Flow::addConnection - Invalid ports";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify ports belong to tasks in this flow
|
||||||
|
BaseTask *sourceTask = qobject_cast<BaseTask *>(sourcePort->parent());
|
||||||
|
BaseTask *targetTask = qobject_cast<BaseTask *>(targetPort->parent());
|
||||||
|
|
||||||
|
if (!sourceTask || !targetTask) {
|
||||||
|
qWarning() << "Flow::addConnection - Ports don't belong to valid tasks";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMutexLocker locker(&m_flowMutex);
|
||||||
|
|
||||||
|
if (!m_tasks.contains(sourceTask->taskId()) || !m_tasks.contains(targetTask->taskId())) {
|
||||||
|
qWarning() << "Flow::addConnection - Tasks not in this flow";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (TaskConnection *existingConnection : m_connections) {
|
||||||
|
if (existingConnection->sourcePort() == sourcePort
|
||||||
|
&& existingConnection->targetPort() == targetPort) {
|
||||||
|
qWarning() << "Flow::addConnection - Connection already exists";
|
||||||
|
return existingConnection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskConnection *connection = new TaskConnection(sourcePort, targetPort, this);
|
||||||
|
m_connections.append(connection);
|
||||||
|
|
||||||
|
emit connectionAdded(connection);
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Flow::removeConnection(TaskConnection *connection)
|
||||||
|
{
|
||||||
|
if (!connection) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMutexLocker locker(&m_flowMutex);
|
||||||
|
|
||||||
|
if (m_connections.removeOne(connection)) {
|
||||||
|
emit connectionRemoved(connection);
|
||||||
|
delete connection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<TaskConnection *> Flow::connections() const
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_flowMutex);
|
||||||
|
return m_connections;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFuture<FlowState> Flow::executeAsync()
|
||||||
|
{
|
||||||
|
return QtConcurrent::run([this]() { return execute(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
FlowState Flow::execute()
|
||||||
|
{
|
||||||
|
emit executionStarted();
|
||||||
|
|
||||||
|
if (!isValid()) {
|
||||||
|
emit executionFinished(FlowState::Failed);
|
||||||
|
return FlowState::Failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasCircularDependencies()) {
|
||||||
|
qWarning() << "Flow::execute - Circular dependencies detected";
|
||||||
|
emit executionFinished(FlowState::Failed);
|
||||||
|
return FlowState::Failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<BaseTask *> executionOrder = getExecutionOrder();
|
||||||
|
|
||||||
|
for (BaseTask *task : executionOrder) {
|
||||||
|
TaskState taskResult = task->execute();
|
||||||
|
|
||||||
|
if (taskResult == TaskState::Failed) {
|
||||||
|
qWarning() << "Flow::execute - Task" << task->taskId() << "failed";
|
||||||
|
emit executionFinished(FlowState::Failed);
|
||||||
|
return FlowState::Failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (taskResult == TaskState::Cancelled) {
|
||||||
|
qWarning() << "Flow::execute - Task" << task->taskId() << "cancelled";
|
||||||
|
emit executionFinished(FlowState::Cancelled);
|
||||||
|
return FlowState::Cancelled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emit executionFinished(FlowState::Success);
|
||||||
|
return FlowState::Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Flow::isValid() const
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_flowMutex);
|
||||||
|
|
||||||
|
// Check all connections are valid
|
||||||
|
for (TaskConnection *connection : m_connections) {
|
||||||
|
if (!connection->isValid()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Flow::hasCircularDependencies() const
|
||||||
|
{
|
||||||
|
return detectCircularDependencies();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Flow::flowStateAsString(FlowState state)
|
||||||
|
{
|
||||||
|
switch (state) {
|
||||||
|
case FlowState::Success:
|
||||||
|
return "Success";
|
||||||
|
case FlowState::Failed:
|
||||||
|
return "Failed";
|
||||||
|
case FlowState::Cancelled:
|
||||||
|
return "Cancelled";
|
||||||
|
}
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList Flow::getTaskIds() const
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_flowMutex);
|
||||||
|
return m_tasks.keys();
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<BaseTask *> Flow::getExecutionOrder() const
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_flowMutex);
|
||||||
|
|
||||||
|
QList<BaseTask *> result;
|
||||||
|
QSet<BaseTask *> visited;
|
||||||
|
QList<BaseTask *> allTasks = m_tasks.values();
|
||||||
|
|
||||||
|
std::function<void(BaseTask *)> visit = [&](BaseTask *task) {
|
||||||
|
if (visited.contains(task)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
visited.insert(task);
|
||||||
|
|
||||||
|
QList<BaseTask *> dependencies = getTaskDependencies(task);
|
||||||
|
for (BaseTask *dependency : dependencies) {
|
||||||
|
visit(dependency);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.append(task);
|
||||||
|
};
|
||||||
|
|
||||||
|
for (BaseTask *task : allTasks) {
|
||||||
|
visit(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Flow::detectCircularDependencies() const
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&m_flowMutex);
|
||||||
|
|
||||||
|
QSet<BaseTask *> visited;
|
||||||
|
QSet<BaseTask *> recursionStack;
|
||||||
|
bool hasCycle = false;
|
||||||
|
|
||||||
|
for (BaseTask *task : m_tasks.values()) {
|
||||||
|
if (!visited.contains(task)) {
|
||||||
|
visitTask(task, visited, recursionStack, hasCycle);
|
||||||
|
if (hasCycle) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Flow::visitTask(
|
||||||
|
BaseTask *task, QSet<BaseTask *> &visited, QSet<BaseTask *> &recursionStack, bool &hasCycle) const
|
||||||
|
{
|
||||||
|
if (hasCycle) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
visited.insert(task);
|
||||||
|
recursionStack.insert(task);
|
||||||
|
|
||||||
|
for (TaskConnection *connection : m_connections) {
|
||||||
|
if (connection->sourceTask() == task) {
|
||||||
|
BaseTask *dependentTask = connection->targetTask();
|
||||||
|
|
||||||
|
if (recursionStack.contains(dependentTask)) {
|
||||||
|
hasCycle = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!visited.contains(dependentTask)) {
|
||||||
|
visitTask(dependentTask, visited, recursionStack, hasCycle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
recursionStack.remove(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<BaseTask *> Flow::getTaskDependencies(BaseTask *task) const
|
||||||
|
{
|
||||||
|
QList<BaseTask *> dependencies;
|
||||||
|
|
||||||
|
for (TaskConnection *connection : m_connections) {
|
||||||
|
if (connection->targetTask() == task) {
|
||||||
|
BaseTask *dependencyTask = connection->sourceTask();
|
||||||
|
if (!dependencies.contains(dependencyTask)) {
|
||||||
|
dependencies.append(dependencyTask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QodeAssist::TaskFlow
|
||||||
95
TaskFlow/core/Flow.hpp
Normal file
95
TaskFlow/core/Flow.hpp
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QFuture>
|
||||||
|
#include <QHash>
|
||||||
|
#include <QList>
|
||||||
|
#include <QMetaType>
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include "BaseTask.hpp"
|
||||||
|
#include "TaskConnection.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::TaskFlow {
|
||||||
|
|
||||||
|
enum class FlowState { Success, Failed, Cancelled };
|
||||||
|
|
||||||
|
class Flow : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Flow(QObject *parent = nullptr);
|
||||||
|
~Flow() override;
|
||||||
|
|
||||||
|
QString flowId() const;
|
||||||
|
void setFlowId(const QString &flowId);
|
||||||
|
|
||||||
|
void addTask(BaseTask *task);
|
||||||
|
void removeTask(const QString &taskId);
|
||||||
|
void removeTask(BaseTask *task);
|
||||||
|
|
||||||
|
BaseTask *getTask(const QString &taskId) const;
|
||||||
|
bool hasTask(const QString &taskId) const;
|
||||||
|
QHash<QString, BaseTask *> tasks() const;
|
||||||
|
|
||||||
|
TaskConnection *addConnection(TaskPort *sourcePort, TaskPort *targetPort);
|
||||||
|
void removeConnection(TaskConnection *connection);
|
||||||
|
QList<TaskConnection *> connections() const;
|
||||||
|
|
||||||
|
QFuture<FlowState> executeAsync();
|
||||||
|
virtual FlowState execute();
|
||||||
|
|
||||||
|
bool isValid() const;
|
||||||
|
bool hasCircularDependencies() const;
|
||||||
|
|
||||||
|
static QString flowStateAsString(FlowState state);
|
||||||
|
QStringList getTaskIds() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void taskAdded(const QString &taskId);
|
||||||
|
void taskRemoved(const QString &taskId);
|
||||||
|
void connectionAdded(QodeAssist::TaskFlow::TaskConnection *connection);
|
||||||
|
void connectionRemoved(QodeAssist::TaskFlow::TaskConnection *connection);
|
||||||
|
void executionStarted();
|
||||||
|
void executionFinished(FlowState result);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_flowId;
|
||||||
|
QHash<QString, BaseTask *> m_tasks;
|
||||||
|
QList<TaskConnection *> m_connections;
|
||||||
|
mutable QMutex m_flowMutex;
|
||||||
|
|
||||||
|
QList<BaseTask *> getExecutionOrder() const;
|
||||||
|
bool detectCircularDependencies() const;
|
||||||
|
void visitTask(
|
||||||
|
BaseTask *task,
|
||||||
|
QSet<BaseTask *> &visited,
|
||||||
|
QSet<BaseTask *> &recursionStack,
|
||||||
|
bool &hasCycle) const;
|
||||||
|
QList<BaseTask *> getTaskDependencies(BaseTask *task) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::TaskFlow
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(QodeAssist::TaskFlow::Flow *)
|
||||||
|
Q_DECLARE_METATYPE(QodeAssist::TaskFlow::FlowState)
|
||||||
112
TaskFlow/core/FlowManager.cpp
Normal file
112
TaskFlow/core/FlowManager.cpp
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "FlowManager.hpp"
|
||||||
|
|
||||||
|
#include <Logger.hpp>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
|
||||||
|
#include "FlowRegistry.hpp"
|
||||||
|
#include "TaskRegistry.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::TaskFlow {
|
||||||
|
|
||||||
|
FlowManager::FlowManager(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_taskRegistry(new TaskRegistry(this))
|
||||||
|
, m_flowRegistry(new FlowRegistry(this))
|
||||||
|
{
|
||||||
|
LOG_MESSAGE("FlowManager created");
|
||||||
|
}
|
||||||
|
|
||||||
|
FlowManager::~FlowManager()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flow *FlowManager::createFlow(const QString &flowId)
|
||||||
|
// {
|
||||||
|
// Flow *flow = new Flow(flowId, m_taskRegistry, this);
|
||||||
|
// if (!m_flows.contains(flow->flowId())) {
|
||||||
|
// m_flows.insert(flowId, flow);
|
||||||
|
// } else {
|
||||||
|
// LOG_MESSAGE(
|
||||||
|
// QString("FlowManager::createFlow - flow with id %1 already exists").arg(flow->flowId()));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return flow;
|
||||||
|
// }
|
||||||
|
|
||||||
|
void FlowManager::addFlow(Flow *flow)
|
||||||
|
{
|
||||||
|
qDebug() << "FlowManager::addFlow" << flow->flowId();
|
||||||
|
if (!m_flows.contains(flow->flowId())) {
|
||||||
|
m_flows.insert(flow->flowId(), flow);
|
||||||
|
flow->setParent(this);
|
||||||
|
emit flowAdded(flow->flowId());
|
||||||
|
} else {
|
||||||
|
LOG_MESSAGE(
|
||||||
|
QString("FlowManager::addFlow - flow with id %1 already exists").arg(flow->flowId()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlowManager::clear()
|
||||||
|
{
|
||||||
|
LOG_MESSAGE(QString("FlowManager::clear - removing %1 flows").arg(m_flows.size()));
|
||||||
|
|
||||||
|
qDeleteAll(m_flows);
|
||||||
|
m_flows.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList FlowManager::getAvailableTasksTypes()
|
||||||
|
{
|
||||||
|
return m_taskRegistry->getAvailableTypes();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList FlowManager::getAvailableFlows()
|
||||||
|
{
|
||||||
|
return m_flowRegistry->getAvailableTypes();
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<QString, Flow *> FlowManager::flows() const
|
||||||
|
{
|
||||||
|
return m_flows;
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskRegistry *FlowManager::taskRegistry() const
|
||||||
|
{
|
||||||
|
return m_taskRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
|
FlowRegistry *FlowManager::flowRegistry() const
|
||||||
|
{
|
||||||
|
return m_flowRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
|
Flow *FlowManager::getFlow(const QString &flowId) const
|
||||||
|
{
|
||||||
|
// if (flowId.isEmpty()) {
|
||||||
|
// return m_flows.begin().value();
|
||||||
|
// }
|
||||||
|
// return m_flows.value(flowId, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QodeAssist::TaskFlow
|
||||||
69
TaskFlow/core/FlowManager.hpp
Normal file
69
TaskFlow/core/FlowManager.hpp
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QHash>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#include "Flow.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::TaskFlow {
|
||||||
|
|
||||||
|
class TaskRegistry;
|
||||||
|
class FlowRegistry;
|
||||||
|
|
||||||
|
class FlowManager : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit FlowManager(QObject *parent = nullptr);
|
||||||
|
~FlowManager() override;
|
||||||
|
|
||||||
|
// Flow *createFlow(const QString &flowId);
|
||||||
|
void addFlow(Flow *flow);
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
QStringList getAvailableTasksTypes();
|
||||||
|
QStringList getAvailableFlows();
|
||||||
|
|
||||||
|
QHash<QString, Flow *> flows() const;
|
||||||
|
|
||||||
|
TaskRegistry *taskRegistry() const;
|
||||||
|
FlowRegistry *flowRegistry() const;
|
||||||
|
|
||||||
|
Flow *getFlow(const QString &flowId = {}) const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void flowAdded(const QString &flowId);
|
||||||
|
void flowRemoved(const QString &flowId);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QHash<QString, Flow *> m_flows;
|
||||||
|
|
||||||
|
TaskRegistry *m_taskRegistry;
|
||||||
|
FlowRegistry *m_flowRegistry;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::TaskFlow
|
||||||
43
TaskFlow/core/FlowRegistry.cpp
Normal file
43
TaskFlow/core/FlowRegistry.cpp
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#include "FlowRegistry.hpp"
|
||||||
|
#include "Logger.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::TaskFlow {
|
||||||
|
|
||||||
|
FlowRegistry::FlowRegistry(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void FlowRegistry::registerFlow(const QString &flowType, FlowCreator creator)
|
||||||
|
{
|
||||||
|
m_flowCreators[flowType] = creator;
|
||||||
|
LOG_MESSAGE(QString("FlowRegistry: Registered flow type '%1'").arg(flowType));
|
||||||
|
}
|
||||||
|
|
||||||
|
Flow *FlowRegistry::createFlow(const QString &flowType, FlowManager *flowManager) const
|
||||||
|
{
|
||||||
|
LOG_MESSAGE(QString("Trying to create flow: %1").arg(flowType));
|
||||||
|
|
||||||
|
if (m_flowCreators.contains(flowType)) {
|
||||||
|
LOG_MESSAGE(QString("Found creator for flow type: %1").arg(flowType));
|
||||||
|
try {
|
||||||
|
Flow *flow = m_flowCreators[flowType](flowManager);
|
||||||
|
if (flow) {
|
||||||
|
LOG_MESSAGE(QString("Successfully created flow: %1").arg(flowType));
|
||||||
|
return flow;
|
||||||
|
}
|
||||||
|
} catch (...) {
|
||||||
|
LOG_MESSAGE(QString("Exception while creating flow of type: %1").arg(flowType));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG_MESSAGE(QString("No creator found for flow type: %1").arg(flowType));
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList FlowRegistry::getAvailableTypes() const
|
||||||
|
{
|
||||||
|
return m_flowCreators.keys();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QodeAssist::TaskFlow
|
||||||
29
TaskFlow/core/FlowRegistry.hpp
Normal file
29
TaskFlow/core/FlowRegistry.hpp
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <QHash>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace QodeAssist::TaskFlow {
|
||||||
|
|
||||||
|
class Flow;
|
||||||
|
class FlowManager;
|
||||||
|
|
||||||
|
class FlowRegistry : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
using FlowCreator = std::function<Flow *(FlowManager *flowManager)>;
|
||||||
|
|
||||||
|
explicit FlowRegistry(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
void registerFlow(const QString &flowType, FlowCreator creator);
|
||||||
|
Flow *createFlow(const QString &flowType, FlowManager *flowManager = nullptr) const;
|
||||||
|
QStringList getAvailableTypes() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QHash<QString, FlowCreator> m_flowCreators;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::TaskFlow
|
||||||
125
TaskFlow/core/TaskConnection.cpp
Normal file
125
TaskFlow/core/TaskConnection.cpp
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "TaskConnection.hpp"
|
||||||
|
#include "BaseTask.hpp"
|
||||||
|
#include "TaskPort.hpp"
|
||||||
|
#include <QMetaEnum>
|
||||||
|
|
||||||
|
namespace QodeAssist::TaskFlow {
|
||||||
|
|
||||||
|
TaskConnection::TaskConnection(TaskPort *sourcePort, TaskPort *targetPort, QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_sourcePort(sourcePort)
|
||||||
|
, m_targetPort(targetPort)
|
||||||
|
{
|
||||||
|
setupConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskConnection::~TaskConnection()
|
||||||
|
{
|
||||||
|
cleanupConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseTask *TaskConnection::sourceTask() const
|
||||||
|
{
|
||||||
|
return m_sourcePort ? qobject_cast<BaseTask *>(m_sourcePort->parent()) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseTask *TaskConnection::targetTask() const
|
||||||
|
{
|
||||||
|
return m_targetPort ? qobject_cast<BaseTask *>(m_targetPort->parent()) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskPort *TaskConnection::sourcePort() const
|
||||||
|
{
|
||||||
|
return m_sourcePort;
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskPort *TaskConnection::targetPort() const
|
||||||
|
{
|
||||||
|
return m_targetPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TaskConnection::isValid() const
|
||||||
|
{
|
||||||
|
return m_sourcePort && m_targetPort && m_sourcePort != m_targetPort && sourceTask()
|
||||||
|
&& targetTask() && sourceTask() != targetTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TaskConnection::isTypeCompatible() const
|
||||||
|
{
|
||||||
|
if (!isValid()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_targetPort->isConnectionTypeCompatible(m_sourcePort);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TaskConnection::toString() const
|
||||||
|
{
|
||||||
|
if (!isValid()) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseTask *srcTask = sourceTask();
|
||||||
|
BaseTask *tgtTask = targetTask();
|
||||||
|
|
||||||
|
return QString("%1.%2->%3.%4")
|
||||||
|
.arg(srcTask->taskId())
|
||||||
|
.arg(m_sourcePort->name())
|
||||||
|
.arg(tgtTask->taskId())
|
||||||
|
.arg(m_targetPort->name());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TaskConnection::operator==(const TaskConnection &other) const
|
||||||
|
{
|
||||||
|
return m_sourcePort == other.m_sourcePort && m_targetPort == other.m_targetPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskConnection::setupConnection()
|
||||||
|
{
|
||||||
|
if (!isValid()) {
|
||||||
|
qWarning() << "TaskConnection::setupConnection - Invalid connection parameters";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isTypeCompatible()) {
|
||||||
|
QMetaEnum metaEnum = QMetaEnum::fromType<TaskPort::ValueType>();
|
||||||
|
qWarning() << "TaskConnection::setupConnection - Type incompatible connection:"
|
||||||
|
<< metaEnum.valueToKey(static_cast<int>(m_sourcePort->valueType())) << "to"
|
||||||
|
<< metaEnum.valueToKey(static_cast<int>(m_targetPort->valueType()));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_sourcePort->setConnection(this);
|
||||||
|
m_targetPort->setConnection(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskConnection::cleanupConnection()
|
||||||
|
{
|
||||||
|
if (m_sourcePort && m_sourcePort->connection() == this) {
|
||||||
|
m_sourcePort->setConnection(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_targetPort && m_targetPort->connection() == this) {
|
||||||
|
m_targetPort->setConnection(nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QodeAssist::TaskFlow
|
||||||
65
TaskFlow/core/TaskConnection.hpp
Normal file
65
TaskFlow/core/TaskConnection.hpp
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace QodeAssist::TaskFlow {
|
||||||
|
|
||||||
|
class BaseTask;
|
||||||
|
class TaskPort;
|
||||||
|
|
||||||
|
class TaskConnection : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Constructor automatically sets up the connection
|
||||||
|
explicit TaskConnection(TaskPort *sourcePort, TaskPort *targetPort, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
// Destructor automatically cleans up the connection
|
||||||
|
~TaskConnection() override;
|
||||||
|
|
||||||
|
// Getters
|
||||||
|
BaseTask *sourceTask() const;
|
||||||
|
BaseTask *targetTask() const;
|
||||||
|
TaskPort *sourcePort() const;
|
||||||
|
TaskPort *targetPort() const;
|
||||||
|
|
||||||
|
// Validation
|
||||||
|
bool isValid() const;
|
||||||
|
bool isTypeCompatible() const;
|
||||||
|
|
||||||
|
// Utility
|
||||||
|
QString toString() const;
|
||||||
|
|
||||||
|
// Comparison
|
||||||
|
bool operator==(const TaskConnection &other) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
TaskPort *m_sourcePort;
|
||||||
|
TaskPort *m_targetPort;
|
||||||
|
|
||||||
|
void setupConnection();
|
||||||
|
void cleanupConnection();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::TaskFlow
|
||||||
122
TaskFlow/core/TaskPort.cpp
Normal file
122
TaskFlow/core/TaskPort.cpp
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "TaskPort.hpp"
|
||||||
|
#include "TaskConnection.hpp"
|
||||||
|
#include <QMetaEnum>
|
||||||
|
|
||||||
|
namespace QodeAssist::TaskFlow {
|
||||||
|
|
||||||
|
TaskPort::TaskPort(const QString &name, ValueType type, QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_name(name)
|
||||||
|
, m_valueType(type)
|
||||||
|
{}
|
||||||
|
|
||||||
|
QString TaskPort::name() const
|
||||||
|
{
|
||||||
|
return m_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskPort::setValueType(ValueType type)
|
||||||
|
{
|
||||||
|
if (m_valueType != type)
|
||||||
|
m_valueType = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskPort::ValueType TaskPort::valueType() const
|
||||||
|
{
|
||||||
|
return m_valueType;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskPort::setValue(const QVariant &value)
|
||||||
|
{
|
||||||
|
if (!isValueTypeCompatible(value)) {
|
||||||
|
qWarning() << "TaskPort::setValue - Type mismatch for port" << m_name << "Expected:"
|
||||||
|
<< QMetaEnum::fromType<ValueType>().valueToKey(static_cast<int>(m_valueType))
|
||||||
|
<< "Got:" << value.typeName();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_value != value) {
|
||||||
|
m_value = value;
|
||||||
|
emit valueChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant TaskPort::value() const
|
||||||
|
{
|
||||||
|
if (hasConnection() && m_connection->sourcePort()) {
|
||||||
|
return m_connection->sourcePort()->m_value;
|
||||||
|
}
|
||||||
|
return m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TaskPort::setConnection(TaskConnection *connection)
|
||||||
|
{
|
||||||
|
if (m_connection != connection) {
|
||||||
|
m_connection = connection;
|
||||||
|
emit connectionChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskConnection *TaskPort::connection() const
|
||||||
|
{
|
||||||
|
return m_connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TaskPort::hasConnection() const
|
||||||
|
{
|
||||||
|
return m_connection != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TaskPort::isValueTypeCompatible(const QVariant &value) const
|
||||||
|
{
|
||||||
|
if (m_valueType == ValueType::Any) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (m_valueType) {
|
||||||
|
case ValueType::String:
|
||||||
|
return value.canConvert<QString>();
|
||||||
|
|
||||||
|
case ValueType::Number:
|
||||||
|
return value.canConvert<double>() || value.canConvert<int>();
|
||||||
|
|
||||||
|
case ValueType::Boolean:
|
||||||
|
return value.canConvert<bool>();
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TaskPort::isConnectionTypeCompatible(const TaskPort *sourcePort) const
|
||||||
|
{
|
||||||
|
if (!sourcePort) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sourcePort->valueType() == ValueType::Any || m_valueType == ValueType::Any) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sourcePort->valueType() == m_valueType;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QodeAssist::TaskFlow
|
||||||
75
TaskFlow/core/TaskPort.hpp
Normal file
75
TaskFlow/core/TaskPort.hpp
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
|
#include "TaskConnection.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::TaskFlow {
|
||||||
|
|
||||||
|
class TaskPort : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum class ValueType {
|
||||||
|
Any, // QVariant
|
||||||
|
String, // QString
|
||||||
|
Number, // int/double
|
||||||
|
Boolean // bool
|
||||||
|
};
|
||||||
|
Q_ENUM(ValueType)
|
||||||
|
|
||||||
|
explicit TaskPort(
|
||||||
|
const QString &name, ValueType type = ValueType::Any, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
QString name() const;
|
||||||
|
|
||||||
|
ValueType valueType() const;
|
||||||
|
void setValueType(ValueType type);
|
||||||
|
|
||||||
|
void setValue(const QVariant &value);
|
||||||
|
QVariant value() const;
|
||||||
|
|
||||||
|
void setConnection(TaskConnection *connection);
|
||||||
|
TaskConnection *connection() const;
|
||||||
|
bool hasConnection() const;
|
||||||
|
|
||||||
|
bool isValueTypeCompatible(const QVariant &value) const;
|
||||||
|
bool isConnectionTypeCompatible(const TaskPort *sourcePort) const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void valueChanged();
|
||||||
|
void connectionChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_name;
|
||||||
|
ValueType m_valueType;
|
||||||
|
QVariant m_value;
|
||||||
|
TaskConnection *m_connection = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::TaskFlow
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(QodeAssist::TaskFlow::TaskPort *)
|
||||||
|
Q_DECLARE_METATYPE(QodeAssist::TaskFlow::TaskPort::ValueType)
|
||||||
59
TaskFlow/core/TaskRegistry.cpp
Normal file
59
TaskFlow/core/TaskRegistry.cpp
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "TaskRegistry.hpp"
|
||||||
|
|
||||||
|
#include <Logger.hpp>
|
||||||
|
|
||||||
|
#include "BaseTask.hpp"
|
||||||
|
|
||||||
|
namespace QodeAssist::TaskFlow {
|
||||||
|
|
||||||
|
TaskRegistry::TaskRegistry(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
BaseTask *TaskRegistry::createTask(const QString &taskType, QObject *parent) const
|
||||||
|
{
|
||||||
|
LOG_MESSAGE(QString("Trying to create task: %1").arg(taskType));
|
||||||
|
|
||||||
|
if (m_creators.contains(taskType)) {
|
||||||
|
LOG_MESSAGE(QString("Found creator for task type: %1").arg(taskType));
|
||||||
|
try {
|
||||||
|
BaseTask *task = m_creators[taskType](parent);
|
||||||
|
if (task) {
|
||||||
|
LOG_MESSAGE(QString("Successfully created task: %1").arg(taskType));
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
} catch (...) {
|
||||||
|
LOG_MESSAGE(QString("Exception while creating task of type: %1").arg(taskType));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG_MESSAGE(QString("No creator found for task type: %1").arg(taskType));
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList TaskRegistry::getAvailableTypes() const
|
||||||
|
{
|
||||||
|
return m_creators.keys();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QodeAssist::TaskFlow
|
||||||
51
TaskFlow/core/TaskRegistry.hpp
Normal file
51
TaskFlow/core/TaskRegistry.hpp
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Petr Mironychev
|
||||||
|
*
|
||||||
|
* This file is part of QodeAssist.
|
||||||
|
*
|
||||||
|
* QodeAssist is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* QodeAssist is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with QodeAssist. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <QHash>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
namespace QodeAssist::TaskFlow {
|
||||||
|
|
||||||
|
class BaseTask;
|
||||||
|
|
||||||
|
class TaskRegistry : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
using TaskCreator = std::function<BaseTask *(QObject *parent)>;
|
||||||
|
|
||||||
|
explicit TaskRegistry(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void registerTask(const QString &taskType)
|
||||||
|
{
|
||||||
|
m_creators[taskType] = [](QObject *parent) -> BaseTask * { return new T(parent); };
|
||||||
|
}
|
||||||
|
BaseTask *createTask(const QString &taskType, QObject *parent = nullptr) const;
|
||||||
|
QStringList getAvailableTypes() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QHash<QString, TaskCreator> m_creators;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace QodeAssist::TaskFlow
|
||||||
Reference in New Issue
Block a user