box-of-eases/src/dropdown.lua

384 lines
13 KiB
Lua

local self = {}
local icon = {
inease = love.graphics.newImage('assets/textures/inease-icon.png'),
outease = love.graphics.newImage('assets/textures/outease-icon.png'),
inoutease = love.graphics.newImage('assets/textures/inoutease-icon.png'),
transientease = love.graphics.newImage('assets/textures/transientease-icon.png'),
unknownease = love.graphics.newImage('assets/textures/unknownease-icon.png'),
linearease = love.graphics.newImage('assets/textures/linearease-icon.png'),
instantease = love.graphics.newImage('assets/textures/instantease-icon.png'),
pulseease = love.graphics.newImage('assets/textures/pulseease-icon.png'),
}
local dropdowns = {}
local dropdownValueCache = {}
local dropdownScrollCache = {}
local maxDropdown = 16
self.openDropdown = 0
local dropdownScroll = 0
local dropdownScrollE = 0
local scrollbarSize = 6
local function skeys(t)
local k = {}
for n,v in pairs(t) do table.insert(k, {n, v}) end
table.sort(k, function(a, b) return a[2].i < b[2].i end)
local k2 = {}
for _,v in ipairs(k) do table.insert(k2, v[1]) end
return k2
end
local function icons(keys)
local e = {}
for i,v in ipairs(keys) do
table.insert(e, (ease.eases[v].type or 'unknown') .. 'ease')
end
return e
end
local function params(keys)
local e = {}
for i,v in ipairs(keys) do
table.insert(e, ease.eases[v].params)
end
return e
end
function self.get(index)
return dropdowns[index]
end
function self.selected(index)
return dropdowns[index].options[dropdowns[index].selected]
end
function self.kget(key)
for _, v in ipairs(dropdowns) do
if v.name == key then
return v
end
end
end
function self.kselected(key)
for _, v in ipairs(dropdowns) do
if v.name == key then
return v.options[v.selected]
end
end
end
function self.swap(key, key2)
local a, b = self.kget(key), self.kget(key2)
local s = a.selected
a.selected = b.selected
b.selected = s
end
local dropdownId
local function insertDropdown(tab, f)
dropdownId = dropdownId + 1
f.selected = (self.kget(f.name) or dropdownValueCache[f.name] or {selected = 1}).selected
f.selected = (f.selected - 1) % #f.options + 1
f.open = (self.kget(f.name) or {open = 0}).open
return table.insert(tab, f)
end
function self.createDropdowns()
local d = {}
dropdownId = 0
insertDropdown(d, {
x = outerpadding,
y = outerpadding,
width = dropdownWidth,
options = {
'Preview Ease',
'Mix Eases',
'Multiply Eases',
'Create Ease'
},
name = 'mode'
})
local param1 = {}
local param2 = {}
local eases = skeys(ease.eases)
if d[dropdownId].selected == modes.preview then -- preview ease
insertDropdown(d, {
x = outerpadding + dropdownWidth + padding,
y = outerpadding,
width = dropdownWidth,
options = eases,
name = 'ease1',
icons = icons(eases),
params = params(eases)
})
local _e = ease.eases[d[dropdownId].options[d[dropdownId].selected]]
param1[1] = slider.kvalue(_e.name .. 'param11') or (_e.params[1] and _e.params[1].default) or 1
param1[2] = slider.kvalue(_e.name .. 'param12') or (_e.params[2] and _e.params[2].default) or 1
ease.ease = function(x)
return _e.f(x, param1[1], param1[2])
end
elseif d[dropdownId].selected == modes.mix then -- mix eases
insertDropdown(d, {
x = outerpadding + dropdownWidth + padding,
y = outerpadding,
width = dropdownWidth,
options = eases,
name = 'ease1',
icons = icons(eases),
params = params(eases)
})
insertDropdown(d, {
x = outerpadding + dropdownWidth + padding + dropdownWidth + padding,
y = outerpadding,
width = dropdownWidth,
options = eases,
name = 'ease2',
icons = icons(eases),
params = params(eases)
})
local _e1 = ease.eases[d[dropdownId - 1].options[d[dropdownId - 1].selected]]
local _e2 = ease.eases[d[dropdownId].options[d[dropdownId].selected]]
param1[1] = slider.kvalue(_e1.name .. 'param11') or (_e1.params[1] and _e1.params[1].default) or 1
param1[2] = slider.kvalue(_e1.name .. 'param12') or (_e1.params[2] and _e1.params[2].default) or 1
param2[1] = slider.kvalue(_e2.name .. 'param21') or (_e2.params[1] and _e2.params[1].default) or 1
param2[2] = slider.kvalue(_e2.name .. 'param22') or (_e2.params[2] and _e2.params[2].default) or 1
ease.ease = ease.mixEase(_e1.f, _e2.f, slider.kvalue('mix'), param1, param2)
elseif d[dropdownId].selected == modes.multiply then -- mult eases
insertDropdown(d, {
x = outerpadding + dropdownWidth + padding,
y = outerpadding,
width = dropdownWidth,
options = eases,
name = 'ease1',
icons = icons(eases),
params = params(eases),
tooltip = 'The a in b(a(x))'
})
insertDropdown(d, {
x = outerpadding + dropdownWidth + padding + dropdownWidth + padding,
y = outerpadding,
width = dropdownWidth,
options = eases,
name = 'ease2',
icons = icons(eases),
params = params(eases),
tooltip = 'The b in b(a(x))'
})
local _e1 = ease.eases[d[dropdownId - 1].options[d[dropdownId - 1].selected]]
local _e2 = ease.eases[d[dropdownId].options[d[dropdownId].selected]]
param1[1] = slider.kvalue(_e1.name .. 'param11') or (_e1.params[1] and _e1.params[1].default) or 1
param1[2] = slider.kvalue(_e1.name .. 'param12') or (_e1.params[2] and _e1.params[2].default) or 1
param2[1] = slider.kvalue(_e2.name .. 'param21') or (_e2.params[1] and _e2.params[1].default) or 1
param2[2] = slider.kvalue(_e2.name .. 'param22') or (_e2.params[2] and _e2.params[2].default) or 1
ease.ease = function(x)
return _e2.f(_e1.f(x, param1[1], param1[2]), param2[1], param2[2])
end
elseif d[dropdownId].selected == modes.create then -- create eases
insertDropdown(d, {
x = outerpadding + dropdownWidth + padding,
y = outerpadding,
width = dropdownWidth,
options = eases,
name = 'ease1',
icons = icons(eases)
})
end
dropdowns = d
minEase = (self.kselected('ease1') and ease.eases[self.kselected('ease1')].min == -1) or (self.kselected('ease2') and ease.eases[self.kselected('ease2')].min == -1)
mode = dropdown.kget('mode').selected
end
function self.update(dt)
maxDropdown = math.floor(math.min(16 * (margin + fontHeight), love.graphics.getHeight() * 0.75) / (margin + fontHeight))
if self.openDropdown ~= 0 then
dropdownScroll = math.max(dropdownScroll, -(#self.get(self.openDropdown).options - maxDropdown + 1))
dropdownScroll = math.min(dropdownScroll, 0)
dropdownScrollE = mix(dropdownScrollE, dropdownScroll, dt * 10)
end
for i, v in ipairs(dropdowns) do
if i == self.openDropdown then
v.open = mix(v.open, 1, dt * 14)
else
v.open = mix(v.open, 0, dt * 20)
end
end
if love.mouse.isDown(1) then
local x, y = getMousePosition()
for i,v in ipairs(dropdowns) do
local h = fontHeight + margin
if self.openDropdown == i then
if x > v.x and x > v.x + v.width - scrollbarSize and y > v.y + h and y < v.y + h * (math.min(#v.options, maxDropdown) + 1) and not (#v.options < maxDropdown) then
dropdownScroll = ((y - (v.y + h)) / (h * (math.min(#v.options, maxDropdown)))) * -(#v.options - maxDropdown + 1)
end
end
end
end
end
function self.render()
local mx, my = getMousePosition()
for i,v in ipairs(dropdowns) do
local x, y, w, h = v.x, v.y, v.width, fontHeight + margin
love.graphics.setColor(0.06, 0.06, 0.12, 0.6)
if mx > x and mx < x + w and my > y and my < y + h then
love.graphics.setColor(0.8, 0.8, 1, love.mouse.isDown(1) and 0.4 or 0.3)
if v.tooltip then tooltips.show(v.tooltip) end
end
love.graphics.rectangle('fill', x, y, w, h)
love.graphics.setColor(1, 1, 1, 1)
love.graphics.rectangle('line', x, y, w, h)
if v.icons and v.icons[v.selected] then
local sprite = icon[v.icons[v.selected]]
love.graphics.draw(sprite, x + 2, y + 2, 0, fontHeight / sprite:getWidth(), fontHeight / sprite:getHeight())
love.graphics.print(self.selected(i), x + margin/2 + fontHeight, y + margin/2)
else
love.graphics.print(self.selected(i), x + margin/2, y + margin/2)
end
-- love.graphics.rectangle('line', x + w - h, y, h, h)
love.graphics.setLineWidth(1)
love.graphics.polygon('line', x + w - h/2 + 0.3 * h, y + h/2 - 0.3 * h, x + w - h/2 - 0.3 * h, y + h/2 - 0.3 * h, x + w - h/2, y + h/2 + 0.3 * h)
if self.openDropdown == i or v.open > 0.01 then
for i,o in ipairs(v.options) do
local x, y, w, h = x, y + ((i - 1) * v.open + 1) * h, w, h * v.open
y = y + dropdownScrollE * h * v.open
local gi = y / h
if gi > (maxDropdown + 1) or gi < 1 then
goto continue
end
-- help
local a = (1 - math.min(math.max((1 - (maxDropdown - gi)) * (1 - (math.abs(dropdownScrollE) / (#v.options - maxDropdown + 1))), 0), 1)) * math.max(math.min(gi - 1, 1), 0) * v.open
love.graphics.setColor(0.06, 0.06, 0.12, 0.6 * a)
if mx > x and mx < x + w and my > y and my < y + h then
love.graphics.setColor(0.4, 0.4, 1, (love.mouse.isDown(1) and 0.8 or 0.7) * a)
end
love.graphics.rectangle('fill', x, y, w, h)
love.graphics.setColor(1, 1, 1, 0.75 * a)
love.graphics.rectangle('line', x, y, w, h)
love.graphics.setColor(1, 1, 1, 1 * a)
if i == v.selected then
love.graphics.setColor(0.5, 0.5, 1, 1 * a)
end
if v.icons and v.icons[i] then
local sprite = icon[v.icons[i]]
love.graphics.draw(sprite, x + 2, y + 2, 0, fontHeight / sprite:getWidth(), fontHeight / sprite:getHeight())
love.graphics.print(v.options[i], x + 2 + fontHeight, y + 2)
else
love.graphics.print(v.options[i], x + 2, y + 2)
end
if v.params and v.params[i] then
local str = ''
for _,p in ipairs(v.params[i]) do
str = str .. ' ' .. string.sub(p.name, 1, 1)
end
love.graphics.setFont(getFont(0.8, true))
love.graphics.setColor(0.8, 0.8, 1, 0.8 * a)
love.graphics.printf(str, x, y, w - 2, 'right')
love.graphics.setFont(interfaceFont)
end
::continue::
end
-- scrollwheel
if #v.options > maxDropdown then
local displayed = maxDropdown / (#v.options)
local scroll = math.abs(dropdownScrollE) / (#v.options - maxDropdown + 1)
local size = scrollbarSize
love.graphics.setColor(1, 1, 1, 0.8 * v.open)
love.graphics.rectangle('fill', x + w - size, y + h + scroll * (1 - displayed) * (maxDropdown - 1) * h * v.open, size, displayed * (maxDropdown - 1) * h * v.open)
end
end
love.graphics.setLineWidth(lineWidth)
end
end
function self.mousepressed(x, y, m)
local clickedDropdown = false
for i,v in ipairs(dropdowns) do
local h = fontHeight + margin
if self.openDropdown == 0 then
if x > v.x and x < v.x + v.width and y > v.y and y < v.y + h + margin then
if m == 1 then
self.openDropdown = i
clickedDropdown = true
dropdownScroll = dropdownScrollCache[i] or 0
dropdownScrollE = dropdownScrollCache[i] or 0
elseif m == 3 then
dropdowns[i].selected = math.random(1, #dropdowns[i].options)
createUI()
end
end
elseif self.openDropdown == i then
if x > v.x and x < v.x + v.width and y > v.y + h and y < v.y + h * (math.min(#v.options, maxDropdown) + 1) and m == 1 then
clickedDropdown = true
end
end
end
if not clickedDropdown and m == 1 then
dropdownScrollCache[self.openDropdown] = dropdownScroll
self.openDropdown = 0
end
end
function self.mousereleased(x, y, m)
for i,v in ipairs(dropdowns) do
local h = fontHeight + margin
if self.openDropdown == i then
if x > v.x and x < v.x + v.width and y > v.y + h and y < v.y + h * (math.min(#v.options, maxDropdown) + 1) and m == 1 and (x < v.x + v.width - scrollbarSize or #v.options < maxDropdown) then
v.selected = math.floor((y - v.y) / h - dropdownScrollE)
self.openDropdown = 0
dropdownValueCache[v.name] = {selected = v.selected}
dropdownScrollCache[i] = dropdownScroll
createUI()
end
end
end
end
function self.wheelmoved(x, y)
if self.openDropdown ~= 0 then
dropdownScroll = dropdownScroll + y
else
local mx, my = getMousePosition()
for i,v in ipairs(dropdowns) do
local h = fontHeight + margin
if mx > v.x and mx < v.x + v.width and my > v.y and my < v.y + h + margin then
dropdowns[i].selected = dropdowns[i].selected - math.floor(y)
dropdowns[i].selected = (dropdowns[i].selected - 1) % #dropdowns[i].options + 1
createUI()
end
end
end
end
return self