FanItem.qml 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. /*
  2. * Copyright (C) 2015 Malte Veerman <malte.veerman@gmail.com>
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU Lesser General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU Lesser General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Lesser General Public License along
  15. * with this program; if not, write to the Free Software Foundation, Inc.,
  16. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  17. *
  18. */
  19. import QtQuick 2.6
  20. import QtQuick.Controls 2.1
  21. import QtQuick.Layouts 1.2
  22. import org.kde.kirigami 2.3 as Kirigami
  23. import Fancontrol.Qml 1.0 as Fancontrol
  24. import "math.js" as MoreMath
  25. import "units.js" as Units
  26. import "colors.js" as Colors
  27. Item {
  28. property QtObject fan
  29. property QtObject systemdCom
  30. property QtObject tempModel
  31. property real minTemp: Fancontrol.Base.minTemp
  32. property real maxTemp: Fancontrol.Base.maxTemp
  33. property int margin: Kirigami.Units.smallSpacing
  34. property string unit: Fancontrol.Base.unit
  35. property real convertedMinTemp: Units.fromCelsius(minTemp, unit)
  36. property real convertedMaxTemp: Units.fromCelsius(maxTemp, unit)
  37. id: root
  38. clip: false
  39. onConvertedMinTempChanged: {
  40. meshCanvas.requestPaint();
  41. curveCanvas.requestPaint();
  42. }
  43. onConvertedMaxTempChanged: {
  44. meshCanvas.requestPaint();
  45. curveCanvas.requestPaint();
  46. }
  47. onFanChanged: curveCanvas.requestPaint()
  48. Item {
  49. id: graph
  50. property int fontSize: MoreMath.bound(8, height / 20 + 1, 16)
  51. property int verticalScalaCount: 6
  52. property var horIntervals: MoreMath.intervals(root.convertedMinTemp, root.convertedMaxTemp, 10)
  53. anchors {
  54. left: parent.left
  55. right: parent.right
  56. top: parent.top
  57. bottom: settingsArea.top
  58. }
  59. visible: graphBackground.height > 0 && graphBackground.width > 0
  60. Item {
  61. id: verticalScala
  62. anchors {
  63. top: graphBackground.top
  64. bottom: graphBackground.bottom
  65. left: parent.left
  66. }
  67. width: MoreMath.maxWidth(children) + graph.fontSize
  68. Repeater {
  69. id: verticalRepeater
  70. model: graph.verticalScalaCount
  71. Text {
  72. x: verticalScala.width - implicitWidth - graph.fontSize / 3
  73. y: graphBackground.height - graphBackground.height / (graph.verticalScalaCount - 1) * index - graph.fontSize * 2 / 3
  74. horizontalAlignment: Text.AlignRight
  75. color: Kirigami.Theme.textColor
  76. text: Number(index * (100 / (graph.verticalScalaCount - 1))).toLocaleString(locale, 'f', 0) + locale.percent
  77. font.pixelSize: graph.fontSize
  78. }
  79. }
  80. }
  81. Item {
  82. id: horizontalScala
  83. anchors {
  84. right: graphBackground.right
  85. bottom: parent.bottom
  86. left: graphBackground.left
  87. }
  88. height: graph.fontSize * 2
  89. Repeater {
  90. model: graph.horIntervals.length;
  91. Text {
  92. x: graphBackground.scaleX(Units.toCelsius(graph.horIntervals[index], unit)) - width/2
  93. y: horizontalScala.height / 2 - implicitHeight / 2
  94. color: Kirigami.Theme.textColor
  95. text: Number(graph.horIntervals[index]).toLocaleString() + i18n(unit)
  96. font.pixelSize: graph.fontSize
  97. }
  98. }
  99. }
  100. Rectangle {
  101. id: graphBackground
  102. color: Kirigami.Theme.backgroundColor
  103. border.color: Kirigami.Theme.textColor
  104. border.width: 2
  105. radius: 1
  106. anchors {
  107. top: parent.top
  108. left: verticalScala.right
  109. bottom: horizontalScala.top
  110. right: parent.right
  111. topMargin: parent.fontSize
  112. bottomMargin: 0
  113. rightMargin: parent.fontSize * 2
  114. leftMargin: 0
  115. }
  116. function scaleX(temp) {
  117. return (temp - minTemp) * width / (maxTemp - minTemp);
  118. }
  119. function scaleY(pwm) {
  120. return height - pwm * height / 255;
  121. }
  122. function scaleTemp(x) {
  123. return x / width * (maxTemp - minTemp) + minTemp;
  124. }
  125. function scalePwm(y) {
  126. return 255 - y / height * 255;
  127. }
  128. Canvas {
  129. id: curveCanvas
  130. anchors.fill: parent
  131. anchors.margins: parent.border.width
  132. renderStrategy: Canvas.Cooperative
  133. onPaint: {
  134. var c = curveCanvas.getContext("2d");
  135. c.clearRect(0, 0, width, height);
  136. if (!fan || !fan.hasTemp) {
  137. return;
  138. }
  139. var gradient = c.createLinearGradient(0, 0, width, 0);
  140. gradient.addColorStop(0, "rgb(0, 0, 255)");
  141. gradient.addColorStop(1, "rgb(255, 0, 0)");
  142. c.fillStyle = gradient;
  143. c.lineWidth = graph.fontSize / 3;
  144. c.strokeStyle = gradient;
  145. c.lineJoin = "round";
  146. c.beginPath();
  147. if (fan.minPwm == 0) {
  148. c.moveTo(stopPoint.centerX, height);
  149. } else {
  150. c.moveTo(0, stopPoint.centerY);
  151. c.lineTo(stopPoint.centerX, stopPoint.centerY);
  152. }
  153. c.lineTo(stopPoint.centerX, stopPoint.centerY);
  154. c.lineTo(maxPoint.centerX, maxPoint.centerY);
  155. c.lineTo(width, maxPoint.centerY);
  156. c.stroke();
  157. c.lineTo(width, height);
  158. if (fan.minPwm == 0) {
  159. c.lineTo(stopPoint.centerX, height);
  160. } else {
  161. c.lineTo(0, height);
  162. }
  163. c.fill();
  164. //blend graphBackground
  165. gradient = c.createLinearGradient(0, 0, 0, height);
  166. gradient.addColorStop(0, Colors.setAlpha(graphBackground.color, 0.5));
  167. gradient.addColorStop(1, Colors.setAlpha(graphBackground.color, 0.9));
  168. c.fillStyle = gradient;
  169. c.fill();
  170. }
  171. }
  172. Canvas {
  173. id: meshCanvas
  174. anchors.fill: parent
  175. anchors.margins: parent.border.width
  176. renderStrategy: Canvas.Cooperative
  177. onPaint: {
  178. var c = meshCanvas.getContext("2d");
  179. c.clearRect(0, 0, width, height);
  180. //draw mesh
  181. c.beginPath();
  182. c.strokeStyle = Colors.setAlpha(Kirigami.Theme.textColor, 0.3);
  183. //horizontal lines
  184. for (var i=0; i<=100; i+=20) {
  185. var y = graphBackground.scaleY(i*2.55);
  186. if (i != 0 && i != 100) {
  187. for (var j=0; j<=width; j+=15) {
  188. c.moveTo(j, y);
  189. c.lineTo(Math.min(j+5, width), y);
  190. }
  191. }
  192. }
  193. //vertical lines
  194. if (graph.horIntervals.length > 1) {
  195. for (var i=1; i<graph.horIntervals.length; i++) {
  196. var x = graphBackground.scaleX(Units.toCelsius(graph.horIntervals[i], unit));
  197. for (var j=0; j<=height; j+=20) {
  198. c.moveTo(x, j);
  199. c.lineTo(x, Math.min(j+5, height));
  200. }
  201. }
  202. }
  203. c.stroke();
  204. }
  205. }
  206. StatusPoint {
  207. id: currentPwm
  208. size: graph.fontSize
  209. visible: graphBackground.contains(center) && !!fan && fan.hasTemp
  210. fan: root.fan
  211. }
  212. PwmPoint {
  213. id: stopPoint
  214. color: !!fan ? fan.hasTemp ? "blue" : Qt.tint(Kirigami.Theme.disabledTextColor, Qt.rgba(0, 0, 1, 0.5)) : "transparent"
  215. size: graph.fontSize
  216. visible: !!fan ? fan.hasTemp : false
  217. drag.maximumX: Math.min(graphBackground.scaleX(graphBackground.scaleTemp(maxPoint.x)-1), maxPoint.x-1)
  218. drag.minimumY: Math.max(graphBackground.scaleY(graphBackground.scalePwm(maxPoint.y)-1), maxPoint.y+1)
  219. x: !!fan && fan.hasTemp ? graphBackground.scaleX(MoreMath.bound(root.minTemp, fan.minTemp, root.maxTemp)) - width/2 : -width/2
  220. y: !!fan && fan.hasTemp ? graphBackground.scaleY(fan.minStop) - height/2 : -height/2
  221. temp: !!fan && fan.hasTemp ? drag.active ? graphBackground.scaleTemp(centerX) : fan.minTemp : root.minTemp
  222. pwm: !!fan && fan.hasTemp ? drag.active ? graphBackground.scalePwm(centerY) : fan.minStop : 255
  223. drag.onActiveChanged: {
  224. if (!drag.active) {
  225. fan.minStop = Math.round(graphBackground.scalePwm(centerY));
  226. fan.minTemp = Math.round(graphBackground.scaleTemp(centerX));
  227. if (!fanOffCheckBox.checked) fan.minPwm = fan.minStop;
  228. }
  229. }
  230. onPositionChanged: {
  231. var left = fanOffCheckBox.checked ? x : 0;
  232. var width = maxPoint.x - left;
  233. var height = y - maxPoint.y;
  234. curveCanvas.markDirty(Qt.rect(left, maxPoint.y, width, height));
  235. }
  236. }
  237. PwmPoint {
  238. id: maxPoint
  239. color: !!fan ? fan.hasTemp ? "red" : Qt.tint(Kirigami.Theme.disabledTextColor, Qt.rgba(1, 0, 0, 0.5)) : "transparent"
  240. size: graph.fontSize
  241. visible: !!fan ? fan.hasTemp : false
  242. drag.minimumX: Math.max(graphBackground.scaleX(graphBackground.scaleTemp(stopPoint.x)+1), stopPoint.x+1)
  243. drag.maximumY: Math.min(graphBackground.scaleY(graphBackground.scalePwm(stopPoint.y)+1), stopPoint.y-1)
  244. x: !!fan && fan.hasTemp ? graphBackground.scaleX(MoreMath.bound(root.minTemp, fan.maxTemp, root.maxTemp)) - width/2 : graphBackground.width - width/2
  245. y: !!fan && fan.hasTemp ? graphBackground.scaleY(fan.maxPwm) - height/2 : -height/2
  246. temp: !!fan && fan.hasTemp ? drag.active ? graphBackground.scaleTemp(centerX) : fan.maxTemp : root.maxTemp
  247. pwm: !!fan && fan.hasTemp ? drag.active ? graphBackground.scalePwm(centerY) : fan.maxPwm : 255
  248. drag.onActiveChanged: {
  249. if (!drag.active) {
  250. fan.maxPwm = Math.round(graphBackground.scalePwm(centerY));
  251. fan.maxTemp = Math.round(graphBackground.scaleTemp(centerX));
  252. }
  253. }
  254. onPositionChanged: {
  255. var width = x - stopPoint.x;
  256. var height = stopPoint.y - y;
  257. curveCanvas.markDirty(Qt.rect(stopPoint.x, y, width, height));
  258. }
  259. }
  260. }
  261. }
  262. ColumnLayout {
  263. property int padding: 10
  264. id: settingsArea
  265. anchors {
  266. left: parent.left
  267. leftMargin: padding
  268. right: parent.right
  269. rightMargin: padding
  270. bottom: parent.bottom
  271. bottomMargin: padding
  272. }
  273. visible: root.height >= height + 2*margin
  274. clip: true
  275. spacing: 2
  276. RowLayout {
  277. CheckBox {
  278. id: hasTempCheckBox
  279. text: i18n("Controlled by:")
  280. checked: !!fan ? fan.hasTemp : false
  281. Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
  282. onCheckedChanged: {
  283. if (!!fan) {
  284. fan.hasTemp = checked;
  285. if (checked && !!tempModel.temps[tempBox.currentIndex]) {
  286. fan.temp = tempModel.temps[tempBox.currentIndex];
  287. }
  288. curveCanvas.requestPaint();
  289. }
  290. }
  291. Connections {
  292. target: root
  293. onFanChanged: hasTempCheckBox.checked = !!fan ? fan.hasTemp : false
  294. }
  295. Connections {
  296. target: fan
  297. onHasTempChanged: hasTempCheckBox.checked = fan.hasTemp
  298. }
  299. }
  300. RowLayout {
  301. ComboBox {
  302. id: tempBox
  303. Layout.fillWidth: true
  304. model: tempModel
  305. textRole: "display"
  306. enabled: hasTempCheckBox.checked
  307. onCurrentIndexChanged: {
  308. if (hasTempCheckBox.checked)
  309. fan.temp = tempModel.temps[currentIndex];
  310. }
  311. }
  312. Connections {
  313. target: root
  314. onFanChanged: if (!!fan && fan.hasTemp) tempBox.currentIndex = tempModel.temps.indexOf(fan.temp)
  315. }
  316. Connections {
  317. target: fan
  318. onTempChanged: if (fan.hasTemp) tempBox.currentIndex = tempModel.temps.indexOf(fan.temp)
  319. }
  320. }
  321. }
  322. CheckBox {
  323. id: fanOffCheckBox
  324. text: i18n("Turn Fan off if temp < MINTEMP")
  325. enabled: hasTempCheckBox.checked
  326. checked: !!fan ? fan.minPwm == 0 : false
  327. onCheckedChanged: {
  328. if (!!fan) {
  329. fan.minPwm = checked ? 0 : fan.minStop;
  330. curveCanvas.markDirty(Qt.rect(0, 0, stopPoint.x, stopPoint.y));
  331. }
  332. }
  333. Connections {
  334. target: root
  335. onFanChanged: if (!!fan) fanOffCheckBox.checked = fan.minPwm == 0
  336. }
  337. Connections {
  338. target: fan
  339. onMinPwmChanged: fanOffCheckBox.checked = fan.minPwm == 0
  340. }
  341. }
  342. RowLayout {
  343. enabled: fanOffCheckBox.checked && fanOffCheckBox.enabled
  344. Label {
  345. text: i18n("Pwm value for fan to start:")
  346. Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
  347. renderType: Text.NativeRendering
  348. }
  349. SpinBox {
  350. id: minStartInput
  351. Layout.fillWidth: true
  352. from: 0
  353. to: 100
  354. editable: true
  355. value: !!fan ? Math.round(fan.minStart / 2.55) : 0
  356. textFromValue: function(value, locale) { return Number(value).toLocaleString(locale, 'f', 1) + locale.percent }
  357. onValueModified: {
  358. if (!!fan) {
  359. fan.minStart = Math.round(value * 2.55)
  360. }
  361. }
  362. Connections {
  363. target: root
  364. onFanChanged: if (!!fan) minStartInput.value = Math.round(fan.minStart / 2.55)
  365. }
  366. Connections {
  367. target: fan
  368. onMinStartChanged: minStartInput.value = Math.round(fan.minStart / 2.55)
  369. }
  370. }
  371. }
  372. }
  373. }