From 00e984efdcf4295ca46718e847243cf799bb537e Mon Sep 17 00:00:00 2001 From: Alexey Gulev Date: Mon, 25 Mar 2019 21:11:38 +0100 Subject: [PATCH] First dirty commit of old code --- .gitignore | 10 + README.md | 12 + druid/components/andr_back_btn.lua | 11 + druid/components/button.lua | 119 ++++++++ druid/components/counter.lua | 67 ++++ druid/components/flying_particles.lua | 52 ++++ druid/components/image.lua | 46 +++ druid/components/pie_progress_bar.lua | 51 ++++ druid/components/progress_bar.lua | 93 ++++++ druid/components/scrolling_box.lua | 237 +++++++++++++++ druid/components/spine_anim.lua | 62 ++++ druid/components/tab_page.lua | 92 ++++++ druid/components/tabs_container.lua | 50 +++ druid/components/text_field.lua | 42 +++ druid/components/timer.lua | 53 ++++ druid/input.lua | 25 ++ druid/ui_animate.lua | 169 +++++++++++ druid/ui_factory.lua | 421 ++++++++++++++++++++++++++ druid/ui_helper.lua | 16 + game.project | 16 + input/game.input_binding | 4 + main/main.collection | 2 + 22 files changed, 1650 insertions(+) create mode 100644 .gitignore create mode 100644 druid/components/andr_back_btn.lua create mode 100644 druid/components/button.lua create mode 100644 druid/components/counter.lua create mode 100644 druid/components/flying_particles.lua create mode 100644 druid/components/image.lua create mode 100644 druid/components/pie_progress_bar.lua create mode 100644 druid/components/progress_bar.lua create mode 100644 druid/components/scrolling_box.lua create mode 100644 druid/components/spine_anim.lua create mode 100644 druid/components/tab_page.lua create mode 100644 druid/components/tabs_container.lua create mode 100644 druid/components/text_field.lua create mode 100644 druid/components/timer.lua create mode 100644 druid/input.lua create mode 100644 druid/ui_animate.lua create mode 100644 druid/ui_factory.lua create mode 100644 druid/ui_helper.lua create mode 100644 game.project create mode 100644 input/game.input_binding create mode 100644 main/main.collection diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a32d29f --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +/.internal +/build +.externalToolBuilders +.DS_Store +Thumbs.db +.lock-wscript +*.pyc +.project +.cproject +builtins \ No newline at end of file diff --git a/README.md b/README.md index f28cfc1..dc028cf 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,14 @@ # druid Defold UI library + +This project was created from the "empty" project template. + +The settings in ["game.project"](defold://open?path=/game.project) are all the default. A bootstrap empty ["main.collection"](defold://open?path=/main/main.collection) is included. + +Check out [the documentation pages](https://defold.com/learn) for examples, tutorials, manuals and API docs. + +If you run into trouble, help is available in [our forum](https://forum.defold.com). + +Happy Defolding! + +--- \ No newline at end of file diff --git a/druid/components/andr_back_btn.lua b/druid/components/andr_back_btn.lua new file mode 100644 index 0000000..a237675 --- /dev/null +++ b/druid/components/andr_back_btn.lua @@ -0,0 +1,11 @@ +local M = {} + +--- input handler +-- @param action_id - input action id +-- @param action - input action +function M.on_input(instance, action_id, action) + instance.callback(instance.parent.parent) + return true +end + +return M diff --git a/druid/components/button.lua b/druid/components/button.lua new file mode 100644 index 0000000..357fd5f --- /dev/null +++ b/druid/components/button.lua @@ -0,0 +1,119 @@ +local M = {} + +local ui_animate = require "modules.ui.ui_animate" + +M.DEFAULT_SCALE_CHANGE = vmath.vector3(-0.05, - 0.1, 1) +M.DEFAULT_POS_CHANGE = vmath.vector3(0, - 10, 0) +M.DEFAULT_MOVE_SPEED = 5 +M.DEFAULT_ALPHA_DOWN = 0.8 +M.DEFAULT_TIME_ANIM = 0.1 +M.DEFAULT_DEACTIVATE_COLOR = vmath.vector4(0, 0, 0, 0) +M.DEFAULT_DEACTIVATE_SCALE = vmath.vector3(0.8, 0.9, 1) +M.DEFAULT_ACTIVATE_SCALE = vmath.vector3(1, 1, 1) +M.DEFAUL_ACTIVATION_TIME = 0.2 + +--- Set text to text field +-- @param action_id - input action id +-- @param action - input action +function M.on_input(instance, action_id, action) + if gui.is_enabled(instance.node) and gui.pick_node(instance.node, action.x, action.y) then + if not instance.disabled then + instance.tap_anim(instance) + return true + else + instance.sound_disable() + return false + end + end + return false +end + +function M.tap_scale_animation(instance) + ui_animate.scale_to(instance, instance.anim_node, instance.scale_to, + function() + if instance.back_anim then + instance.back_anim(instance) + end + instance.sound() + instance.callback(instance.parent.parent, instance.params, instance) + end + ) +end + +function M.back_scale_animation(instance) + ui_animate.scale_to(instance, instance.anim_node, instance.scale_from) +end + +function M.tap_tab_animation(instance, force) + ui_animate.alpha(instance, instance.anim_node, M.DEFAULT_ALPHA_DOWN, nil, M.DEFAULT_TIME_ANIM) + ui_animate.fly_to(instance, instance.anim_node, instance.pos + M.DEFAULT_POS_CHANGE, M.DEFAULT_MOVE_SPEED) + ui_animate.scale_to(instance, instance.anim_node, instance.scale_to, + function() + if instance.back_anim then + instance.back_anim(instance) + end + instance.callback(instance.parent.parent, instance.params, force) + end + ) +end + +function M.back_tab_animation(instance) + ui_animate.alpha(instance, instance.anim_node, 1, nil, M.DEFAULT_TIME_ANIM) + ui_animate.fly_to(instance, instance.anim_node, instance.pos, M.DEFAULT_MOVE_SPEED) + ui_animate.scale_to(instance, instance.anim_node, instance.scale_from) +end + +function M.deactivate(instance, is_animate, callback) + instance.disabled = true + if is_animate then + local counter = 0 + local clbk = function() + counter = counter + 1 + if counter == 3 and callback then + callback(instance.parent.parent) + end + end + ui_animate.color(instance, instance.node, M.DEFAULT_DEACTIVATE_COLOR, clbk, M.DEFAUL_ACTIVATION_TIME, 0, + gui.EASING_OUTBOUNCE) + ui_animate.scale_y_from_to(instance, instance.node, M.DEFAULT_ACTIVATE_SCALE.x, M.DEFAULT_DEACTIVATE_SCALE.x, clbk, + M.DEFAUL_ACTIVATION_TIME, gui.EASING_OUTBOUNCE) + ui_animate.scale_x_from_to(instance, instance.node, M.DEFAULT_ACTIVATE_SCALE.y, M.DEFAULT_DEACTIVATE_SCALE.y, clbk, + M.DEFAUL_ACTIVATION_TIME, gui.EASING_OUTBOUNCE) + else + gui.set_color(instance.node, M.DEFAULT_DEACTIVATE_COLOR) + gui.set_scale(instance.node, M.DEFAULT_DEACTIVATE_SCALE) + if callback then + callback(instance.parent.parent) + end + end +end + +function M.activate(instance, is_animate, callback) + if is_animate then + local counter = 0 + local clbk = function() + counter = counter + 1 + if counter == 3 then + instance.disabled = false + if callback then + callback(instance.parent.parent) + end + end + end + ui_animate.color(instance, instance.node, ui_animate.TINT_SHOW, clbk, M.DEFAUL_ACTIVATION_TIME, 0, + gui.EASING_OUTBOUNCE) + ui_animate.scale_y_from_to(instance, instance.node, M.DEFAULT_DEACTIVATE_SCALE.x, M.DEFAULT_ACTIVATE_SCALE.x, clbk, + M.DEFAUL_ACTIVATION_TIME, gui.EASING_OUTBOUNCE) + ui_animate.scale_x_from_to(instance, instance.node, M.DEFAULT_DEACTIVATE_SCALE.y, M.DEFAULT_ACTIVATE_SCALE.y, clbk, + M.DEFAUL_ACTIVATION_TIME, gui.EASING_OUTBOUNCE) + else + gui.set_color(instance.node, ui_animate.TINT_SHOW) + gui.set_scale(instance.node, M.DEFAULT_ACTIVATE_SCALE) + instance.disabled = false + if callback then + callback(instance.parent.parent) + end + end +end + +return M diff --git a/druid/components/counter.lua b/druid/components/counter.lua new file mode 100644 index 0000000..ef708bb --- /dev/null +++ b/druid/components/counter.lua @@ -0,0 +1,67 @@ +local M = {} + +local text_field = require "modules.ui.components.text_field" + +local FRAMES = 60 + +--- Bounce text field +M.bounce = text_field.bounce + +--- Set text to text field +-- @param set_to - set value to text field +M.set_to = text_field.set_to + +--- Set color +-- @param color +M.set_color = text_field.set_color + +--- Set scale +-- @param scale +M.set_scale = text_field.set_scale + +--- Called when layout updated (rotate for example) +function M.on_layout_updated(instance) + text_field.on_layout_updated(instance) + if instance.last_value then + text_field.set_to(instance, instance.last_value) + end +end + +--- Animate counter +-- @param set_to - set value to text field +function M.animate_to(instance, set_to, frames) + if set_to == instance.last_value then + text_field.set_to(instance, set_to) + elseif not instance.is_animate then + frames = frames or FRAMES + instance.end_anim_value = set_to + local diff = set_to - instance.last_value + instance.anim_step = math.floor((set_to - instance.last_value) / frames) + if diff ~= 0 and instance.anim_step == 0 then + instance.anim_step = diff > 0 and 1 or - 1 + end + instance.is_animate = true + else + instance.end_anim_value = set_to + end +end + +--- Called when update +-- @param dt - delta time +function M.on_updated(instance, dt) + if instance.is_animate then + instance.last_value = instance.last_value + instance.anim_step + text_field.set_to(instance, instance.last_value) + if not instance.is_in_bounce then + instance.is_in_bounce = true + text_field.bounce(instance, function() instance.is_in_bounce = false end) + end + if instance.anim_step > 0 and instance.last_value >= instance.end_anim_value or + instance.anim_step < 0 and instance.last_value <= instance.end_anim_value then + instance.is_animate = false + text_field.set_to(instance, instance.end_anim_value) + end + end +end + +return M diff --git a/druid/components/flying_particles.lua b/druid/components/flying_particles.lua new file mode 100644 index 0000000..5b29d26 --- /dev/null +++ b/druid/components/flying_particles.lua @@ -0,0 +1,52 @@ +local M = {} + +local ui_animate = require "modules.ui.ui_animate" + +local function fly_to(instance, pos_from, speed, callback) + local pos_to = instance.get_pos_func() + instance.last_speed = speed + instance.last_callback = callback + + instance.last_particle = instance.last_particle + 1 + if instance.last_particle > #instance.fly_particles then + instance.last_particle = 1 + end + local fly_particle = instance.fly_particles[instance.last_particle] + if pos_from then + gui.set_position(fly_particle, pos_from) + end + gui.play_particlefx(fly_particle) + instance.is_anim = true + ui_animate.fly_to(nil, fly_particle, pos_to, speed, + function() + instance.is_anim = false + gui.stop_particlefx(fly_particle) + if callback then + callback(instance.parent.parent) + instance.last_callback = nil + end + end, + 0, gui.EASING_INSINE) +end + +--- Start animation of a flying particles +-- @param pos_from - fly from this position +-- @param speed - speed of flying +-- @param callback - callback when progress ended if need +function M.fly_to(instance, pos_from, speed, callback) + fly_to(instance, pos_from, speed, callback) +end + +--- Called when layout updated (rotate for example) +function M.on_layout_updated(instance) + if instance.is_anim then + instance.last_particle = instance.last_particle - 1 + if instance.last_particle < 1 then + instance.last_particle = #instance.fly_particles + end + fly_to(instance, nil, instance.last_speed, instance.last_callback) + end +end + + +return M diff --git a/druid/components/image.lua b/druid/components/image.lua new file mode 100644 index 0000000..d6cdf7a --- /dev/null +++ b/druid/components/image.lua @@ -0,0 +1,46 @@ +local M = {} + +local ui_animate = require "modules.ui.ui_animate" + +--- Bounce image +function M.bounce(instance) + gui.set_scale(instance.node, instance.scale_from) + ui_animate.bounce(nil, instance.node, instance.scale_to) +end + +--- Set image anim +-- @param set_to - index of animation or animation name +function M.set_to(instance, set_to) + instance.last_value = set_to + gui.play_flipbook(instance.node, instance.anim_table and instance.anim_table[set_to] or set_to) +end + +--- Set position to the image +-- @param pos - set position of the image +function M.set_pos(instance, pos) + instance.last_pos = pos + gui.set_position(instance.node, pos) +end + +--- Set tint to the image +-- @param color - set color of the image +function M.set_color(instance, color) + instance.last_color = color + gui.set_color(instance.node, color) +end + +--- Called when layout updated (rotate for example) +function M.on_layout_updated(instance) + if instance.last_value then + M.set_to(instance, instance.last_value) + end + if instance.last_pos then + M.set_pos(instance, instance.last_pos) + end + if instance.last_color then + M.set_color(instance, instance.last_color) + end +end + + +return M diff --git a/druid/components/pie_progress_bar.lua b/druid/components/pie_progress_bar.lua new file mode 100644 index 0000000..ec8533b --- /dev/null +++ b/druid/components/pie_progress_bar.lua @@ -0,0 +1,51 @@ +local M = {} + +local FULL_FILL = 360 + +local function set_bar_to(instance, set_to) + instance.last_value = set_to + gui.cancel_animation(instance.node, gui.PROP_FILL_ANGLE) + gui.set_fill_angle(instance.node, FULL_FILL * set_to) +end + +--- Fill a pie progress bar and stop progress animation +function M.fill_bar(instance) + set_bar_to(instance, 1) +end + +--- To empty a pie progress bar +function M.empty_bar(instance) + set_bar_to(instance, 0) +end + +--- Set fill a pie progress bar to value +-- @param to - value between 0..1 +function M.set_to(instance, to) + set_bar_to(instance, to) +end + +--- Start animation of a pie progress bar +-- @param to - value between 0..1 +-- @param duration - time of animation +-- @param callback - callback when progress ended if need +function M.start_progress_to(instance, to, duration, callback) + instance.is_anim = true + instance.last_value = to + gui.animate(instance.node, gui.PROP_FILL_ANGLE, FULL_FILL * to, gui.EASING_LINEAR, duration, 0, + function() + instance.is_anim = false + if callback then + callback(instance.parent.parent) + end + end + ) +end + +--- Called when layout updated (rotate for example) +function M.on_layout_updated(instance) + if not instance.is_anim then + set_bar_to(instance, instance.last_value) + end +end + +return M diff --git a/druid/components/progress_bar.lua b/druid/components/progress_bar.lua new file mode 100644 index 0000000..bb60693 --- /dev/null +++ b/druid/components/progress_bar.lua @@ -0,0 +1,93 @@ +local M = {} + +local function set_bar_to(instance, set_to) + instance.last_value = set_to + gui.cancel_animation(instance.node, instance.prop) + instance.scale[instance.key] = set_to + gui.set_scale(instance.node, instance.scale) +end + +local function circle_anim(instance, full, steps, num, full_duration, callback) + local duration = (math.abs(steps[num - 1] - steps[num]) / full) * full_duration + local to = steps[num] + gui.animate(instance.node, instance.prop, to, gui.EASING_LINEAR, duration, 0, + function() + callback(num, callback) + end + ) +end + +--- Fill a progress bar and stop progress animation +function M.fill_bar(instance) + set_bar_to(instance, 1) +end + +--- To empty a progress bar +function M.empty_bar(instance) + set_bar_to(instance, 0) +end + +--- Set fill a progress bar to value +-- @param to - value between 0..1 +function M.set_to(instance, to) + set_bar_to(instance, to) +end + +--- Start animation of a progress bar +-- @param to - value between 0..1 +-- @param duration - time of animation +-- @param callback - callback when progress ended if need +-- @param callback_values - whitch values should callback +function M.start_progress_to(instance, to, duration, callback, callback_values) + instance.is_anim = true + local steps + if callback_values then + steps = {instance.last_value} + if instance.last_value > to then + table.sort(callback_values, function(a, b) return a > b end) + else + table.sort(callback_values, function(a, b) return a < b end) + end + for i, v in ipairs(callback_values) do + if (instance.last_value > v and to < v) or (instance.last_value < v and to > v) then + steps[#steps + 1] = v + end + end + steps[#steps + 1] = to + end + if not steps then + gui.animate(instance.node, instance.prop, to, gui.EASING_LINEAR, duration, 0, + function() + set_bar_to(instance, to) + instance.is_anim = false + if callback then + callback(instance.parent.parent, to) + end + end + ) + else + local full = math.abs(steps[1] - steps[#steps]) + local _callback = function (num, _callback) + if num == #steps then + set_bar_to(instance, steps[num]) + instance.is_anim = false + callback(instance.parent.parent, steps[num]) + else + callback(instance.parent.parent, steps[num]) + num = num + 1 + circle_anim(instance, full, steps, num, duration, _callback) + end + end + circle_anim(instance, full, steps, 2, duration, _callback) + end +end + +--- Called when layout updated (rotate for example) +function M.on_layout_updated(instance) + if not instance.is_anim then + set_bar_to(instance, instance.last_value) + end +end + + +return M diff --git a/druid/components/scrolling_box.lua b/druid/components/scrolling_box.lua new file mode 100644 index 0000000..b4c05f2 --- /dev/null +++ b/druid/components/scrolling_box.lua @@ -0,0 +1,237 @@ +local M = {} + +local input = require "modules.input.input" +local ui_animate = require "modules.ui.ui_animate" +local extra_math = require "modules.utils.extra_math" + +M.START = hash("START") +M.FINISH = hash("FINISH") + +M.SCROLLING = hash("SCROLLING") +M.INTEREST_MOVE = hash("INTEREST_MOVE") +M.OUT_OF_ZONE_MOVE = hash("OUT_OF_ZONE_MOVE") + +M.BACK_TIME = 0.2 +M.ANIM_TIME = 0.4 + +local function callback(instance, event, type, param) + if instance.callback then + instance.callback(instance.parent.parent, event, type, param) + end +end + +local function checkSwipeDirection(swipe, action) + swipe.xDistance = math.abs(swipe.endX - swipe.beginX) + swipe.yDistance = math.abs(swipe.endY - swipe.beginY) + if swipe.is_x and swipe.xDistance > swipe.yDistance then + if swipe.beginX > swipe.endX then + swipe.totalSwipeDistanceLeft = swipe.beginX - swipe.endX + if swipe.totalSwipeDistanceLeft > swipe.minSwipeDistance then + swipe.speed.x = action.dx * swipe.speed_up_coef.x * swipe.end_move_coef_x + return true + else + return false + end + else + swipe.totalSwipeDistanceRight = swipe.endX - swipe.beginX + if swipe.totalSwipeDistanceRight > swipe.minSwipeDistance then + swipe.speed.x = action.dx * swipe.speed_up_coef.x * swipe.end_move_coef_x + return true + else + return false + end + end + elseif swipe.is_y and swipe.xDistance < swipe.yDistance then + if swipe.beginY > swipe.endY then + swipe.totalSwipeDistanceUp = swipe.beginY - swipe.endY + if swipe.totalSwipeDistanceUp > swipe.minSwipeDistance then + swipe.speed.y = action.dy * swipe.speed_up_coef.y * swipe.end_move_coef_y + return true + else + return false + end + else + swipe.totalSwipeDistanceDown = swipe.endY - swipe.beginY + if swipe.totalSwipeDistanceDown > swipe.minSwipeDistance then + swipe.speed.y = action.dy * swipe.speed_up_coef.y * swipe.end_move_coef_y + return true + else + return false + end + end + end +end + +local function back_move(instance) + if not instance.swipe.end_position_x and not instance.swipe.end_position_y then + if instance.points_of_interest then + local min_index, min_lenght = 0, math.huge + local len + for k, v in pairs(instance.points_of_interest) do + len = extra_math.lenght(instance.pos.x, instance.pos.y, v.x, v.y) + if len < min_lenght then + min_lenght = len + min_index = k + end + end + instance.swipe.speed.x = 0 + instance.swipe.speed.y = 0 + gui.cancel_animation(instance.node, gui.PROP_POSITION) + instance.swipe.special_move = true + callback(instance, M.START, M.INTEREST_MOVE, instance.points_of_interest[min_index]) + gui.animate(instance.node, gui.PROP_POSITION, instance.points_of_interest[min_index], + gui.EASING_LINEAR, M.ANIM_TIME, 0, + function() + instance.swipe.special_move = false + instance.pos.x = instance.points_of_interest[min_index].x + instance.pos.y = instance.points_of_interest[min_index].y + callback(instance, M.FINISH, M.SCROLLING, instance.pos) + callback(instance, M.FINISH, M.INTEREST_MOVE, instance.pos) + end + ) + else + callback(instance, M.FINISH, M.SCROLLING, instance.pos) + end + end + + if instance.swipe.end_position_x then + local swipe = instance.swipe + swipe.speed.x = 0 + instance.pos.x = swipe.end_position_x + swipe.special_move = true + callback(instance, M.START, M.OUT_OF_ZONE_MOVE, instance.pos) + gui.animate(instance.node, ui_animate.PROP_POS_X, swipe.end_position_x, gui.EASING_INSINE, M.BACK_TIME, 0, + function() + swipe.special_move = false + callback(instance, M.FINISH, M.SCROLLING, instance.pos) + callback(instance, M.FINISH, M.OUT_OF_ZONE_MOVE, instance.pos) + end + ) + swipe.end_position_x = nil + end + if instance.swipe.end_position_y then + local swipe = instance.swipe + swipe.speed.y = 0 + instance.pos.y = swipe.end_position_y + swipe.special_move = true + callback(instance, M.START, M.OUT_OF_ZONE_MOVE, instance.pos) + gui.animate(instance.node, ui_animate.PROP_POS_Y, swipe.end_position_y, gui.EASING_INSINE, M.BACK_TIME, 0, + function() + swipe.special_move = false + callback(instance, M.FINISH, M.SCROLLING, instance.pos) + callback(instance, M.FINISH, M.OUT_OF_ZONE_MOVE, instance.pos) + end + ) + swipe.end_position_y = nil + end +end + +--- Set text to text field +-- @param action_id - input action id +-- @param action - input action +function M.on_input(instance, action_id, action) + if action_id == input.A_CLICK then + if gui.pick_node(instance.scrolling_zone, action.x, action.y) then + local swipe = instance.swipe + if action.pressed then + swipe.pressed = true + swipe.beginX = action.x + swipe.beginY = action.y + input.is_swipe = false + swipe.end_move_coef_x = 1 + elseif not action.released and not action.pressed and not swipe.special_move then + swipe.endX = action.x + swipe.endY = action.y + local before = swipe.is_swipe + swipe.is_swipe = checkSwipeDirection(swipe, action) + if not before and swipe.is_swipe and not swipe.special_move and not swipe.waiting_for_back_move then + callback(instance, M.START, M.SCROLLING, instance.pos) + end + return swipe.is_swipe or swipe.special_move + elseif action.released then + swipe.beginX = 0 + swipe.beginY = 0 + swipe.endX = 0 + swipe.endY = 0 + swipe.pressed = false + if swipe.waiting_for_back_move then + back_move(instance) + swipe.waiting_for_back_move = false + end + return swipe.is_swipe or swipe.special_move + end + elseif action.released then + instance.swipe.pressed = false + if instance.swipe.waiting_for_back_move then + back_move(instance) + instance.swipe.waiting_for_back_move = false + end + end + end +end + +--- Called when update +-- @param dt - delta time +function M.on_updated(instance, dt) + if instance.swipe.speed.x ~= 0 or instance.swipe.speed.y ~= 0 then + local swipe = instance.swipe + instance.pos.x = instance.pos.x + swipe.speed.x + instance.pos.y = instance.pos.y + swipe.speed.y + if instance.pos.x < instance.start_pos.x then + swipe.end_move_coef_x = swipe.back_slow_coef + swipe.end_position_x = instance.start_pos.x + elseif instance.pos.x > instance.maximum.x then + swipe.end_move_coef_x = swipe.back_slow_coef + swipe.end_position_x = instance.maximum.x + else + swipe.end_move_coef_x = 1 + swipe.end_position_x = nil + end + if instance.pos.y < instance.start_pos.y then + swipe.end_move_coef_y = swipe.back_slow_coef + swipe.end_position_y = instance.start_pos.y + elseif instance.pos.y > instance.maximum.y then + swipe.end_move_coef_y = swipe.back_slow_coef + swipe.end_position_y = instance.maximum.y + else + swipe.end_move_coef_y = 1 + swipe.end_position_y = nil + end + gui.set_position(instance.node, instance.pos) + swipe.speed.x = swipe.speed.x / swipe.speed_down_coef * swipe.end_move_coef_x + swipe.speed.y = swipe.speed.y / swipe.speed_down_coef * swipe.end_move_coef_y + if swipe.speed.x < swipe.min_speed and swipe.speed.x > - swipe.min_speed then + swipe.speed.x = 0 + if not swipe.pressed then + back_move(instance) + else + swipe.waiting_for_back_move = true + end + if swipe.speed.y < swipe.min_speed and swipe.speed.y > - swipe.min_speed then + swipe.speed.y = 0 + end + if swipe.speed.y == 0 and swipe.speed.x == 0 then + swipe.is_swipe = false + end + end + end +end + +--- Scroll position to +-- @param pos - positon for set +-- @param is_animate - is animated set +function M.scroll_to(instance, pos, is_animate, cb, time_scrolling) + local time = is_animate and M.ANIM_TIME or 0 + time = time_scrolling or time + instance.pos.x = pos.x + instance.pos.y = pos.y + gui.animate(instance.node, gui.PROP_POSITION, instance.pos, gui.EASING_INSINE, time, 0, + function() + if cb then + cb(instance.parent.parent) + end + end + ) +end + +return M diff --git a/druid/components/spine_anim.lua b/druid/components/spine_anim.lua new file mode 100644 index 0000000..40861c2 --- /dev/null +++ b/druid/components/spine_anim.lua @@ -0,0 +1,62 @@ +local M = {} + +--- Set animation scene +-- @param scene - animations scene +function M.set_scene(instance, scene) + instance.last_scene = scene + gui.set_spine_scene(instance.node, scene) +end + + +--- Set idle animation +-- @param anim - idle animation name or index in idle table +-- @param properties - properties of the animation +function M.play_idle(instance, anim, properties) + if not anim then + return + end + anim = (instance.idle_table and instance.idle_table[anim]) and instance.idle_table[anim] or anim + instance.last_value = anim + properties = properties or {} + gui.play_spine_anim(instance.node, anim, gui.PLAYBACK_LOOP_FORWARD, properties) +end + +--- Set active animation +-- @param anim - active animation name or index in active table +-- @param callback - call when animation done +-- @param idle_after - set idle after active anim +function M.play_active(instance, anim, callback, idle_after) + instance.is_play_now = true + anim = instance.active_table and instance.active_table[anim] or anim + instance.last_value = anim + instance.callback = callback + M.play_idle(instance, idle_after) + gui.play_spine_anim(instance.node, anim, gui.PLAYBACK_ONCE_FORWARD, {}, + function() + M.play_idle(instance, idle_after) + instance.is_play_now = false + if callback then + callback(instance.parent.parent) + end + end + ) +end + +--- Called when layout updated (rotate for example) +function M.on_layout_updated(instance) + if instance.last_scene then + M.set_scene(instance, instance.last_scene) + end + if instance.last_value then + M.play_idle(instance, instance.last_value) + end + if instance.is_play_now then + instance.is_play_now = false + if instance.callback then + instance.callback(instance.parent.parent) + end + end +end + + +return M diff --git a/druid/components/tab_page.lua b/druid/components/tab_page.lua new file mode 100644 index 0000000..f6771ce --- /dev/null +++ b/druid/components/tab_page.lua @@ -0,0 +1,92 @@ +--[[ Single tab screen module. Assumed to be used with Tab Bar module. + +-- Create tab screen with parental gui node: +self.tab = tab.create "tab_bkg" + +-- Show and hide tab manually: +self.tab.slide_in(self, vmath.vector3(0, -540, 0)) +self.tab.slide_out(self, vmath.vector3(0, -540, 0)) + +-- Or receive show and hide messages: +function on_message(self, message_id, message, sender) + self.tab.on_message(self, message_id, message) +end + +]] + +local M = {} +M.T_SLIDE_IN = hash("t_slide_in") +M.T_SLIDE_OUT = hash("t_slide_out") + +M.STATE_START = hash("state_start") +M.STATE_FINISH = hash("state_finish") + +local ENABLE = hash("enable") +local DISABLE = hash("disable") +local PATH_COMP = "#" + +M.DEFAULT_EASING = gui.EASING_INOUTQUAD +M.DEFAULT_DURATION = 0.3 + +function M.slide_in(instance, out_pos, is_force) + msg.post(PATH_COMP, ENABLE) + if instance.callback then + instance.callback(instance.parent.parent, instance, M.T_SLIDE_IN, M.STATE_START) + end + if is_force then + gui.set_position(instance.node, instance.in_pos) + if instance.callback then + instance.callback(instance.parent.parent, instance, M.T_SLIDE_IN, M.STATE_FINISH) + end + else + instance.in_action = true + out_pos = out_pos or instance.put_pos + gui.set_position(instance.node, out_pos or instance.out_pos) + gui.animate(instance.node, gui.PROP_POSITION, instance.in_pos, instance.easing, instance.duration, 0, + function() + instance.in_action = false + if instance.callback then + instance.callback(instance.parent.parent, instance, M.T_SLIDE_IN, M.STATE_FINISH) + end + end + ) + end +end + +function M.slide_out(instance, out_pos, is_force) + out_pos = out_pos or instance.put_pos + if instance.callback then + instance.callback(instance.parent.parent, instance, M.T_SLIDE_OUT, M.STATE_START) + end + if is_force then + gui.set_position(instance.node, out_pos) + if instance.callback then + instance.callback(instance.parent.parent, instance, M.T_SLIDE_OUT, M.STATE_FINISH) + end + msg.post(PATH_COMP, DISABLE) + else + instance.in_action = true + gui.set_position(instance.node, instance.in_pos) + gui.animate(instance.node, gui.PROP_POSITION, out_pos, instance.easing, instance.duration, 0, + function() + instance.in_action = false + if instance.callback then + instance.callback(instance.parent.parent, instance, M.T_SLIDE_OUT, M.STATE_FINISH) + end + msg.post(PATH_COMP, DISABLE) + end + ) + end +end + +function M.on_message(instance, message_id, message, sender) + if message_id == M.T_SLIDE_IN then + M.slide_in(instance, message.out_pos, message.is_force) + return true + elseif message_id == M.T_SLIDE_OUT then + M.slide_out(instance, message.out_pos, message.is_force) + return true + end +end + +return M diff --git a/druid/components/tabs_container.lua b/druid/components/tabs_container.lua new file mode 100644 index 0000000..7286911 --- /dev/null +++ b/druid/components/tabs_container.lua @@ -0,0 +1,50 @@ +local M = {} + +local helper = require "modules.render.helper" +local tab_page = require "modules.ui.components.tab_page" + +local DISABLE = hash("disable") + +function M.update_sizes(instance, width) + width = width or helper.config_x + instance.left = vmath.vector3(width * - 1, 0, 0) + instance.right = vmath.vector3(width * 1, 0, 0) +end + +--- Called when layout updated (rotate for example) +function M.on_layout_updated(instance, message) + local width = helper.settings_x + M.update_sizes(instance, width) +end + +function M.switch_tab(instance, params, force) + if instance.current == params then + return + end + if instance.current then + instance.btns[instance.current.index]:manual_back() + end + local out_pos + if instance.current and instance.current.url then + out_pos = (instance.current and instance.current.index < params.index) and instance.left or instance.right + msg.post(instance.current.url, tab_page.T_SLIDE_OUT, { out_pos = out_pos, is_force = force }) + end + if params and params.url then + out_pos = (instance.current and instance.current.index > params.index) and instance.left or instance.right + msg.post(params.url, tab_page.T_SLIDE_IN, { out_pos = out_pos, is_force = force }) + instance.current = params + end +end + +--- Select current tab manually +function M.select(instance, node_name) + for k, v in pairs(instance.btns) do + if k == instance[node_name].index then + v:tap_anim(true) + else + msg.post(instance[v.name].url, DISABLE) + end + end +end + +return M diff --git a/druid/components/text_field.lua b/druid/components/text_field.lua new file mode 100644 index 0000000..94d08e7 --- /dev/null +++ b/druid/components/text_field.lua @@ -0,0 +1,42 @@ +local M = {} + +local ui_animate = require "modules.ui.ui_animate" + +--- Bounce text field +function M.bounce(instance, callback) + gui.set_scale(instance.node, instance.scale_from) + ui_animate.bounce(nil, instance.node, instance.scale_to, callback) +end + +--- Set text to text field +-- @param set_to - set value to text field +function M.set_to(instance, set_to) + instance.last_value = set_to + gui.set_text(instance.node, set_to) +end + +--- Set color +-- @param color +function M.set_color(instance, color) + instance.last_color = color + gui.set_color(instance.node, color) +end + +--- Set scale +-- @param scale +function M.set_scale(instance, scale) + instance.last_scale = scale + gui.set_scale(instance.node, scale) +end + +--- Called when layout updated (rotate for example) +function M.on_layout_updated(instance) + if instance.last_color then + M.set_color(instance, instance.last_color) + end + if instance.last_scale then + M.set_scale(instance, instance.last_scale) + end +end + +return M diff --git a/druid/components/timer.lua b/druid/components/timer.lua new file mode 100644 index 0000000..68b4ba9 --- /dev/null +++ b/druid/components/timer.lua @@ -0,0 +1,53 @@ +local M = {} + +local formats = require "modules.utils.formats" + +--- Set text to text field +-- @param set_to - set value in seconds +function M.set_to(instance, set_to) + instance.last_value = set_to + gui.set_text(instance.node, formats.second_string_min(set_to)) +end + +--- Called when layout updated (rotate for example) +function M.on_layout_updated(instance) + M.set_to(instance, instance.last_value) +end + +--- Called when update +-- @param is_on - boolean is timer on +function M.set_work_mode(instance, is_on) + instance.is_on = is_on +end + +--- Set time interval +-- @param from - "from" time in seconds +-- @param to - "to" time in seconds +function M.set_interval(instance, from, to) + instance.second_from = from + instance.seconds_counter = from + instance.seconds_temp = 0 + instance.seconds_to = to + instance.second_step = from < to and 1 or - 1 + M.set_work_mode(instance, true) + M.set_to(instance, from) +end + +--- Called when update +-- @param dt - delta time +function M.on_updated(instance, dt) + if instance.is_on then + instance.seconds_temp = instance.seconds_temp + dt + if instance.seconds_temp > 1 then + instance.seconds_temp = instance.seconds_temp - 1 + instance.seconds_counter = instance.seconds_counter + instance.second_step + M.set_to(instance, instance.seconds_counter) + if instance.seconds_counter == instance.seconds_to then + instance.is_on = false + instance.callback(instance) + end + end + end +end + +return M diff --git a/druid/input.lua b/druid/input.lua new file mode 100644 index 0000000..114c9bd --- /dev/null +++ b/druid/input.lua @@ -0,0 +1,25 @@ +local M = {} + +local ADD_FOCUS = hash("acquire_input_focus") +local REMOVE_FOCUS = hash("release_input_focus") + +local PATH_OBJ = "." + +M.A_CLICK = hash("click") +M.A_TEXT = hash("text") +M.A_BACKSPACE = hash("backspace") +M.A_ENTER = hash("enter") +M.A_ANDR_BACK = hash("back") + +M.RELEASED = "released" +M.PRESSED = "pressed" + +function M.focus() + msg.post(PATH_OBJ, ADD_FOCUS) +end + +function M.remove() + msg.post(PATH_OBJ, REMOVE_FOCUS) +end + +return M diff --git a/druid/ui_animate.lua b/druid/ui_animate.lua new file mode 100644 index 0000000..394c153 --- /dev/null +++ b/druid/ui_animate.lua @@ -0,0 +1,169 @@ +local M = {} + +local PROP_SCALE = gui.PROP_SCALE +local PROP_POSITION = gui.PROP_POSITION +M.PROP_POS_X = hash("position.x") +M.PROP_POS_Y = hash("position.y") +M.PROP_ALPHA = hash("color.w") +local PROP_COLOR = hash("color") +local PROP_SCALE_X = "scale.x" +local PROP_SCALE_Y = "scale.y" + +M.TINT_HIDE = vmath.vector4(1, 1, 1, 0) +M.TINT_SHOW = vmath.vector4(1, 1, 1, 1) + +M.V3_ONE = vmath.vector3(1, 1, 1) +M.V3_ZERO = vmath.vector3(0, 0, 1) + +M.SCALE_ANIMATION_TIME = 0.1 +M.BOUNCE_ANIMATION_TIME = 0.25 +M.ALPHA_ANIMATION_TIME = 0.25 + +function M.alpha(self, node, alpha, callback, time, delay, easing, playback) + time = time or M.ALPHA_ANIMATION_TIME + delay = delay or 0 + easing = easing or gui.EASING_LINEAR + playback = playback or gui.PLAYBACK_ONCE_FORWARD + gui.animate(node, M.PROP_ALPHA, alpha, easing, time, delay, + function() + if callback then + callback(self, node) + end + end, + playback) +end + +function M.color(self, node, color, callback, time, delay, easing, playback) + time = time or M.ALPHA_ANIMATION_TIME + delay = delay or 0 + easing = easing or gui.EASING_LINEAR + playback = playback or gui.PLAYBACK_ONCE_FORWARD + gui.animate(node, PROP_COLOR, color, easing, time, delay, + function() + if callback then + callback(self, node) + end + end, + playback) +end + +function M.shake(self, node, callback, str, time) + str = str or - 30 + time = time or 0.25 + local pos = gui.get_position(node) + pos.x = pos.x + str + gui.animate(node, PROP_POSITION, pos, gui.EASING_INELASTIC, time, + 0, + function() + if callback then + callback(self) + end + end, + gui.PLAYBACK_ONCE_BACKWARD + ) +end + +function M.bounce(self, node, change_to, callback, time, easing, playback, delaly) + time = time or M.BOUNCE_ANIMATION_TIME + delaly = delaly or 0 + easing = easing or gui.EASING_OUTSINE + playback = playback or gui.PLAYBACK_ONCE_PINGPONG + gui.animate(node, PROP_SCALE, change_to, easing, time, delaly, + function() + if callback then + callback(self) + end + end, + playback) +end + +function M.fly_to(self, node, to_pos, speed, callback, delay, easing) + easing = easing or gui.EASING_OUTSINE + delay = delay or 0 + local time = vmath.length(to_pos - gui.get_position(node)) / 100 / speed + gui.animate(node, gui.PROP_POSITION, to_pos, easing, time, delay, + function() + if callback then + callback(self, node) + end + end) +end + +function M.fly_by_x(self, node, to_x, time, callback, delay, easing, playback) + playback = playback or gui.PLAYBACK_ONCE_FORWARD + easing = easing or gui.EASING_OUTSINE + delay = delay or 0 + gui.animate(node, M.PROP_POS_X, to_x, easing, time, delay, + function() + if callback then + callback(self, node) + end + end, + playback) +end + +function M.fly_by_y(self, node, to_y, time, callback, delay, easing, playback) + playback = playback or gui.PLAYBACK_ONCE_FORWARD + easing = easing or gui.EASING_OUTSINE + delay = delay or 0 + time = time or 0.25 + gui.animate(node, M.PROP_POS_Y, to_y, easing, time, delay, + function() + if callback then + callback(self, node) + end + end, + playback) +end + +function M.scale_to(self, node, to, callback, time, delay, easing) + easing = easing or gui.EASING_INSINE + time = time or M.SCALE_ANIMATION_TIME + delay = delay or 0 + time = time or 0.25 + gui.animate(node, PROP_SCALE, to, easing, time, delay, + function() + if callback then + callback(self, node) + end + end + ) +end + +function M.scale_x_from_to(self, node, from, to, callback, time, easing, delay, playback) + easing = easing or gui.EASING_INSINE + time = time or M.SCALE_ANIMATION_TIME + delay = delay or 0 + playback = playback or gui.PLAYBACK_ONCE_FORWARD + local scale = gui.get_scale(node) + scale.x = from + gui.set_scale(node, scale) + gui.animate(node, PROP_SCALE_X, to, easing, time, delay, + function() + if callback then + callback(self) + end + end, + playback + ) +end + +function M.scale_y_from_to(self, node, from, to, callback, time, easing, delay, playback) + easing = easing or gui.EASING_INSINE + time = time or M.SCALE_ANIMATION_TIME + delay = delay or 0 + playback = playback or gui.PLAYBACK_ONCE_FORWARD + local scale = gui.get_scale(node) + scale.y = from + gui.set_scale(node, scale) + gui.animate(node, PROP_SCALE_Y, to, easing, time, delay, + function() + if callback then + callback(self) + end + end, + playback + ) +end + +return M diff --git a/druid/ui_factory.lua b/druid/ui_factory.lua new file mode 100644 index 0000000..840eb9c --- /dev/null +++ b/druid/ui_factory.lua @@ -0,0 +1,421 @@ +local M = {} + +local lang = require "modules.localize.lang" +local input = require "modules.input.input" +M.input = input + +local pie_progress_bar = require "modules.ui.components.pie_progress_bar" +local progress_bar = require "modules.ui.components.progress_bar" +local flying_particles = require "modules.ui.components.flying_particles" +local text_field = require "modules.ui.components.text_field" +local counter = require "modules.ui.components.counter" +local image = require "modules.ui.components.image" +local button = require "modules.ui.components.button" +local timer = require "modules.ui.components.timer" +local tab_page = require "modules.ui.components.tab_page" +local tabs_container = require "modules.ui.components.tabs_container" +local spine_anim = require "modules.ui.components.spine_anim" +local scrolling_box = require "modules.ui.components.scrolling_box" + +local andr_back_btn = require "modules.ui.components.andr_back_btn" + +local LAYOUT_CHANGED = hash("layout_changed") +local ON_MESSAGE = hash("on_message") +local ON_INPUT = hash("on_input") +local ON_SWIPE = hash("on_swipe") +local ON_UPDATE = hash("on_update") +M.TRANSLATABLE = hash("TRANSLATABLE") + +local STRING = "string" + +--- Call this method when you need to update translations. +function M.translate(factory) + if factory[M.TRANSLATABLE] then + local key, result + for i, v in ipairs(factory[M.TRANSLATABLE]) do + key = v.lang_key or v.name + if key then + if v.lang_params then + result = lang.txp(key, v.lang_params) + else + result = lang.txt(key) + end + if result then + lang.set_node_properties(v.node, key) + end + result = result or v.last_value + v:set_to(result) + end + end + end +end + +--- Called on_message +function M.on_message(factory, message_id, message, sender) + if message_id == LAYOUT_CHANGED then + if factory[LAYOUT_CHANGED] then + M.translate(factory) + for i, v in ipairs(factory[LAYOUT_CHANGED]) do + v:on_layout_updated(message) + end + end + elseif message_id == M.TRANSLATABLE then + M.translate(factory) + else + if factory[ON_MESSAGE] then + for i, v in ipairs(factory[ON_MESSAGE]) do + v:on_message(message_id, message, sender) + end + end + end +end + +--- Called ON_INPUT +function M.on_input(factory, action_id, action) + if factory[ON_SWIPE] then + local v, result + local len = #factory[ON_SWIPE] + for i = 1, len do + v = factory[ON_SWIPE][i] + result = result or v:on_input(action_id, action) + end + if result then + return true + end + end + if factory[ON_INPUT] then + local v + local len = #factory[ON_INPUT] + for i = 1, len do + v = factory[ON_INPUT][i] + if action_id == v.event and action[v.action] and v:on_input(action_id, action) then + return true + end + end + return false + end + return false +end + +--- Called on_update +function M.on_update(factory, dt) + if factory[ON_UPDATE] then + for i, v in ipairs(factory[ON_UPDATE]) do + v:on_updated(dt) + end + end +end + +--- Create UI instance for ui elements +-- @return instance with all ui components +function M.new(self) + local factory = setmetatable({}, {__index = M}) + factory.parent = self + return factory +end + +local function input_init(factory) + if not factory.input_inited then + factory.input_inited = true + input.focus() + end +end + +-------------------------------------------------------------------------------- + +local function create(meta, factory, name, ...) + local instance = setmetatable({}, {__index = meta}) + instance.parent = factory + if name then + if type(name) == STRING then + instance.name = name + instance.node = gui.get_node(name) + else + --name already is node + instance.name = nil + instance.node = name + end + end + factory[#factory + 1] = instance + local register_to = {...} + for i, v in ipairs(register_to) do + if not factory[v] then + factory[v] = {} + end + factory[v][#factory[v] + 1] = instance + end + return instance +end + +--- Create new instance of a text_field +-- @param factory - parent factory +-- @param name - name of text node +-- @param init_value - init ui object with this value +-- @return instance of a text_field +function M.new_text_field(factory, name, init_value, bounce_in) + local instance = create(text_field, factory, name, M.TRANSLATABLE, LAYOUT_CHANGED) + instance.scale_from = gui.get_scale(instance.node) + instance.scale_to = bounce_in and vmath.mul_per_elem(instance.scale_from, bounce_in) or instance.scale_from + instance:set_to(init_value or 0) + return instance +end + +--- Create new instance of a counter +-- @param factory - parent factory +-- @param name - name of text node +-- @param init_value - init ui object with this value +-- @return instance of a text_field +function M.new_counter(factory, name, init_value) + local instance = create(counter, factory, name, LAYOUT_CHANGED, ON_UPDATE) + instance.scale_from = gui.get_scale(instance.node) + instance.scale_to = instance.scale_from * 1.2 + instance:set_to(init_value or 0) + return instance +end + +--- Create new instance of an image +-- @param factory - parent factory +-- @param name - name of image node +-- @param anim_table - table with animations or frames +-- @param init_frame - init with this frame +-- @return instance of an image +function M.new_image(factory, name, anim_table, init_frame, bounce_in) + local instance = create(image, factory, name, LAYOUT_CHANGED) + instance.scale_from = gui.get_scale(instance.node) + instance.scale_to = bounce_in and vmath.mul_per_elem(instance.scale_from, bounce_in) or instance.scale_from + instance.anim_table = anim_table + if init_frame then + instance:set_to(init_frame) + elseif anim_table then + instance:set_to(1) + end + return instance +end + +--- Create new instance of a timer +-- @param factory - parent factory +-- @param name - name of image node +-- @param second_from - start time +-- @param seconds_to - end time +-- @param callback - call when timer finished +-- @return instance of a timer +function M.new_timer(factory, name, second_from, seconds_to, callback) + local instance = create(timer, factory, name, LAYOUT_CHANGED, ON_UPDATE) + instance:set_to(second_from) + instance:set_interval(second_from, seconds_to) + instance.is_on = true + instance.callback = callback + return instance +end + +--- Add new pie progress component for handling +-- @param factory - parent factory +-- @param name - a node name for a pie progress instance +-- @param init_value - init ui object with this value +-- @return instance with pie_progress +function M.new_pie_progress(factory, name, init_value) + local instance = create(pie_progress_bar, factory, name, LAYOUT_CHANGED) + instance:set_to(init_value or 1) + return instance +end + +--- Add new progress bar component for handling +-- @param factory - parent factory +-- @param name - name of the fill node +-- @param key - x or y - key for scale +-- @param init_value - init ui object with this value +-- @return instance with pie_progress +function M.new_progress_bar(factory, name, key, init_value) + local instance = create(progress_bar, factory, name, LAYOUT_CHANGED) + instance.prop = hash("scale."..key) + instance.key = key + instance.node = gui.get_node(name) + instance.scale = gui.get_scale(instance.node) + instance:set_to(init_value or 1) + return instance +end + +--- Create new instance of a flying particles +-- @param factory - parent factory +-- @param name - name of prototype +-- @param count - how many particles need to cache +-- @param get_pos_func - function that returns target pos for flying +-- @return instance of a flying particles +function M.new_flying_particles(factory, name, count, get_pos_func) + local instance = create(flying_particles, factory, name, LAYOUT_CHANGED) + instance.get_pos_func = get_pos_func + local node = instance.node + instance.node = node + instance.fly_particles = {} + instance.fly_particles[1] = node + for i = 2, count do + instance.fly_particles[i] = gui.clone(node) + end + instance.scale = gui.get_scale(node) + instance.last_particle = 0 + return instance +end + +M.BTN_SOUND_FUNC = function() end +M.BTN_SOUND_DISABLE_FUNC = function()end + +--- Add new button component for handling +-- @param factory - parent factory +-- @param name - a node name for a button instance +-- @param callback - click button callback +-- @param params - callback parameters, will be returned with self callback(self, params) +-- @param animate_node_name - node for animation, if it's not a main node +-- @return instance of button +function M.new_button(factory, name, callback, params, animate_node_name, event, action, sound, sound_disable) + input_init(factory) + local instance = create(button, factory, name, ON_INPUT) + instance.event = event or input.A_CLICK + instance.action = action or input.RELEASED + instance.anim_node = animate_node_name and gui.get_node(animate_node_name) or instance.node + instance.scale_from = gui.get_scale(instance.anim_node) + instance.scale_to = instance.scale_from + button.DEFAULT_SCALE_CHANGE + instance.pos = gui.get_position(instance.anim_node) + instance.callback = callback + instance.params = params + instance.tap_anim = button.tap_scale_animation + instance.back_anim = button.back_scale_animation + instance.sound = sound or M.BTN_SOUND_FUNC + instance.sound_disable = sound_disable or M.BTN_SOUND_DISABLE_FUNC + return instance +end + +--- Add reaction for back btn (on Android for example) +-- @param factory - parent factory +-- @param callback - tap button callback +function M.new_back_handler(factory, callback) + input_init(factory) + local instance = create(andr_back_btn, factory, nil, ON_INPUT) + instance.event = input.A_ANDR_BACK + instance.action = input.RELEASED + instance.callback = callback + return instance +end + +--- Create new tab page instance +-- @param factory - parent factory +-- @param name - name of parental node that represents tab page content +-- @param easing - easing for tab page +-- @param duration - duration of animation for tab page +-- @param callback - call when change page +-- @return instance that represents the tab page +function M.new_tab_page(factory, name, easing, duration, callback) + local instance = create(tab_page, factory, name, ON_MESSAGE) + instance.in_pos = gui.get_position(instance.node) + instance.out_pos = gui.get_position(instance.node) + instance.easing = easing or tab_page.DEFAULT_EASING + instance.duration = duration or tab_page.DEFAULT_DURATION + instance.callback = callback + return instance +end + +--- Create new tab btns container instance +-- @param factory - parent factory +-- @param name - name of parental node that represents tab btns container +-- @return instance that represents the tab btns container +function M.new_tabs_container(factory, name, callback) + local instance = create(tabs_container, factory, name, LAYOUT_CHANGED) + instance:update_sizes() + instance.url = msg.url() + --- Create new tab btn instance + -- @param name - name of parental node that represents tab btn + -- @return instance that represents the tab btn + function instance.new_tab_btn(_instance, _name, url, index) + local params = {url = url, index = index, name = _name} + local btn = M.new_button(factory, _name, nil, params) + btn.back_anim = nil + btn.manual_back = button.back_tab_animation + btn.tap_anim = button.tap_tab_animation + btn.callback = function(_, _, force) + instance.switch_tab(instance, params, force) + if callback then + callback(factory.parent, index, force) + end + end + instance[_name] = params + if not instance.btns then + instance.btns = {} + end + instance.btns[index] = btn + return btn + end + + return instance +end + +--- Add new spine animation +-- @param factory - parent factory +-- @param name - a node name for a spine anim +-- @param idle_table - table with idle animations +-- @param active_table - table with active animations +-- @param init_idle - init idle animation name or index in idle table +-- @return instance with spine anim +function M.new_spine_anim(factory, name, idle_table, active_table, init_idle) + local instance = create(spine_anim, factory, name, LAYOUT_CHANGED) + instance.idle_table = idle_table + instance.active_table = active_table + instance:play_idle(init_idle) + return instance +end + +--- Add new scrolling box +-- @param factory - parent factory +-- @param name - a node name for a spine anim +-- @param zone_name - node name of zone for tap +-- @param speed_coef - vector3 coef. of speed for scrolling +-- @param maximum - vector3 maximum position for scrolling +-- @param points_of_interest - table with vector3 point of interes +-- @param callback - scrolling events callback +-- @return instance with scrolling box +function M.new_scrolling_box(factory, name, zone_name, speed_coef, maximum, points_of_interest, callback) + local instance = create(scrolling_box, factory, name, ON_UPDATE, ON_SWIPE) + instance.pos = gui.get_position(instance.node) + instance.start_pos = vmath.vector3(instance.pos) + instance.maximum = maximum + instance.points_of_interest = points_of_interest + instance.callback = callback + if instance.start_pos.x > instance.maximum.x then + instance.start_pos.x, instance.maximum.x = instance.maximum.x, instance.start_pos.x + end + if instance.start_pos.y > instance.maximum.y then + instance.start_pos.y, instance.maximum.y = instance.maximum.y, instance.start_pos.y + end + if type(name) == STRING then + instance.scrolling_zone = gui.get_node(zone_name) + else + instance.scrolling_zone = zone_name + end + instance.swipe = { + minSwipeDistance = 40, + speed_down_coef = 1.1, + speed_up_coef = speed_coef or vmath.vector3(1.1, 1.1, 0), + speed = vmath.vector3(0, 0, 0), + maximum = vmath.vector3(0, 0, 0), + min_speed = 2, + beginX = 0, + beginY = 0, + endX = 0, + endY = 0, + xDistance = nil, + yDistance = nil, + totalSwipeDistanceLeft = nil, + totalSwipeDistanceRight = nil, + totalSwipeDistanceUp = nil, + totalSwipeDistanceDown = nil, + is_swipe = nil, + end_move_coef_x = 1, + end_move_coef_y = 1, + back_slow_coef = 0.4, + end_position_x = nil, + end_position_y = nil, + is_x = instance.start_pos.x ~= instance.maximum.x, + is_y = instance.start_pos.y ~= instance.maximum.y + } + return instance +end + +return M diff --git a/druid/ui_helper.lua b/druid/ui_helper.lua new file mode 100644 index 0000000..89e5aa5 --- /dev/null +++ b/druid/ui_helper.lua @@ -0,0 +1,16 @@ +local M = {} + +function M.centrate_text_with_icon(text_node, icon_node) + local metr = gui.get_text_metrics_from_node(text_node) + local scl = gui.get_scale(text_node).x + local scl_i = gui.get_scale(icon_node).x + local pos_i = gui.get_position(icon_node) + local pos = gui.get_position(text_node) + local w = metr.width * scl * scl_i + local icon_w = gui.get_size(icon_node).x * scl_i + local width = w + icon_w + (math.abs(pos.x) - icon_w / 2) * scl_i + pos_i.x = width / 2 - (icon_w / 2) + gui.set_position(icon_node, pos_i) +end + +return M diff --git a/game.project b/game.project new file mode 100644 index 0000000..44a9a6f --- /dev/null +++ b/game.project @@ -0,0 +1,16 @@ +[bootstrap] +main_collection = /main/main.collectionc + +[script] +shared_state = 1 + +[display] +width = 960 +height = 640 + +[project] +title = druid + +[library] +include_dirs = druid + diff --git a/input/game.input_binding b/input/game.input_binding new file mode 100644 index 0000000..8ed1d4e --- /dev/null +++ b/input/game.input_binding @@ -0,0 +1,4 @@ +mouse_trigger { + input: MOUSE_BUTTON_1 + action: "touch" +} diff --git a/main/main.collection b/main/main.collection new file mode 100644 index 0000000..37059e4 --- /dev/null +++ b/main/main.collection @@ -0,0 +1,2 @@ +name: "main" +scale_along_z: 0