FanItem.qml 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  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 int margin: Kirigami.Units.smallSpacing
  30. property bool showControls: true
  31. readonly property QtObject systemdCom: Fancontrol.Base.hasSystemdCommunicator ? Fancontrol.Base.systemdCom : null
  32. readonly property QtObject tempModel: Fancontrol.Base.tempModel
  33. readonly property real minTemp: Fancontrol.Base.minTemp
  34. readonly property real maxTemp: Fancontrol.Base.maxTemp
  35. readonly property string unit: Fancontrol.Base.unit
  36. readonly property real convertedMinTemp: Units.fromCelsius(minTemp, unit)
  37. readonly property real convertedMaxTemp: Units.fromCelsius(maxTemp, unit)
  38. id: root
  39. clip: false
  40. onConvertedMinTempChanged: {
  41. meshCanvas.requestPaint();
  42. curveCanvas.requestPaint();
  43. }
  44. onConvertedMaxTempChanged: {
  45. meshCanvas.requestPaint();
  46. curveCanvas.requestPaint();
  47. }
  48. onFanChanged: curveCanvas.requestPaint()
  49. Item {
  50. id: graph
  51. property int fontSize: MoreMath.bound(8, height / 20 + 1, 16)
  52. property int verticalScalaCount: 6
  53. property var horIntervals: MoreMath.intervals(root.convertedMinTemp, root.convertedMaxTemp, 10)
  54. anchors {
  55. left: parent.left
  56. right: parent.right
  57. top: parent.top
  58. bottom: root.showControls ? settingsArea.top : parent.bottom
  59. bottomMargin: root.margin
  60. }
  61. visible: graphBackground.height > 0 && graphBackground.width > 0
  62. Item {
  63. id: verticalScala
  64. anchors {
  65. top: graphBackground.top
  66. bottom: graphBackground.bottom
  67. left: parent.left
  68. }
  69. width: MoreMath.maxWidth(children) + graph.fontSize / 3
  70. Repeater {
  71. id: verticalRepeater
  72. model: graph.verticalScalaCount
  73. Text {
  74. x: verticalScala.width - implicitWidth - graph.fontSize / 3
  75. y: graphBackground.height - graphBackground.height / (graph.verticalScalaCount - 1) * index - graph.fontSize * 2 / 3
  76. horizontalAlignment: Text.AlignRight
  77. color: Kirigami.Theme.textColor
  78. text: Number(index * (100 / (graph.verticalScalaCount - 1))).toLocaleString(Qt.locale(), 'f', 0) + Qt.locale().percent
  79. font.pixelSize: graph.fontSize
  80. }
  81. }
  82. }
  83. Item {
  84. id: horizontalScala
  85. anchors {
  86. right: graphBackground.right
  87. bottom: parent.bottom
  88. left: graphBackground.left
  89. }
  90. height: graph.fontSize * 2
  91. Repeater {
  92. model: graph.horIntervals.length;
  93. Text {
  94. x: graphBackground.scaleX(Units.toCelsius(graph.horIntervals[index], unit)) - width/2
  95. y: horizontalScala.height / 2 - implicitHeight / 2
  96. color: Kirigami.Theme.textColor
  97. text: Number(graph.horIntervals[index]).toLocaleString() + i18n(unit)
  98. font.pixelSize: graph.fontSize
  99. }
  100. }
  101. }
  102. Rectangle {
  103. id: graphBackground
  104. color: Kirigami.Theme.backgroundColor
  105. border.color: Kirigami.Theme.textColor
  106. border.width: 2
  107. radius: 1
  108. anchors {
  109. top: parent.top
  110. left: verticalScala.right
  111. bottom: horizontalScala.top
  112. right: parent.right
  113. topMargin: parent.fontSize
  114. bottomMargin: 0
  115. rightMargin: parent.fontSize * 2
  116. leftMargin: 0
  117. }
  118. function scaleX(temp) {
  119. return (temp - minTemp) * width / (maxTemp - minTemp);
  120. }
  121. function scaleY(pwm) {
  122. return height - pwm * height / 255;
  123. }
  124. function scaleTemp(x) {
  125. return x / width * (maxTemp - minTemp) + minTemp;
  126. }
  127. function scalePwm(y) {
  128. return 255 - y / height * 255;
  129. }
  130. Canvas {
  131. id: curveCanvas
  132. anchors.fill: parent
  133. anchors.margins: parent.border.width
  134. renderStrategy: Canvas.Threaded
  135. renderTarget: Canvas.FramebufferObject
  136. onPaint: {
  137. var c = curveCanvas.getContext("2d");
  138. c.clearRect(0, 0, width, height);
  139. if (!fan || !fan.hasTemp) {
  140. return;
  141. }
  142. var gradient = c.createLinearGradient(0, 0, width, 0);
  143. gradient.addColorStop(0, "rgb(0, 0, 255)");
  144. gradient.addColorStop(1, "rgb(255, 0, 0)");
  145. c.fillStyle = gradient;
  146. c.lineWidth = graph.fontSize / 3;
  147. c.strokeStyle = gradient;
  148. c.lineJoin = "round";
  149. c.beginPath();
  150. if (fan.minPwm == 0) {
  151. c.moveTo(stopPoint.centerX, height);
  152. } else {
  153. c.moveTo(0, stopPoint.centerY);
  154. c.lineTo(stopPoint.centerX, stopPoint.centerY);
  155. }
  156. c.lineTo(stopPoint.centerX, stopPoint.centerY);
  157. c.lineTo(maxPoint.centerX, maxPoint.centerY);
  158. c.lineTo(width, maxPoint.centerY);
  159. c.stroke();
  160. c.lineTo(width, height);
  161. if (fan.minPwm == 0) {
  162. c.lineTo(stopPoint.centerX, height);
  163. } else {
  164. c.lineTo(0, height);
  165. }
  166. c.fill();
  167. //blend graphBackground
  168. gradient = c.createLinearGradient(0, 0, 0, height);
  169. gradient.addColorStop(0, Colors.setAlpha(graphBackground.color, 0.5));
  170. gradient.addColorStop(1, Colors.setAlpha(graphBackground.color, 0.9));
  171. c.fillStyle = gradient;
  172. c.fill();
  173. }
  174. Connections {
  175. target: fan
  176. onTempChanged: curveCanvas.requestPaint()
  177. onMinPwmChanged: curveCanvas.markDirty(Qt.rect(0, 0, stopPoint.x, stopPoint.y))
  178. }
  179. }
  180. Canvas {
  181. id: meshCanvas
  182. anchors.fill: parent
  183. anchors.margins: parent.border.width
  184. renderStrategy: Canvas.Threaded
  185. renderTarget: Canvas.FramebufferObject
  186. onPaint: {
  187. var c = meshCanvas.getContext("2d");
  188. c.clearRect(0, 0, width, height);
  189. //draw mesh
  190. c.beginPath();
  191. c.strokeStyle = Colors.setAlpha(Kirigami.Theme.textColor, 0.3);
  192. //horizontal lines
  193. for (var i=0; i<=100; i+=20) {
  194. var y = graphBackground.scaleY(i*2.55);
  195. if (i != 0 && i != 100) {
  196. for (var j=0; j<=width; j+=15) {
  197. c.moveTo(j, y);
  198. c.lineTo(Math.min(j+5, width), y);
  199. }
  200. }
  201. }
  202. //vertical lines
  203. if (graph.horIntervals.length > 1) {
  204. for (var i=1; i<graph.horIntervals.length; i++) {
  205. var x = graphBackground.scaleX(Units.toCelsius(graph.horIntervals[i], unit));
  206. for (var j=0; j<=height; j+=20) {
  207. c.moveTo(x, j);
  208. c.lineTo(x, Math.min(j+5, height));
  209. }
  210. }
  211. }
  212. c.stroke();
  213. }
  214. }
  215. StatusPoint {
  216. id: currentPwm
  217. size: graph.fontSize
  218. visible: graphBackground.contains(center) && !!fan && fan.hasTemp
  219. fan: root.fan
  220. }
  221. PwmPoint {
  222. id: stopPoint
  223. color: !!fan ? fan.hasTemp ? "blue" : Qt.tint(Kirigami.Theme.disabledTextColor, Qt.rgba(0, 0, 1, 0.5)) : "transparent"
  224. size: graph.fontSize
  225. visible: !!fan ? fan.hasTemp : false
  226. drag.maximumX: Math.min(graphBackground.scaleX(graphBackground.scaleTemp(maxPoint.x)-1), maxPoint.x-1)
  227. drag.minimumY: Math.max(graphBackground.scaleY(graphBackground.scalePwm(maxPoint.y)-1), maxPoint.y+1)
  228. x: !!fan && fan.hasTemp ? graphBackground.scaleX(MoreMath.bound(root.minTemp, fan.minTemp, root.maxTemp)) - width/2 : -width/2
  229. y: !!fan && fan.hasTemp ? graphBackground.scaleY(fan.minStop) - height/2 : -height/2
  230. temp: !!fan && fan.hasTemp ? drag.active ? graphBackground.scaleTemp(centerX) : fan.minTemp : root.minTemp
  231. pwm: !!fan && fan.hasTemp ? drag.active ? graphBackground.scalePwm(centerY) : fan.minStop : 255
  232. drag.onActiveChanged: {
  233. if (!drag.active) {
  234. fan.minStop = Math.round(graphBackground.scalePwm(centerY));
  235. fan.minTemp = Math.round(graphBackground.scaleTemp(centerX));
  236. if (fan.minPwm !== 0) fan.minPwm = fan.minStop;
  237. }
  238. }
  239. onPositionChanged: {
  240. var left = fan.minPwm === 0 ? x : 0;
  241. var width = maxPoint.x - left;
  242. var height = y - maxPoint.y;
  243. curveCanvas.markDirty(Qt.rect(left, maxPoint.y, width, height));
  244. }
  245. }
  246. PwmPoint {
  247. id: maxPoint
  248. color: !!fan ? fan.hasTemp ? "red" : Qt.tint(Kirigami.Theme.disabledTextColor, Qt.rgba(1, 0, 0, 0.5)) : "transparent"
  249. size: graph.fontSize
  250. visible: !!fan ? fan.hasTemp : false
  251. drag.minimumX: Math.max(graphBackground.scaleX(graphBackground.scaleTemp(stopPoint.x)+1), stopPoint.x+1)
  252. drag.maximumY: Math.min(graphBackground.scaleY(graphBackground.scalePwm(stopPoint.y)+1), stopPoint.y-1)
  253. x: !!fan && fan.hasTemp ? graphBackground.scaleX(MoreMath.bound(root.minTemp, fan.maxTemp, root.maxTemp)) - width/2 : graphBackground.width - width/2
  254. y: !!fan && fan.hasTemp ? graphBackground.scaleY(fan.maxPwm) - height/2 : -height/2
  255. temp: !!fan && fan.hasTemp ? drag.active ? graphBackground.scaleTemp(centerX) : fan.maxTemp : root.maxTemp
  256. pwm: !!fan && fan.hasTemp ? drag.active ? graphBackground.scalePwm(centerY) : fan.maxPwm : 255
  257. drag.onActiveChanged: {
  258. if (!drag.active) {
  259. fan.maxPwm = Math.round(graphBackground.scalePwm(centerY));
  260. fan.maxTemp = Math.round(graphBackground.scaleTemp(centerX));
  261. }
  262. }
  263. onPositionChanged: {
  264. var width = x - stopPoint.x;
  265. var height = stopPoint.y - y;
  266. curveCanvas.markDirty(Qt.rect(stopPoint.x, y, width, height));
  267. }
  268. }
  269. }
  270. }
  271. FanControls {
  272. id: settingsArea
  273. fan: root.fan
  274. padding: root.margin
  275. anchors {
  276. left: parent.left
  277. leftMargin: padding
  278. right: parent.right
  279. rightMargin: padding
  280. bottom: parent.bottom
  281. bottomMargin: padding
  282. }
  283. visible: root.showControls && root.height >= height + 2*margin
  284. }
  285. }