Merge pull request #222 from Insality/tests

Initial tests for Druid GUI
This commit is contained in:
Maksim Tuprikov 2023-02-07 19:05:48 +02:00 committed by GitHub
commit 2733c88655
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 758 additions and 49 deletions

View File

@ -45,8 +45,12 @@ globals = {
"debug", "debug",
"timer", "timer",
"window", "window",
"buffer", "buffer",
"resource", "resource",
"defos", "defos",
"html5", "html5",
"describe",
"before",
"after",
"it",
} }

View File

@ -445,4 +445,4 @@ And yeah, the new **Druid** logo is here!
- **#189** [System] Add optional flag to `component:set_input_priority` to mark it as temporary. It will reset to default input priority after the `component:reset_input_priority` - **#189** [System] Add optional flag to `component:set_input_priority` to mark it as temporary. It will reset to default input priority after the `component:reset_input_priority`
- **#204** [System] Fix: wrong code example link, if open example from direct URL - **#204** [System] Fix: wrong code example link, if open example from direct URL
- **#202** [System] Enabled stencil check to true by default. To disable this, use `druid.no_stencil_check` in game.project settings - **#202** [System] Enabled stencil check to true by default. To disable this, use `druid.no_stencil_check` in game.project settings
- [Examples] Add layout, layout fit, progres bar, data list + component examples - [Examples] Add layout, layout fit, progress bar, data list + component examples

View File

@ -145,7 +145,7 @@ local function on_button_release(self)
self.can_action = false self.can_action = false
local time = socket.gettime() local time = socket.gettime()
local is_long_click = (time - self.last_pressed_time) > self.style.LONGTAP_TIME local is_long_click = (time - self.last_pressed_time) >= self.style.LONGTAP_TIME
is_long_click = is_long_click and self.on_long_click:is_exist() is_long_click = is_long_click and self.on_long_click:is_exist()
local is_double_click = (time - self.last_released_time) < self.style.DOUBLETAP_TIME local is_double_click = (time - self.last_released_time) < self.style.DOUBLETAP_TIME
@ -248,6 +248,10 @@ function Button.on_input(self, action_id, action)
return false return false
end end
if not self:is_enabled() then
return false
end
local is_pick = true local is_pick = true
local is_key_trigger = (action_id == self.key_trigger) local is_key_trigger = (action_id == self.key_trigger)
if not is_key_trigger then if not is_key_trigger then
@ -280,7 +284,7 @@ function Button.on_input(self, action_id, action)
-- While hold button, repeat rate pick from input.repeat_interval -- While hold button, repeat rate pick from input.repeat_interval
if action.repeated then if action.repeated then
if not self.disabled and self.on_repeated_click:is_exist() and self.can_action then if self.on_repeated_click:is_exist() and self.can_action then
on_button_repeated_click(self) on_button_repeated_click(self)
return true return true
end end
@ -290,7 +294,7 @@ function Button.on_input(self, action_id, action)
return on_button_release(self) return on_button_release(self)
end end
if not self.disabled and self.can_action and self.on_long_click:is_exist() then if self.can_action and self.on_long_click:is_exist() then
local press_time = socket.gettime() - self.last_pressed_time local press_time = socket.gettime() - self.last_pressed_time
if self.style.AUTOHOLD_TRIGGER <= press_time then if self.style.AUTOHOLD_TRIGGER <= press_time then

View File

@ -63,6 +63,7 @@ local function start_touch(self, touch)
self.x = touch.x self.x = touch.x
self.y = touch.y self.y = touch.y
self._scene_scale = helper.get_scene_scale(self.node)
self.on_touch_start:trigger(self:get_context()) self.on_touch_start:trigger(self:get_context())
end end
@ -155,9 +156,11 @@ end
-- or create your own style -- or create your own style
-- @table style -- @table style
-- @tfield[opt=10] number DRAG_DEADZONE Distance in pixels to start dragging -- @tfield[opt=10] number DRAG_DEADZONE Distance in pixels to start dragging
-- @tfield[opt=false] boolean NO_USE_SCREEN_KOEF If screen aspect ratio affects on drag values
function Drag.on_style_change(self, style) function Drag.on_style_change(self, style)
self.style = {} self.style = {}
self.style.DRAG_DEADZONE = style.DRAG_DEADZONE or 10 self.style.DRAG_DEADZONE = style.DRAG_DEADZONE or 10
self.style.NO_USE_SCREEN_KOEF = style.NO_USE_SCREEN_KOEF or false
end end
@ -181,6 +184,8 @@ function Drag.init(self, node, on_drag_callback)
self.can_x = true self.can_x = true
self.can_y = true self.can_y = true
self._scene_scale = helper.get_scene_scale(self.node)
self.click_zone = nil self.click_zone = nil
self.on_touch_start = Event() self.on_touch_start = Event()
self.on_touch_end = Event() self.on_touch_end = Event()
@ -206,6 +211,7 @@ function Drag.on_window_resized(self)
local x_koef, y_koef = helper.get_screen_aspect_koef() local x_koef, y_koef = helper.get_screen_aspect_koef()
self._x_koef = x_koef self._x_koef = x_koef
self._y_koef = y_koef self._y_koef = y_koef
self._scene_scale = helper.get_scene_scale(self.node)
end end
@ -229,7 +235,6 @@ function Drag.on_input(self, action_id, action)
if self.click_zone then if self.click_zone then
is_pick = is_pick and gui.pick_node(self.click_zone, action.x, action.y) is_pick = is_pick and gui.pick_node(self.click_zone, action.x, action.y)
end end
if not is_pick and not self.is_drag then if not is_pick and not self.is_drag then
end_touch(self) end_touch(self)
return false return false
@ -278,11 +283,16 @@ function Drag.on_input(self, action_id, action)
end end
if self.is_drag then if self.is_drag 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.on_drag:trigger(self:get_context(),
self.dx * self._x_koef, self.dx * x_koef / self._scene_scale.x,
self.dy * self._y_koef, self.dy * y_koef / self._scene_scale.y,
(self.x - self.touch_start_pos.x) * self._x_koef, (self.x - self.touch_start_pos.x) * x_koef / self._scene_scale.x,
(self.y - self.touch_start_pos.y) * self._y_koef) (self.y - self.touch_start_pos.y) * y_koef / self._scene_scale.y)
end end
return self.is_drag return self.is_drag

View File

@ -101,6 +101,15 @@ function Hover.set_hover(self, state)
end end
end end
--- Return current hover state. True if touch action was on the node at current time
-- @tparam Hover self @{Hover}
-- @treturn bool The current hovered state
function Hover.is_hovered(self)
return self._is_hovered
end
--- Set mouse hover state --- Set mouse hover state
-- @tparam Hover self @{Hover} -- @tparam Hover self @{Hover}
-- @tparam bool state The mouse hover state -- @tparam bool state The mouse hover state
@ -112,6 +121,14 @@ function Hover.set_mouse_hover(self, state)
end end
--- Return current hover state. True if nil action_id (usually desktop mouse) was on the node at current time
-- @tparam Hover self @{Hover}
-- @treturn bool The current hovered state
function Hover.is_mouse_hovered(self)
return self._is_mouse_hovered
end
--- Strict hover click area. Useful for --- Strict hover click area. Useful for
-- no click events outside stencil node -- no click events outside stencil node
-- @tparam Hover self @{Hover} -- @tparam Hover self @{Hover}

View File

@ -41,6 +41,8 @@ function Layout.init(self, node, mode, on_size_changed_callback)
self._max_gui_upscale = nil self._max_gui_upscale = nil
self._fit_node = nil self._fit_node = nil
self._anchors = {}
self.mode = mode or const.LAYOUT_MODE.FIT self.mode = mode or const.LAYOUT_MODE.FIT
self.on_size_changed = Event(on_size_changed_callback) self.on_size_changed = Event(on_size_changed_callback)

View File

@ -64,7 +64,7 @@ function Slider.init(self, node, end_pos, callback)
self.start_pos = gui.get_position(self.node) self.start_pos = gui.get_position(self.node)
self.pos = gui.get_position(self.node) self.pos = gui.get_position(self.node)
self.target_pos = self.pos self.target_pos = vmath.vector3(self.pos)
self.end_pos = end_pos self.end_pos = end_pos
self.dist = self.end_pos - self.start_pos self.dist = self.end_pos - self.start_pos
@ -72,6 +72,7 @@ function Slider.init(self, node, end_pos, callback)
self.value = 0 self.value = 0
self.on_change_value = Event(callback) self.on_change_value = Event(callback)
self:on_window_resized()
assert(self.dist.x == 0 or self.dist.y == 0, "Slider for now can be only vertical or horizontal") assert(self.dist.x == 0 or self.dist.y == 0, "Slider for now can be only vertical or horizontal")
end end
@ -82,6 +83,14 @@ function Slider.on_layout_change(self)
end end
function Slider.on_window_resized(self)
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 Slider.on_input(self, action_id, action) function Slider.on_input(self, action_id, action)
if action_id ~= const.ACTION_TOUCH then if action_id ~= const.ACTION_TOUCH then
return false return false
@ -90,15 +99,17 @@ function Slider.on_input(self, action_id, action)
if gui.pick_node(self.node, action.x, action.y) then if gui.pick_node(self.node, action.x, action.y) then
if action.pressed then if action.pressed then
self.pos = gui.get_position(self.node) self.pos = gui.get_position(self.node)
self._scene_scale = helper.get_scene_scale(self.node)
self.is_drag = true self.is_drag = true
end end
end end
if not self.is_drag and self._input_node and gui.pick_node(self._input_node, action.x, action.y) then if not self.is_drag and self._input_node and gui.pick_node(self._input_node, action.x, action.y) then
if action.pressed and gui.screen_to_local then if action.pressed and gui.screen_to_local then
self._scene_scale = helper.get_scene_scale(self.node)
self.pos = gui.screen_to_local(self.node, vmath.vector3(action.screen_x, action.screen_y, 0)) self.pos = gui.screen_to_local(self.node, vmath.vector3(action.screen_x, action.screen_y, 0))
self.pos.x = helper.clamp(self.pos.x, self.start_pos.x, self.end_pos.x) self.pos.x = helper.clamp(self.pos.x / self._scene_scale.x, self.start_pos.x, self.end_pos.x)
self.pos.y = helper.clamp(self.pos.y, self.start_pos.y, self.end_pos.y) self.pos.y = helper.clamp(self.pos.y / self._scene_scale.y, self.start_pos.y, self.end_pos.y)
gui.set_position(self.node, self.pos) gui.set_position(self.node, self.pos)
self.is_drag = true self.is_drag = true
@ -107,8 +118,8 @@ function Slider.on_input(self, action_id, action)
if self.is_drag and not action.pressed then if self.is_drag and not action.pressed then
-- move -- move
self.pos.x = self.pos.x + action.dx self.pos.x = self.pos.x + action.dx * self._x_koef / self._scene_scale.x
self.pos.y = self.pos.y + action.dy self.pos.y = self.pos.y + action.dy * self._y_koef / self._scene_scale.y
local prev_x = self.target_pos.x local prev_x = self.target_pos.x
local prev_y = self.target_pos.y local prev_y = self.target_pos.y

View File

@ -173,6 +173,21 @@ function M.contains(t, value)
end end
--- Get text metric from gui node. Replacement of previous gui.get_text_metrics_from_node function
-- @tparam Node text_node
-- @treturn table {width, height, max_ascent, max_descent}
function M.get_text_metrics_from_node(text_node)
local font_name = gui.get_font(text_node)
local font = gui.get_font_resource(font_name)
return resource.get_text_metrics(font, gui.get_text(text_node), {
width = gui.get_size(text_node).x,
leading = gui.get_leading(text_node),
tracking = gui.get_tracking(text_node),
line_break = gui.get_line_break(text_node),
})
end
--- Check if node is enabled in gui hierarchy. --- Check if node is enabled in gui hierarchy.
-- Return false, if node or any his parent is disabled -- Return false, if node or any his parent is disabled
-- @function helper.is_enabled -- @function helper.is_enabled
@ -190,8 +205,8 @@ function M.is_enabled(node)
end end
--- Return current node scene scale --- Get cumulative parent's node scale
-- @function helper.is_enabled -- @function helper.get_scene_scale
-- @tparam node node Gui node -- @tparam node node Gui node
-- @tparam bool include_passed_node_scale True if add current node scale to result -- @tparam bool include_passed_node_scale True if add current node scale to result
-- @treturn vector3 The scene node scale -- @treturn vector3 The scene node scale

View File

@ -56,6 +56,7 @@ M["button"] = {
M["drag"] = { M["drag"] = {
DRAG_DEADZONE = 10, -- Size in pixels of drag deadzone DRAG_DEADZONE = 10, -- Size in pixels of drag deadzone
NO_USE_SCREEN_KOEF = false,
} }

View File

@ -49,16 +49,17 @@ local static_grid = require("druid.base.static_grid")
local swipe = require("druid.base.swipe") local swipe = require("druid.base.swipe")
local text = require("druid.base.text") local text = require("druid.base.text")
local checkbox = require("druid.extended.checkbox") -- To use this components, you should register them first
local checkbox_group = require("druid.extended.checkbox_group") -- local checkbox = require("druid.extended.checkbox")
local dynamic_grid = require("druid.extended.dynamic_grid") -- local checkbox_group = require("druid.extended.checkbox_group")
local input = require("druid.extended.input") -- local dynamic_grid = require("druid.extended.dynamic_grid")
local lang_text = require("druid.extended.lang_text") -- local input = require("druid.extended.input")
local progress = require("druid.extended.progress") -- local lang_text = require("druid.extended.lang_text")
local radio_group = require("druid.extended.radio_group") -- local progress = require("druid.extended.progress")
local slider = require("druid.extended.slider") -- local radio_group = require("druid.extended.radio_group")
local timer_component = require("druid.extended.timer") -- local slider = require("druid.extended.slider")
local data_list = require("druid.extended.data_list") -- local timer_component = require("druid.extended.timer")
-- local data_list = require("druid.extended.data_list")
local DruidInstance = class("druid.druid_instance") local DruidInstance = class("druid.druid_instance")
@ -645,8 +646,7 @@ end
-- @tparam node parent The gui node parent, where items will be placed -- @tparam node parent The gui node parent, where items will be placed
-- @treturn DynamicGrid grid component -- @treturn DynamicGrid grid component
function DruidInstance.new_dynamic_grid(self, parent) function DruidInstance.new_dynamic_grid(self, parent)
-- return helper.extended_component("dynamic_grid") return helper.extended_component("dynamic_grid")
return DruidInstance.new(self, dynamic_grid, parent)
end end
@ -657,8 +657,7 @@ end
-- @tparam bool no_adjust If true, will not correct text size -- @tparam bool no_adjust If true, will not correct text size
-- @treturn LangText lang_text component -- @treturn LangText lang_text component
function DruidInstance.new_lang_text(self, node, locale_id, no_adjust) function DruidInstance.new_lang_text(self, node, locale_id, no_adjust)
-- return helper.extended_component("lang_text") return helper.extended_component("lang_text")
return DruidInstance.new(self, lang_text, node, locale_id, no_adjust)
end end
@ -669,8 +668,7 @@ end
-- @tparam[opt] function callback On slider change callback -- @tparam[opt] function callback On slider change callback
-- @treturn Slider slider component -- @treturn Slider slider component
function DruidInstance.new_slider(self, node, end_pos, callback) function DruidInstance.new_slider(self, node, end_pos, callback)
-- return helper.extended_component("slider") return helper.extended_component("slider")
return DruidInstance.new(self, slider, node, end_pos, callback)
end end
@ -682,8 +680,7 @@ end
-- @tparam[opt=false] boolean initial_state The initial state of checkbox, default - false -- @tparam[opt=false] boolean initial_state The initial state of checkbox, default - false
-- @treturn Checkbox checkbox component -- @treturn Checkbox checkbox component
function DruidInstance.new_checkbox(self, node, callback, click_node, initial_state) function DruidInstance.new_checkbox(self, node, callback, click_node, initial_state)
-- return helper.extended_component("checkbox") return helper.extended_component("checkbox")
return DruidInstance.new(self, checkbox, node, callback, click_node, initial_state)
end end
@ -694,8 +691,7 @@ end
-- @tparam[opt] number keyboard_type Gui keyboard type for input field -- @tparam[opt] number keyboard_type Gui keyboard type for input field
-- @treturn Input input component -- @treturn Input input component
function DruidInstance.new_input(self, click_node, text_node, keyboard_type) function DruidInstance.new_input(self, click_node, text_node, keyboard_type)
-- return helper.extended_component("input") return helper.extended_component("input")
return DruidInstance.new(self, input, click_node, text_node, keyboard_type)
end end
@ -706,8 +702,7 @@ end
-- @tparam[opt=node] node[] click_nodes Array of trigger nodes, by default equals to nodes -- @tparam[opt=node] node[] click_nodes Array of trigger nodes, by default equals to nodes
-- @treturn CheckboxGroup checkbox_group component -- @treturn CheckboxGroup checkbox_group component
function DruidInstance.new_checkbox_group(self, nodes, callback, click_nodes) function DruidInstance.new_checkbox_group(self, nodes, callback, click_nodes)
-- return helper.extended_component("checkbox_group") return helper.extended_component("checkbox_group")
return DruidInstance.new(self, checkbox_group, nodes, callback, click_nodes)
end end
@ -718,8 +713,7 @@ end
-- @tparam function create_function The create function callback(self, data, index, data_list). Function should return (node, [component]) -- @tparam function create_function The create function callback(self, data, index, data_list). Function should return (node, [component])
-- @treturn DataList data_list component -- @treturn DataList data_list component
function DruidInstance.new_data_list(self, druid_scroll, druid_grid, create_function) function DruidInstance.new_data_list(self, druid_scroll, druid_grid, create_function)
-- return helper.extended_component("data_list") return helper.extended_component("data_list")
return DruidInstance.new(self, data_list, druid_scroll, druid_grid, create_function)
end end
@ -730,8 +724,7 @@ end
-- @tparam[opt=node] node[] click_nodes Array of trigger nodes, by default equals to nodes -- @tparam[opt=node] node[] click_nodes Array of trigger nodes, by default equals to nodes
-- @treturn RadioGroup radio_group component -- @treturn RadioGroup radio_group component
function DruidInstance.new_radio_group(self, nodes, callback, click_nodes) function DruidInstance.new_radio_group(self, nodes, callback, click_nodes)
-- return helper.extended_component("radio_group") return helper.extended_component("radio_group")
return DruidInstance.new(self, radio_group, nodes, callback, click_nodes)
end end
@ -743,8 +736,7 @@ end
-- @tparam[opt] function callback Function on timer end -- @tparam[opt] function callback Function on timer end
-- @treturn Timer timer component -- @treturn Timer timer component
function DruidInstance.new_timer(self, node, seconds_from, seconds_to, callback) function DruidInstance.new_timer(self, node, seconds_from, seconds_to, callback)
-- return helper.extended_component("timer") return helper.extended_component("timer")
return DruidInstance.new(self, timer_component, node, seconds_from, seconds_to, callback)
end end
@ -755,8 +747,7 @@ end
-- @tparam[opt=1] number init_value Initial value of progress bar -- @tparam[opt=1] number init_value Initial value of progress bar
-- @treturn Progress progress component -- @treturn Progress progress component
function DruidInstance.new_progress(self, node, key, init_value) function DruidInstance.new_progress(self, node, key, init_value)
-- return helper.extended_component("progress") return helper.extended_component("progress")
return DruidInstance.new(self, progress, node, key, init_value)
end end

View File

@ -3,6 +3,17 @@ local druid = require("druid.druid")
local monarch = require("monarch.monarch") local monarch = require("monarch.monarch")
local default_style = require("druid.styles.default.style") local default_style = require("druid.styles.default.style")
local checkbox = require("druid.extended.checkbox")
local checkbox_group = require("druid.extended.checkbox_group")
local dynamic_grid = require("druid.extended.dynamic_grid")
local input = require("druid.extended.input")
local lang_text = require("druid.extended.lang_text")
local progress = require("druid.extended.progress")
local radio_group = require("druid.extended.radio_group")
local slider = require("druid.extended.slider")
local timer_component = require("druid.extended.timer")
local data_list = require("druid.extended.data_list")
local cache_path = sys.get_save_file("druid", "cache") local cache_path = sys.get_save_file("druid", "cache")
@ -222,12 +233,28 @@ local function check_loading(self)
end end
local function register_druid_extended_components(self)
druid.register("checkbox", checkbox)
druid.register("checkbox_group", checkbox_group)
druid.register("dynamic_grid", dynamic_grid)
druid.register("input", input)
druid.register("lang_text", lang_text)
druid.register("progress", progress)
druid.register("radio_group", radio_group)
druid.register("slider", slider)
druid.register("timer", timer_component)
druid.register("data_list", data_list)
end
function init(self) function init(self)
-- Main lobby have more render priority (top panel) -- Main lobby have more render priority (top panel)
gui.set_render_order(10) gui.set_render_order(10)
window.set_listener(on_window_callback) window.set_listener(on_window_callback)
druid.set_default_style(default_style) druid.set_default_style(default_style)
register_druid_extended_components(self)
self.druid = druid.new(self) self.druid = druid.new(self)
self.cache = sys.load(cache_path) or {} self.cache = sys.load(cache_path) or {}

View File

@ -0,0 +1,62 @@
local M = {}
function M.click_pressed(x, y)
return hash("touch"), {
pressed = true,
x = x,
y = y,
}
end
function M.click_released(x, y)
return hash("touch"), {
released = true,
x = x,
y = y,
}
end
function M.key_pressed(key_id)
return hash(key_id), {
pressed = true
}
end
function M.key_released(key_id)
return hash(key_id), {
released = true
}
end
function M.click_repeated(x, y)
return hash("touch"), {
repeated = true,
x = x,
y = y,
}
end
function M.input_empty(x, y)
return hash("touch"), {
x = x,
y = y,
}
end
function M.input_empty_action_nil(x, y)
return nil, {
x = x,
y = y,
}
end
return M

View File

@ -0,0 +1,19 @@
local mock = require("deftest.mock.mock")
local M = {}
-- Userdata type instead of script self
function M.get_context()
return vmath.vector()
end
-- Callback for return value from function
function M.get_function(callback)
local listener = {}
listener.callback = function(...) if callback then return callback(...) end end
mock.mock(listener)
return function(...) return listener.callback(...) end, listener.callback
end
return M

View File

@ -4,7 +4,7 @@ embedded_instances {
id: "test" id: "test"
data: "components {\n" data: "components {\n"
" id: \"test\"\n" " id: \"test\"\n"
" component: \"/test/test.script\"\n" " component: \"/test/test.gui\"\n"
" position {\n" " position {\n"
" x: 0.0\n" " x: 0.0\n"
" y: 0.0\n" " y: 0.0\n"
@ -16,6 +16,8 @@ embedded_instances {
" z: 0.0\n" " z: 0.0\n"
" w: 1.0\n" " w: 1.0\n"
" }\n" " }\n"
" property_decls {\n"
" }\n"
"}\n" "}\n"
"" ""
position { position {

10
test/test.gui Normal file
View File

@ -0,0 +1,10 @@
script: "/test/test.gui_script"
background_color {
x: 0.0
y: 0.0
z: 0.0
w: 0.0
}
material: "/builtins/materials/gui.material"
adjust_reference: ADJUST_REFERENCE_PARENT
max_nodes: 512

View File

@ -1,7 +1,10 @@
local deftest = require "deftest.deftest" local deftest = require("deftest.deftest")
local tests = { local tests = {
-- Test list -- Test list
require("test.tests.test_button"),
require("test.tests.test_hover"),
require("test.tests.test_drag"),
} }

266
test/tests/test_button.lua Normal file
View File

@ -0,0 +1,266 @@
local mock_gui = require("deftest.mock.gui")
local mock_time = require("deftest.mock.time")
local mock_input = require("test.helper.mock_input")
local test_helper = require("test.helper.test_helper")
local druid_system = require("druid.druid")
return function()
local druid = nil
local context = test_helper.get_context()
describe("Button Component", function()
before(function()
mock_gui.mock()
mock_time.mock()
mock_time.set(60)
druid = druid_system.new(context)
end)
after(function()
mock_gui.unmock()
mock_time.unmock()
druid:final(context)
druid = nil
end)
it("Should do usual click", function()
local button = mock_gui.add_box("button", 0, 0, 100, 50)
local button_params = {}
local on_click, on_click_mock = test_helper.get_function()
local instance = druid:new_button(button, on_click, button_params)
local is_clicked_pressed = druid:on_input(mock_input.click_pressed(10, 10))
local is_clicked_released = druid:on_input(mock_input.click_released(20, 10))
assert(is_clicked_pressed)
assert(is_clicked_released)
assert(on_click_mock.calls == 1)
assert(on_click_mock.params[1] == context)
assert(on_click_mock.params[2] == button_params)
assert(on_click_mock.params[3] == instance)
end)
it("Should do long click if exists", function()
local button = mock_gui.add_box("button", 0, 0, 100, 50)
local button_params = {}
local on_click, on_click_mock = test_helper.get_function()
local on_long_click, on_long_click_mock = test_helper.get_function()
local instance = druid:new_button(button, on_click, button_params)
instance.on_long_click:subscribe(on_long_click)
druid:on_input(mock_input.click_pressed(10, 10))
mock_time.elapse(0.3)
druid:on_input(mock_input.click_released(20, 10))
assert(on_click_mock.calls == 1)
assert(on_long_click_mock.calls == 0)
druid:on_input(mock_input.click_pressed(10, 10))
mock_time.elapse(0.5)
druid:on_input(mock_input.click_released(20, 10))
assert(on_click_mock.calls == 1)
assert(on_long_click_mock.calls == 1)
assert(on_long_click_mock.params[1] == context)
assert(on_long_click_mock.params[2] == button_params)
assert(on_long_click_mock.params[3] == instance)
end)
it("Should do not long click if not exists", function()
local button = mock_gui.add_box("button", 0, 0, 100, 50)
local button_params = {}
local on_click, on_click_mock = test_helper.get_function()
druid:new_button(button, on_click, button_params)
druid:on_input(mock_input.click_pressed(10, 10))
mock_time.elapse(0.5)
druid:on_input(mock_input.click_released(20, 10))
assert(on_click_mock.calls == 1)
end)
it("Should do double click if exists", function()
local button = mock_gui.add_box("button", 0, 0, 100, 50)
local button_params = {}
local on_click, on_click_mock = test_helper.get_function()
local on_double_click, on_double_click_mock = test_helper.get_function()
local instance = druid:new_button(button, on_click, button_params)
instance.on_double_click:subscribe(on_double_click)
druid:on_input(mock_input.click_pressed(10, 10))
druid:on_input(mock_input.click_released(20, 10))
mock_time.elapse(0.1)
druid:on_input(mock_input.click_pressed(10, 10))
druid:on_input(mock_input.click_released(20, 10))
assert(on_click_mock.calls == 1)
assert(on_double_click_mock.calls == 1)
assert(on_double_click_mock.params[1] == context)
assert(on_double_click_mock.params[2] == button_params)
assert(on_double_click_mock.params[3] == instance)
mock_time.elapse(1)
druid:on_input(mock_input.click_pressed(10, 10))
druid:on_input(mock_input.click_released(20, 10))
mock_time.elapse(0.5) -- The double click should not register, big time between clicks
druid:on_input(mock_input.click_pressed(10, 10))
druid:on_input(mock_input.click_released(20, 10))
assert(on_click_mock.calls == 3)
assert(on_double_click_mock.calls == 1)
end)
it("Should do not double click if not exists", function()
local button = mock_gui.add_box("button", 0, 0, 100, 50)
local button_params = {}
local on_click, on_click_mock = test_helper.get_function()
druid:new_button(button, on_click, button_params)
druid:on_input(mock_input.click_pressed(10, 10))
druid:on_input(mock_input.click_released(20, 10))
mock_time.elapse(0.1)
druid:on_input(mock_input.click_pressed(10, 10))
druid:on_input(mock_input.click_released(20, 10))
assert(on_click_mock.calls == 2)
end)
it("Should do hold click if exists", function()
local button = mock_gui.add_box("button", 0, 0, 100, 50)
local button_params = {}
local on_click, on_click_mock = test_helper.get_function()
local on_long_click, on_long_click_mock = test_helper.get_function()
local on_hold_callback, on_hold_callback_mock = test_helper.get_function()
local instance = druid:new_button(button, on_click, button_params)
instance.on_long_click:subscribe(on_long_click) -- long click required for hold callback
instance.on_hold_callback:subscribe(on_hold_callback)
druid:on_input(mock_input.click_pressed(10, 10))
mock_time.elapse(0.5) -- time between hold treshold and autorelease hold time
druid:on_input(mock_input.click_repeated(10, 10))
assert(on_click_mock.calls == 0)
assert(on_hold_callback_mock.calls == 1)
assert(on_hold_callback_mock.params[1] == context)
assert(on_hold_callback_mock.params[2] == button_params)
assert(on_hold_callback_mock.params[3] == instance)
druid:on_input(mock_input.click_released(10, 10))
assert(on_click_mock.calls == 0)
assert(on_long_click_mock.calls == 1)
end)
it("Should do click outside if exists", function()
local button = mock_gui.add_box("button", 0, 0, 100, 50)
local button_params = {}
local on_click = test_helper.get_function()
local on_click_outside, on_click_outside_mock = test_helper.get_function()
local instance = druid:new_button(button, on_click, button_params)
instance.on_click_outside:subscribe(on_click_outside)
druid:on_input(mock_input.click_pressed(-10, 10))
druid:on_input(mock_input.click_released(-10, 10))
assert(on_click_outside_mock.calls == 1)
mock_time.elapse(1)
druid:on_input(mock_input.click_pressed(10, 10))
druid:on_input(mock_input.click_released(-10, 10))
assert(on_click_outside_mock.calls == 2)
end)
it("Should not click if mouse was outside while clicking", function()
local button = mock_gui.add_box("button", 0, 0, 100, 50)
local button_params = {}
local on_click, on_click_mock = test_helper.get_function()
druid:new_button(button, on_click, button_params)
druid:on_input(mock_input.click_pressed(10, 10))
druid:on_input(mock_input.input_empty(-10, 10))
druid:on_input(mock_input.click_released(20, 10))
assert(on_click_mock.calls == 0)
end)
it("Should work with check function", function()
local button = mock_gui.add_box("button", 0, 0, 100, 50)
local button_params = {}
local on_click, on_click_mock = test_helper.get_function()
local instance = druid:new_button(button, on_click, button_params)
local check_function, check_function_mock = test_helper.get_function(function() return false end)
local failure_function, failure_function_mock = test_helper.get_function()
instance:set_check_function(check_function, failure_function)
druid:on_input(mock_input.click_pressed(10, 10))
druid:on_input(mock_input.click_released(20, 10))
assert(on_click_mock.calls == 0)
assert(check_function_mock.calls == 1)
assert(failure_function_mock.calls == 1)
local check_true_function, check_function_true_mock = test_helper.get_function(function() return true end)
instance:set_check_function(check_true_function, failure_function)
mock_time.elapse(1)
druid:on_input(mock_input.click_pressed(10, 10))
druid:on_input(mock_input.click_released(20, 10))
assert(on_click_mock.calls == 1)
assert(check_function_true_mock.calls == 1)
assert(failure_function_mock.calls == 1)
end)
it("Should have key trigger", function()
local button = mock_gui.add_box("button", 0, 0, 100, 50)
local button_params = {}
local on_click, on_click_mock = test_helper.get_function()
local instance = druid:new_button(button, on_click, button_params)
instance:set_key_trigger("key_a")
druid:on_input(mock_input.key_pressed("key_a"))
druid:on_input(mock_input.key_released("key_a"))
assert(on_click_mock.calls == 1)
assert(instance:get_key_trigger() == hash("key_a"))
end)
it("Should work with click zone", function()
local button = mock_gui.add_box("button", 0, 0, 100, 50)
local zone = mock_gui.add_box("zone", 25, 25, 25, 25)
local button_params = {}
local on_click, on_click_mock = test_helper.get_function()
local instance = druid:new_button(button, on_click, button_params)
instance:set_click_zone(zone)
druid:on_input(mock_input.click_pressed(10, 10))
druid:on_input(mock_input.click_released(10, 10))
assert(on_click_mock.calls == 0)
druid:on_input(mock_input.click_pressed(25, 25))
druid:on_input(mock_input.click_released(25, 25))
assert(on_click_mock.calls == 1)
end)
it("Should work with set_enabled", function()
local button = mock_gui.add_box("button", 0, 0, 100, 50)
local button_params = {}
local on_click, on_click_mock = test_helper.get_function()
local instance = druid:new_button(button, on_click, button_params)
instance:set_enabled(false)
local is_clicked_pressed = druid:on_input(mock_input.click_pressed(10, 10))
local is_clicked_released = druid:on_input(mock_input.click_released(10, 10))
assert(is_clicked_pressed == false)
assert(is_clicked_released == false)
assert(on_click_mock.calls == 0)
assert(instance:is_enabled() == false)
instance:set_enabled(true)
local is_clicked_pressed2 = druid:on_input(mock_input.click_pressed(10, 10))
assert(is_clicked_pressed2 == true)
local is_clicked_released2 = druid:on_input(mock_input.click_released(10, 10))
assert(is_clicked_released2 == true)
assert(on_click_mock.calls == 1)
assert(instance:is_enabled() == true)
end)
end)
end

134
test/tests/test_drag.lua Normal file
View File

@ -0,0 +1,134 @@
local mock_gui = require "deftest.mock.gui"
local mock_time = require("deftest.mock.time")
local mock_input = require("test.helper.mock_input")
local test_helper = require("test.helper.test_helper")
local druid_system = require("druid.druid")
return function()
local druid = nil
local context = test_helper.get_context()
local function create_drag_instance(on_drag)
local button = mock_gui.add_box("button", 0, 0, 20, 20)
local instance = druid:new_drag(button, on_drag)
instance.style.NO_USE_SCREEN_KOEF = true
instance.style.DRAG_DEADZONE = 4
return instance
end
describe("Drag component", function()
before(function()
mock_gui.mock()
mock_time.mock()
mock_time.set(60)
druid = druid_system.new(context)
end)
after(function()
mock_gui.unmock()
mock_time.unmock()
druid:final(context)
druid = nil
end)
it("Should call drag callback on node", function()
local on_drag, on_drag_mock = test_helper.get_function()
local instance = create_drag_instance(on_drag)
druid:on_input(mock_input.click_pressed(10, 10))
assert(instance.is_touch == true)
druid:on_input(mock_input.input_empty(12, 10))
assert(on_drag_mock.calls == 0)
druid:on_input(mock_input.input_empty(14, 10))
assert(on_drag_mock.calls == 1)
assert(on_drag_mock.params[2] == 2) -- From the last input dx
assert(on_drag_mock.params[3] == 0)
assert(on_drag_mock.params[4] == 4) -- Total X from drag start point
assert(on_drag_mock.params[5] == 0)
end)
it("Should call all included events", function()
local on_drag, on_drag_mock = test_helper.get_function()
local instance = create_drag_instance(on_drag)
local on_touch_start, on_touch_start_mock = test_helper.get_function()
instance.on_touch_start:subscribe(on_touch_start)
local on_touch_end, on_touch_end_mock = test_helper.get_function()
instance.on_touch_end:subscribe(on_touch_end)
local on_drag_start, on_drag_start_mock = test_helper.get_function()
instance.on_drag_start:subscribe(on_drag_start)
local on_drag_end, on_drag_end_mock = test_helper.get_function()
instance.on_drag_end:subscribe(on_drag_end)
assert(on_touch_start_mock.calls == 0)
druid:on_input(mock_input.click_pressed(10, 10))
assert(on_touch_start_mock.calls == 1)
assert(on_touch_end_mock.calls == 0)
druid:on_input(mock_input.click_released(12, 10))
assert(on_touch_end_mock.calls == 1)
druid:on_input(mock_input.click_pressed(10, 10))
assert(on_drag_start_mock.calls == 0)
druid:on_input(mock_input.input_empty(15, 10))
assert(on_drag_start_mock.calls == 1)
assert(on_drag_mock.calls == 1)
assert(on_drag_end_mock.calls == 0)
druid:on_input(mock_input.click_released(15, 10))
assert(on_drag_end_mock.calls == 1)
end)
it("Should work with set_enabled", function()
local on_drag, on_drag_mock = test_helper.get_function()
local instance = create_drag_instance(on_drag)
instance:set_enabled(false)
assert(instance:is_enabled() == false)
druid:on_input(mock_input.click_pressed(10, 10))
assert(instance.is_touch == false)
druid:on_input(mock_input.input_empty(12, 10))
assert(on_drag_mock.calls == 0)
druid:on_input(mock_input.input_empty(15, 10))
assert(on_drag_mock.calls == 0)
instance:set_enabled(true)
assert(instance:is_enabled() == true)
druid:on_input(mock_input.click_pressed(10, 10))
assert(instance.is_touch == true)
druid:on_input(mock_input.input_empty(12, 10))
assert(on_drag_mock.calls == 0)
druid:on_input(mock_input.input_empty(15, 10))
assert(on_drag_mock.calls == 1)
end)
it("Should work with click zone", function()
local on_drag, on_drag_mock = test_helper.get_function()
local instance = create_drag_instance(on_drag)
local zone = mock_gui.add_box("zone", 10, 10, 10, 10)
instance:set_click_zone(zone)
druid:on_input(mock_input.click_pressed(5, 5))
assert(instance.is_touch == false)
druid:on_input(mock_input.input_empty(10, 5))
assert(on_drag_mock.calls == 0)
druid:on_input(mock_input.input_empty(15, 10))
assert(on_drag_mock.calls == 0)
druid:on_input(mock_input.click_pressed(15, 15))
assert(instance.is_touch == true)
druid:on_input(mock_input.input_empty(20, 15))
assert(on_drag_mock.calls == 1)
end)
end)
end

102
test/tests/test_hover.lua Normal file
View File

@ -0,0 +1,102 @@
local mock_gui = require "deftest.mock.gui"
local mock_time = require("deftest.mock.time")
local mock_input = require("test.helper.mock_input")
local test_helper = require("test.helper.test_helper")
local druid_system = require("druid.druid")
return function()
local druid = nil
local context = test_helper.get_context()
describe("Hover component", function()
before(function()
mock_gui.mock()
mock_time.mock()
mock_time.set(60)
druid = druid_system.new(context)
end)
after(function()
mock_gui.unmock()
mock_time.unmock()
druid:final(context)
druid = nil
end)
it("Should fire callback on touch hover and unhover", function()
local button = mock_gui.add_box("button", 0, 0, 100, 50)
local is_hovered = false
local on_hover, on_hover_mock = test_helper.get_function(function(_, state)
is_hovered = state
end)
local instance = druid:new_hover(button, on_hover)
druid:on_input(mock_input.input_empty(10, 10))
assert(is_hovered == true)
assert(instance:is_hovered() == true)
assert(instance:is_mouse_hovered() == false)
druid:on_input(mock_input.input_empty(-10, 10))
assert(is_hovered == false)
assert(instance:is_hovered() == false)
assert(instance:is_mouse_hovered() == false)
end)
it("Should fire callback on mouse hover and unhover", function()
local button = mock_gui.add_box("button", 0, 0, 100, 50)
local is_hovered = false
local on_hover, on_hover_mock = test_helper.get_function(function(_, state)
is_hovered = state
end)
local instance = druid:new_hover(button)
instance.on_mouse_hover:subscribe(on_hover)
druid:on_input(mock_input.input_empty_action_nil(10, 10))
assert(is_hovered == true)
assert(instance:is_hovered() == false)
assert(instance:is_mouse_hovered() == true)
druid:on_input(mock_input.input_empty_action_nil(-10, 10))
assert(is_hovered == false)
assert(instance:is_hovered() == false)
assert(instance:is_mouse_hovered() == false)
end)
it("Should work with click zone", function()
local button = mock_gui.add_box("button", 0, 0, 100, 50)
local zone = mock_gui.add_box("zone", 25, 25, 25, 25)
local on_hover, on_hover_mock = test_helper.get_function()
local instance = druid:new_hover(button, on_hover)
instance:set_click_zone(zone)
druid:on_input(mock_input.input_empty(10, 10))
assert(instance:is_hovered() == false)
druid:on_input(mock_input.input_empty(25, 25))
assert(instance:is_hovered() == true)
druid:on_input(mock_input.input_empty(24, 24))
assert(instance:is_hovered() == false)
end)
it("Should have set_enabled function", function()
local button = mock_gui.add_box("button", 0, 0, 100, 50)
local on_hover, on_hover_mock = test_helper.get_function()
local instance = druid:new_hover(button, on_hover)
druid:on_input(mock_input.input_empty(10, 10))
assert(instance:is_hovered() == true)
instance:set_enabled(false)
assert(instance:is_enabled() == false)
assert(instance:is_hovered() == false)
druid:on_input(mock_input.input_empty(12, 12))
assert(instance:is_hovered() == false)
instance:set_enabled(true)
druid:on_input(mock_input.input_empty(12, 12))
assert(instance:is_enabled() == true)
assert(instance:is_hovered() == true)
druid:on_input(mock_input.input_empty(-10, 10))
assert(instance:is_hovered() == false)
end)
end)
end

View File

@ -0,0 +1,29 @@
local mock_gui = require "deftest.mock.gui"
local mock_time = require("deftest.mock.time")
local mock_input = require("test.helper.mock_input")
local test_helper = require("test.helper.test_helper")
local druid_system = require("druid.druid")
return function()
local druid = nil
local context = test_helper.get_context()
describe("Template component", function()
before(function()
mock_gui.mock()
mock_time.mock()
mock_time.set(60)
druid = druid_system.new(context)
end)
after(function()
mock_gui.unmock()
mock_time.unmock()
druid:final(context)
druid = nil
end)
it("Should do something right", function()
assert(2 == 1 + 1)
end)
end)
end