Compare commits

..

11 commits

Author SHA1 Message Date
f9cf276952 add snapping to most sliders
this will add a bit of impercision, but will be better for both the user and the internals
2021-09-19 15:07:12 +03:00
15316b918f make copy to clipboard button 2021-09-19 15:04:31 +03:00
0266e2c2e9 actually implement buttons 2021-09-19 14:37:46 +03:00
7aeb9afb89 change default filter to linear for non-textures 2021-09-19 14:25:23 +03:00
fd30c294b0 dont do button animation if dropdown is open 2021-09-19 14:24:00 +03:00
775d97ef2a add more tooltips & tweak visuals 2021-09-19 14:22:47 +03:00
1b572c67a5 better looking tooltips 2021-09-19 14:14:38 +03:00
94305e5477 add tooltip class 2021-09-19 13:56:48 +03:00
57ac1247fa create buttons 2021-09-19 13:32:12 +03:00
fbd5277cdb multiply mode 2021-09-19 13:16:27 +03:00
c44579b508 unhardcode modes 2021-09-19 01:11:44 +03:00
7 changed files with 299 additions and 11 deletions

150
button.lua Normal file
View file

@ -0,0 +1,150 @@
local self = {}
local buttons = {}
function self.get(index)
return buttons[index]
end
function self.kget(key)
for _, v in ipairs(buttons) do
if v.name == key then
return v
end
end
end
local buttonId
local function insertButton(tab, f)
buttonId = buttonId + 1
f.press = (self.kget(f.name) or {press = 1}).press
return table.insert(tab, f)
end
function self.createButtons()
local s = {}
buttonId = 0
if mode == modes.mix or mode == modes.preview or mode == modes.multiply then
insertButton(s, {
x = outerpadding,
y = love.graphics.getHeight() - outerpadding - fontHeight * 4 - padding * 2 - 32,
size = 32,
name = 'clipboard',
displayname = 'Copy to Clipboard',
tooltip = 'Copy to Clipboard',
func = function()
local s = ''
local param1 = {}
local param2 = {}
if mode == modes.preview then
local e = ease.eases[dropdown.kselected('ease1')]
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
local p1 = ''
for i,v in ipairs(param1) do
p1 = p1 .. (i > 1 and (', ' .. v) or v)
end
s = e.name .. (p1 ~= '' and ('.params(' .. p1 .. ')') or '')
elseif mode == modes.mix then
local e1 = ease.eases[dropdown.kselected('ease1')]
local e2 = ease.eases[dropdown.kselected('ease2')]
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
local p1 = ''
for i,v in ipairs(param1) do
p1 = p1 .. (i > 1 and (', ' .. v) or v)
end
local p2 = ''
for i,v in ipairs(param2) do
p2 = p2 .. (i > 1 and (', ' .. v) or v)
end
s = 'mixEase(' .. e1.name .. (p1 ~= '' and ('.params(' .. p1 .. ')') or '') .. ', ' .. e2.name .. (p2 ~= '' and ('.params(' .. p2 .. ')') or '') .. ', ' .. slider.kvalue('mix') .. ')'
elseif mode == modes.multiply then
local e1 = ease.eases[dropdown.kselected('ease1')]
local e2 = ease.eases[dropdown.kselected('ease2')]
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
local p1 = ''
for i,v in ipairs(param1) do
p1 = p1 .. (i > 1 and (', ' .. v) or v)
end
local p2 = ''
for i,v in ipairs(param2) do
p2 = p2 .. (i > 1 and (', ' .. v) or v)
end
s = 'function(x) ' .. e2.name .. (p2 ~= '' and ('.params(' .. p2 .. ')') or '') .. '(' .. e1.name .. (p1 ~= '' and ('.params(' .. p1 .. ')') or '') .. '(x)) end'
end
love.system.setClipboardText(s)
end
})
end
buttons = s
end
function self.update(dt)
for i, v in ipairs(buttons) do
local mx, my = love.mouse.getPosition()
local targetsize = 1
if mx > v.x and mx < v.x + v.size and my > v.y and my < v.y + v.size and dropdown.openDropdown == 0 then
if love.mouse.isDown(1) then
targetsize = 0.8
else
targetsize = 0.95
end
end
v.press = mix(v.press, targetsize, dt * 12)
end
end
function self.render()
local mx, my = love.mouse.getPosition()
for i, v in ipairs(buttons) do
local x, y, w, h = v.x, v.y, v.size, v.size
w = w * v.press
h = h * v.press
x = x + (v.size - w) / 2
y = y + (v.size - h) / 2
local hovering = mx > x and mx < x + w and my > y and my < y + h and dropdown.openDropdown == 0
local clicking = hovering and love.mouse.isDown(1)
love.graphics.setColor(0, 0, 0, 1)
if hovering or dragging then
love.graphics.setColor(0.2, 0.2, 0.3, 1)
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)
end
end
function self.mousepressed(x, y, m)
for i, v in ipairs(buttons) do
if x > v.x and x < v.x + v.size and y > v.y and y < v.y + v.size and m == 1 then
if v.func then v.func() end
end
end
end
return self

View file

@ -89,6 +89,7 @@ function self.createDropdowns()
options = {
'Preview Ease',
'Mix Eases',
'Multiply Eases',
'Create Ease'
},
name = 'mode'
@ -99,7 +100,7 @@ function self.createDropdowns()
local eases = skeys(ease.eases)
if d[dropdownId].selected == 1 then -- preview ease
if d[dropdownId].selected == modes.preview then -- preview ease
insertDropdown(d, {
x = outerpadding + dropdownWidth + padding,
y = outerpadding,
@ -115,7 +116,7 @@ function self.createDropdowns()
ease.ease = function(x)
return _e.f(x, param1[1], param1[2])
end
elseif d[dropdownId].selected == 2 then -- mix eases
elseif d[dropdownId].selected == modes.mix then -- mix eases
insertDropdown(d, {
x = outerpadding + dropdownWidth + padding,
y = outerpadding,
@ -142,7 +143,38 @@ function self.createDropdowns()
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 == 3 then -- create eases
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,
@ -182,6 +214,7 @@ function self.render()
love.graphics.setColor(0.06, 0.06, 0.12, 0.6)
if love.mouse.getX() > x and love.mouse.getX() < x + w and love.mouse.getY() > y and love.mouse.getY() < 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)
@ -294,7 +327,6 @@ function self.mousepressed(x, y, m)
if not clickedDropdown and m == 1 then
dropdownScrollCache[self.openDropdown] = dropdownScroll
self.openDropdown = 0
return true
end
end

View file

@ -46,7 +46,7 @@ end
function self.render()
local sw, sh = love.graphics.getDimensions()
if mode == 1 or mode == 2 then
if mode == modes.preview or mode == modes.mix or mode == modes.multiply then
local csize = 10 -- preview point size
local size = math.min((sw - outerpadding) - ((dropdown.kget('ease2') or dropdown.kget('ease1')).x + dropdownWidth + padding), sh - outerpadding * 2 - padding * 3 - csize)
@ -65,7 +65,7 @@ function self.render()
end
-- mixease point
if mode == 2 and slider.kget('mix') then
if mode == modes.mix and slider.kget('mix') then
love.graphics.setColor(1, 1, 1, 0.2 + self.touchtimer * 0.6)
love.graphics.line(x + margin + slider.kvalue('mix') * w, y, x + margin + slider.kvalue('mix') * w, y + h)
end

View file

@ -11,10 +11,20 @@ ease = require 'ease'
slider = require 'slider'
dropdown = require 'dropdown'
graph = require 'graph'
button = require 'button'
tooltips = require 'tooltips'
modes = {
preview = 1,
mix = 2,
multiply = 3,
create = 4
}
function createUI()
dropdown.createDropdowns()
slider.createSliders()
button.createButtons()
end
require 'util' -- exports into global table
@ -42,12 +52,17 @@ function love.update(dt)
graph.update(dt)
slider.update(dt)
dropdown.update(dt)
button.update(dt)
tooltips.update(dt)
end
function love.draw()
local sw, sh = love.graphics.getDimensions()
local mx, my = love.mouse.getPosition()
-- this is fine to do since all textures are already loaded with nearest
love.graphics.setDefaultFilter('linear', 'linear')
love.graphics.setLineWidth(2)
love.graphics.setColor(0.09, 0.09, 0.12, 1)
@ -58,15 +73,22 @@ function love.draw()
love.graphics.setColor(0.2, 0.2, 0.3, 1)
love.graphics.print('Box of Eases by oatmealine', outerpadding, sh - fontHeight - outerpadding)
tooltips.prerender()
button.render()
slider.render()
dropdown.render()
graph.render()
tooltips.render()
end
function love.mousepressed(x, y, m)
if dropdown.mousepressed(x, y, m) then return end
button.mousepressed(x, y, m)
end
function love.mousereleased(x, y, m)

View file

@ -38,7 +38,7 @@ function self.createSliders()
local s = {}
sliderId = 0
if mode == 2 then -- mix eases
if mode == modes.mix then -- mix eases
insertSlider(s, {
x = outerpadding,
y = outerpadding + fontHeight * 2.5 + padding,
@ -47,10 +47,12 @@ function self.createSliders()
max = 1,
default = 0.5,
name = 'mix',
displayname = 'Mix'
displayname = 'Mix',
snap = 0.01,
tooltip = 'The point at which the first ease snaps into the second one'
})
end
if mode == 1 or mode == 2 then -- bpm slider
if mode == modes.preview or mode == modes.mix or mode == modes.multiply then -- bpm slider
insertSlider(s, {
x = outerpadding,
y = love.graphics.getHeight() - outerpadding - fontHeight * 3 - padding,
@ -61,6 +63,7 @@ function self.createSliders()
name = 'bpm',
displayname = 'BPM',
snap = 1,
tooltip = 'The speed of the preview dot in Beats Per Minute'
})
end
@ -80,7 +83,8 @@ function self.createSliders()
max = v.max,
default = v.default,
name = ease1.name .. 'param1' .. i,
displayname = 'Parameter ' .. v.name
displayname = 'Parameter ' .. v.name,
snap = 0.001
})
end
end
@ -94,7 +98,8 @@ function self.createSliders()
max = v.max,
default = v.default,
name = ease2.name .. 'param2' .. i,
displayname = 'Parameter ' .. v.name
displayname = 'Parameter ' .. v.name,
snap = 0.001
})
end
end
@ -150,6 +155,7 @@ function self.render()
love.graphics.setColor(0, 0, 0, 1)
if hovering or dragging then
love.graphics.setColor(0.2, 0.2, 0.3, 1)
if v.tooltip then tooltips.show(v.tooltip) end
end
love.graphics.rectangle('fill', -ssize/2, -ssize/2, ssize, ssize)
love.graphics.setColor(1, 1, 1, 1)

72
tooltips.lua Normal file
View file

@ -0,0 +1,72 @@
local self = {}
local tooltipwidth = 0
local tooltipx = 0
local tooltipy = 0
local tooltipopen = false
local tooltiptext = ''
local tooltiptargetwidth = 0
local tooltipframe = false
function self.update(dt)
local mx, my = love.mouse.getPosition()
tooltipx = mix(tooltipx, mx, dt * 18)
tooltipy = mix(tooltipy, my, dt * 18)
tooltipwidth = mix(tooltipwidth, tooltiptargetwidth, dt * 12)
end
function self.prerender()
tooltipframe = false
end
function self.show(text)
if love.mouse.isDown(1) or dropdown.openDropdown ~= 0 then return end
tooltipframe = true
tooltiptext = text
tooltiptargetwidth = love.graphics.newText(love.graphics.getFont(), text):getWidth()
end
local function softlimit(x, f)
local sign = math.sign(x)
return sign * (f - (1.027 ^ (-10 * math.abs(x))) * f)
end
function self.render()
local mx, my = love.mouse.getPosition()
if not tooltipframe then
tooltiptargetwidth = 0
end
if tooltipwidth > 1 then
local a = math.min((tooltipwidth - 1) / 6, 1)
local x, y, w, h = mx + 8, my + 8, (tooltipwidth + 4 + margin), (fontHeight + margin)
local easiness = 3 -- hehe. magic numbers
local scale = 0.8
local sx, sy = ((w - softlimit(mx - tooltipx, w/easiness)/easiness) / w) * scale, ((h - softlimit(my - tooltipy, h/easiness)/easiness) / h) * scale
love.graphics.push()
love.graphics.translate(x, y)
love.graphics.scale(sx, sy)
love.graphics.setColor(0.2, 0.2, 0.3, a)
love.graphics.rectangle('fill', 0, 0, w, h)
love.graphics.setScissor(0, 0, math.max(mx + (tooltipwidth + 2 + margin/2 + 16) * sx, 0), love.graphics.getHeight())
love.graphics.setColor(1, 1, 1, a)
love.graphics.print(tooltiptext, 2 + margin/2, 2 + margin/2)
love.graphics.setScissor()
love.graphics.pop()
end
end
return self

View file

@ -1,3 +1,9 @@
function mix(x, y, a)
return x * (1 - a) + y * a
end
function math.sign(x)
if x < 0 then return -1 end
if x > 0 then return 1 end
return 0
end