mirror of
https://github.com/Insality/druid
synced 2025-06-27 10:27:48 +02:00
342 lines
7.7 KiB
Lua
342 lines
7.7 KiB
Lua
local event = require("event.event")
|
|
local const = require("druid.const")
|
|
local helper = require("druid.helper")
|
|
local component = require("druid.component")
|
|
|
|
---@class druid.drag.style
|
|
---@field DRAG_DEADZONE number Distance in pixels to start dragging. Default: 10
|
|
---@field NO_USE_SCREEN_KOEF boolean If screen aspect ratio affects on drag values. Default: false
|
|
|
|
---@class druid.drag: druid.component
|
|
---@field node node
|
|
---@field on_touch_start event
|
|
---@field on_touch_end event
|
|
---@field on_drag_start event
|
|
---@field on_drag event
|
|
---@field on_drag_end event
|
|
---@field style druid.drag.style
|
|
---@field click_zone node|nil
|
|
---@field is_touch boolean
|
|
---@field is_drag boolean
|
|
---@field can_x boolean
|
|
---@field can_y boolean
|
|
---@field dx number
|
|
---@field dy number
|
|
---@field touch_id number
|
|
---@field x number
|
|
---@field y number
|
|
---@field screen_x number
|
|
---@field screen_y number
|
|
---@field touch_start_pos vector3
|
|
---@field private _is_enabled boolean
|
|
---@field private _x_koef number
|
|
---@field private _y_koef number
|
|
local M = component.create("drag", const.PRIORITY_INPUT_HIGH)
|
|
|
|
|
|
local function start_touch(self, touch)
|
|
self.is_touch = true
|
|
self.is_drag = false
|
|
|
|
self.touch_start_pos.x = touch.x
|
|
self.touch_start_pos.y = touch.y
|
|
|
|
self.x = touch.x
|
|
self.y = touch.y
|
|
|
|
self.screen_x = touch.screen_x
|
|
self.screen_y = touch.screen_y
|
|
|
|
self._scene_scale = helper.get_scene_scale(self.node)
|
|
|
|
self.on_touch_start:trigger(self:get_context(), touch)
|
|
end
|
|
|
|
|
|
local function end_touch(self, touch)
|
|
if self.is_drag then
|
|
self.on_drag_end:trigger(
|
|
self:get_context(),
|
|
self.x - self.touch_start_pos.x,
|
|
self.y - self.touch_start_pos.y,
|
|
touch
|
|
)
|
|
end
|
|
|
|
self.is_drag = false
|
|
if self.is_touch then
|
|
self.is_touch = false
|
|
self.on_touch_end:trigger(self:get_context(), touch)
|
|
end
|
|
self:reset_input_priority()
|
|
self.touch_id = 0
|
|
end
|
|
|
|
|
|
local function process_touch(self, touch)
|
|
if not self.can_x then
|
|
self.touch_start_pos.x = touch.x
|
|
end
|
|
|
|
if not self.can_y then
|
|
self.touch_start_pos.y = touch.y
|
|
end
|
|
|
|
local distance = helper.distance(touch.x, touch.y, self.touch_start_pos.x, self.touch_start_pos.y)
|
|
if not self.is_drag and distance >= self.style.DRAG_DEADZONE then
|
|
self.is_drag = true
|
|
self.on_drag_start:trigger(self:get_context(), touch)
|
|
self:set_input_priority(const.PRIORITY_INPUT_MAX, true)
|
|
end
|
|
end
|
|
|
|
|
|
---Return current touch action from action input data
|
|
---If touch_id stored - return exact this touch action
|
|
---@param action_id hash Action id from on_input
|
|
---@param action table Action from on_input
|
|
---@param touch_id number Touch id
|
|
---@return table|nil Touch action
|
|
local function find_touch(action_id, action, touch_id)
|
|
local act = helper.is_mobile() and const.ACTION_MULTITOUCH or const.ACTION_TOUCH
|
|
|
|
if action_id ~= act then
|
|
return
|
|
end
|
|
|
|
if action.touch then
|
|
local touch = action.touch
|
|
for i = 1, #touch do
|
|
if touch[i].id == touch_id then
|
|
return touch[i]
|
|
end
|
|
end
|
|
return touch[1]
|
|
else
|
|
return action
|
|
end
|
|
end
|
|
|
|
|
|
---Process on touch release. We should to find, if any other
|
|
---touches exists to switch to another touch.
|
|
---@param self druid.drag
|
|
---@param action_id hash Action id from on_input
|
|
---@param action table Action from on_input
|
|
local function on_touch_release(self, action_id, action)
|
|
if #action.touch >= 2 then
|
|
-- Find next unpressed touch
|
|
local next_touch
|
|
for i = 1, #action.touch do
|
|
if not action.touch[i].released then
|
|
next_touch = action.touch[i]
|
|
break
|
|
end
|
|
end
|
|
|
|
if next_touch then
|
|
self.x = next_touch.x
|
|
self.y = next_touch.y
|
|
self.touch_id = next_touch.id
|
|
else
|
|
end_touch(self)
|
|
end
|
|
elseif #action.touch == 1 then
|
|
end_touch(self)
|
|
end
|
|
end
|
|
|
|
|
|
---@param style druid.drag.style
|
|
function M:on_style_change(style)
|
|
self.style = {
|
|
DRAG_DEADZONE = style.DRAG_DEADZONE or 10,
|
|
NO_USE_SCREEN_KOEF = style.NO_USE_SCREEN_KOEF or false,
|
|
}
|
|
end
|
|
|
|
|
|
---Drag constructor
|
|
---@param node_or_node_id node|string
|
|
---@param on_drag_callback function
|
|
function M:init(node_or_node_id, on_drag_callback)
|
|
self.druid = self:get_druid()
|
|
self.node = self:get_node(node_or_node_id)
|
|
self.hover = self.druid:new_hover(self.node)
|
|
|
|
self.dx = 0
|
|
self.dy = 0
|
|
self.touch_id = 0
|
|
self.x = 0
|
|
self.y = 0
|
|
self.screen_x = 0
|
|
self.screen_y = 0
|
|
self.is_touch = false
|
|
self.is_drag = false
|
|
self.touch_start_pos = vmath.vector3(0)
|
|
self._is_enabled = true
|
|
|
|
self.can_x = true
|
|
self.can_y = true
|
|
|
|
self._scene_scale = helper.get_scene_scale(self.node)
|
|
|
|
self.click_zone = nil
|
|
self.on_touch_start = event.create()
|
|
self.on_touch_end = event.create()
|
|
self.on_drag_start = event.create()
|
|
self.on_drag = event.create(on_drag_callback)
|
|
self.on_drag_end = event.create()
|
|
|
|
self:on_window_resized()
|
|
self:set_drag_cursors(true)
|
|
end
|
|
|
|
|
|
---Set Drag component enabled state.
|
|
---@param is_enabled boolean
|
|
function M:set_drag_cursors(is_enabled)
|
|
if defos and is_enabled then
|
|
self.hover.style.ON_HOVER_CURSOR = defos.CURSOR_CROSSHAIR
|
|
self.hover.style.ON_MOUSE_HOVER_CURSOR = defos.CURSOR_HAND
|
|
else
|
|
self.hover.style.ON_HOVER_CURSOR = nil
|
|
self.hover.style.ON_MOUSE_HOVER_CURSOR = nil
|
|
end
|
|
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
|
|
|
|
|
|
function M:on_window_resized()
|
|
local x_koef, y_koef = helper.get_screen_aspect_koef()
|
|
self._x_koef = x_koef
|
|
self._y_koef = y_koef
|
|
self._scene_scale = helper.get_scene_scale(self.node)
|
|
end
|
|
|
|
|
|
function M:on_input_interrupt()
|
|
if self.is_drag or self.is_touch then
|
|
end_touch(self)
|
|
end
|
|
end
|
|
|
|
|
|
---@local
|
|
---@param action_id hash
|
|
---@param action table
|
|
function M:on_input(action_id, action)
|
|
if action_id ~= const.ACTION_TOUCH and action_id ~= const.ACTION_MULTITOUCH then
|
|
return false
|
|
end
|
|
|
|
if not self._is_enabled or 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 and not self.is_drag then
|
|
end_touch(self)
|
|
return false
|
|
end
|
|
|
|
|
|
local touch = find_touch(action_id, action, self.touch_id)
|
|
if not touch then
|
|
return false
|
|
end
|
|
|
|
if touch.id then
|
|
self.touch_id = touch.id
|
|
end
|
|
|
|
self.dx = 0
|
|
self.dy = 0
|
|
|
|
if touch.pressed and not self.is_touch then
|
|
start_touch(self, touch)
|
|
end
|
|
|
|
if touch.released and self.is_touch then
|
|
if action.touch then
|
|
-- Mobile
|
|
on_touch_release(self, action_id, action)
|
|
else
|
|
-- PC
|
|
end_touch(self, touch)
|
|
end
|
|
end
|
|
|
|
if self.is_touch then
|
|
process_touch(self, touch)
|
|
end
|
|
|
|
local touch_modified = find_touch(action_id, action, self.touch_id)
|
|
if touch_modified and self.is_drag then
|
|
self.dx = touch_modified.x - self.x
|
|
self.dy = touch_modified.y - self.y
|
|
end
|
|
|
|
if touch_modified then
|
|
self.x = touch_modified.x
|
|
self.y = touch_modified.y
|
|
|
|
self.screen_x = touch_modified.screen_x
|
|
self.screen_y = touch_modified.screen_y
|
|
end
|
|
|
|
if self.is_drag and (self.dx ~= 0 or self.dy ~= 0) then
|
|
local x_koef, y_koef = self._x_koef, self._y_koef
|
|
if self.style.NO_USE_SCREEN_KOEF then
|
|
x_koef, y_koef = 1, 1
|
|
end
|
|
|
|
self.on_drag:trigger(self:get_context(),
|
|
self.dx * x_koef / self._scene_scale.x,
|
|
self.dy * y_koef / self._scene_scale.y,
|
|
(self.x - self.touch_start_pos.x) * x_koef / self._scene_scale.x,
|
|
(self.y - self.touch_start_pos.y) * y_koef / self._scene_scale.y, touch_modified)
|
|
end
|
|
|
|
return self.is_drag
|
|
end
|
|
|
|
|
|
---Set Drag click zone
|
|
---@param node node|string|nil
|
|
---@return druid.drag self Current instance
|
|
function M:set_click_zone(node)
|
|
self.click_zone = node and self:get_node(node) or nil
|
|
|
|
return self
|
|
end
|
|
|
|
|
|
---Set Drag component enabled state.
|
|
---@param is_enabled boolean
|
|
---@return druid.drag self Current instance
|
|
function M:set_enabled(is_enabled)
|
|
self._is_enabled = is_enabled
|
|
|
|
return self
|
|
end
|
|
|
|
|
|
---Check if Drag component is capture input
|
|
---@return boolean
|
|
function M:is_enabled()
|
|
return self._is_enabled
|
|
end
|
|
|
|
|
|
return M
|