| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540 |
- import QtQuick 2.15
- import QtQuick.Controls 2.15
- import QtQuick.Layouts 1.15
- import Qt.labs.folderlistmodel 2.15
- import "../components"
- Page {
- id: page
- property var backend: null
- property var stackView: null
- property string patternName: ""
- property string patternPreview: ""
-
- // Get current pattern info from backend
- property string currentPattern: backend ? backend.currentFile : ""
- property string currentPreviewPath: ""
- property var allPossiblePaths: []
- property int currentPathIndex: 0
- property string activeImageSource: "" // Separate property to avoid binding loop
- property string repoRoot: "" // Will hold the absolute path to repository root
- property bool imageRetryInProgress: false // Prevent multiple retry attempts
-
- // Debug backend connection
- onBackendChanged: {
- console.log("ExecutionPage: backend changed to", backend)
- if (backend) {
- console.log("ExecutionPage: backend.serialConnected =", backend.serialConnected)
- console.log("ExecutionPage: backend.isConnected =", backend.isConnected)
- }
- }
-
- Component.onCompleted: {
- console.log("ExecutionPage: Component completed, backend =", backend)
- if (backend) {
- console.log("ExecutionPage: initial serialConnected =", backend.serialConnected)
- }
-
- // Find repository root directory
- findRepoRoot()
- }
-
- // Direct connection to backend signals
- Connections {
- target: backend
-
- function onSerialConnectionChanged(connected) {
- console.log("ExecutionPage: received serialConnectionChanged signal:", connected)
- }
-
- function onConnectionChanged() {
- console.log("ExecutionPage: received connectionChanged signal")
- if (backend) {
- console.log("ExecutionPage: after connectionChanged, serialConnected =", backend.serialConnected)
- }
- }
- }
-
- onCurrentPatternChanged: {
- if (currentPattern) {
- // Generate preview path from current pattern
- updatePreviewPath()
- }
- }
-
- function updatePreviewPath() {
- if (!currentPattern) {
- console.log("🔍 No current pattern, clearing preview path")
- currentPreviewPath = ""
- return
- }
-
- console.log("🔍 Updating preview for pattern:", currentPattern)
-
- // Extract just the filename from the path
- var fileName = currentPattern.split('/').pop() // Get last part of path
- var baseName = fileName.replace(".thr", "")
- console.log("🔍 File name:", fileName, "Base name:", baseName)
-
- // Use absolute paths based on discovered repository root
- var possibleBasePaths = []
-
- if (repoRoot) {
- // Use the discovered repository root
- possibleBasePaths = [
- "file://" + repoRoot + "/patterns/cached_images/"
- ]
- console.log("🎯 Using repository root for paths:", repoRoot)
- } else {
- console.log("⚠️ Repository root not found, using fallback relative paths")
- // Fallback to relative paths if repo root discovery failed
- possibleBasePaths = [
- "../../../patterns/cached_images/", // Three levels up from QML file location
- "../../patterns/cached_images/", // Two levels up (backup)
- "../../../../patterns/cached_images/" // Four levels up (backup)
- ]
- }
-
- var possiblePaths = []
-
- // Build paths using all possible base paths
- // Prioritize PNG format since WebP is not supported on this system
- for (var i = 0; i < possibleBasePaths.length; i++) {
- var basePath = possibleBasePaths[i]
- // First try with .thr suffix (e.g., pattern.thr.png) - PNG first since WebP failed
- possiblePaths.push(basePath + fileName + ".png")
- possiblePaths.push(basePath + fileName + ".jpg")
- possiblePaths.push(basePath + fileName + ".jpeg")
- // Then try without .thr suffix (e.g., pattern.png)
- possiblePaths.push(basePath + baseName + ".png")
- possiblePaths.push(basePath + baseName + ".jpg")
- possiblePaths.push(basePath + baseName + ".jpeg")
- }
-
- console.log("🔍 Possible preview paths:", JSON.stringify(possiblePaths))
-
- // Store all possible paths for fallback mechanism
- allPossiblePaths = possiblePaths
- currentPathIndex = 0
-
- // Set the active image source to avoid binding loops
- if (possiblePaths.length > 0) {
- currentPreviewPath = possiblePaths[0]
- activeImageSource = possiblePaths[0]
- console.log("🎯 Setting preview path to:", currentPreviewPath)
- console.log("🎯 Setting active image source to:", activeImageSource)
- } else {
- console.log("❌ No possible paths found")
- currentPreviewPath = ""
- activeImageSource = ""
- }
- }
-
- function tryNextPreviewPath() {
- if (allPossiblePaths.length === 0) {
- console.log("❌ No more paths to try")
- return false
- }
-
- currentPathIndex++
- if (currentPathIndex >= allPossiblePaths.length) {
- console.log("❌ All paths exhausted")
- return false
- }
-
- currentPreviewPath = allPossiblePaths[currentPathIndex]
- activeImageSource = allPossiblePaths[currentPathIndex]
- console.log("🔄 Trying next preview path:", currentPreviewPath)
- console.log("🔄 Setting active image source to:", activeImageSource)
- return true
- }
-
- function findRepoRoot() {
- // Start from the current QML file location and work our way up
- var currentPath = Qt.resolvedUrl(".").toString()
- console.log("🔍 Starting search from QML file location:", currentPath)
-
- // Remove file:// prefix and get directory parts
- if (currentPath.startsWith("file://")) {
- currentPath = currentPath.substring(7)
- }
-
- var pathParts = currentPath.split("/")
- console.log("🔍 Path parts:", JSON.stringify(pathParts))
-
- // Look for the dune-weaver directory by going up the path
- for (var i = pathParts.length - 1; i >= 0; i--) {
- if (pathParts[i] === "dune-weaver" || pathParts[i] === "dune-weaver-touch") {
- // Found it! Build the repo root path
- var rootPath = "/" + pathParts.slice(1, i + (pathParts[i] === "dune-weaver" ? 1 : 0)).join("/")
- if (pathParts[i] === "dune-weaver-touch") {
- // We need to go up one more level to get to dune-weaver
- rootPath = "/" + pathParts.slice(1, i).join("/")
- }
- repoRoot = rootPath
- console.log("🎯 Found repository root:", repoRoot)
- return
- }
- }
-
- console.log("❌ Could not find repository root")
- }
-
- // Timer to handle image retry without causing binding loops
- Timer {
- id: imageRetryTimer
- interval: 100 // Small delay to break the binding cycle
- onTriggered: {
- if (tryNextPreviewPath()) {
- console.log("🔄 Retrying with new path after timer...")
- }
- imageRetryInProgress = false
- }
- }
-
- Rectangle {
- anchors.fill: parent
- color: "#f5f5f5"
- }
-
- ColumnLayout {
- anchors.fill: parent
- spacing: 0
-
- // Header (consistent with other pages)
- Rectangle {
- Layout.fillWidth: true
- Layout.preferredHeight: 50
- color: "white"
-
- // Bottom border
- Rectangle {
- anchors.bottom: parent.bottom
- width: parent.width
- height: 1
- color: "#e5e7eb"
- }
-
- RowLayout {
- anchors.fill: parent
- anchors.leftMargin: 15
- anchors.rightMargin: 10
-
- ConnectionStatus {
- backend: page.backend
- Layout.rightMargin: 8
- }
-
- Label {
- text: "Pattern Execution"
- font.pixelSize: 18
- font.bold: true
- color: "#333"
- }
-
- Item {
- Layout.fillWidth: true
- }
- }
- }
-
- // Content - Side by side layout
- Item {
- Layout.fillWidth: true
- Layout.fillHeight: true
-
- Row {
- anchors.fill: parent
- spacing: 0
-
- // Left side - Pattern Preview (60% of width)
- Rectangle {
- width: parent.width * 0.6
- height: parent.height
- color: "#ffffff"
-
- Image {
- anchors.fill: parent
- anchors.margins: 10
- source: {
- var finalSource = ""
-
- // Try different sources in priority order
- if (patternPreview) {
- finalSource = "file:///" + patternPreview
- console.log("🖼️ Using patternPreview:", finalSource)
- } else if (activeImageSource) {
- // Use the activeImageSource to avoid binding loops
- finalSource = activeImageSource
- console.log("🖼️ Using activeImageSource:", finalSource)
- } else {
- console.log("🖼️ No preview source available")
- }
-
- return finalSource
- }
- fillMode: Image.PreserveAspectFit
-
- onStatusChanged: {
- console.log("📷 Image status:", status, "for source:", source)
- if (status === Image.Error) {
- console.log("❌ Image failed to load:", source)
- // Use timer to avoid binding loop
- if (!imageRetryInProgress) {
- imageRetryInProgress = true
- imageRetryTimer.start()
- }
- } else if (status === Image.Ready) {
- console.log("✅ Image loaded successfully:", source)
- imageRetryInProgress = false // Reset on successful load
- } else if (status === Image.Loading) {
- console.log("🔄 Image loading:", source)
- }
- }
-
- onSourceChanged: {
- console.log("🔄 Image source changed to:", source)
- }
-
- Rectangle {
- anchors.fill: parent
- color: "#f0f0f0"
- visible: parent.status === Image.Error || parent.source == ""
-
- Column {
- anchors.centerIn: parent
- spacing: 10
-
- Text {
- text: "⚙️"
- font.pixelSize: 48
- color: "#ccc"
- anchors.horizontalCenter: parent.horizontalCenter
- }
-
- Text {
- text: "Pattern Preview"
- color: "#999"
- font.pixelSize: 14
- anchors.horizontalCenter: parent.horizontalCenter
- }
- }
- }
- }
- }
-
- // Divider
- Rectangle {
- width: 1
- height: parent.height
- color: "#e5e7eb"
- }
-
- // Right side - Controls (40% of width)
- Rectangle {
- width: parent.width * 0.4 - 1
- height: parent.height
- color: "white"
-
- Column {
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: parent.top
- anchors.margins: 10
- spacing: 15
-
- // Pattern Name
- Rectangle {
- width: parent.width
- height: 50
- radius: 8
- color: "#f8f9fa"
- border.color: "#e5e7eb"
- border.width: 1
-
- Column {
- anchors.centerIn: parent
- spacing: 4
-
- Label {
- text: "Current Pattern"
- font.pixelSize: 10
- color: "#666"
- anchors.horizontalCenter: parent.horizontalCenter
- }
-
- Label {
- text: {
- // Use WebSocket current pattern first, then fallback to passed parameter
- var displayName = ""
- if (backend && backend.currentFile) displayName = backend.currentFile
- else if (patternName) displayName = patternName
- else return "No pattern running"
-
- // Clean up the name for display
- var parts = displayName.split('/')
- displayName = parts[parts.length - 1]
- displayName = displayName.replace('.thr', '')
- return displayName
- }
- font.pixelSize: 12
- font.bold: true
- color: "#333"
- anchors.horizontalCenter: parent.horizontalCenter
- width: parent.parent.width - 20
- elide: Text.ElideMiddle
- horizontalAlignment: Text.AlignHCenter
- }
- }
- }
-
- // Progress
- Rectangle {
- width: parent.width
- height: 70
- radius: 8
- color: "#f8f9fa"
- border.color: "#e5e7eb"
- border.width: 1
-
- Column {
- anchors.fill: parent
- anchors.margins: 10
- spacing: 8
-
- Label {
- text: "Progress"
- font.pixelSize: 12
- font.bold: true
- color: "#333"
- }
-
- ProgressBar {
- width: parent.width
- height: 8
- value: backend ? backend.progress / 100 : 0
- }
-
- Label {
- text: backend ? Math.round(backend.progress) + "%" : "0%"
- anchors.horizontalCenter: parent.horizontalCenter
- font.pixelSize: 14
- font.bold: true
- color: "#333"
- }
- }
- }
-
- // Control Buttons
- Rectangle {
- width: parent.width
- height: 180
- radius: 8
- color: "#f8f9fa"
- border.color: "#e5e7eb"
- border.width: 1
-
- Column {
- anchors.fill: parent
- anchors.margins: 10
- spacing: 10
-
- Label {
- text: "Controls"
- font.pixelSize: 12
- font.bold: true
- color: "#333"
- }
-
- // Pause/Resume button
- Rectangle {
- width: parent.width
- height: 35
- radius: 6
- color: pauseMouseArea.pressed ? "#1e40af" : (backend && backend.currentFile !== "" ? "#2563eb" : "#9ca3af")
-
- Text {
- anchors.centerIn: parent
- text: (backend && backend.isRunning) ? "⏸ Pause" : "▶ Resume"
- color: "white"
- font.pixelSize: 12
- font.bold: true
- }
-
- MouseArea {
- id: pauseMouseArea
- anchors.fill: parent
- enabled: backend && backend.currentFile !== ""
- onClicked: {
- if (backend) {
- if (backend.isRunning) {
- backend.pauseExecution()
- } else {
- backend.resumeExecution()
- }
- }
- }
- }
- }
-
- // Stop button
- Rectangle {
- width: parent.width
- height: 35
- radius: 6
- color: stopMouseArea.pressed ? "#b91c1c" : (backend && backend.currentFile !== "" ? "#dc2626" : "#9ca3af")
-
- Text {
- anchors.centerIn: parent
- text: "⏹ Stop"
- color: "white"
- font.pixelSize: 12
- font.bold: true
- }
-
- MouseArea {
- id: stopMouseArea
- anchors.fill: parent
- enabled: backend && backend.currentFile !== ""
- onClicked: {
- if (backend) {
- backend.stopExecution()
- }
- }
- }
- }
-
- // Skip button
- Rectangle {
- width: parent.width
- height: 35
- radius: 6
- color: skipMouseArea.pressed ? "#525252" : (backend && backend.currentFile !== "" ? "#6b7280" : "#9ca3af")
-
- Text {
- anchors.centerIn: parent
- text: "⏭ Skip"
- color: "white"
- font.pixelSize: 12
- font.bold: true
- }
-
- MouseArea {
- id: skipMouseArea
- anchors.fill: parent
- enabled: backend && backend.currentFile !== ""
- onClicked: {
- if (backend) {
- backend.skipPattern()
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
|