mirror of
https://github.com/Palm1r/QodeAssist.git
synced 2025-07-19 05:24:59 -04:00
feat: Add basic task flow run and edit (#212)
This commit is contained in:
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 }
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user