Merge branch 'develop' into feature/D20-radio-component

This commit is contained in:
Maxim Tuprikov 2019-09-28 17:15:02 +03:00 committed by GitHub
commit 07ebb202ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 899 additions and 162 deletions

View File

@ -10,6 +10,12 @@ Defold UI library
# API
# LDoc
Generate with `ldoc .` with `config.ld` file
Insctruction here: https://github.com/stevedonovan/LDoc
# Authors
# License

8
config.ld Normal file
View File

@ -0,0 +1,8 @@
project='Druid'
title='Defold Druid UI Library'
description='Documentation for Druid Library'
format='lunamark'
file={"./druid"}
dir='./docs'
style='!pale'
no_space_before_args=true

49
docs/index.html Normal file
View File

@ -0,0 +1,49 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<head>
<title>Defold Druid UI Library</title>
<link rel="stylesheet" href="ldoc_pale.css" type="text/css" />
</head>
<body>
<div id="container">
<div id="product">
<div id="product_logo"></div>
<div id="product_name"><big><b></b></big></div>
<div id="product_description"></div>
</div> <!-- id="product" -->
<div id="main">
<!-- Menu -->
<div id="navigation">
<br/>
<h1>Druid</h1>
</div>
<div id="content">
<h2>Documentation for Druid Library</h2>
</div> <!-- id="content" -->
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i>
<i style="float:right;">Last updated 2019-09-25 19:57:43 </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>
</html>

303
docs/ldoc_pale.css Normal file
View File

@ -0,0 +1,303 @@
/* BEGIN RESET
Copyright (c) 2010, Yahoo! Inc. All rights reserved.
Code licensed under the BSD License:
http://developer.yahoo.com/yui/license.html
version: 2.8.2r1
*/
html {
color: #000;
background: #FFF;
}
body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td {
margin: 0;
padding: 0;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
fieldset,img {
border: 0;
}
address,caption,cite,code,dfn,em,strong,th,var,optgroup {
font-style: inherit;
font-weight: inherit;
}
del,ins {
text-decoration: none;
}
li {
margin-left: 20px;
}
caption,th {
text-align: left;
}
h1,h2,h3,h4,h5,h6 {
font-size: 100%;
font-weight: bold;
}
q:before,q:after {
content: '';
}
abbr,acronym {
border: 0;
font-variant: normal;
}
sup {
vertical-align: baseline;
}
sub {
vertical-align: baseline;
}
legend {
color: #000;
}
input,button,textarea,select,optgroup,option {
font-family: inherit;
font-size: inherit;
font-style: inherit;
font-weight: inherit;
}
input,button,textarea,select {*font-size:100%;
}
/* END RESET */
body {
margin-left: 1em;
margin-right: 1em;
font-family: arial, helvetica, geneva, sans-serif;
background-color: #ffffff; margin: 0px;
}
code, tt { font-family: monospace; font-size: 1.1em; }
span.parameter { font-family:monospace; }
span.parameter:after { content:":"; }
span.types:before { content:"("; }
span.types:after { content:")"; }
.type { font-weight: bold; font-style:italic }
body, p, td, th { font-size: .95em; line-height: 1.2em;}
p, ul { margin: 10px 0 0 0px;}
strong { font-weight: bold;}
em { font-style: italic;}
h1 {
font-size: 1.5em;
margin: 0 0 20px 0;
}
h2, h3, h4 { margin: 15px 0 10px 0; }
h2 { font-size: 1.25em; }
h3 { font-size: 1.15em; }
h4 { font-size: 1.06em; }
a:link { font-weight: bold; color: #004080; text-decoration: none; }
a:visited { font-weight: bold; color: #006699; text-decoration: none; }
a:link:hover { text-decoration: underline; }
hr {
color:#cccccc;
background: #00007f;
height: 1px;
}
blockquote { margin-left: 3em; }
ul { list-style-type: disc; }
p.name {
font-family: "Andale Mono", monospace;
padding-top: 1em;
}
pre {
background-color: rgb(245, 245, 245);
border: 1px solid #C0C0C0; /* silver */
padding: 10px;
margin: 10px 0 10px 0;
overflow: auto;
font-family: "Andale Mono", monospace;
}
pre.example {
font-size: .85em;
}
table.index { border: 1px #00007f; }
table.index td { text-align: left; vertical-align: top; }
#container {
margin-left: 1em;
margin-right: 1em;
background-color: #ffffff;
}
#product {
text-align: center;
border-bottom: 1px solid #cccccc;
background-color: #ffffff;
}
#product big {
font-size: 2em;
}
#main {
background-color:#FFFFFF; // #f0f0f0;
//border-left: 2px solid #cccccc;
}
#navigation {
float: left;
width: 14em;
vertical-align: top;
background-color:#FFFFFF; // #f0f0f0;
border-right: 2px solid #cccccc;
overflow: visible;
}
#navigation h2 {
background-color:#FFFFFF;//:#e7e7e7;
font-size:1.1em;
color:#000000;
text-align: left;
padding:0.2em;
//border-top:1px solid #dddddd;
border-bottom:1px solid #dddddd;
}
#navigation ul
{
font-size:1em;
list-style-type: none;
margin: 1px 1px 10px 1px;
}
#navigation li {
text-indent: -1em;
display: block;
margin: 3px 0px 0px 22px;
}
#navigation li li a {
margin: 0px 3px 0px -1em;
}
#content {
margin-left: 14em;
padding: 1em;
width: 700px;
border-left: 2px solid #cccccc;
// border-right: 2px solid #cccccc;
background-color: #ffffff;
}
#about {
clear: both;
padding: 5px;
border-top: 2px solid #cccccc;
background-color: #ffffff;
}
@media print {
body {
font: 12pt "Times New Roman", "TimeNR", Times, serif;
}
a { font-weight: bold; color: #004080; text-decoration: underline; }
#main {
background-color: #ffffff;
border-left: 0px;
}
#container {
margin-left: 2%;
margin-right: 2%;
background-color: #ffffff;
}
#content {
padding: 1em;
background-color: #ffffff;
}
#navigation {
display: none;
}
pre.example {
font-family: "Andale Mono", monospace;
font-size: 10pt;
page-break-inside: avoid;
}
}
table.module_list {
border-width: 1px;
border-style: solid;
border-color: #cccccc;
border-collapse: collapse;
}
table.module_list td {
border-width: 1px;
padding: 3px;
border-style: solid;
border-color: #cccccc;
}
table.module_list td.name { background-color: #f0f0f0; ; min-width: 200px; }
table.module_list td.summary { width: 100%; }
table.function_list {
border-width: 1px;
border-style: solid;
border-color: #cccccc;
border-collapse: collapse;
}
table.function_list td {
border-width: 1px;
padding: 3px;
border-style: solid;
border-color: #cccccc;
}
table.function_list td.name { background-color: #f6f6ff; ; min-width: 200px; }
table.function_list td.summary { width: 100%; }
dl.table dt, dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;}
dl.table dd, dl.function dd {padding-bottom: 1em; margin: 10px 0 0 20px;}
dl.table h3, dl.function h3 {font-size: .95em;}
ul.nowrap {
overflow:auto;
whitespace:nowrap;
}
/* stop sublists from having initial vertical space */
ul ul { margin-top: 0px; }
ol ul { margin-top: 0px; }
ol ol { margin-top: 0px; }
ul ol { margin-top: 0px; }
/* make the target distinct; helps when we're navigating to a function */
a:target + * {
background-color: #FF9;
}
/* styles for prettification of source */
pre .comment { color: #558817; }
pre .constant { color: #a8660d; }
pre .escape { color: #844631; }
pre .keyword { color: #aa5050; font-weight: bold; }
pre .library { color: #0e7c6b; }
pre .marker { color: #512b1e; background: #fedc56; font-weight: bold; }
pre .string { color: #8080ff; }
pre .number { color: #f8660d; }
pre .operator { color: #2239a8; font-weight: bold; }
pre .preprocessor, pre .prepro { color: #a33243; }
pre .global { color: #800080; }
pre .user-keyword { color: #800080; }
pre .prompt { color: #558817; }
pre .url { color: #272fc2; text-decoration: underline; }

View File

@ -1,27 +0,0 @@
local data = require("druid.data")
local M = {}
M.interest = {
data.ON_INPUT
}
function M.init(self, callback, params)
self.event = data.A_ANDR_BACK
self.callback = callback
self.params = params
end
--- input handler
-- @param action_id - input action id
-- @param action - input action
function M.on_input(self, action_id, action)
if action[data.RELEASED] then
self.callback(self.parent.parent, self.params)
end
return true
end
return M

View File

@ -0,0 +1,34 @@
--- Component to handle back key
-- @module base.back_handler
local const = require("druid.const")
local M = {}
M.interest = {
const.ON_INPUT
}
--- Component init function
-- @tparam table self component instance
-- @tparam callback callback on back button
-- @tparam[opt] params callback argument
function M.init(self, callback, params)
self.event = const.ACTION_BACK
self.callback = callback
self.params = params
end
--- Input handler for component
-- @tparam string action_id on_input action id
-- @tparam table action on_input action
function M.on_input(self, action_id, action)
if action[const.RELEASED] then
self.callback(self.parent.parent, self.params)
end
return true
end
return M

33
druid/base/blocker.lua Normal file
View File

@ -0,0 +1,33 @@
--- Component to block input on specify zone (node)
-- @module base.blocker
local const = require("druid.const")
local helper = require("druid.helper")
local M = {}
M.interest = {
const.ON_SWIPE
}
function M.init(self, node)
self.node = helper.get_node(node)
self.event = const.ACTION_TOUCH
end
function M.on_input(self, action_id, action)
if not helper.is_enabled(self.node) then
return false
end
if gui.pick_node(self.node, action.x, action.y) then
return true
end
return false
end
return M

View File

@ -1,4 +1,11 @@
local data = require("druid.data")
--- Component to handle basic GUI button
-- @module base.button
-- TODO: Add button mode:
-- Long tap
-- Repeated tap
local const = require("druid.const")
local ui_animate = require("druid.helper.druid_animate")
local settings = require("druid.settings")
local helper = require("druid.helper")
@ -6,18 +13,18 @@ local b_settings = settings.button
local M = {}
M.interest = {
data.ON_INPUT
const.ON_INPUT
}
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
M.DEFAULT_ACTIVATION_TIME = 0.2
function M.init(self, node, callback, params, anim_node, event)
self.node = helper.get_node(node)
self.event = data.A_TOUCH
self.event = const.ACTION_TOUCH
self.anim_node = anim_node and helper.get_node(anim_node) or self.node
self.scale_from = gui.get_scale(self.anim_node)
self.scale_to = self.scale_from + b_settings.SCALE_CHANGE
@ -30,14 +37,23 @@ function M.init(self, node, callback, params, anim_node, event)
self.hover_anim = b_settings.IS_HOVER
self.sound = b_settings.BTN_SOUND
self.sound_disable = b_settings.BTN_SOUND_DISABLED
self.ext_zone = nil
self.click_zone = nil
-- TODO: to separate component "block_input"?
-- If callback is nil, so the buttons is stub and without anim
-- Used for zones, what should dont pass the input to other UI elements
if not callback then
self.stub = true
self.hover_anim = false
self.callback = function() end
end
end
local function set_hover(self, state)
if self.hover_anim and self._is_hovered ~= state then
local target_scale = state and self.scale_hover_to or self.scale_from
ui_animate.scale(self, self.node, target_scale, b_settings.HOVER_TIME)
ui_animate.scale(self, self.anim_node, target_scale, b_settings.HOVER_TIME)
self._is_hovered = state
end
end
@ -47,15 +63,17 @@ local function on_button_release(self)
if not self.disabled then
if not self.stub and self.can_action then
self.can_action = false
self.tap_anim(self)
settings.play_sound(self.sound)
if self.tap_anim then
self.tap_anim(self)
end
self.callback(self.parent.parent, self.params, self)
settings.play_sound(self.sound)
else
set_hover(self, false)
end
return true
else
self.sound_disable()
settings.play_sound(self.sound_disable)
return false
end
end
@ -70,30 +88,32 @@ function M.on_input(self, action_id, action)
end
local is_pick = gui.pick_node(self.node, action.x, action.y)
if self.ext_zone then
is_pick = is_pick and gui.pick_node(self.ext_zone, action.x, action.y)
if self.click_zone then
is_pick = is_pick and gui.pick_node(self.click_zone, action.x, action.y)
end
if is_pick then
if action.pressed then
-- Can interact if start touch on the button
self.can_action = true
return true
end
if action.released then
set_hover(self, false)
return on_button_release(self)
else
set_hover(self, true)
end
return not self.disabled
else
if not is_pick then
-- Can't interact, if touch outside of button
self.can_action = false
set_hover(self, false)
return false
end
if action.pressed then
-- Can interact if start touch on the button
self.can_action = true
self.repeated_counter = 0
return true
end
if action.released then
set_hover(self, false)
return on_button_release(self)
else
set_hover(self, true)
end
return not self.disabled
end
@ -123,21 +143,21 @@ end
function M.deactivate(self, is_animate, callback)
self.disabled = true
if is_animate then
local counter = 0
local clbk = function()
counter = counter + 1
if counter == 3 and callback then
-- callback call three times in gui.animation
local clbk = helper.after(3, function()
if callback then
callback(self.parent.parent)
end
end
end)
ui_animate.color(self, self.node, M.DEFAULT_DEACTIVATE_COLOR,
clbk, M.DEFAUL_ACTIVATION_TIME, 0, gui.EASING_OUTBOUNCE)
clbk, M.DEFAULT_ACTIVATION_TIME, 0, gui.EASING_OUTBOUNCE)
ui_animate.scale_y_from_to(self, self.node, M.DEFAULT_ACTIVATE_SCALE.x,
M.DEFAULT_DEACTIVATE_SCALE.x, clbk, M.DEFAUL_ACTIVATION_TIME, gui.EASING_OUTBOUNCE)
M.DEFAULT_DEACTIVATE_SCALE.x, clbk, M.DEFAULT_ACTIVATION_TIME, gui.EASING_OUTBOUNCE)
ui_animate.scale_x_from_to(self, self.node, M.DEFAULT_ACTIVATE_SCALE.y,
M.DEFAULT_DEACTIVATE_SCALE.y, clbk, M.DEFAUL_ACTIVATION_TIME, gui.EASING_OUTBOUNCE)
M.DEFAULT_DEACTIVATE_SCALE.y, clbk, M.DEFAULT_ACTIVATION_TIME, gui.EASING_OUTBOUNCE)
else
gui.set_color(self.node, M.DEFAULT_DEACTIVATE_COLOR)
gui.set_scale(self.node, M.DEFAULT_DEACTIVATE_SCALE)
@ -150,24 +170,22 @@ end
function M.activate(self, is_animate, callback)
if is_animate then
local counter = 0
local clbk = function()
counter = counter + 1
if counter == 3 then
-- callback call three times in gui.animation
local clbk = helper.after(3, function()
self.disabled = false
if callback then
callback(self.parent.parent)
end
end
end
end)
ui_animate.color(self, self.node, ui_animate.TINT_SHOW,
clbk, M.DEFAUL_ACTIVATION_TIME, 0, gui.EASING_OUTBOUNCE)
clbk, M.DEFAULT_ACTIVATION_TIME, 0, gui.EASING_OUTBOUNCE)
ui_animate.scale_y_from_to(self, self.node, M.DEFAULT_DEACTIVATE_SCALE.x,
M.DEFAULT_ACTIVATE_SCALE.x, clbk, M.DEFAUL_ACTIVATION_TIME, gui.EASING_OUTBOUNCE)
M.DEFAULT_ACTIVATE_SCALE.x, clbk, M.DEFAULT_ACTIVATION_TIME, gui.EASING_OUTBOUNCE)
ui_animate.scale_x_from_to(self, self.node, M.DEFAULT_DEACTIVATE_SCALE.y,
M.DEFAULT_ACTIVATE_SCALE.y, clbk, M.DEFAUL_ACTIVATION_TIME, gui.EASING_OUTBOUNCE)
M.DEFAULT_ACTIVATE_SCALE.y, clbk, M.DEFAULT_ACTIVATION_TIME, gui.EASING_OUTBOUNCE)
else
gui.set_color(self.node, ui_animate.TINT_SHOW)
gui.set_scale(self.node, M.DEFAULT_ACTIVATE_SCALE)
@ -178,10 +196,18 @@ function M.activate(self, is_animate, callback)
end
end
function M.disable_animation(self)
self.hover_anim = false
self.tap_anim = nil
self.back_anim = nil
end
--- Set additional node, what need to be clicked on button click
-- Used, if need setup, what button can be clicked only in special zone
function M.set_ext_zone(self, zone)
self.ext_zone = helper.get_node(zone)
function M.set_click_zone(self, zone)
self.click_zone = helper.get_node(zone)
end

50
druid/base/checkbox.lua Normal file
View File

@ -0,0 +1,50 @@
--- Druid checkbox component
-- @module base.checkbox
local helper = require("druid.helper")
local M = {}
local function state_animate(node, state)
local target = state and 1 or 0
gui.animate(node, "color.w", target, gui.EASING_OUTSINE, 0.1)
end
function M.set_state(self, state, is_silence)
if self.state == state then
return
end
self.state = state
state_animate(self.node, state)
if not is_silence and self.callback then
self.callback(self.parent.parent, state)
end
end
function M.get_state(self)
return self.state
end
-- TODO: pass self as first parameter
local function on_click(context, self)
M.set_state(self, not self.state)
end
function M.init(self, node, callback, click_node)
self.node = helper.get_node(node)
self.click_node = helper.get_node(click_node)
self.callback = callback
self.button = self.parent:new_button(self.click_node or self.node, on_click, self)
M.set_state(self, false, true)
end
return M

View File

@ -0,0 +1,49 @@
--- Checkboux group module
-- @module base.checkbox_group
local M = {}
local function on_checkbox_click(self, index)
if self.callback then
self.callback(self.parent.parent, index)
end
end
function M.set_state(self, indexes)
for i = 1, #indexes do
if self.checkbox[indexes[i]] then
self.checkbox[indexes[i]]:set_state(true, true)
end
end
end
function M.get_state(self)
local result = {}
for i = 1, #self.checkboxes do
table.insert(result, self.checkboxes[i]:get_state())
end
return result
end
function M.init(self, nodes, callback, click_nodes)
self.checkboxes = {}
self.callback = callback
for i = 1, #nodes do
local click_node = click_nodes and click_nodes[i] or nil
local checkbox = self.parent:new_checkbox(nodes[i], function()
on_checkbox_click(self, i)
end, click_node)
table.insert(self.checkboxes, checkbox)
end
end
return M

View File

@ -1,11 +1,11 @@
--- Component to handle placing components by row and columns.
-- Grid can anchor your elements, get content size and other
-- @module base.grid
local helper = require("druid.helper")
local M = {}
--- Sort and placing nodes
-- Plans: placing by max width, placing with max in_row
-- Allow different node sizes, allow animation with node insert
function M.init(self, parent, element, in_row)
self.parent = helper.get_node(parent)

View File

@ -1,4 +1,7 @@
local data = require("druid.data")
--- Component to handle progress bars
-- @module base.progress
local const = require("druid.const")
local helper = require("druid.helper")
local settings = require("druid.settings")
local p_settings = settings.progress
@ -6,7 +9,7 @@ local p_settings = settings.progress
local M = {}
M.interest = {
data.ON_UPDATE,
const.ON_UPDATE,
}
local PROP_Y = "y"

View File

@ -1,5 +1,8 @@
--- Component to handle scroll content
-- @module base.scroll
local helper = require("druid.helper")
local data = require("druid.data")
local const = require("druid.const")
local settings = require("druid.settings").scroll
local M = {}
@ -8,8 +11,8 @@ local SIDE_X = "x"
local SIDE_Y = "y"
M.interest = {
data.ON_UPDATE,
data.ON_SWIPE,
const.ON_UPDATE,
const.ON_SWIPE,
}
-- Global on all scrolls
@ -99,10 +102,7 @@ end
local function get_zone_center(self)
local pos = vmath.vector3(self.pos)
pos.x = pos.x + self.center_offset.x
pos.y = pos.y + self.center_offset.y
return pos
return self.pos + self.center_offset
end
@ -257,7 +257,7 @@ end
function M.on_input(self, action_id, action)
if action_id ~= data.A_TOUCH then
if action_id ~= const.ACTION_TOUCH then
return false
end
local inp = self.input
@ -289,18 +289,21 @@ function M.on_input(self, action_id, action)
M.current_scroll = self
end
end
if M.current_scroll == self then
add_delta(self, action.dx, action.dy)
result = true
end
end
end
if inp.touch and not action.pressed then
if M.current_scroll == self then
add_delta(self, action.dx, action.dy)
result = true
end
end
if action.released then
inp.touch = false
inp.side = false
if M.current_scroll == self then
M.current_scroll = nil
inp.touch = false
inp.side = false
result = true
end
check_threshold(self)
@ -310,8 +313,12 @@ function M.on_input(self, action_id, action)
end
--- Start scroll to point (x, y, z)
function M.scroll_to(self, point)
--- Start scroll to target point
-- @tparam point vector3 target point
-- @tparam[opt] bool is_instant instant scroll flag
-- @usage scroll:scroll_to(vmath.vector3(0, 50, 0))
-- @usage scroll:scroll_to(vmath.vector3(0), true)
function M.scroll_to(self, point, is_instant)
local b = self.border
local target = vmath.vector3(point)
target.x = helper.clamp(point.x - self.center_offset.x, b.x, b.z)
@ -319,12 +326,18 @@ function M.scroll_to(self, point)
cancel_animate(self)
self.animate = true
gui.animate(self.node, gui.PROP_POSITION, target, gui.EASING_OUTSINE, settings.ANIM_SPEED, 0, function()
self.animate = false
self.animate = not is_instant
if is_instant then
self.target = target
set_pos(self, target)
end)
else
gui.animate(self.node, gui.PROP_POSITION, target, gui.EASING_OUTSINE, settings.ANIM_SPEED, 0, function()
self.animate = false
self.target = target
set_pos(self, target)
end)
end
end

93
druid/base/slider.lua Normal file
View File

@ -0,0 +1,93 @@
--- Druid slider component
-- @module base.slider
local helper = require("druid.helper")
local const = require("druid.const")
local M = {}
M.interest = {
const.ON_SWIPE
}
local function on_change_value(self)
if self.callback then
self.callback(self.parent.parent, self.value)
end
end
function M.init(self, node, end_pos, callback)
self.node = helper.get_node(node)
self.start_pos = gui.get_position(self.node)
self.pos = gui.get_position(self.node)
self.target_pos = self.pos
self.end_pos = end_pos
self.dist = self.end_pos - self.start_pos
self.is_drag = false
self.value = 0
self.callback = callback
assert(self.dist.x == 0 or self.dist.y == 0, "Slider for now can be only vertical or horizontal")
end
function M.on_input(self, action_id, action)
if action_id ~= const.ACTION_TOUCH then
return false
end
if gui.pick_node(self.node, action.x, action.y) then
if action.pressed then
self.pos = gui.get_position(self.node)
self.is_drag = true
end
end
if self.is_drag and not action.pressed then
-- move
self.pos.x = self.pos.x + action.dx
self.pos.y = self.pos.y + action.dy
local prev_x = self.target_pos.x
local prev_y = self.target_pos.y
self.target_pos.x = helper.clamp(self.pos.x, self.start_pos.x, self.end_pos.x)
self.target_pos.y = helper.clamp(self.pos.y, self.start_pos.y, self.end_pos.y)
gui.set_position(self.node, self.target_pos)
if prev_x ~= self.target_pos.x or prev_y ~= self.target_pos.y then
if self.dist.x > 0 then
self.value = (self.target_pos.x - self.start_pos.x) / self.dist.x
end
if self.dist.y > 0 then
self.value = (self.target_pos.y - self.start_pos.y) / self.dist.y
end
on_change_value(self)
end
end
if action.released then
self.is_drag = false
end
return self.is_drag
end
function M.set(self, value)
value = helper.clamp(value, 0, 1)
gui.set_position(self.node, self.start_pos + self.dist * value)
self.value = value
on_change_value(self)
end
return M

View File

@ -1,10 +1,14 @@
local data = require("druid.data")
--- Component to handle all GUI texts
-- Good working with localization system
-- @module base.text
local const = require("druid.const")
local settings = require("druid.settings")
local helper = require("druid.helper")
local M = {}
M.interest = {
data.ON_CHANGE_LANGUAGE,
const.ON_CHANGE_LANGUAGE,
}

View File

@ -1,14 +1,18 @@
local data = require("druid.data")
--- Component to handle GUI timers
-- @module base.timer
local const = require("druid.const")
local formats = require("druid.helper.formats")
local helper = require("druid.helper")
local M = {}
M.interest = {
data.ON_UPDATE
const.ON_UPDATE
}
local empty = function() end
function M.init(self, node, seconds_from, seconds_to, callback)
self.node = helper.get_node(node)
seconds_from = math.max(seconds_from, 0)

View File

@ -1,31 +1,32 @@
--- Druid constants
-- @module constants
local M = {}
-- actions
M.A_TOUCH = hash("touch")
M.A_TEXT = hash("text")
M.A_BACKSPACE = hash("backspace")
M.A_ENTER = hash("enter")
M.A_ANDR_BACK = hash("back")
-- Actions
M.ACTION_TOUCH = hash("touch")
M.ACTION_TEXT = hash("text")
M.ACTION_BACKSPACE = hash("backspace")
M.ACTION_ENTER = hash("enter")
M.ACTION_BACK = hash("back")
M.RELEASED = "released"
M.PRESSED = "pressed"
--- interests
--- Interests
M.ON_MESSAGE = hash("on_message")
M.ON_UPDATE = hash("on_update")
-- input
-- Input
M.ON_SWIPE = hash("on_swipe")
M.ON_INPUT = hash("on_input")
M.ui_input = {
[M.ON_SWIPE] = true,
[M.ON_INPUT] = true
}
-- ui messages
-- UI messages
M.ON_CHANGE_LANGUAGE = hash("on_change_language")
M.ON_LAYOUT_CHANGED = hash("on_layout_changed")

View File

@ -1,4 +1,10 @@
local data = require("druid.data")
--- Druid UI Library.
-- Component based UI library to make your life easier.
-- Contains a lot of base components and give API
-- to create your own rich components.
-- @module druid
local const = require("druid.const")
local druid_input = require("druid.helper.druid_input")
local settings = require("druid.settings")
@ -7,9 +13,11 @@ local M = {}
local log = settings.log
local _fct_metatable = {}
--- Basic components
M.comps = {
button = require("druid.base.button"),
android_back = require("druid.base.android_back"),
back_handler = require("druid.base.back_handler"),
blocker = require("druid.base.blocker"),
text = require("druid.base.text"),
timer = require("druid.base.timer"),
progress = require("druid.base.progress"),
@ -17,6 +25,8 @@ M.comps = {
scroll = require("druid.base.scroll"),
checkbox = require("druid.base.checkbox"),
radio_group = require("druid.base.radio_group"),
checkbox_group = require("druid.base.checkbox_group"),
slider = require("druid.base.slider"),
progress_rich = require("druid.rich.progress_rich"),
}
@ -33,6 +43,9 @@ local function register_basic_components()
end
--- Register external module
-- @tparam string name module name
-- @tparam table module lua table with module
function M.register(name, module)
-- TODO: Find better solution to creating elements?
_fct_metatable["new_" .. name] = function(self, ...)
@ -78,7 +91,7 @@ local function create(self, module)
end
self[v][#self[v] + 1] = instance
if data.ui_input[v] then
if const.ui_input[v] then
input_init(self)
end
end
@ -122,7 +135,7 @@ end
--- Called on_message
function _fct_metatable.on_message(self, message_id, message, sender)
local specific_ui_message = data.specific_ui_messages[message_id]
local specific_ui_message = const.specific_ui_messages[message_id]
if specific_ui_message then
local array = self[message_id]
if array then
@ -133,7 +146,7 @@ function _fct_metatable.on_message(self, message_id, message, sender)
end
end
else
local array = self[data.ON_MESSAGE]
local array = self[const.ON_MESSAGE]
if array then
for i = 1, #array do
array[i]:on_message(message_id, message, sender)
@ -144,10 +157,10 @@ end
local function notify_input_on_swipe(self)
if self[data.ON_INPUT] then
local len = #self[data.ON_INPUT]
if self[const.ON_INPUT] then
local len = #self[const.ON_INPUT]
for i = len, 1, -1 do
local comp = self[data.ON_INPUT][i]
local comp = self[const.ON_INPUT][i]
if comp.on_swipe then
comp:on_swipe()
end
@ -171,7 +184,7 @@ end
--- Called ON_INPUT
function _fct_metatable.on_input(self, action_id, action)
local array = self[data.ON_SWIPE]
local array = self[const.ON_SWIPE]
if array then
local v, result
local len = #array
@ -184,7 +197,7 @@ function _fct_metatable.on_input(self, action_id, action)
return true
end
end
array = self[data.ON_INPUT]
array = self[const.ON_INPUT]
if array then
local v
local len = #array
@ -202,7 +215,7 @@ end
--- Called on_update
function _fct_metatable.update(self, dt)
local array = self[data.ON_UPDATE]
local array = self[const.ON_UPDATE]
if array then
for i = 1, #array do
array[i]:update(dt)

View File

@ -1,20 +1,81 @@
--- Druid helper module
-- @module helper
local M = {}
function M.centrate_text_with_icon(text_node, icon_node, offset_x)
offset_x = offset_x or 0
local metr = gui.get_text_metrics_from_node(text_node)
local scl = gui.get_scale(text_node).x
local pos = gui.get_position(text_node)
local scl_i = gui.get_scale(icon_node).x
local pos_i = gui.get_position(icon_node)
local w = metr.width * scl -- text width
local icon_w = gui.get_size(icon_node).x * scl_i -- icon width
local width = w + icon_w
--- Text node or icon node can be nil
local function get_text_width(text_node)
if text_node then
local text_metrics = gui.get_text_metrics_from_node(text_node)
local text_scale = gui.get_scale(text_node).x
return text_metrics.width * text_scale
end
pos.x = -width/2 + w + offset_x
gui.set_position(text_node, pos)
pos_i.x = width/2 - icon_w + offset_x
gui.set_position(icon_node, pos_i)
return 0
end
local function get_icon_width(icon_node)
if icon_node then
local icon_scale_x = gui.get_scale(icon_node).x
return gui.get_size(icon_node).x * icon_scale_x -- icon width
end
return 0
end
--- Text node or icon node can be nil
function M.centrate_text_with_icon(text_node, icon_node, margin)
margin = margin or 0
local text_width = get_text_width(text_node)
local icon_width = get_icon_width(icon_node)
local width = text_width + icon_width
if text_node then
local pos = gui.get_position(text_node)
pos.x = -width/2 + text_width - margin/2
gui.set_position(text_node, pos)
end
if icon_node then
local icon_pos = gui.get_position(icon_node)
icon_pos.x = width/2 - icon_width + margin/2
gui.set_position(icon_node, icon_pos)
end
end
--- Icon node or text node can be nil
function M.centrate_icon_with_text(icon_node, text_node, margin)
margin = margin or 0
local icon_width = get_icon_width(icon_node)
local text_width = get_text_width(text_node)
local width = text_width + icon_width
if text_node then
local pos = gui.get_position(text_node)
pos.x = width/2 - text_width + margin/2
gui.set_position(text_node, pos)
end
if icon_node then
local icon_pos = gui.get_position(icon_node)
icon_pos.x = -width/2 + icon_width - margin/2
gui.set_position(icon_node, icon_pos)
end
end
-- call callback after count calls
function M.after(count, callback)
local closure = function()
count = count - 1
if count <= 0 then
callback()
end
end
return closure
end
@ -96,4 +157,5 @@ function M.get_pivot_offset(pivot)
return pivots[pivot]
end
return M

View File

@ -1,3 +1,6 @@
--- Druid helper module for animating GUI nodes
-- @module helper.animate
local M = {}
local PROP_SCALE = gui.PROP_SCALE

View File

@ -1,3 +1,7 @@
--- Druid inner module to acquire/release input
-- @module helper.input
-- @local
local M = {}
local ADD_FOCUS = hash("acquire_input_focus")

View File

@ -1,10 +1,13 @@
--- Druid module with utils on string formats
-- @module helper.formats
local M = {}
local ZERO = "0"
-- Return number with zero number prefix
-- @param num - number for conversion
-- @param count - count of numerals
--- Return number with zero number prefix
-- @param num number for conversion
-- @param count count of numerals
-- @return string with need count of zero (1,3) -> 001
function M.add_prefix_zeros(num, count)
local result = tostring(num)
@ -15,8 +18,8 @@ function M.add_prefix_zeros(num, count)
end
-- Convert seconds to string minutes:seconds
-- @param num - number of seconds
--- Convert seconds to string minutes:seconds
-- @param num number of seconds
-- @return string minutes:seconds
function M.second_string_min(sec)
local mins = math.floor(sec / 60)
@ -25,11 +28,11 @@ function M.second_string_min(sec)
end
-- Interpolate string with named Parameters in Table
-- @param s - string for interpolate
-- @param tab - table with parameters
--- Interpolate string with named Parameters in Table
-- @param s string for interpolate
-- @param tab table with parameters
-- @return string with replaced parameters
function M.interpolate_strinng(s, tab)
function M.interpolate_string(s, tab)
return (s:gsub('($%b{})', function(w) return tab[w:sub(3, -2)] or w end))
end

View File

@ -1,7 +1,10 @@
--- Druid settings file
-- @module settings
local M = {}
-- TODO: to JSON?
M.is_debug = false
M.button = {
IS_HOVER = true,
IS_HOLD = true,
@ -33,13 +36,13 @@ M.scroll = {
}
function M.get_text(name)
-- override to get text for localized text
return "locales not inited"
-- override to get text for localized text
return "[Druid]: locales not inited"
end
function M.play_sound(name)
-- override to play sound with name
-- override to play sound with name
end