main.qml 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. import QtQuick 2.15
  2. import QtQuick.Controls 2.15
  3. import QtQuick.Layouts 1.15
  4. import QtQuick.VirtualKeyboard 2.15
  5. import DuneWeaver 1.0
  6. import "components"
  7. import "components" as Components
  8. ApplicationWindow {
  9. id: window
  10. visible: true
  11. width: 800
  12. height: 480
  13. title: "Dune Weaver Touch"
  14. // Solid background color for ApplicationWindow
  15. color: Components.ThemeManager.backgroundColor
  16. property int currentPageIndex: 0
  17. property alias stackView: stackView
  18. property alias backend: backend
  19. property bool shouldNavigateToExecution: false
  20. property string currentPatternName: ""
  21. property string currentPatternPreview: ""
  22. onCurrentPageIndexChanged: {
  23. }
  24. onShouldNavigateToExecutionChanged: {
  25. if (shouldNavigateToExecution) {
  26. // If we're in a sub-page (like PatternDetailPage), pop back to main view first
  27. if (stackView.depth > 1) {
  28. stackView.pop()
  29. }
  30. // Then navigate to ExecutionPage tab (index 4)
  31. currentPageIndex = 4
  32. shouldNavigateToExecution = false
  33. }
  34. }
  35. Backend {
  36. id: backend
  37. onExecutionStarted: function(patternName, patternPreview) {
  38. // Store pattern info for ExecutionPage
  39. window.currentPatternName = patternName
  40. window.currentPatternPreview = patternPreview
  41. // Navigate to Execution tab (index 3) instead of pushing page
  42. shouldNavigateToExecution = true
  43. }
  44. // Preview rendered after the pattern started (it wasn't cached yet)
  45. onPatternPreviewReady: function(patternName, preview) {
  46. if (patternName === window.currentPatternName) {
  47. window.currentPatternPreview = preview
  48. }
  49. }
  50. onErrorOccurred: function(error) {
  51. // Always the in-scene themed dialog: the native MessageDialog
  52. // renders as an unreadable empty box on some platforms and can't
  53. // follow the Pi 5's 180° rotation.
  54. customErrorDialog.errorText = error
  55. customErrorDialog.open()
  56. }
  57. onScreenStateChanged: function(isOn) {
  58. }
  59. }
  60. // Global touch/mouse handler for activity tracking
  61. MouseArea {
  62. anchors.fill: parent
  63. acceptedButtons: Qt.NoButton // Don't interfere with other mouse areas
  64. hoverEnabled: true
  65. propagateComposedEvents: true
  66. onPressed: {
  67. backend.resetActivityTimer()
  68. }
  69. onPositionChanged: {
  70. backend.resetActivityTimer()
  71. }
  72. onClicked: {
  73. backend.resetActivityTimer()
  74. }
  75. }
  76. PatternModel {
  77. id: patternModel
  78. }
  79. // Rotation container for Pi 5
  80. Item {
  81. id: rotationContainer
  82. anchors.fill: parent
  83. rotation: typeof rotateDisplay !== 'undefined' && rotateDisplay ? 180 : 0
  84. transformOrigin: Item.Center
  85. StackView {
  86. id: stackView
  87. anchors.fill: parent
  88. // The UI is always usable regardless of table reachability; the
  89. // per-page ConnectionStatus dot (green/red) reflects the link state
  90. // and Table Control handles picking/reconnecting a table.
  91. initialItem: mainSwipeView
  92. Component {
  93. id: mainSwipeView
  94. Item {
  95. // Main content area
  96. StackLayout {
  97. id: stackLayout
  98. anchors.top: parent.top
  99. anchors.left: parent.left
  100. anchors.right: parent.right
  101. anchors.bottom: bottomNav.top
  102. currentIndex: window.currentPageIndex
  103. Component.onCompleted: {
  104. }
  105. // Patterns Page
  106. Loader {
  107. source: "pages/ModernPatternListPage.qml"
  108. onLoaded: {
  109. item.patternModel = patternModel
  110. item.backend = backend
  111. item.stackView = stackView
  112. }
  113. }
  114. // Playlists Page
  115. Loader {
  116. source: "pages/ModernPlaylistPage.qml"
  117. onLoaded: {
  118. item.backend = backend
  119. item.stackView = stackView
  120. item.mainWindow = window
  121. }
  122. }
  123. // Control Page
  124. Loader {
  125. source: "pages/TableControlPage.qml"
  126. onLoaded: {
  127. item.backend = backend
  128. }
  129. }
  130. // LED Control Page (index 3)
  131. Loader {
  132. source: "pages/LedControlPage.qml"
  133. onLoaded: {
  134. item.backend = backend
  135. }
  136. }
  137. // Execution Page (index 4)
  138. Loader {
  139. source: "pages/ExecutionPage.qml"
  140. onLoaded: {
  141. item.backend = backend
  142. item.stackView = stackView
  143. item.patternName = Qt.binding(function() { return window.currentPatternName })
  144. item.patternPreview = Qt.binding(function() { return window.currentPatternPreview })
  145. }
  146. }
  147. }
  148. // Bottom Navigation
  149. BottomNavigation {
  150. id: bottomNav
  151. anchors.bottom: parent.bottom
  152. anchors.left: parent.left
  153. anchors.right: parent.right
  154. currentIndex: window.currentPageIndex
  155. onTabClicked: function(index) {
  156. window.currentPageIndex = index
  157. }
  158. }
  159. }
  160. }
  161. }
  162. } // End rotationContainer
  163. // Virtual Keyboard Support - outside rotation container for proper positioning
  164. InputPanel {
  165. id: inputPanel
  166. z: 99999
  167. y: window.height
  168. anchors.left: parent.left
  169. anchors.right: parent.right
  170. // Rotate keyboard for Pi 5
  171. rotation: typeof rotateDisplay !== 'undefined' && rotateDisplay ? 180 : 0
  172. transformOrigin: Item.Center
  173. states: State {
  174. name: "visible"
  175. when: inputPanel.active
  176. PropertyChanges {
  177. target: inputPanel
  178. y: typeof rotateDisplay !== 'undefined' && rotateDisplay ? 0 : window.height - inputPanel.height
  179. }
  180. }
  181. transitions: Transition {
  182. from: ""
  183. to: "visible"
  184. reversible: true
  185. ParallelAnimation {
  186. NumberAnimation {
  187. target: inputPanel
  188. property: "y"
  189. duration: 250
  190. easing.type: Easing.InOutQuad
  191. }
  192. }
  193. }
  194. }
  195. // Error dialog (in-scene so it themes and rotates with the UI)
  196. Popup {
  197. id: customErrorDialog
  198. modal: true
  199. x: (window.width - width) / 2
  200. y: (window.height - height) / 2
  201. width: 380
  202. height: Math.max(190, errorColumn.implicitHeight + 50)
  203. closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
  204. property string errorText: ""
  205. background: Rectangle {
  206. color: Components.ThemeManager.surfaceColor
  207. radius: Components.ThemeManager.radiusMd
  208. border.color: Components.ThemeManager.borderColor
  209. border.width: 1
  210. // Rotate the entire dialog content for Pi 5
  211. rotation: typeof rotateDisplay !== 'undefined' && rotateDisplay ? 180 : 0
  212. transformOrigin: Item.Center
  213. }
  214. contentItem: Item {
  215. rotation: typeof rotateDisplay !== 'undefined' && rotateDisplay ? 180 : 0
  216. transformOrigin: Item.Center
  217. Column {
  218. id: errorColumn
  219. anchors.fill: parent
  220. anchors.margins: 20
  221. spacing: 15
  222. Label {
  223. text: "Something went wrong"
  224. font.family: Components.ThemeManager.fontDisplay
  225. font.pixelSize: Components.ThemeManager.fontSizeTitle
  226. color: Components.ThemeManager.danger
  227. anchors.horizontalCenter: parent.horizontalCenter
  228. }
  229. Label {
  230. text: customErrorDialog.errorText
  231. wrapMode: Text.WordWrap
  232. width: parent.width
  233. horizontalAlignment: Text.AlignHCenter
  234. color: Components.ThemeManager.textPrimary
  235. font.family: Components.ThemeManager.fontBody
  236. font.pixelSize: Components.ThemeManager.fontSizeBody
  237. }
  238. Components.ModernControlButton {
  239. text: "OK"
  240. width: 120
  241. height: Components.ThemeManager.touchTarget
  242. anchors.horizontalCenter: parent.horizontalCenter
  243. onClicked: customErrorDialog.close()
  244. }
  245. }
  246. }
  247. }
  248. }