diff --git a/button.lua b/button.lua new file mode 100644 index 0000000..74bb8de --- /dev/null +++ b/button.lua @@ -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 \ No newline at end of file diff --git a/dropdown.lua b/dropdown.lua index ddd79c8..5be76ca 100644 --- a/dropdown.lua +++ b/dropdown.lua @@ -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 diff --git a/graph.lua b/graph.lua index a65818c..a0165e1 100644 --- a/graph.lua +++ b/graph.lua @@ -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 diff --git a/main.lua b/main.lua index ddd2940..c540901 100644 --- a/main.lua +++ b/main.lua @@ -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) diff --git a/slider.lua b/slider.lua index cd9828d..94543f9 100644 --- a/slider.lua +++ b/slider.lua @@ -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) diff --git a/tooltips.lua b/tooltips.lua new file mode 100644 index 0000000..1c98e7c --- /dev/null +++ b/tooltips.lua @@ -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 \ No newline at end of file diff --git a/util.lua b/util.lua index b6e5ccf..2e6c9a2 100644 --- a/util.lua +++ b/util.lua @@ -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 \ No newline at end of file