DefaultColorDialog.qml 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. /****************************************************************************
  2. **
  3. ** Copyright (C) 2016 The Qt Company Ltd.
  4. ** Contact: https://www.qt.io/licensing/
  5. **
  6. ** This file is part of the Qt Quick Dialogs module of the Qt Toolkit.
  7. **
  8. ** $QT_BEGIN_LICENSE:LGPL$
  9. ** Commercial License Usage
  10. ** Licensees holding valid commercial Qt licenses may use this file in
  11. ** accordance with the commercial license agreement provided with the
  12. ** Software or, alternatively, in accordance with the terms contained in
  13. ** a written agreement between you and The Qt Company. For licensing terms
  14. ** and conditions see https://www.qt.io/terms-conditions. For further
  15. ** information use the contact form at https://www.qt.io/contact-us.
  16. **
  17. ** GNU Lesser General Public License Usage
  18. ** Alternatively, this file may be used under the terms of the GNU Lesser
  19. ** General Public License version 3 as published by the Free Software
  20. ** Foundation and appearing in the file LICENSE.LGPL3 included in the
  21. ** packaging of this file. Please review the following information to
  22. ** ensure the GNU Lesser General Public License version 3 requirements
  23. ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
  24. **
  25. ** GNU General Public License Usage
  26. ** Alternatively, this file may be used under the terms of the GNU
  27. ** General Public License version 2.0 or (at your option) the GNU General
  28. ** Public license version 3 or any later version approved by the KDE Free
  29. ** Qt Foundation. The licenses are as published by the Free Software
  30. ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
  31. ** included in the packaging of this file. Please review the following
  32. ** information to ensure the GNU General Public License requirements will
  33. ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
  34. ** https://www.gnu.org/licenses/gpl-3.0.html.
  35. **
  36. ** $QT_END_LICENSE$
  37. **
  38. ****************************************************************************/
  39. import QtQuick 2.4
  40. import QtQuick.Controls 1.2
  41. import QtQuick.Controls.Private 1.0
  42. import QtQuick.Dialogs 1.0
  43. import QtQuick.Window 2.1
  44. import "qml"
  45. AbstractColorDialog {
  46. id: root
  47. property bool __valueSet: true // guard to prevent binding loops
  48. function __setControlsFromColor() {
  49. __valueSet = false
  50. hueSlider.value = root.currentHue
  51. saturationSlider.value = root.currentSaturation
  52. lightnessSlider.value = root.currentLightness
  53. alphaSlider.value = root.currentAlpha
  54. crosshairs.x = root.currentLightness * paletteMap.width
  55. crosshairs.y = (1.0 - root.currentSaturation) * paletteMap.height
  56. __valueSet = true
  57. }
  58. onCurrentColorChanged: __setControlsFromColor()
  59. Rectangle {
  60. id: content
  61. implicitHeight: Math.min(root.__maximumDimension, Screen.pixelDensity * (usePaletteMap ? 120 : 50))
  62. implicitWidth: Math.min(root.__maximumDimension, usePaletteMap ? Screen.pixelDensity * (usePaletteMap ? 100 : 50) : implicitHeight * 1.5)
  63. color: palette.window
  64. focus: root.visible
  65. property real bottomMinHeight: sliders.height + buttonRow.height + outerSpacing * 3
  66. property real spacing: 8
  67. property real outerSpacing: 12
  68. property bool usePaletteMap: true
  69. Keys.onPressed: {
  70. event.accepted = true
  71. switch (event.key) {
  72. case Qt.Key_Return:
  73. case Qt.Key_Select:
  74. accept()
  75. break
  76. case Qt.Key_Escape:
  77. case Qt.Key_Back:
  78. reject()
  79. break
  80. case Qt.Key_C:
  81. if (event.modifiers & Qt.ControlModifier)
  82. colorField.copyAll()
  83. break
  84. case Qt.Key_V:
  85. if (event.modifiers & Qt.ControlModifier) {
  86. colorField.paste()
  87. root.currentColor = colorField.text
  88. }
  89. break
  90. default:
  91. // do nothing
  92. event.accepted = false
  93. break
  94. }
  95. }
  96. SystemPalette { id: palette }
  97. Item {
  98. id: paletteFrame
  99. visible: content.usePaletteMap
  100. anchors {
  101. top: parent.top
  102. left: parent.left
  103. right: parent.right
  104. margins: content.outerSpacing
  105. }
  106. height: Math.min(content.height - content.bottomMinHeight, content.width - content.outerSpacing * 2)
  107. Image {
  108. id: paletteMap
  109. x: (parent.width - width) / 2
  110. width: height
  111. onWidthChanged: root.__setControlsFromColor()
  112. height: parent.height
  113. source: "images/checkers.png"
  114. fillMode: Image.Tile
  115. // note we smoothscale the shader from a smaller version to improve performance
  116. ShaderEffect {
  117. id: map
  118. width: 64
  119. height: 64
  120. opacity: alphaSlider.value
  121. scale: paletteMap.width / width;
  122. layer.enabled: true
  123. layer.smooth: true
  124. anchors.centerIn: parent
  125. property real hue: hueSlider.value
  126. fragmentShader: content.OpenGLInfo.profile === OpenGLInfo.CoreProfile ? "#version 150
  127. in vec2 qt_TexCoord0;
  128. uniform float qt_Opacity;
  129. uniform float hue;
  130. out vec4 fragColor;
  131. float hueToIntensity(float v1, float v2, float h) {
  132. h = fract(h);
  133. if (h < 1.0 / 6.0)
  134. return v1 + (v2 - v1) * 6.0 * h;
  135. else if (h < 1.0 / 2.0)
  136. return v2;
  137. else if (h < 2.0 / 3.0)
  138. return v1 + (v2 - v1) * 6.0 * (2.0 / 3.0 - h);
  139. return v1;
  140. }
  141. vec3 HSLtoRGB(vec3 color) {
  142. float h = color.x;
  143. float l = color.z;
  144. float s = color.y;
  145. if (s < 1.0 / 256.0)
  146. return vec3(l, l, l);
  147. float v1;
  148. float v2;
  149. if (l < 0.5)
  150. v2 = l * (1.0 + s);
  151. else
  152. v2 = (l + s) - (s * l);
  153. v1 = 2.0 * l - v2;
  154. float d = 1.0 / 3.0;
  155. float r = hueToIntensity(v1, v2, h + d);
  156. float g = hueToIntensity(v1, v2, h);
  157. float b = hueToIntensity(v1, v2, h - d);
  158. return vec3(r, g, b);
  159. }
  160. void main() {
  161. vec4 c = vec4(1.0);
  162. c.rgb = HSLtoRGB(vec3(hue, 1.0 - qt_TexCoord0.t, qt_TexCoord0.s));
  163. fragColor = c * qt_Opacity;
  164. }
  165. " : "
  166. varying mediump vec2 qt_TexCoord0;
  167. uniform highp float qt_Opacity;
  168. uniform highp float hue;
  169. highp float hueToIntensity(highp float v1, highp float v2, highp float h) {
  170. h = fract(h);
  171. if (h < 1.0 / 6.0)
  172. return v1 + (v2 - v1) * 6.0 * h;
  173. else if (h < 1.0 / 2.0)
  174. return v2;
  175. else if (h < 2.0 / 3.0)
  176. return v1 + (v2 - v1) * 6.0 * (2.0 / 3.0 - h);
  177. return v1;
  178. }
  179. highp vec3 HSLtoRGB(highp vec3 color) {
  180. highp float h = color.x;
  181. highp float l = color.z;
  182. highp float s = color.y;
  183. if (s < 1.0 / 256.0)
  184. return vec3(l, l, l);
  185. highp float v1;
  186. highp float v2;
  187. if (l < 0.5)
  188. v2 = l * (1.0 + s);
  189. else
  190. v2 = (l + s) - (s * l);
  191. v1 = 2.0 * l - v2;
  192. highp float d = 1.0 / 3.0;
  193. highp float r = hueToIntensity(v1, v2, h + d);
  194. highp float g = hueToIntensity(v1, v2, h);
  195. highp float b = hueToIntensity(v1, v2, h - d);
  196. return vec3(r, g, b);
  197. }
  198. void main() {
  199. lowp vec4 c = vec4(1.0);
  200. c.rgb = HSLtoRGB(vec3(hue, 1.0 - qt_TexCoord0.t, qt_TexCoord0.s));
  201. gl_FragColor = c * qt_Opacity;
  202. }
  203. "
  204. }
  205. MouseArea {
  206. id: mapMouseArea
  207. anchors.fill: parent
  208. onPositionChanged: {
  209. if (pressed && containsMouse) {
  210. var xx = Math.max(0, Math.min(mouse.x, parent.width))
  211. var yy = Math.max(0, Math.min(mouse.y, parent.height))
  212. saturationSlider.value = 1.0 - yy / parent.height
  213. lightnessSlider.value = xx / parent.width
  214. // TODO if we constrain the movement here, can avoid the containsMouse test
  215. crosshairs.x = mouse.x - crosshairs.radius
  216. crosshairs.y = mouse.y - crosshairs.radius
  217. }
  218. }
  219. onPressed: positionChanged(mouse)
  220. }
  221. Image {
  222. id: crosshairs
  223. property int radius: width / 2 // truncated to int
  224. source: "images/crosshairs.png"
  225. }
  226. BorderImage {
  227. anchors.fill: parent
  228. anchors.margins: -1
  229. anchors.leftMargin: -2
  230. source: "images/sunken_frame.png"
  231. border.left: 8
  232. border.right: 8
  233. border.top: 8
  234. border.bottom: 8
  235. }
  236. }
  237. }
  238. Column {
  239. id: sliders
  240. anchors {
  241. top: paletteFrame.bottom
  242. left: parent.left
  243. right: parent.right
  244. margins: content.outerSpacing
  245. }
  246. ColorSlider {
  247. id: hueSlider
  248. value: 0.5
  249. onValueChanged: if (__valueSet) root.currentColor = Qt.hsla(hueSlider.value, saturationSlider.value, lightnessSlider.value, alphaSlider.value)
  250. text: qsTr("Hue")
  251. trackDelegate: Rectangle {
  252. rotation: -90
  253. transformOrigin: Item.TopLeft
  254. gradient: Gradient {
  255. GradientStop {position: 0.000; color: Qt.rgba(1, 0, 0, 1)}
  256. GradientStop {position: 0.167; color: Qt.rgba(1, 1, 0, 1)}
  257. GradientStop {position: 0.333; color: Qt.rgba(0, 1, 0, 1)}
  258. GradientStop {position: 0.500; color: Qt.rgba(0, 1, 1, 1)}
  259. GradientStop {position: 0.667; color: Qt.rgba(0, 0, 1, 1)}
  260. GradientStop {position: 0.833; color: Qt.rgba(1, 0, 1, 1)}
  261. GradientStop {position: 1.000; color: Qt.rgba(1, 0, 0, 1)}
  262. }
  263. }
  264. }
  265. ColorSlider {
  266. id: saturationSlider
  267. visible: !content.usePaletteMap
  268. value: 0.5
  269. onValueChanged: if (__valueSet) root.currentColor = Qt.hsla(hueSlider.value, saturationSlider.value, lightnessSlider.value, alphaSlider.value)
  270. text: qsTr("Saturation")
  271. trackDelegate: Rectangle {
  272. rotation: -90
  273. transformOrigin: Item.TopLeft
  274. gradient: Gradient {
  275. GradientStop { position: 0; color: Qt.hsla(hueSlider.value, 0.0, lightnessSlider.value, 1.0) }
  276. GradientStop { position: 1; color: Qt.hsla(hueSlider.value, 1.0, lightnessSlider.value, 1.0) }
  277. }
  278. }
  279. }
  280. ColorSlider {
  281. id: lightnessSlider
  282. visible: !content.usePaletteMap
  283. value: 0.5
  284. onValueChanged: if (__valueSet) root.currentColor = Qt.hsla(hueSlider.value, saturationSlider.value, lightnessSlider.value, alphaSlider.value)
  285. text: qsTr("Luminosity")
  286. trackDelegate: Rectangle {
  287. rotation: -90
  288. transformOrigin: Item.TopLeft
  289. gradient: Gradient {
  290. GradientStop { position: 0; color: "black" }
  291. GradientStop { position: 0.5; color: Qt.hsla(hueSlider.value, saturationSlider.value, 0.5, 1.0) }
  292. GradientStop { position: 1; color: "white" }
  293. }
  294. }
  295. }
  296. ColorSlider {
  297. id: alphaSlider
  298. minimum: 0.0
  299. maximum: 1.0
  300. value: 1.0
  301. onValueChanged: if (__valueSet) root.currentColor = Qt.hsla(hueSlider.value, saturationSlider.value, lightnessSlider.value, alphaSlider.value)
  302. text: qsTr("Alpha")
  303. visible: root.showAlphaChannel
  304. trackDelegate: Item {
  305. rotation: -90
  306. transformOrigin: Item.TopLeft
  307. Image {
  308. anchors {fill: parent}
  309. source: "images/checkers.png"
  310. fillMode: Image.TileVertically
  311. }
  312. Rectangle {
  313. anchors.fill: parent
  314. gradient: Gradient {
  315. GradientStop { position: 0; color: "transparent" }
  316. GradientStop { position: 1; color: Qt.hsla(hueSlider.value,
  317. saturationSlider.value,
  318. lightnessSlider.value, 1.0) }
  319. } }
  320. }
  321. }
  322. }
  323. Item {
  324. id: buttonRow
  325. height: Math.max(buttonsOnly.height, copyIcon.height)
  326. width: parent.width
  327. anchors {
  328. left: parent.left
  329. right: parent.right
  330. bottom: content.bottom
  331. margins: content.outerSpacing
  332. }
  333. Row {
  334. spacing: content.spacing
  335. height: visible ? parent.height : 0
  336. visible: !Settings.isMobile
  337. TextField {
  338. id: colorField
  339. text: root.currentColor.toString()
  340. anchors.verticalCenter: parent.verticalCenter
  341. onAccepted: root.currentColor = text
  342. Component.onCompleted: width = implicitWidth + 10
  343. }
  344. Image {
  345. id: copyIcon
  346. anchors.verticalCenter: parent.verticalCenter
  347. source: "images/copy.png"
  348. MouseArea {
  349. anchors.fill: parent
  350. onClicked: colorField.copyAll()
  351. }
  352. }
  353. }
  354. Row {
  355. id: buttonsOnly
  356. spacing: content.spacing
  357. anchors.right: parent.right
  358. Button {
  359. id: cancelButton
  360. text: qsTr("Cancel")
  361. onClicked: root.reject()
  362. }
  363. Button {
  364. id: okButton
  365. text: qsTr("OK")
  366. onClicked: root.accept()
  367. }
  368. }
  369. }
  370. }
  371. }