2025-03-18 21:23:30 +02:00

159 lines
4.1 KiB
Lua

local event = require("event.event")
local const = require("druid.const")
local helper = require("druid.helper")
local component = require("druid.component")
---@class druid.swipe.style
---@field SWIPE_TIME number|nil Maximum time for swipe trigger. Default: 0.4
---@field SWIPE_THRESHOLD number|nil Minimum distance for swipe trigger. Default: 50
---@field SWIPE_TRIGGER_ON_MOVE boolean|nil If true, trigger on swipe moving, not only release action. Default: false
---The component to manage swipe events over a node
---@class druid.swipe: druid.component
---@field node node The node to manage the swipe
---@field on_swipe event fun(context, side, dist, dt) The event triggered when a swipe is detected
---@field style druid.swipe.style The style of the swipe
---@field click_zone node The click zone of the swipe
---@field private _trigger_on_move boolean True if the swipe should trigger on move
---@field private _swipe_start_time number The time the swipe started
---@field private _start_pos vector3 The start position of the swipe
---@field private _is_enabled boolean True if the swipe is enabled
---@field private _is_mobile boolean True if the swipe is on a mobile device
local M = component.create("swipe")
---@param node_or_node_id node|string
---@param on_swipe_callback function
function M:init(node_or_node_id, on_swipe_callback)
self._trigger_on_move = self.style.SWIPE_TRIGGER_ON_MOVE
self.node = self:get_node(node_or_node_id)
self._swipe_start_time = 0
self._start_pos = vmath.vector3(0)
self.click_zone = nil
self.on_swipe = event.create(on_swipe_callback)
end
function M:on_late_init()
if not self.click_zone then
local stencil_node = helper.get_closest_stencil_node(self.node)
if stencil_node then
self:set_click_zone(stencil_node)
end
end
end
---@param style druid.swipe.style
function M:on_style_change(style)
self.style = {
SWIPE_TIME = style.SWIPE_TIME or 0.4,
SWIPE_THRESHOLD = style.SWIPE_THRESHOLD or 50,
SWIPE_TRIGGER_ON_MOVE = style.SWIPE_TRIGGER_ON_MOVE or false,
}
end
---@param action_id hash The action id
---@param action action The action table
---@return boolean is_consumed True if the input was consumed
function M:on_input(action_id, action)
if action_id ~= const.ACTION_TOUCH then
return false
end
if not gui.is_enabled(self.node, true) then
return false
end
local is_pick = helper.pick_node(self.node, action.x, action.y, self.click_zone)
if not is_pick then
self:_reset_swipe()
return false
end
if self._swipe_start_time ~= 0 and (self._trigger_on_move or action.released) then
self:_check_swipe(action)
end
if action.pressed then
self:_start_swipe(action)
end
if action.released then
self:_reset_swipe()
end
return true
end
function M:on_input_interrupt()
self:_reset_swipe()
end
---Set the click zone for the swipe, useful for restricting events outside stencil node
---@param zone node|string|nil Gui node
function M:set_click_zone(zone)
if not zone then
self.click_zone = nil
return
end
self.click_zone = self:get_node(zone)
end
---Start swipe event
---@param action action The action table
function M:_start_swipe(action)
self._swipe_start_time = socket.gettime()
self._start_pos.x = action.x
self._start_pos.y = action.y
end
---Reset swipe event
function M:_reset_swipe()
self._swipe_start_time = 0
end
---Check swipe event
---@param self druid.swipe
---@param action action
function M:_check_swipe(action)
local dx = action.x - self._start_pos.x
local dy = action.y - self._start_pos.y
local dist = helper.distance(self._start_pos.x, self._start_pos.y, action.x, action.y)
local delta_time = socket.gettime() - self._swipe_start_time
local is_swipe = self.style.SWIPE_THRESHOLD <= dist and delta_time <= self.style.SWIPE_TIME
if is_swipe then
local is_x_swipe = math.abs(dx) >= math.abs(dy)
local swipe_side = "undefined"
if is_x_swipe and dx > 0 then
swipe_side = "right"
end
if is_x_swipe and dx < 0 then
swipe_side = "left"
end
if not is_x_swipe and dy > 0 then
swipe_side = "up"
end
if not is_x_swipe and dy < 0 then
swipe_side = "down"
end
self.on_swipe:trigger(self:get_context(), swipe_side, dist, delta_time)
self:_reset_swipe()
end
end
return M