dotfiles/dot_config/awesome/lib/animation/init.lua
2024-04-14 18:41:15 -07:00

225 lines
5.6 KiB
Lua

-------------------------------------------
-- @author https://github.com/Kasper24
-- @copyright 2021-2022 Kasper24
-------------------------------------------
local GLib = require("lgi").GLib
local gobject = require("gears.object")
local gtable = require("gears.table")
local subscribable = require("lib.animation.subscribable")
local tween = require("lib.animation.tween")
local ipairs = ipairs
local table = table
local pairs = pairs
local animation_manager = {}
animation_manager.easing = {
linear = "linear",
inQuad = "inQuad",
outQuad = "outQuad",
inOutQuad = "inOutQuad",
outInQuad = "outInQuad",
inCubic = "inCubic",
outCubic = "outCubic",
inOutCubic = "inOutCubic",
outInCubic = "outInCubic",
inQuart = "inQuart",
outQuart = "outQuart",
inOutQuart = "inOutQuart",
outInQuart = "outInQuart",
inQuint = "inQuint",
outQuint = "outQuint",
inOutQuint = "inOutQuint",
outInQuint = "outInQuint",
inSine = "inSine",
outSine = "outSine",
inOutSine = "inOutSine",
outInSine = "outInSine",
inExpo = "inExpo",
outExpo = "outExpo",
inOutExpo = "inOutExpo",
outInExpo = "outInExpo",
inCirc = "inCirc",
outCirc = "outCirc",
inOutCirc = "inOutCirc",
outInCirc = "outInCirc",
inElastic = "inElastic",
outElastic = "outElastic",
inOutElastic = "inOutElastic",
outInElastic = "outInElastic",
inBack = "inBack",
outBack = "outBack",
inOutBack = "inOutBack",
outInBack = "outInBack",
inBounce = "inBounce",
outBounce = "outBounce",
inOutBounce = "inOutBounce",
outInBounce = "outInBounce",
}
local animation = {}
local instance = nil
local ANIMATION_FRAME_DELAY = 5
local function second_to_micro(sec)
return sec * 1000000
end
function animation:start(args)
args = args or {}
-- Awestoer/Rubbto compatibility
-- I'd rather this always be a table, but Awestoer/Rubbto
-- except the :set() method to have 1 number value parameter
-- used to set the target
local is_table = type(args) == "table"
local initial = is_table and (args.pos or self.pos) or self.pos
local subject = is_table and (args.subject or self.subject) or self.subject
local target = is_table and (args.target or self.target) or args
local duration = is_table and (args.duration or self.duration) or self.duration
local easing = is_table and (args.easing or self.easing) or self.easing
duration = self._private.anim_manager._private.instant == true and 0.01 or duration
if self.tween == nil or self.reset_on_stop == true then
self.tween = tween.new({
initial = initial,
subject = subject,
target = target,
duration = second_to_micro(duration),
easing = easing,
})
end
if self._private.anim_manager._private.animations[self.index] == nil then
table.insert(self._private.anim_manager._private.animations, self)
end
self.state = true
self.last_elapsed = GLib.get_monotonic_time()
self.started:fire()
self:emit_signal("started")
end
function animation:set(args)
self:start(args)
self:emit_signal("set")
end
function animation:stop()
self.state = false
self:emit_signal("stopped")
end
function animation:abort()
animation:stop()
self:emit_signal("aborted")
end
function animation:initial()
return self._private.initial
end
function animation_manager:set_instant(value)
self._private.instant = value
end
function animation_manager:new(args)
args = args or {}
args.pos = args.pos or 0
args.subject = args.subject or nil
args.target = args.target or nil
args.duration = args.duration or 0
args.easing = args.easing or nil
args.loop = args.loop or false
args.signals = args.signals or {}
args.update = args.update or nil
args.reset_on_stop = args.reset_on_stop == nil and true or args.reset_on_stop
-- Awestoer/Rubbto compatibility
args.subscribed = args.subscribed or nil
local ret = subscribable()
ret.started = subscribable()
ret.ended = subscribable()
if args.subscribed ~= nil then
ret:subscribe(args.subscribed)
end
for sig, sigfun in pairs(args.signals) do
ret:connect_signal(sig, sigfun)
end
if args.update ~= nil then
ret:connect_signal("update", args.update)
end
gtable.crush(ret, args, true)
gtable.crush(ret, animation, true)
ret._private = {}
ret._private.anim_manager = self
ret._private.initial = args.pos
return ret
end
local function new()
local ret = gobject({})
gtable.crush(ret, animation_manager, true)
ret._private = {}
ret._private.animations = {}
ret._private.instant = false
GLib.timeout_add(GLib.PRIORITY_DEFAULT, ANIMATION_FRAME_DELAY, function()
---@diagnostic disable-next-line: redefined-local
for index, animation in ipairs(ret._private.animations) do
if animation.state == true then
-- compute delta time
local time = GLib.get_monotonic_time()
local delta = time - animation.last_elapsed
animation.last_elapsed = time
-- If pos is true, the animation has ended
local pos = animation.tween:update(delta)
if pos == true then
-- Loop the animation, don't end it.
-- Useful for widgets like the spinning cicle
if animation.loop == true then
animation.tween:reset()
else
-- Snap to end
animation.pos = animation.tween.target
animation:fire(animation.pos)
animation:emit_signal("update", animation.pos)
animation.state = false
animation.ended:fire(pos)
table.remove(ret._private.animations, index)
animation:emit_signal("ended", animation.pos)
end
-- Animation in process, keep updating
else
animation.pos = pos
animation:fire(animation.pos)
animation:emit_signal("update", animation.pos)
end
else
table.remove(ret._private.animations, index)
end
end
-- call again the function after cooldown
return true
end)
return ret
end
if not instance then
instance = new()
end
return instance