All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
167 lines
4.7 KiB
Lua
167 lines
4.7 KiB
Lua
--- @section Focus
|
|
|
|
local FOCUS_DEFAULT_SPEED = 5
|
|
|
|
local active = false
|
|
local closing = false
|
|
local driven = false
|
|
local center_x = 0
|
|
local center_y = 0
|
|
local radius = 0
|
|
local speed = FOCUS_DEFAULT_SPEED
|
|
local on_complete = nil
|
|
local driven_initial_r = 0
|
|
local driven_max_r = 0
|
|
|
|
local function max_radius(cx, cy)
|
|
local dx = math.max(cx, Config.screen.width - cx)
|
|
local dy = math.max(cy, Config.screen.height - cy)
|
|
return math.sqrt(dx * dx + dy * dy)
|
|
end
|
|
|
|
--- Starts a focus overlay that reveals content through an expanding circle.
|
|
--- @within Focus
|
|
--- @param cx number The x-coordinate of the circle center.
|
|
--- @param cy number The y-coordinate of the circle center.
|
|
--- @param[opt] params table Optional parameters: `speed` (number) expansion rate in pixels/frame, `initial_radius` (number) starting radius in pixels (default 0), `on_complete` (function) callback when overlay disperses.
|
|
function Focus.start(cx, cy, params)
|
|
params = params or {}
|
|
active = true
|
|
closing = false
|
|
driven = false
|
|
center_x = cx
|
|
center_y = cy
|
|
radius = params.initial_radius or 0
|
|
speed = params.speed or FOCUS_DEFAULT_SPEED
|
|
on_complete = params.on_complete
|
|
end
|
|
|
|
--- Starts a closing focus overlay that hides content by shrinking the visible circle.
|
|
--- @within Focus
|
|
--- @param cx number The x-coordinate of the circle center.
|
|
--- @param cy number The y-coordinate of the circle center.
|
|
--- @param[opt] params table Optional parameters: `speed` (number) shrink rate in pixels/frame, `on_complete` (function) callback when screen is fully covered.
|
|
function Focus.close(cx, cy, params)
|
|
params = params or {}
|
|
active = true
|
|
closing = true
|
|
driven = false
|
|
center_x = cx
|
|
center_y = cy
|
|
radius = max_radius(cx, cy)
|
|
speed = params.speed or FOCUS_DEFAULT_SPEED
|
|
on_complete = params.on_complete
|
|
end
|
|
|
|
--- Starts a driven focus overlay whose radius is controlled externally via Focus.set_percentage().
|
|
--- The radius maps linearly from initial_radius (at 0%) to the screen corner distance (at 100%).
|
|
--- @within Focus
|
|
--- @param cx number The x-coordinate of the circle center.
|
|
--- @param cy number The y-coordinate of the circle center.
|
|
--- @param[opt] params table Optional parameters: `initial_radius` (number) radius at 0% (default 0).
|
|
function Focus.start_driven(cx, cy, params)
|
|
params = params or {}
|
|
active = true
|
|
closing = false
|
|
driven = true
|
|
center_x = cx
|
|
center_y = cy
|
|
driven_initial_r = params.initial_radius or 0
|
|
driven_max_r = max_radius(cx, cy)
|
|
radius = driven_initial_r
|
|
on_complete = nil
|
|
end
|
|
|
|
--- Sets the visible radius as a percentage of the full screen extent.
|
|
--- Only has effect when the overlay is in driven mode (started via Focus.start_driven).
|
|
--- @within Focus
|
|
--- @param pct number A value from 0 to 1 (0 = initial_radius, 1 = full screen).
|
|
function Focus.set_percentage(pct)
|
|
if not driven then return end
|
|
radius = driven_initial_r + pct * (driven_max_r - driven_initial_r)
|
|
end
|
|
|
|
--- Checks whether the focus overlay is currently active.
|
|
--- @within Focus
|
|
--- @return boolean Whether the focus overlay is active.
|
|
function Focus.is_active()
|
|
return active
|
|
end
|
|
|
|
--- Stops the focus overlay immediately.
|
|
--- @within Focus
|
|
function Focus.stop()
|
|
active = false
|
|
closing = false
|
|
driven = false
|
|
radius = 0
|
|
on_complete = nil
|
|
end
|
|
|
|
--- Updates the focus overlay animation. No-op in driven mode.
|
|
--- @within Focus
|
|
function Focus.update()
|
|
if not active then return end
|
|
if driven then return end
|
|
|
|
if closing then
|
|
radius = radius - speed
|
|
if radius <= 0 then
|
|
local cb = on_complete
|
|
Focus.stop()
|
|
if cb then cb() end
|
|
end
|
|
else
|
|
radius = radius + speed
|
|
if radius >= max_radius(center_x, center_y) then
|
|
local cb = on_complete
|
|
Focus.stop()
|
|
if cb then cb() end
|
|
end
|
|
end
|
|
end
|
|
|
|
--- Draws the focus overlay (black screen with circular cutout).
|
|
--- Must be called after all other drawing to appear on top of every visual layer.
|
|
--- @within Focus
|
|
function Focus.draw()
|
|
if not active then return end
|
|
|
|
local cx = center_x
|
|
local cy = center_y
|
|
local r = radius
|
|
local w = Config.screen.width
|
|
local h = Config.screen.height
|
|
local color = Config.colors.black
|
|
|
|
if closing and r <= 0 then
|
|
rect(0, 0, w, h, color)
|
|
return
|
|
end
|
|
|
|
local top = math.max(0, math.floor(cy - r))
|
|
local bottom = math.min(h - 1, math.ceil(cy + r))
|
|
|
|
if top > 0 then
|
|
rect(0, 0, w, top, color)
|
|
end
|
|
|
|
if bottom < h - 1 then
|
|
rect(0, bottom + 1, w, h - bottom - 1, color)
|
|
end
|
|
|
|
for y = top, bottom do
|
|
local dy = y - cy
|
|
local half_w = math.sqrt(math.max(0, r * r - dy * dy))
|
|
local left = math.floor(cx - half_w)
|
|
local right = math.ceil(cx + half_w)
|
|
|
|
if left > 0 then
|
|
rect(0, y, left, 1, color)
|
|
end
|
|
if right < w then
|
|
rect(right, y, w - right, 1, color)
|
|
end
|
|
end
|
|
end
|