dropdown.lua 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. local g = love.graphics
  2. require 'lib/component'
  3. Dropdown = extend(Component)
  4. function Dropdown:activate()
  5. self.value = nil
  6. self.choices = self.choices or {}
  7. self.factor = 0
  8. self.prevFactor = self.factor
  9. self.hoverFactor = 0
  10. self.prevHoverFactor = self.hoverFactor
  11. self.choiceHoverFactors = {}
  12. self.prevChoiceHoverFactors = {}
  13. self.hoverDirty = false
  14. end
  15. function Dropdown:update()
  16. local mx, my = love.mouse.getPosition()
  17. local ox, oy = self:getOffset()
  18. mx, my = mx + ox, my + oy
  19. local hover = self:contains(mx, my)
  20. self.prevFactor = self.factor
  21. self.prevHoverFactor = self.hoverFactor
  22. self.factor = math.lerp(self.factor, self:focused() and 1 or 0, math.min(16 * ls.tickrate, 1))
  23. self.hoverFactor = math.lerp(self.hoverFactor, (self:focused() or hover) and 1 or 0, math.min(16 * ls.tickrate, 1))
  24. if self:focused() then
  25. local hoverIndex = self:contains(mx, my)
  26. local hoverAmount = 1 + (love.mouse.isDown('l') and .5 or 0)
  27. for i = 1, #self.choices do
  28. self.prevChoiceHoverFactors[i] = self.choiceHoverFactors[i] or 0
  29. self.choiceHoverFactors[i] = math.lerp(self.prevChoiceHoverFactors[i], i == hoverIndex and hoverAmount or 0, math.min(16 * ls.tickrate, 1))
  30. end
  31. if hover then
  32. if self.hoverDirty ~= hoverIndex and hoverIndex ~= 0 and (not self.gooey.focused or self.gooey.focused == self) then
  33. ctx.sound:play('juju1', function(sound) sound:setPitch(.75) end)
  34. self.hoverDirty = hoverIndex
  35. end
  36. else
  37. self.hoverDirty = false
  38. end
  39. end
  40. if hover then
  41. if not self.hoverDirty and (not self.gooey.focused or self.gooey.focused == self) then
  42. ctx.sound:play('juju1', function(sound) sound:setPitch(.75) end)
  43. self.hoverDirty = true
  44. end
  45. else
  46. self.hoverDirty = false
  47. end
  48. end
  49. function Dropdown:render()
  50. local u, v = ctx.u, ctx.v
  51. local x, y, w, h = unpack(self.geometry())
  52. local mx, my = love.mouse.getPosition()
  53. local ox, oy = self:getOffset()
  54. mx, my = mx + ox, my + oy
  55. local hoverIndex = self:contains(mx, my)
  56. local choiceHoverFactors = table.interpolate(self.prevChoiceHoverFactors, self.choiceHoverFactors, ls.accum / ls.tickrate)
  57. local hoverFactor = math.lerp(self.prevHoverFactor, self.hoverFactor, ls.accum / ls.tickrate)
  58. local factor = math.lerp(self.prevFactor, self.factor, ls.accum / ls.tickrate)
  59. local dropdownHeight = self:getDropdownHeight() * factor
  60. local font = g.setFont('mesmerize', h - .02 * v)
  61. g.setColor(255, 255, 255, 40 + (20 * hoverFactor))
  62. g.rectangle('fill', x, y, w, h)
  63. g.setColor(0, 0, 0, 255 * factor)
  64. g.rectangle('fill', x, y + h, w, dropdownHeight)
  65. if hoverIndex and hoverIndex > 0 then
  66. g.setColor(255, 255, 255, 30 * choiceHoverFactors[hoverIndex])
  67. g.rectangle('fill', x, y + h * hoverIndex, w, h)
  68. end
  69. --[[g.setColor(255, 255, 255, 255)
  70. g.rectangle('line', math.round(x) + .5, math.round(y) + .5, w, h)]]
  71. for i = 1, #self.choices do
  72. local factor = factor
  73. local hoverFactor = 0
  74. if self:focused() then
  75. local prev = self:getDropdownHeight() * (i - 1) / #self.choices
  76. factor = math.clamp((dropdownHeight - prev) / h, 0, 1) ^ 4
  77. hoverFactor = choiceHoverFactors[i]
  78. end
  79. local alpha = math.min(180 * factor + (75 * hoverFactor), 255)
  80. if self.choices[i] == self.value then g.setColor(100, 200, 50, 255 * factor)
  81. else g.setColor(220, 220, 220, alpha) end
  82. g.print(self.choices[i], x + .01 * v, y + h * i + .01 * v)
  83. end
  84. g.setColor(255, 255, 255)
  85. g.print(self.label, x + .01 * v, y + .01 * v)
  86. g.setColor(100, 200, 50, 255)
  87. g.print(self.value, x + w - .01 * v - font:getWidth(self.value), y + .01 * v)
  88. end
  89. function Dropdown:mousepressed(mx, my, b)
  90. local ox, oy = self:getOffset()
  91. mx, my = mx + ox, my + oy
  92. if b == 'l' and self:contains(mx, my) then
  93. self.gooey.hot = self
  94. if self:focused() then return true end
  95. end
  96. end
  97. function Dropdown:mousereleased(mx, my, b)
  98. local ox, oy = self:getOffset()
  99. mx, my = mx + ox, my + oy
  100. if b == 'l' then
  101. if not self:focused() then
  102. if self.gooey.hot == self and self:contains(mx, my) then
  103. self.gooey:focus(self)
  104. ctx.sound:play('juju1', function(sound) sound:setPitch(1) end)
  105. end
  106. else
  107. local hit = self.gooey.hot == self and self:contains(mx, my)
  108. self.gooey:unfocus()
  109. if hit then
  110. self.value = self.choices[hit] or self.value
  111. self:emit('change', {component = self})
  112. ctx.sound:play('juju1', function(sound) sound:setPitch(1) end)
  113. return true
  114. end
  115. end
  116. end
  117. end
  118. function Dropdown:contains(mx, my)
  119. local x, y, w, h = unpack(self.geometry())
  120. if math.inside(mx, my, x, y, w, h) then return 0 end
  121. if self:focused() then
  122. for i = 1, #self.choices do
  123. if math.inside(mx, my, x, y + h * i, w, h) then
  124. return i
  125. end
  126. end
  127. end
  128. return false
  129. end
  130. function Dropdown:getDropdownHeight()
  131. local h = self.geometry()[4]
  132. return h * (#self.choices)
  133. end