ColorPicker.lua 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  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 LayoutManager = require(SLAB_PATH .. '.Internal.UI.LayoutManager')
  26. local Mouse = require(SLAB_PATH .. '.Internal.Input.Mouse')
  27. local Style = require(SLAB_PATH .. '.Style')
  28. local Text = require(SLAB_PATH .. '.Internal.UI.Text')
  29. local Utility = require(SLAB_PATH .. '.Internal.Core.Utility')
  30. local Window = require(SLAB_PATH .. '.Internal.UI.Window')
  31. local ColorPicker = {}
  32. local SaturationMeshes = nil
  33. local SaturationSize = 200.0
  34. local SaturationStep = 5
  35. local SaturationFocused = false
  36. local TintMeshes = nil
  37. local TintW = 30.0
  38. local TintH = SaturationSize
  39. local TintFocused = false
  40. local AlphaMesh = nil
  41. local AlphaW = TintW
  42. local AlphaH = TintH
  43. local AlphaFocused = false
  44. local CurrentColor = {1.0, 1.0, 1.0, 1.0}
  45. local ColorH = 25.0
  46. local function InputColor(Component, Value, OffsetX)
  47. local Changed = false
  48. Text.Begin(string.format("%s ", Component))
  49. Cursor.SameLine()
  50. Cursor.SetRelativeX(OffsetX)
  51. if Input.Begin('ColorPicker_' .. Component, {W = 40.0, NumbersOnly = true, Text = tostring(math.ceil(Value * 255)), ReturnOnText = false}) then
  52. local NewValue = tonumber(Input.GetText())
  53. if NewValue ~= nil then
  54. NewValue = math.max(NewValue, 0)
  55. NewValue = math.min(NewValue, 255)
  56. Value = NewValue / 255
  57. Changed = true
  58. end
  59. end
  60. return Value, Changed
  61. end
  62. local function UpdateSaturationColors()
  63. if SaturationMeshes ~= nil then
  64. local MeshIndex = 1
  65. local Step = SaturationStep
  66. local C00 = {1.0, 1.0, 1.0, 1.0}
  67. local C10 = {1.0, 1.0, 1.0, 1.0}
  68. local C01 = {1.0, 1.0, 1.0, 1.0}
  69. local C11 = {1.0, 1.0, 1.0, 1.0}
  70. local StepX, StepY = 0, 0
  71. local Hue, Sat, Val = Utility.RGBtoHSV(CurrentColor[1], CurrentColor[2], CurrentColor[3])
  72. for I = 1, Step, 1 do
  73. for J = 1, Step, 1 do
  74. local S0 = StepX / Step
  75. local S1 = (StepX + 1) / Step
  76. local V0 = 1.0 - (StepY / Step)
  77. local V1 = 1.0 - ((StepY + 1) / Step)
  78. C00[1], C00[2], C00[3] = Utility.HSVtoRGB(Hue, S0, V0)
  79. C10[1], C10[2], C10[3] = Utility.HSVtoRGB(Hue, S1, V0)
  80. C01[1], C01[2], C01[3] = Utility.HSVtoRGB(Hue, S0, V1)
  81. C11[1], C11[2], C11[3] = Utility.HSVtoRGB(Hue, S1, V1)
  82. local Mesh = SaturationMeshes[MeshIndex]
  83. MeshIndex = MeshIndex + 1
  84. Mesh:setVertexAttribute(1, 3, C00[1], C00[2], C00[3], C00[4])
  85. Mesh:setVertexAttribute(2, 3, C10[1], C10[2], C10[3], C10[4])
  86. Mesh:setVertexAttribute(3, 3, C11[1], C11[2], C11[3], C11[4])
  87. Mesh:setVertexAttribute(4, 3, C01[1], C01[2], C01[3], C01[4])
  88. StepX = StepX + 1
  89. end
  90. StepX = 0
  91. StepY = StepY + 1
  92. end
  93. end
  94. end
  95. local function InitializeSaturationMeshes()
  96. if SaturationMeshes == nil then
  97. SaturationMeshes = {}
  98. local Step = SaturationStep
  99. local X, Y = 0.0, 0.0
  100. local Size = SaturationSize / Step
  101. for I = 1, Step, 1 do
  102. for J = 1, Step, 1 do
  103. local Verts = {
  104. {
  105. X, Y,
  106. 0.0, 0.0
  107. },
  108. {
  109. X + Size, Y,
  110. 1.0, 0.0
  111. },
  112. {
  113. X + Size, Y + Size,
  114. 1.0, 1.0
  115. },
  116. {
  117. X, Y + Size,
  118. 0.0, 1.0
  119. }
  120. }
  121. local NewMesh = love.graphics.newMesh(Verts)
  122. table.insert(SaturationMeshes, NewMesh)
  123. X = X + Size
  124. end
  125. X = 0.0
  126. Y = Y + Size
  127. end
  128. end
  129. UpdateSaturationColors()
  130. end
  131. local function InitializeTintMeshes()
  132. if TintMeshes == nil then
  133. TintMeshes = {}
  134. local Step = 6
  135. local X, Y = 0.0, 0.0
  136. local C0 = {1.0, 1.0, 1.0, 1.0}
  137. local C1 = {1.0, 1.0, 1.0, 1.0}
  138. local I = 0
  139. local Colors = {
  140. {1.0, 0.0, 0.0, 1.0},
  141. {1.0, 1.0, 0.0, 1.0},
  142. {0.0, 1.0, 0.0, 1.0},
  143. {0.0, 1.0, 1.0, 1.0},
  144. {0.0, 0.0, 1.0, 1.0},
  145. {1.0, 0.0, 1.0, 1.0},
  146. {1.0, 0.0, 0.0, 1.0}
  147. }
  148. for Index = 1, Step, 1 do
  149. C0 = Colors[Index]
  150. C1 = Colors[Index + 1]
  151. local Verts = {
  152. {
  153. X, Y,
  154. 0.0, 0.0,
  155. C0[1], C0[2], C0[3], C0[4]
  156. },
  157. {
  158. TintW, Y,
  159. 1.0, 0.0,
  160. C0[1], C0[2], C0[3], C0[4]
  161. },
  162. {
  163. TintW, Y + TintH / Step,
  164. 1.0, 1.0,
  165. C1[1], C1[2], C1[3], C1[4]
  166. },
  167. {
  168. X, Y + TintH / Step,
  169. 0.0, 1.0,
  170. C1[1], C1[2], C1[3], C1[4]
  171. }
  172. }
  173. local NewMesh = love.graphics.newMesh(Verts)
  174. table.insert(TintMeshes, NewMesh)
  175. Y = Y + TintH / Step
  176. end
  177. end
  178. end
  179. local function InitializeAlphaMesh()
  180. if AlphaMesh == nil then
  181. local Verts = {
  182. {
  183. 0.0, 0.0,
  184. 0.0, 0.0,
  185. 1.0, 1.0, 1.0, 1.0
  186. },
  187. {
  188. AlphaW, 0.0,
  189. 1.0, 0.0,
  190. 1.0, 1.0, 1.0, 1.0
  191. },
  192. {
  193. AlphaW, AlphaH,
  194. 1.0, 1.0,
  195. 0.0, 0.0, 0.0, 1.0
  196. },
  197. {
  198. 0.0, AlphaH,
  199. 0.0, 1.0,
  200. 0.0, 0.0, 0.0, 1.0
  201. }
  202. }
  203. AlphaMesh = love.graphics.newMesh(Verts)
  204. end
  205. end
  206. function ColorPicker.Begin(Options)
  207. Options = Options == nil and {} or Options
  208. Options.Color = Options.Color == nil and {1.0, 1.0, 1.0, 1.0} or Options.Color
  209. if SaturationMeshes == nil then
  210. InitializeSaturationMeshes()
  211. end
  212. if TintMeshes == nil then
  213. InitializeTintMeshes()
  214. end
  215. if AlphaMesh == nil then
  216. InitializeAlphaMesh()
  217. end
  218. local DeltaVisibleTime = love.timer.getTime() - Window.GetLastVisibleTime('ColorPicker')
  219. if DeltaVisibleTime > 1.0 then
  220. CurrentColor[1] = Options.Color[1]
  221. CurrentColor[2] = Options.Color[2]
  222. CurrentColor[3] = Options.Color[3]
  223. CurrentColor[4] = Options.Color[4]
  224. UpdateSaturationColors()
  225. end
  226. Window.Begin('ColorPicker', {Title = "Color Picker"})
  227. local X, Y = Cursor.GetPosition()
  228. local MouseX, MouseY = Window.GetMousePosition()
  229. local H, S, V = Utility.RGBtoHSV(CurrentColor[1], CurrentColor[2], CurrentColor[3])
  230. local UpdateColor = false
  231. if SaturationMeshes ~= nil then
  232. for I, V in ipairs(SaturationMeshes) do
  233. DrawCommands.Mesh(V, X, Y)
  234. end
  235. Window.AddItem(X, Y, SaturationSize, SaturationSize)
  236. local UpdateSaturation = false
  237. if X <= MouseX and MouseX < X + SaturationSize and Y <= MouseY and MouseY < Y + SaturationSize then
  238. if Mouse.IsClicked(1) then
  239. SaturationFocused = true
  240. UpdateSaturation = true
  241. end
  242. end
  243. if SaturationFocused and Mouse.IsDragging(1) then
  244. UpdateSaturation = true
  245. end
  246. if UpdateSaturation then
  247. local CanvasX = math.max(MouseX - X, 0)
  248. CanvasX = math.min(CanvasX, SaturationSize)
  249. local CanvasY = math.max(MouseY - Y, 0)
  250. CanvasY = math.min(CanvasY, SaturationSize)
  251. S = CanvasX / SaturationSize
  252. V = 1 - (CanvasY / SaturationSize)
  253. UpdateColor = true
  254. end
  255. local SaturationX = S * SaturationSize
  256. local SaturationY = (1.0 - V) * SaturationSize
  257. DrawCommands.Circle('line', X + SaturationX, Y + SaturationY, 4.0, {1.0, 1.0, 1.0, 1.0})
  258. X = X + SaturationSize + Cursor.PadX()
  259. end
  260. if TintMeshes ~= nil then
  261. for I, V in ipairs(TintMeshes) do
  262. DrawCommands.Mesh(V, X, Y)
  263. end
  264. Window.AddItem(X, Y, TintW, TintH)
  265. local UpdateTint = false
  266. if X <= MouseX and MouseX < X + TintW and Y <= MouseY and MouseY < Y + TintH then
  267. if Mouse.IsClicked(1) then
  268. TintFocused = true
  269. UpdateTint = true
  270. end
  271. end
  272. if TintFocused and Mouse.IsDragging(1) then
  273. UpdateTint = true
  274. end
  275. if UpdateTint then
  276. local CanvasY = math.max(MouseY - Y, 0)
  277. CanvasY = math.min(CanvasY, TintH)
  278. H = CanvasY / TintH
  279. UpdateColor = true
  280. end
  281. local TintY = H * TintH
  282. DrawCommands.Line(X, Y + TintY, X + TintW, Y + TintY, 2.0, {1.0, 1.0, 1.0, 1.0})
  283. X = X + TintW + Cursor.PadX()
  284. DrawCommands.Mesh(AlphaMesh, X, Y)
  285. Window.AddItem(X, Y, AlphaW, AlphaH)
  286. local UpdateAlpha = false
  287. if X <= MouseX and MouseX < X + AlphaW and Y <= MouseY and MouseY < Y + AlphaH then
  288. if Mouse.IsClicked(1) then
  289. AlphaFocused = true
  290. UpdateAlpha = true
  291. end
  292. end
  293. if AlphaFocused and Mouse.IsDragging(1) then
  294. UpdateAlpha = true
  295. end
  296. if UpdateAlpha then
  297. local CanvasY = math.max(MouseY - Y, 0)
  298. CanvasY = math.min(CanvasY, AlphaH)
  299. CurrentColor[4] = 1.0 - CanvasY / AlphaH
  300. UpdateColor = true
  301. end
  302. local A = 1.0 - CurrentColor[4]
  303. local AlphaY = A * AlphaH
  304. DrawCommands.Line(X, Y + AlphaY, X + AlphaW, Y + AlphaY, 2.0, {A, A, A, 1.0})
  305. Y = Y + AlphaH + Cursor.PadY()
  306. end
  307. if UpdateColor then
  308. CurrentColor[1], CurrentColor[2], CurrentColor[3] = Utility.HSVtoRGB(H, S, V)
  309. UpdateSaturationColors()
  310. end
  311. local OffsetX = Text.GetWidth("##")
  312. Cursor.AdvanceY(SaturationSize)
  313. X, Y = Cursor.GetPosition()
  314. local R = CurrentColor[1]
  315. local G = CurrentColor[2]
  316. local B = CurrentColor[3]
  317. local A = CurrentColor[4]
  318. CurrentColor[1], R = InputColor("R", R, OffsetX)
  319. CurrentColor[2], G = InputColor("G", G, OffsetX)
  320. CurrentColor[3], B = InputColor("B", B, OffsetX)
  321. CurrentColor[4], A = InputColor("A", A, OffsetX)
  322. if R or G or B or A then
  323. UpdateSaturationColors()
  324. end
  325. local InputX, InputY = Cursor.GetPosition()
  326. Cursor.SameLine()
  327. X = Cursor.GetX()
  328. Cursor.SetY(Y)
  329. local WinX, WinY, WinW, WinH = Window.GetBounds()
  330. WinW, WinH = Window.GetBorderlessSize()
  331. OffsetX = Text.GetWidth("####")
  332. local ColorX = X + OffsetX
  333. local ColorW = (WinX + WinW) - ColorX
  334. Cursor.SetPosition(ColorX, Y)
  335. Image.Begin('ColorPicker_CurrentAlpha', {
  336. Path = SLAB_PATH .. "/Internal/Resources/Textures/Transparency.png",
  337. SubW = ColorW,
  338. SubH = ColorH,
  339. WrapH = "repeat",
  340. WrapV = "repeat"
  341. })
  342. DrawCommands.Rectangle('fill', ColorX, Y, ColorW, ColorH, CurrentColor, Style.ButtonRounding)
  343. local LabelW, LabelH = Text.GetSize("New")
  344. Cursor.SetPosition(ColorX - LabelW - Cursor.PadX(), Y + (ColorH * 0.5) - (LabelH * 0.5))
  345. Text.Begin("New")
  346. Y = Y + ColorH + Cursor.PadY()
  347. Cursor.SetPosition(ColorX, Y)
  348. Image.Begin('ColorPicker_CurrentAlpha', {
  349. Path = SLAB_PATH .. "/Internal/Resources/Textures/Transparency.png",
  350. SubW = ColorW,
  351. SubH = ColorH,
  352. WrapH = "repeat",
  353. WrapV = "repeat"
  354. })
  355. DrawCommands.Rectangle('fill', ColorX, Y, ColorW, ColorH, Options.Color, Style.ButtonRounding)
  356. local LabelW, LabelH = Text.GetSize("Old")
  357. Cursor.SetPosition(ColorX - LabelW - Cursor.PadX(), Y + (ColorH * 0.5) - (LabelH * 0.5))
  358. Text.Begin("Old")
  359. if Mouse.IsReleased(1) then
  360. SaturationFocused = false
  361. TintFocused = false
  362. AlphaFocused = false
  363. end
  364. Cursor.SetPosition(InputX, InputY)
  365. Cursor.NewLine()
  366. LayoutManager.Begin('ColorPicker_Buttons_Layout', {AlignX = 'right'})
  367. local Result = {Button = "", Color = Utility.MakeColor(CurrentColor)}
  368. if Button.Begin("OK") then
  369. Result.Button = "OK"
  370. end
  371. LayoutManager.SameLine()
  372. if Button.Begin("Cancel") then
  373. Result.Button = "Cancel"
  374. Result.Color = Utility.MakeColor(Options.Color)
  375. end
  376. LayoutManager.End()
  377. Window.End()
  378. return Result
  379. end
  380. return ColorPicker