main.qml 11 KB

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