1
0

LedControlPage.qml 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. import QtQuick 2.15
  2. import QtQuick.Controls 2.15
  3. import QtQuick.Layouts 1.15
  4. import QtQuick.Dialogs
  5. import "../components"
  6. import "../components" as Components
  7. Page {
  8. id: page
  9. property var backend: null
  10. // Local state
  11. property bool ledPowerOn: false
  12. property int ledBrightness: 100
  13. property string ledProvider: "none"
  14. property bool ledConnected: false
  15. property int currentEffectIndex: 0
  16. property int currentPaletteIndex: 0
  17. property var effectsList: []
  18. property var palettesList: []
  19. // Predefined colors for quick selection (muted tones to fit dark UI)
  20. property var presetColors: [
  21. {"name": "White", "color": "#e8e8e8", "sendColor": "#ffffff"},
  22. {"name": "Warm", "color": "#d4a574", "sendColor": "#ffaa55"},
  23. {"name": "Red", "color": "#c45c5c", "sendColor": "#ff0000"},
  24. {"name": "Orange", "color": "#d4875c", "sendColor": "#ff8800"},
  25. {"name": "Yellow", "color": "#c9b95c", "sendColor": "#ffff00"},
  26. {"name": "Green", "color": "#5cb85c", "sendColor": "#00ff00"},
  27. {"name": "Cyan", "color": "#5cb8b8", "sendColor": "#00ffff"},
  28. {"name": "Blue", "color": "#5c7cc4", "sendColor": "#0000ff"},
  29. {"name": "Purple", "color": "#8b5cc4", "sendColor": "#8800ff"},
  30. {"name": "Pink", "color": "#c45c99", "sendColor": "#ff00ff"}
  31. ]
  32. // Backend signal connections
  33. Connections {
  34. target: backend
  35. function onLedStatusChanged() {
  36. if (backend) {
  37. ledPowerOn = backend.ledPowerOn
  38. ledBrightness = backend.ledBrightness
  39. ledProvider = backend.ledProvider
  40. ledConnected = backend.ledConnected
  41. currentEffectIndex = backend.ledCurrentEffect
  42. currentPaletteIndex = backend.ledCurrentPalette
  43. }
  44. }
  45. function onLedEffectsLoaded(effects) {
  46. effectsList = effects
  47. }
  48. function onLedPalettesLoaded(palettes) {
  49. palettesList = palettes
  50. }
  51. }
  52. // Load LED config on page load
  53. Component.onCompleted: {
  54. if (backend) {
  55. backend.loadLedConfig()
  56. }
  57. }
  58. Rectangle {
  59. anchors.fill: parent
  60. color: Components.ThemeManager.backgroundColor
  61. }
  62. ColumnLayout {
  63. anchors.fill: parent
  64. spacing: 0
  65. // Header
  66. Rectangle {
  67. Layout.fillWidth: true
  68. Layout.preferredHeight: 50
  69. color: Components.ThemeManager.surfaceColor
  70. Rectangle {
  71. anchors.bottom: parent.bottom
  72. width: parent.width
  73. height: 1
  74. color: Components.ThemeManager.borderColor
  75. }
  76. RowLayout {
  77. anchors.fill: parent
  78. anchors.leftMargin: 15
  79. anchors.rightMargin: 10
  80. ConnectionStatus {
  81. backend: page.backend
  82. Layout.rightMargin: 8
  83. }
  84. Label {
  85. text: "LED Control"
  86. font.pixelSize: 18
  87. font.bold: true
  88. color: Components.ThemeManager.textPrimary
  89. }
  90. Item {
  91. Layout.fillWidth: true
  92. }
  93. }
  94. }
  95. // Main Content
  96. ScrollView {
  97. Layout.fillWidth: true
  98. Layout.fillHeight: true
  99. contentWidth: availableWidth
  100. ColumnLayout {
  101. width: parent.width
  102. anchors.margins: 5
  103. spacing: 2
  104. // Provider Info & Power/Brightness Section
  105. Rectangle {
  106. Layout.fillWidth: true
  107. Layout.preferredHeight: ledProvider === "none" ? 100 : (ledProvider === "wled" ? 90 : 110)
  108. Layout.margins: 5
  109. radius: 8
  110. color: Components.ThemeManager.surfaceColor
  111. ColumnLayout {
  112. anchors.fill: parent
  113. anchors.margins: 15
  114. spacing: 10
  115. // Not configured message
  116. ColumnLayout {
  117. visible: ledProvider === "none"
  118. Layout.fillWidth: true
  119. spacing: 8
  120. Label {
  121. text: "LED Not Configured"
  122. font.pixelSize: 14
  123. font.bold: true
  124. color: Components.ThemeManager.textPrimary
  125. }
  126. Label {
  127. text: "Configure LED settings in the main Dune Weaver web interface"
  128. font.pixelSize: 12
  129. color: Components.ThemeManager.textSecondary
  130. wrapMode: Text.WordWrap
  131. Layout.fillWidth: true
  132. }
  133. }
  134. // DW LEDs Controls - Power and Brightness in same section
  135. ColumnLayout {
  136. visible: ledProvider === "dw_leds"
  137. Layout.fillWidth: true
  138. spacing: 8
  139. // Power row with status and toggle
  140. RowLayout {
  141. Layout.fillWidth: true
  142. spacing: 12
  143. // Status indicator with label
  144. RowLayout {
  145. spacing: 6
  146. Rectangle {
  147. width: 12
  148. height: 12
  149. radius: 6
  150. color: ledPowerOn ? "#4CAF50" : "#6b7280"
  151. }
  152. Label {
  153. text: ledPowerOn ? "On" : "Off"
  154. font.pixelSize: 13
  155. font.bold: true
  156. color: Components.ThemeManager.textPrimary
  157. }
  158. }
  159. // Toggle button
  160. ModernControlButton {
  161. Layout.preferredWidth: 100
  162. Layout.preferredHeight: 36
  163. text: ledPowerOn ? "Turn Off" : "Turn On"
  164. icon: ""
  165. buttonColor: ledPowerOn ? "#6b7280" : "#4CAF50"
  166. fontSize: 11
  167. onClicked: {
  168. if (backend) {
  169. backend.toggleLedPower()
  170. }
  171. }
  172. }
  173. Item { Layout.fillWidth: true }
  174. // Connection status (smaller, secondary)
  175. RowLayout {
  176. spacing: 4
  177. Rectangle {
  178. width: 8
  179. height: 8
  180. radius: 4
  181. color: ledConnected ? "#4CAF50" : "#ef4444"
  182. }
  183. Label {
  184. text: ledConnected ? "Connected" : "Disconnected"
  185. font.pixelSize: 10
  186. color: Components.ThemeManager.textTertiary
  187. }
  188. }
  189. }
  190. // Brightness row
  191. RowLayout {
  192. Layout.fillWidth: true
  193. spacing: 10
  194. Label {
  195. text: "Brightness"
  196. font.pixelSize: 12
  197. color: Components.ThemeManager.textSecondary
  198. }
  199. Slider {
  200. id: brightnessSlider
  201. Layout.fillWidth: true
  202. from: 0
  203. to: 100
  204. stepSize: 5
  205. value: ledBrightness
  206. onMoved: {
  207. if (backend) {
  208. backend.setLedBrightness(Math.round(value))
  209. }
  210. }
  211. }
  212. Label {
  213. text: Math.round(brightnessSlider.value) + "%"
  214. font.pixelSize: 12
  215. font.bold: true
  216. color: Components.ThemeManager.textPrimary
  217. Layout.preferredWidth: 35
  218. horizontalAlignment: Text.AlignRight
  219. }
  220. }
  221. }
  222. // WLED Info
  223. ColumnLayout {
  224. visible: ledProvider === "wled"
  225. Layout.fillWidth: true
  226. spacing: 8
  227. Label {
  228. text: "WLED Mode"
  229. font.pixelSize: 14
  230. font.bold: true
  231. color: Components.ThemeManager.textPrimary
  232. }
  233. Label {
  234. text: "Use the main Dune Weaver web interface for WLED controls"
  235. font.pixelSize: 12
  236. color: Components.ThemeManager.textSecondary
  237. wrapMode: Text.WordWrap
  238. Layout.fillWidth: true
  239. }
  240. }
  241. }
  242. }
  243. // Effects Section (only for dw_leds)
  244. Rectangle {
  245. Layout.fillWidth: true
  246. Layout.preferredHeight: effectsList.length > 0 ? 180 : 80
  247. Layout.margins: 5
  248. radius: 8
  249. color: Components.ThemeManager.surfaceColor
  250. visible: ledProvider === "dw_leds"
  251. ColumnLayout {
  252. anchors.fill: parent
  253. anchors.margins: 15
  254. spacing: 10
  255. Label {
  256. text: "Effects"
  257. font.pixelSize: 14
  258. font.bold: true
  259. color: Components.ThemeManager.textPrimary
  260. }
  261. // Show loading or no effects message
  262. Label {
  263. visible: effectsList.length === 0
  264. text: "No effects available"
  265. font.pixelSize: 12
  266. color: Components.ThemeManager.textSecondary
  267. }
  268. // Effects grid
  269. GridLayout {
  270. Layout.fillWidth: true
  271. columns: 4
  272. rowSpacing: 6
  273. columnSpacing: 6
  274. visible: effectsList.length > 0
  275. Repeater {
  276. model: effectsList.slice(0, 12) // Show first 12 effects
  277. Rectangle {
  278. property int effectId: modelData.id !== undefined ? modelData.id : index
  279. property bool isSelected: effectId === currentEffectIndex
  280. Layout.fillWidth: true
  281. Layout.preferredHeight: 35
  282. radius: 6
  283. color: isSelected ?
  284. Components.ThemeManager.selectedBackground :
  285. Components.ThemeManager.buttonBackground
  286. border.color: isSelected ?
  287. Components.ThemeManager.selectedBorder :
  288. Components.ThemeManager.buttonBorder
  289. border.width: 1
  290. Label {
  291. anchors.centerIn: parent
  292. anchors.leftMargin: 4
  293. anchors.rightMargin: 4
  294. width: parent.width - 8
  295. text: modelData.name || ("Effect " + effectId)
  296. font.pixelSize: 10
  297. color: isSelected ? "white" : Components.ThemeManager.textPrimary
  298. elide: Text.ElideRight
  299. horizontalAlignment: Text.AlignHCenter
  300. }
  301. MouseArea {
  302. anchors.fill: parent
  303. onClicked: {
  304. if (backend) {
  305. backend.setLedEffect(effectId)
  306. currentEffectIndex = effectId
  307. }
  308. }
  309. }
  310. }
  311. }
  312. }
  313. }
  314. }
  315. // Palettes Section (only for dw_leds)
  316. Rectangle {
  317. Layout.fillWidth: true
  318. Layout.preferredHeight: palettesList.length > 0 ? 140 : 80
  319. Layout.margins: 5
  320. radius: 8
  321. color: Components.ThemeManager.surfaceColor
  322. visible: ledProvider === "dw_leds"
  323. ColumnLayout {
  324. anchors.fill: parent
  325. anchors.margins: 15
  326. spacing: 10
  327. Label {
  328. text: "Palettes"
  329. font.pixelSize: 14
  330. font.bold: true
  331. color: Components.ThemeManager.textPrimary
  332. }
  333. // Show loading or no palettes message
  334. Label {
  335. visible: palettesList.length === 0
  336. text: "No palettes available"
  337. font.pixelSize: 12
  338. color: Components.ThemeManager.textSecondary
  339. }
  340. // Palettes grid
  341. GridLayout {
  342. Layout.fillWidth: true
  343. columns: 4
  344. rowSpacing: 6
  345. columnSpacing: 6
  346. visible: palettesList.length > 0
  347. Repeater {
  348. model: palettesList.slice(0, 8) // Show first 8 palettes
  349. Rectangle {
  350. property int paletteId: modelData.id !== undefined ? modelData.id : index
  351. property bool isSelected: paletteId === currentPaletteIndex
  352. Layout.fillWidth: true
  353. Layout.preferredHeight: 35
  354. radius: 6
  355. color: isSelected ?
  356. Components.ThemeManager.selectedBackground :
  357. Components.ThemeManager.buttonBackground
  358. border.color: isSelected ?
  359. Components.ThemeManager.selectedBorder :
  360. Components.ThemeManager.buttonBorder
  361. border.width: 1
  362. Label {
  363. anchors.centerIn: parent
  364. anchors.leftMargin: 4
  365. anchors.rightMargin: 4
  366. width: parent.width - 8
  367. text: modelData.name || ("Palette " + paletteId)
  368. font.pixelSize: 10
  369. color: isSelected ? "white" : Components.ThemeManager.textPrimary
  370. elide: Text.ElideRight
  371. horizontalAlignment: Text.AlignHCenter
  372. }
  373. MouseArea {
  374. anchors.fill: parent
  375. onClicked: {
  376. if (backend) {
  377. backend.setLedPalette(paletteId)
  378. currentPaletteIndex = paletteId
  379. }
  380. }
  381. }
  382. }
  383. }
  384. }
  385. }
  386. }
  387. // Quick Colors Section - MOVED TO BOTTOM (only for dw_leds)
  388. Rectangle {
  389. Layout.fillWidth: true
  390. Layout.preferredHeight: 160
  391. Layout.margins: 5
  392. radius: 8
  393. color: Components.ThemeManager.surfaceColor
  394. visible: ledProvider === "dw_leds"
  395. ColumnLayout {
  396. anchors.fill: parent
  397. anchors.margins: 15
  398. spacing: 10
  399. Label {
  400. text: "Quick Colors"
  401. font.pixelSize: 14
  402. font.bold: true
  403. color: Components.ThemeManager.textPrimary
  404. }
  405. GridLayout {
  406. Layout.fillWidth: true
  407. Layout.fillHeight: true
  408. columns: 5
  409. rowSpacing: 8
  410. columnSpacing: 8
  411. Repeater {
  412. model: presetColors
  413. Rectangle {
  414. Layout.fillWidth: true
  415. Layout.fillHeight: true
  416. Layout.minimumHeight: 50
  417. radius: 6
  418. color: Components.ThemeManager.buttonBackground
  419. border.color: Components.ThemeManager.buttonBorder
  420. border.width: 1
  421. RowLayout {
  422. anchors.centerIn: parent
  423. spacing: 6
  424. // Color indicator circle
  425. Rectangle {
  426. width: 14
  427. height: 14
  428. radius: 7
  429. color: modelData.color
  430. border.color: Qt.darker(modelData.color, 1.2)
  431. border.width: 1
  432. }
  433. Label {
  434. text: modelData.name
  435. font.pixelSize: 11
  436. color: Components.ThemeManager.textPrimary
  437. }
  438. }
  439. MouseArea {
  440. anchors.fill: parent
  441. onClicked: {
  442. if (backend) {
  443. backend.setLedColorHex(modelData.sendColor)
  444. }
  445. }
  446. }
  447. // Touch feedback
  448. Rectangle {
  449. id: colorTouchFeedback
  450. anchors.fill: parent
  451. color: Components.ThemeManager.darkMode ? "#ffffff" : "#000000"
  452. opacity: 0
  453. radius: 6
  454. NumberAnimation {
  455. id: colorTouchAnimation
  456. target: colorTouchFeedback
  457. property: "opacity"
  458. from: 0.15
  459. to: 0
  460. duration: 200
  461. }
  462. }
  463. }
  464. }
  465. }
  466. }
  467. }
  468. // Add some bottom spacing for better scrolling
  469. Item {
  470. Layout.preferredHeight: 20
  471. }
  472. }
  473. }
  474. }
  475. }