ColorPicker.lua 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. --[[
  2. MIT License
  3. Copyright (c) 2019 Mitchell Davis <coding.jackalope@gmail.com>
  4. Permission is hereby granted, free of charge, to any person obtaining a copy
  5. of this software and associated documentation files (the "Software"), to deal
  6. in the Software without restriction, including without limitation the rights
  7. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. copies of the Software, and to permit persons to whom the Software is
  9. furnished to do so, subject to the following conditions:
  10. The above copyright notice and this permission notice shall be included in all
  11. copies or substantial portions of the Software.
  12. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  13. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  14. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  15. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  16. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  17. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  18. SOFTWARE.
  19. --]]
  20. local Button = require(SLAB_PATH .. '.Internal.UI.Button')
  21. local Cursor = require(SLAB_PATH .. '.Internal.Core.Cursor')
  22. local DrawCommands = require(SLAB_PATH .. '.Internal.Core.DrawCommands')
  23. local Image = require(SLAB_PATH .. '.Internal.UI.Image')
  24. local Input = require(SLAB_PATH .. '.Internal.UI.Input')
  25. local Mouse = require(SLAB_PATH .. '.Internal.Input.Mouse')
  26. local Style = require(SLAB_PATH .. '.Style')
  27. local Text = require(SLAB_PATH .. '.Internal.UI.Text')
  28. local Utility = require(SLAB_PATH .. '.Internal.Core.Utility')
  29. local Window = require(SLAB_PATH .. '.Internal.UI.Window')
  30. local ColorPicker = {}
  31. local SaturationMeshes = nil
  32. local SaturationSize = 200.0
  33. local SaturationStep = 5
  34. local SaturationFocused = false
  35. local TintMeshes = nil
  36. local TintW = 30.0
  37. local TintH = SaturationSize
  38. local TintFocused = false
  39. local AlphaMesh = nil
  40. local AlphaW = TintW
  41. local AlphaH = TintH
  42. local AlphaFocused = false
  43. local CurrentColor = {1.0, 1.0, 1.0, 1.0}
  44. local ColorH = 25.0
  45. local function InputColor(Component, Value, OffsetX)
  46. local Changed = false
  47. Text.Begin(string.format("%s ", Component))
  48. Cursor.SameLine()
  49. Cursor.SetRelativeX(OffsetX)
  50. if Input.Begin('ColorPicker_' .. Component, {W = 40.0, NumbersOnly = true, Text = tostring(math.ceil(Value * 255)), ReturnOnText = false}) then
  51. local NewValue = tonumber(Input.GetText())
  52. if NewValue ~= nil then
  53. NewValue = math.max(NewValue, 0)
  54. NewValue = math.min(NewValue, 255)
  55. Value = NewValue / 255
  56. Changed = true
  57. end
  58. end
  59. return Value, Changed
  60. end
  61. local function UpdateSaturationColors()
  62. if SaturationMeshes ~= nil then
  63. local MeshIndex = 1
  64. local Step = SaturationStep
  65. local C00 = {1.0, 1.0, 1.0, 1.0}
  66. local C10 = {1.0, 1.0, 1.0, 1.0}
  67. local C01 = {1.0, 1.0, 1.0, 1.0}
  68. local C11 = {1.0, 1.0, 1.0, 1.0}
  69. local StepX, StepY = 0, 0
  70. local Hue, Sat, Val = Utility.RGBtoHSV(CurrentColor[1], CurrentColor[2], CurrentColor[3])
  71. for I = 1, Step, 1 do
  72. for J = 1, Step, 1 do
  73. local S0 = StepX / Step
  74. local S1 = (StepX + 1) / Step
  75. local V0 = 1.0 - (StepY / Step)
  76. local V1 = 1.0 - ((StepY + 1) / Step)
  77. C00[1], C00[2], C00[3] = Utility.HSVtoRGB(Hue, S0, V0)
  78. C10[1], C10[2], C10[3] = Utility.HSVtoRGB(Hue, S1, V0)
  79. C01[1], C01[2], C01[3] = Utility.HSVtoRGB(Hue, S0, V1)
  80. C11[1], C11[2], C11[3] = Utility.HSVtoRGB(Hue, S1, V1)
  81. local Mesh = SaturationMeshes[MeshIndex]
  82. MeshIndex = MeshIndex + 1
  83. Mesh:setVertexAttribute(1, 3, C00[1], C00[2], C00[3], C00[4])
  84. Mesh:setVertexAttribute(2, 3, C10[1], C10[2], C10[3], C10[4])
  85. Mesh:setVertexAttribute(3, 3, C11[1], C11[2], C11[3], C11[4])
  86. Mesh:setVertexAttribute(4, 3, C01[1], C01[2], C01[3], C01[4])
  87. StepX = StepX + 1
  88. end
  89. StepX = 0
  90. StepY = StepY + 1
  91. end
  92. end
  93. end
  94. local function InitializeSaturationMeshes()
  95. if SaturationMeshes == nil then
  96. SaturationMeshes = {}
  97. local Step = SaturationStep
  98. local X, Y = 0.0, 0.0
  99. local Size = SaturationSize / Step
  100. for I = 1, Step, 1 do
  101. for J = 1, Step, 1 do
  102. local Verts = {
  103. {
  104. X, Y,
  105. 0.0, 0.0
  106. },
  107. {
  108. X + Size, Y,
  109. 1.0, 0.0
  110. },
  111. {
  112. X + Size, Y + Size,
  113. 1.0, 1.0
  114. },
  115. {
  116. X, Y + Size,
  117. 0.0, 1.0
  118. }
  119. }
  120. local NewMesh = love.graphics.newMesh(Verts)
  121. table.insert(SaturationMeshes, NewMesh)
  122. X = X + Size
  123. end
  124. X = 0.0
  125. Y = Y + Size
  126. end
  127. end
  128. UpdateSaturationColors()
  129. end
  130. local function InitializeTintMeshes()
  131. if TintMeshes == nil then
  132. TintMeshes = {}
  133. local Step = 6
  134. local X, Y = 0.0, 0.0
  135. local C0 = {1.0, 1.0, 1.0, 1.0}
  136. local C1 = {1.0, 1.0, 1.0, 1.0}
  137. local I = 0
  138. local Colors = {
  139. {1.0, 0.0, 0.0, 1.0},
  140. {1.0, 1.0, 0.0, 1.0},
  141. {0.0, 1.0, 0.0, 1.0},
  142. {0.0, 1.0, 1.0, 1.0},
  143. {0.0, 0.0, 1.0, 1.0},
  144. {1.0, 0.0, 1.0, 1.0},
  145. {1.0, 0.0, 0.0, 1.0}
  146. }
  147. for Index = 1, Step, 1 do
  148. C0 = Colors[Index]
  149. C1 = Colors[Index + 1]
  150. local Verts = {
  151. {
  152. X, Y,
  153. 0.0, 0.0,
  154. C0[1], C0[2], C0[3], C0[4]
  155. },
  156. {
  157. TintW, Y,
  158. 1.0, 0.0,
  159. C0[1], C0[2], C0[3], C0[4]
  160. },
  161. {
  162. TintW, Y + TintH / Step,
  163. 1.0, 1.0,
  164. C1[1], C1[2], C1[3], C1[4]
  165. },
  166. {
  167. X, Y + TintH / Step,
  168. 0.0, 1.0,
  169. C1[1], C1[2], C1[3], C1[4]
  170. }
  171. }
  172. local NewMesh = love.graphics.newMesh(Verts)
  173. table.insert(TintMeshes, NewMesh)
  174. Y = Y + TintH / Step
  175. end
  176. end
  177. end
  178. local function InitializeAlphaMesh()
  179. if AlphaMesh == nil then
  180. local Verts = {
  181. {
  182. 0.0, 0.0,
  183. 0.0, 0.0,
  184. 1.0, 1.0, 1.0, 1.0
  185. },
  186. {
  187. AlphaW, 0.0,
  188. 1.0, 0.0,
  189. 1.0, 1.0, 1.0, 1.0
  190. },
  191. {
  192. AlphaW, AlphaH,
  193. 1.0, 1.0,
  194. 0.0, 0.0, 0.0, 1.0
  195. },
  196. {
  197. 0.0, AlphaH,
  198. 0.0, 1.0,
  199. 0.0, 0.0, 0.0, 1.0
  200. }
  201. }
  202. AlphaMesh = love.graphics.newMesh(Verts)
  203. end
  204. end
  205. function ColorPicker.Begin(Options)
  206. Options = Options == nil and {} or Options
  207. Options.Color = Options.Color == nil and {1.0, 1.0, 1.0, 1.0} or Options.Color
  208. if SaturationMeshes == nil then
  209. InitializeSaturationMeshes()
  210. end
  211. if TintMeshes == nil then
  212. InitializeTintMeshes()
  213. end
  214. if AlphaMesh == nil then
  215. InitializeAlphaMesh()
  216. end
  217. local DeltaVisibleTime = love.timer.getTime() - Window.GetLastVisibleTime('ColorPicker')
  218. if DeltaVisibleTime > 1.0 then
  219. CurrentColor[1] = Options.Color[1]
  220. CurrentColor[2] = Options.Color[2]
  221. CurrentColor[3] = Options.Color[3]
  222. CurrentColor[4] = Options.Color[4]
  223. UpdateSaturationColors()
  224. end
  225. Window.Begin('ColorPicker', {Title = "Color Picker"})
  226. local X, Y = Cursor.GetPosition()
  227. local MouseX, MouseY = Window.GetMousePosition()
  228. local H, S, V = Utility.RGBtoHSV(CurrentColor[1], CurrentColor[2], CurrentColor[3])
  229. local UpdateColor = false
  230. if SaturationMeshes ~= nil then
  231. for I, V in ipairs(SaturationMeshes) do
  232. DrawCommands.Mesh(V, X, Y)
  233. end
  234. Window.AddItem(X, Y, SaturationSize, SaturationSize)
  235. local UpdateSaturation = false
  236. if X <= MouseX and MouseX < X + SaturationSize and Y <= MouseY and MouseY < Y + SaturationSize then
  237. if Mouse.IsClicked(1) then
  238. SaturationFocused = true
  239. UpdateSaturation = true
  240. end
  241. end
  242. if SaturationFocused and Mouse.IsDragging(1) then
  243. UpdateSaturation = true
  244. end
  245. if UpdateSaturation then
  246. local CanvasX = math.max(MouseX - X, 0)
  247. CanvasX = math.min(CanvasX, SaturationSize)
  248. local CanvasY = math.max(MouseY - Y, 0)
  249. CanvasY = math.min(CanvasY, SaturationSize)
  250. S = CanvasX / SaturationSize
  251. V = 1 - (CanvasY / SaturationSize)
  252. UpdateColor = true
  253. end
  254. local SaturationX = S * SaturationSize
  255. local SaturationY = (1.0 - V) * SaturationSize
  256. DrawCommands.Circle('line', X + SaturationX, Y + SaturationY, 4.0, {1.0, 1.0, 1.0, 1.0})
  257. X = X + SaturationSize + Cursor.PadX()
  258. end
  259. if TintMeshes ~= nil then
  260. for I, V in ipairs(TintMeshes) do
  261. DrawCommands.Mesh(V, X, Y)
  262. end
  263. Window.AddItem(X, Y, TintW, TintH)
  264. local UpdateTint = false
  265. if X <= MouseX and MouseX < X + TintW and Y <= MouseY and MouseY < Y + TintH then
  266. if Mouse.IsClicked(1) then
  267. TintFocused = true
  268. UpdateTint = true
  269. end
  270. end
  271. if TintFocused and Mouse.IsDragging(1) then
  272. UpdateTint = true
  273. end
  274. if UpdateTint then
  275. local CanvasY = math.max(MouseY - Y, 0)
  276. CanvasY = math.min(CanvasY, TintH)
  277. H = CanvasY / TintH
  278. UpdateColor = true
  279. end
  280. local TintY = H * TintH
  281. DrawCommands.Line(X, Y + TintY, X + TintW, Y + TintY, 2.0, {1.0, 1.0, 1.0, 1.0})
  282. X = X + TintW + Cursor.PadX()
  283. DrawCommands.Mesh(AlphaMesh, X, Y)
  284. Window.AddItem(X, Y, AlphaW, AlphaH)
  285. local UpdateAlpha = false
  286. if X <= MouseX and MouseX < X + AlphaW and Y <= MouseY and MouseY < Y + AlphaH then
  287. if Mouse.IsClicked(1) then
  288. AlphaFocused = true
  289. UpdateAlpha = true
  290. end
  291. end
  292. if AlphaFocused and Mouse.IsDragging(1) then
  293. UpdateAlpha = true
  294. end
  295. if UpdateAlpha then
  296. local CanvasY = math.max(MouseY - Y, 0)
  297. CanvasY = math.min(CanvasY, AlphaH)
  298. CurrentColor[4] = 1.0 - CanvasY / AlphaH
  299. UpdateColor = true
  300. end
  301. local A = 1.0 - CurrentColor[4]
  302. local AlphaY = A * AlphaH
  303. DrawCommands.Line(X, Y + AlphaY, X + AlphaW, Y + AlphaY, 2.0, {A, A, A, 1.0})
  304. Y = Y + AlphaH + Cursor.PadY()
  305. end
  306. if UpdateColor then
  307. CurrentColor[1], CurrentColor[2], CurrentColor[3] = Utility.HSVtoRGB(H, S, V)
  308. UpdateSaturationColors()
  309. end
  310. local OffsetX = Text.GetWidth("##")
  311. Cursor.AdvanceY(SaturationSize)
  312. X, Y = Cursor.GetPosition()
  313. local R = CurrentColor[1]
  314. local G = CurrentColor[2]
  315. local B = CurrentColor[3]
  316. local A = CurrentColor[4]
  317. CurrentColor[1], R = InputColor("R", R, OffsetX)
  318. CurrentColor[2], G = InputColor("G", G, OffsetX)
  319. CurrentColor[3], B = InputColor("B", B, OffsetX)
  320. CurrentColor[4], A = InputColor("A", A, OffsetX)
  321. if R or G or B or A then
  322. UpdateSaturationColors()
  323. end
  324. local InputX, InputY = Cursor.GetPosition()
  325. Cursor.SameLine()
  326. X = Cursor.GetX()
  327. Cursor.SetY(Y)
  328. local WinX, WinY, WinW, WinH = Window.GetBounds()
  329. WinW, WinH = Window.GetBorderlessSize()
  330. OffsetX = Text.GetWidth("####")
  331. local ColorX = X + OffsetX
  332. local ColorW = (WinX + WinW) - ColorX
  333. Cursor.SetPosition(ColorX, Y)
  334. Image.Begin('ColorPicker_CurrentAlpha', {
  335. Path = SLAB_PATH .. "/Internal/Resources/Textures/Transparency.png",
  336. SubW = ColorW,
  337. SubH = ColorH,
  338. WrapH = "repeat",
  339. WrapV = "repeat"
  340. })
  341. DrawCommands.Rectangle('fill', ColorX, Y, ColorW, ColorH, CurrentColor, Style.ButtonRounding)
  342. local LabelW, LabelH = Text.GetSize("New")
  343. Cursor.SetPosition(ColorX - LabelW - Cursor.PadX(), Y + (ColorH * 0.5) - (LabelH * 0.5))
  344. Text.Begin("New")
  345. Y = Y + ColorH + Cursor.PadY()
  346. Cursor.SetPosition(ColorX, Y)
  347. Image.Begin('ColorPicker_CurrentAlpha', {
  348. Path = SLAB_PATH .. "/Internal/Resources/Textures/Transparency.png",
  349. SubW = ColorW,
  350. SubH = ColorH,
  351. WrapH = "repeat",
  352. WrapV = "repeat"
  353. })
  354. DrawCommands.Rectangle('fill', ColorX, Y, ColorW, ColorH, Options.Color, Style.ButtonRounding)
  355. local LabelW, LabelH = Text.GetSize("Old")
  356. Cursor.SetPosition(ColorX - LabelW - Cursor.PadX(), Y + (ColorH * 0.5) - (LabelH * 0.5))
  357. Text.Begin("Old")
  358. if Mouse.IsReleased(1) then
  359. SaturationFocused = false
  360. TintFocused = false
  361. AlphaFocused = false
  362. end
  363. Cursor.SetPosition(InputX, InputY)
  364. Cursor.NewLine()
  365. local Result = {Button = "", Color = Utility.MakeColor(CurrentColor)}
  366. if Button.Begin("Cancel", {AlignRight = true}) then
  367. Result.Button = "Cancel"
  368. Result.Color = Utility.MakeColor(Options.Color)
  369. end
  370. Cursor.SameLine()
  371. if Button.Begin("OK", {AlignRight = true}) then
  372. Result.Button = "OK"
  373. end
  374. Window.End()
  375. return Result
  376. end
  377. return ColorPicker