139 lines
3.5 KiB
Lua
139 lines
3.5 KiB
Lua
--- A user interface element
|
|
-- @classmod yui.Widget
|
|
-- @copyright 2022, The DoubleFourteen Code Forge
|
|
-- @author Lorenzo Cogotti, Andrea Pasquini
|
|
|
|
--- Functions accepted by all the Widget classes
|
|
-- @field onEnter function is called when the widget gets entered (e.g. hovered by pointer/mouse cursor)
|
|
-- @field onHit function is called when the widget is hit (e.g. clicked)
|
|
-- @field onChange function is called when the widget value is changed (e.g. checkbox is ticked)
|
|
-- @field onLeave function is called when the widget is left (e.g. another widget acquires focus, mouse leaves the widget area)
|
|
-- @table WidgetCallbacks
|
|
|
|
--- Attributes accepted by all the Widget classes
|
|
-- @field w (number) widget width
|
|
-- @field h (number) widget height
|
|
-- @field color (@{yui.theme.ColorPalette|ColorPalette}) widget color
|
|
-- @table WidgetAttributes
|
|
local rectunion = require('lib.gear.rect').union
|
|
|
|
local Widget = {
|
|
__call = function(cls, args) return cls.new(args) end
|
|
}
|
|
Widget.__index = Widget
|
|
|
|
|
|
local function raise(widget)
|
|
local parent = widget.parent
|
|
|
|
-- A parent of a widget is necessarily a Layout
|
|
while parent ~= nil do
|
|
local stack = parent.stack
|
|
|
|
-- Move widget at the end of the stack, so it is rendered last.
|
|
for i,w in ipairs(stack) do
|
|
if w == widget then
|
|
table.remove(stack, i)
|
|
stack[#stack+1] = widget
|
|
break
|
|
end
|
|
end
|
|
|
|
-- Focus widget's container, if any
|
|
widget = parent
|
|
parent = widget.parent
|
|
end
|
|
end
|
|
|
|
function Widget:grabFocus()
|
|
local ui = self.ui
|
|
local focused = ui.focused
|
|
|
|
if focused == self then
|
|
return
|
|
end
|
|
if focused ~= nil then
|
|
-- Notify leave
|
|
focused.hovered = false
|
|
-- Widget specific focus loss
|
|
focused:loseFocus()
|
|
-- Event handler
|
|
focused:onLeave()
|
|
|
|
if focused.grabkeyboard then
|
|
-- If focused widget stole input,
|
|
-- then drop current input snapshot, since
|
|
-- those events should have been already
|
|
-- managed directly
|
|
ui.device:snapshot()
|
|
end
|
|
end
|
|
|
|
local wasHovered = self.hovered
|
|
|
|
self.hovered = true
|
|
if not wasHovered then
|
|
-- First time hovered, notify enter
|
|
self:gainFocus()
|
|
self:onEnter()
|
|
end
|
|
|
|
-- Raise widget
|
|
ui.focused = self
|
|
raise(self)
|
|
end
|
|
|
|
function Widget:isFocused()
|
|
return self.ui.focused == self
|
|
end
|
|
|
|
function Widget:recalculateBounds()
|
|
local widget = self.parent
|
|
while widget ~= nil do
|
|
local rx,ry,rw,rh = widget.x,widget.y,-1,-1
|
|
|
|
for _,w in ipairs(widget) do
|
|
rx,ry,rw,rh = rectunion(rx,ry,rw,rh, w.x,w.y,w.w,w.h)
|
|
end
|
|
|
|
widget.x = rx
|
|
widget.y = ry
|
|
widget.w = rw
|
|
widget.h = rh
|
|
|
|
widget = widget.parent
|
|
end
|
|
end
|
|
|
|
-- Helper for drawing
|
|
function Widget:colorForState()
|
|
if self.active then
|
|
return self.color.active
|
|
elseif self:isFocused() then
|
|
return self.color.hovered
|
|
else
|
|
return self.color.normal
|
|
end
|
|
end
|
|
|
|
-- NOP hooks for UI internal use
|
|
function Widget:loseFocus() end
|
|
function Widget:gainFocus() end
|
|
|
|
-- NOP event handlers, publicly overridable
|
|
|
|
function Widget:onHit() end
|
|
function Widget:onEnter() end
|
|
function Widget:onLeave() end
|
|
function Widget:onChange() end
|
|
|
|
-- NOP input event handlers
|
|
function Widget:onActionInput(action) end
|
|
function Widget:onPointerInput(x,y, clicked) end
|
|
|
|
-- NOP UI event handlers
|
|
function Widget:update(dt) end
|
|
function Widget:draw() end
|
|
|
|
return Widget
|