main.qml 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. import QtQuick 2.15
  2. import QtQuick.Controls 2.15
  3. import QtQuick.Layouts 1.15
  4. import QtQuick.Dialogs
  5. import QtQuick.VirtualKeyboard 2.15
  6. import DuneWeaver 1.0
  7. import "components"
  8. import "components" as Components
  9. ApplicationWindow {
  10. id: window
  11. visible: true
  12. width: 800
  13. height: 480
  14. title: "Dune Weaver Touch"
  15. // Solid background to prevent console bleed-through on linuxfb
  16. // linuxfb doesn't have proper compositing, so we need to ensure
  17. // the entire window is filled with an opaque color
  18. color: Components.ThemeManager.backgroundColor
  19. property int currentPageIndex: 0
  20. property alias stackView: stackView
  21. property alias backend: backend
  22. property bool shouldNavigateToExecution: false
  23. property string currentPatternName: ""
  24. property string currentPatternPreview: ""
  25. onCurrentPageIndexChanged: {
  26. console.log("📱 currentPageIndex changed to:", currentPageIndex)
  27. }
  28. onShouldNavigateToExecutionChanged: {
  29. if (shouldNavigateToExecution) {
  30. console.log("🎯 Navigating to execution page")
  31. console.log("🎯 Current stack depth:", stackView.depth)
  32. // If we're in a sub-page (like PatternDetailPage), pop back to main view first
  33. if (stackView.depth > 1) {
  34. console.log("🎯 Popping back to main view first")
  35. stackView.pop()
  36. }
  37. // Then navigate to ExecutionPage tab (index 4)
  38. console.log("🎯 Setting currentPageIndex to 4")
  39. currentPageIndex = 4
  40. shouldNavigateToExecution = false
  41. }
  42. }
  43. Backend {
  44. id: backend
  45. onExecutionStarted: function(patternName, patternPreview) {
  46. console.log("🎯 QML: ExecutionStarted signal received! patternName='" + patternName + "', preview='" + patternPreview + "'")
  47. console.log("🎯 Setting shouldNavigateToExecution = true")
  48. // Store pattern info for ExecutionPage
  49. window.currentPatternName = patternName
  50. window.currentPatternPreview = patternPreview
  51. // Navigate to Execution tab (index 3) instead of pushing page
  52. shouldNavigateToExecution = true
  53. console.log("🎯 shouldNavigateToExecution set to:", shouldNavigateToExecution)
  54. }
  55. onErrorOccurred: function(error) {
  56. // Use custom dialog on Pi 5 for proper rotation
  57. if (typeof rotateDisplay !== 'undefined' && rotateDisplay) {
  58. customErrorDialog.errorText = error
  59. customErrorDialog.open()
  60. } else {
  61. errorDialog.text = error
  62. errorDialog.open()
  63. }
  64. }
  65. onScreenStateChanged: function(isOn) {
  66. console.log("🖥️ Screen state changed:", isOn ? "ON" : "OFF")
  67. }
  68. onBackendConnectionChanged: function(connected) {
  69. console.log("🔗 Backend connection changed:", connected)
  70. if (connected && stackView.currentItem.toString().indexOf("ConnectionSplash") !== -1) {
  71. console.log("✅ Backend connected, switching to main view")
  72. stackView.replace(mainSwipeView)
  73. } else if (!connected && stackView.currentItem.toString().indexOf("ConnectionSplash") === -1) {
  74. console.log("❌ Backend disconnected, switching to splash screen")
  75. stackView.replace(connectionSplash)
  76. }
  77. }
  78. }
  79. // Global touch/mouse handler for activity tracking
  80. MouseArea {
  81. anchors.fill: parent
  82. acceptedButtons: Qt.NoButton // Don't interfere with other mouse areas
  83. hoverEnabled: true
  84. propagateComposedEvents: true
  85. onPressed: {
  86. console.log("🖥️ QML: Touch/press detected - resetting activity timer")
  87. backend.resetActivityTimer()
  88. }
  89. onPositionChanged: {
  90. console.log("🖥️ QML: Mouse movement detected - resetting activity timer")
  91. backend.resetActivityTimer()
  92. }
  93. onClicked: {
  94. console.log("🖥️ QML: Click detected - resetting activity timer")
  95. backend.resetActivityTimer()
  96. }
  97. }
  98. PatternModel {
  99. id: patternModel
  100. }
  101. // Rotation container for Pi 5 linuxfb
  102. Item {
  103. id: rotationContainer
  104. anchors.fill: parent
  105. rotation: typeof rotateDisplay !== 'undefined' && rotateDisplay ? 180 : 0
  106. transformOrigin: Item.Center
  107. StackView {
  108. id: stackView
  109. anchors.fill: parent
  110. initialItem: backend.backendConnected ? mainSwipeView : connectionSplash
  111. Component {
  112. id: connectionSplash
  113. ConnectionSplash {
  114. statusText: backend.reconnectStatus
  115. showRetryButton: backend.reconnectStatus === "Cannot connect to backend"
  116. onRetryConnection: {
  117. console.log("🔄 Manual retry requested")
  118. backend.retryConnection()
  119. }
  120. }
  121. }
  122. Component {
  123. id: mainSwipeView
  124. Item {
  125. // Main content area
  126. StackLayout {
  127. id: stackLayout
  128. anchors.top: parent.top
  129. anchors.left: parent.left
  130. anchors.right: parent.right
  131. anchors.bottom: bottomNav.top
  132. currentIndex: window.currentPageIndex
  133. Component.onCompleted: {
  134. console.log("📱 StackLayout created with currentIndex:", currentIndex, "bound to window.currentPageIndex:", window.currentPageIndex)
  135. }
  136. // Patterns Page
  137. Loader {
  138. source: "pages/ModernPatternListPage.qml"
  139. onLoaded: {
  140. item.patternModel = patternModel
  141. item.backend = backend
  142. item.stackView = stackView
  143. }
  144. }
  145. // Playlists Page
  146. Loader {
  147. source: "pages/ModernPlaylistPage.qml"
  148. onLoaded: {
  149. item.backend = backend
  150. item.stackView = stackView
  151. item.mainWindow = window
  152. }
  153. }
  154. // Control Page
  155. Loader {
  156. source: "pages/TableControlPage.qml"
  157. onLoaded: {
  158. item.backend = backend
  159. }
  160. }
  161. // LED Control Page (index 3)
  162. Loader {
  163. source: "pages/LedControlPage.qml"
  164. onLoaded: {
  165. item.backend = backend
  166. }
  167. }
  168. // Execution Page (index 4)
  169. Loader {
  170. source: "pages/ExecutionPage.qml"
  171. onLoaded: {
  172. item.backend = backend
  173. item.stackView = stackView
  174. item.patternName = Qt.binding(function() { return window.currentPatternName })
  175. item.patternPreview = Qt.binding(function() { return window.currentPatternPreview })
  176. }
  177. }
  178. }
  179. // Bottom Navigation
  180. BottomNavigation {
  181. id: bottomNav
  182. anchors.bottom: parent.bottom
  183. anchors.left: parent.left
  184. anchors.right: parent.right
  185. currentIndex: window.currentPageIndex
  186. onTabClicked: function(index) {
  187. console.log("📱 Tab clicked:", index)
  188. window.currentPageIndex = index
  189. }
  190. }
  191. }
  192. }
  193. }
  194. } // End rotationContainer
  195. // Virtual Keyboard Support - outside rotation container for proper positioning
  196. InputPanel {
  197. id: inputPanel
  198. z: 99999
  199. y: window.height
  200. anchors.left: parent.left
  201. anchors.right: parent.right
  202. // Rotate keyboard for Pi 5
  203. rotation: typeof rotateDisplay !== 'undefined' && rotateDisplay ? 180 : 0
  204. transformOrigin: Item.Center
  205. states: State {
  206. name: "visible"
  207. when: inputPanel.active
  208. PropertyChanges {
  209. target: inputPanel
  210. y: typeof rotateDisplay !== 'undefined' && rotateDisplay ? 0 : window.height - inputPanel.height
  211. }
  212. }
  213. transitions: Transition {
  214. from: ""
  215. to: "visible"
  216. reversible: true
  217. ParallelAnimation {
  218. NumberAnimation {
  219. target: inputPanel
  220. property: "y"
  221. duration: 250
  222. easing.type: Easing.InOutQuad
  223. }
  224. }
  225. }
  226. }
  227. // Error dialog - note: MessageDialog is a system dialog, rotation may not work
  228. // If rotation doesn't work, we'll need to replace with a custom Dialog
  229. MessageDialog {
  230. id: errorDialog
  231. title: "Error"
  232. buttons: MessageDialog.Ok
  233. }
  234. // Custom error dialog as fallback for Pi 5 rotation
  235. Popup {
  236. id: customErrorDialog
  237. modal: true
  238. x: (window.width - width) / 2
  239. y: (window.height - height) / 2
  240. width: 320
  241. height: 180
  242. closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
  243. property string errorText: ""
  244. background: Rectangle {
  245. color: "#2d2d2d"
  246. radius: 12
  247. border.color: "#404040"
  248. border.width: 1
  249. // Rotate the entire dialog content for Pi 5
  250. rotation: typeof rotateDisplay !== 'undefined' && rotateDisplay ? 180 : 0
  251. transformOrigin: Item.Center
  252. }
  253. contentItem: Item {
  254. rotation: typeof rotateDisplay !== 'undefined' && rotateDisplay ? 180 : 0
  255. transformOrigin: Item.Center
  256. Column {
  257. anchors.fill: parent
  258. anchors.margins: 20
  259. spacing: 15
  260. Label {
  261. text: "Error"
  262. font.pixelSize: 18
  263. font.bold: true
  264. color: "#ff6b6b"
  265. anchors.horizontalCenter: parent.horizontalCenter
  266. }
  267. Label {
  268. text: customErrorDialog.errorText
  269. wrapMode: Text.WordWrap
  270. width: parent.width
  271. horizontalAlignment: Text.AlignHCenter
  272. color: "#ffffff"
  273. font.pixelSize: 14
  274. }
  275. Button {
  276. text: "OK"
  277. anchors.horizontalCenter: parent.horizontalCenter
  278. onClicked: customErrorDialog.close()
  279. }
  280. }
  281. }
  282. }
  283. }