Update README

Button key trigger will not consume input
Update scroll position while animate
Add scroll:set_view_size
Better static grid get_index function
Rework Data List (only static grid)
Update Default style
Remove a component from parent if exists on druid:remove
This commit is contained in:
Insality 2024-09-03 21:48:43 +03:00
parent 0aeb0b3fea
commit 4a095a2a24
18 changed files with 161 additions and 233 deletions

View File

@ -17,7 +17,7 @@ jobs:
- name: Build && Run - name: Build && Run
run: | run: |
deployer_url="https://raw.githubusercontent.com/Insality/defold-deployer/1/deployer.sh" deployer_url="https://raw.githubusercontent.com/Insality/defold-deployer/1/deployer.sh"
curl -s ${deployer_url} | bash -s lbd --headless --settings ./unit_test.txt curl -s ${deployer_url} | bash -s lbd --headless --settings ./test/test.ini
- name: Upload test report - name: Upload test report
run: bash <(curl -s https://codecov.io/bash) run: bash <(curl -s https://codecov.io/bash)

4
.gitignore vendored
View File

@ -10,4 +10,8 @@ Thumbs.db
builtins builtins
dist dist
deployer_version_settings.txt deployer_version_settings.txt
.deployer_cache .deployer_cache
bob*.jar
manifest.private.der
manifest.public.der

View File

@ -1,11 +1,11 @@
[![](media/druid_logo.png)](https://insality.github.io/druid/) [![](media/druid_logo.png)](https://insality.github.io/druid/)
[![Github-sponsors](https://img.shields.io/badge/sponsor-30363D?style=for-the-badge&logo=GitHub-Sponsors&logoColor=#EA4AAA)](https://github.com/sponsors/insality) [![Ko-Fi](https://img.shields.io/badge/Ko--fi-F16061?style=for-the-badge&logo=ko-fi&logoColor=white)](https://ko-fi.com/insality) [![BuyMeACoffee](https://img.shields.io/badge/Buy%20Me%20a%20Coffee-ffdd00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black)](https://www.buymeacoffee.com/insality) [![GitHub release (latest by date)](https://img.shields.io/github/v/tag/insality/druid?style=for-the-badge&label=Release)](https://github.com/Insality/druid/tags)
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/insality/druid/ci-workflow.yml?branch=master&style=for-the-badge)](https://github.com/Insality/druid/actions)
[![codecov](https://img.shields.io/codecov/c/github/Insality/druid?style=for-the-badge)](https://codecov.io/gh/Insality/druid)
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/insality/druid)](https://github.com/Insality/druid/releases) [![Github-sponsors](https://img.shields.io/badge/sponsor-30363D?style=for-the-badge&logo=GitHub-Sponsors&logoColor=#EA4AAA)](https://github.com/sponsors/insality) [![Ko-Fi](https://img.shields.io/badge/Ko--fi-F16061?style=for-the-badge&logo=ko-fi&logoColor=white)](https://ko-fi.com/insality) [![BuyMeACoffee](https://img.shields.io/badge/Buy%20Me%20a%20Coffee-ffdd00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black)](https://www.buymeacoffee.com/insality)
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/insality/druid/ci-workflow.yml?branch=master)](https://github.com/Insality/druid/actions)
[![codecov](https://codecov.io/gh/Insality/druid/branch/master/graph/badge.svg)](https://codecov.io/gh/Insality/druid)
**Druid** - powerful **Defold** component UI framework that empowers developers to create stunning and customizable GUIs by leveraging a wide range of embedded components or effortlessly designing their own game-specific components. **Druid** - powerful **Defold** component UI framework that empowers developers to create stunning and customizable GUIs by leveraging a wide range of embedded components or effortlessly designing their own game-specific components.
@ -23,11 +23,19 @@ To integrate the **Druid** extension into your own project, add this project as
Here is a list of [all releases](https://github.com/Insality/druid/releases). Here is a list of [all releases](https://github.com/Insality/druid/releases).
Size: **67.16 KB**
> The size metrics exlcude the extended components, which are including only on demand. ### Library Size
> **Note:** The library size is calculated based on the build report per platform. The extended components are exlcuded, which are including only on demand.
| Platform | Library Size |
| ---------------- | ------------- |
| HTML5 | **38.96 KB** |
| Desktop / Mobile | **65.97 KB** |
### Input Bindings ### Input Bindings
**Druid** utilizes the `/builtins/input/all.input_binding` input bindings. For custom input bindings, refer to the Input Binding section in the **_[Advanced Setup](docs_md/advanced-setup.md#input-bindings)_**. **Druid** utilizes the `/builtins/input/all.input_binding` input bindings. For custom input bindings, refer to the Input Binding section in the **_[Advanced Setup](docs_md/advanced-setup.md#input-bindings)_**.
@ -173,8 +181,6 @@ Each example page provides a direct link to the corresponding example code, maki
Or refer directly to the [**example folder**](https://github.com/Insality/druid/tree/develop/example) for code examples demonstrating how to use **Druid**. Or refer directly to the [**example folder**](https://github.com/Insality/druid/tree/develop/example) for code examples demonstrating how to use **Druid**.
If you want to see examples of GUIs created with Druid, please refer to the [game_examples.md](docs_md/game_examples.md) file.
## Documentation ## Documentation
To better understand **Druid**, read the following documentation: To better understand **Druid**, read the following documentation:

View File

@ -1,8 +0,0 @@
# Game Examples
## Family Island
## Sea Battle: Universe
## Monkey Mart

View File

@ -588,6 +588,7 @@ local druid__hotkey__style = {}
---@class druid.hover : druid.base_component ---@class druid.hover : druid.base_component
---@field node node Hover node
---@field on_hover druid.event On hover callback(self, state, hover_instance) ---@field on_hover druid.event On hover callback(self, state, hover_instance)
---@field on_mouse_hover druid.event On mouse hover callback(self, state, hover_instance) ---@field on_mouse_hover druid.event On mouse hover callback(self, state, hover_instance)
local druid__hover = {} local druid__hover = {}
@ -998,9 +999,10 @@ function druid__rich_text.init(self, template, nodes) end
function druid__rich_text.set_text(self, text) end function druid__rich_text.set_text(self, text) end
--- Get all words, which has a passed tag. --- Get all words, which has a passed tag.
---@param self druid.rich_text @{RichText}
---@param tag string ---@param tag string
---@return druid.rich_text.word[] words ---@return druid.rich_text.word[] words
function druid__rich_text.tagged(tag) end function druid__rich_text.tagged(self, tag) end
---@class druid.rich_text.style ---@class druid.rich_text.style

View File

@ -343,6 +343,7 @@ function Button.on_input(self, action_id, action)
return false return false
end end
local is_consume = true
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
@ -365,6 +366,7 @@ function Button.on_input(self, action_id, action)
if is_key_trigger then if is_key_trigger then
self.hover:set_hover(not action.released) self.hover:set_hover(not action.released)
is_consume = false
end end
if action.pressed then if action.pressed then
@ -380,19 +382,19 @@ function Button.on_input(self, action_id, action)
on_button_click(self) on_button_click(self)
end) end)
end end
return true return is_consume
end end
-- 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 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 is_consume
end end
end end
if action.released then if action.released then
return on_button_release(self) return on_button_release(self) and is_consume
end end
if self.can_action and self.on_long_click:is_exist() then if self.can_action and self.on_long_click:is_exist() then
@ -400,16 +402,16 @@ function Button.on_input(self, action_id, action)
if self.style.AUTOHOLD_TRIGGER <= press_time then if self.style.AUTOHOLD_TRIGGER <= press_time then
on_button_release(self) on_button_release(self)
return true return is_consume
end end
if press_time >= self.style.LONGTAP_TIME then if press_time >= self.style.LONGTAP_TIME then
on_button_hold(self, press_time) on_button_hold(self, press_time)
return true return is_consume
end end
end end
return not self.disabled return not self.disabled and is_consume
end end

View File

@ -5,6 +5,9 @@
-- @within BaseComponent -- @within BaseComponent
-- @alias druid.hover -- @alias druid.hover
--- Hover node
-- @tfield node node
--- On hover callback(self, state, hover_instance) --- On hover callback(self, state, hover_instance)
-- @tfield DruidEvent on_hover @{DruidEvent} -- @tfield DruidEvent on_hover @{DruidEvent}

View File

@ -216,6 +216,12 @@ end
function Scroll.update(self, dt) function Scroll.update(self, dt)
if self.is_animate then
self.position.x = gui.get(self.content_node, "position.x")
self.position.y = gui.get(self.content_node, "position.y")
self.on_scroll:trigger(self:get_context(), self.position)
end
if self.drag.is_drag then if self.drag.is_drag then
self:_update_hand_scroll(dt) self:_update_hand_scroll(dt)
else else
@ -255,12 +261,12 @@ function Scroll.scroll_to(self, point, is_instant)
if is_instant then if is_instant then
self.target_position = target self.target_position = target
self:_set_scroll_position(target) self:_set_scroll_position(target.x, target.y)
else else
gui.animate(self.content_node, gui.PROP_POSITION, target, gui.EASING_OUTSINE, self.style.ANIM_SPEED, 0, function() gui.animate(self.content_node, gui.PROP_POSITION, target, gui.EASING_OUTSINE, self.style.ANIM_SPEED, 0, function()
self.is_animate = false self.is_animate = false
self.target_position = target self.target_position = target
self:_set_scroll_position(target) self:_set_scroll_position(target.x, target.y)
end) end)
end end
@ -305,6 +311,13 @@ function Scroll.scroll_to_percent(self, percent, is_instant)
0 0
) )
if not self.drag.can_x then
pos.x = self.position.x
end
if not self.drag.can_y then
pos.y = self.position.y
end
self:scroll_to(pos, is_instant) self:scroll_to(pos, is_instant)
end end
@ -338,6 +351,20 @@ function Scroll.set_size(self, size, offset)
end end
--- Set scroll view size.
-- @tparam Scroll self @{Scroll}
-- @tparam vector3 size The new size for view node
-- @treturn druid.scroll Current scroll instance
function Scroll.set_view_size(self, size)
gui.set_size(self.view_node, size)
self.view_size = size
self.view_border = helper.get_border(self.view_node)
self:_update_size()
return self
end
--- Enable or disable scroll inert. --- Enable or disable scroll inert.
-- If disabled, scroll through points (if exist) -- If disabled, scroll through points (if exist)
-- If no points, just simple drag without inertion -- If no points, just simple drag without inertion
@ -583,14 +610,14 @@ function Scroll._cancel_animate(self)
end end
function Scroll._set_scroll_position(self, position) function Scroll._set_scroll_position(self, position_x, position_y)
local available_extra = self.available_pos_extra local available_extra = self.available_pos_extra
position.x = helper.clamp(position.x, available_extra.x, available_extra.z) position_x = helper.clamp(position_x, available_extra.x, available_extra.z)
position.y = helper.clamp(position.y, available_extra.w, available_extra.y) position_y = helper.clamp(position_y, available_extra.w, available_extra.y)
if self.position.x ~= position.x or self.position.y ~= position.y then if self.position.x ~= position_x or self.position.y ~= position_y then
self.position.x = position.x self.position.x = position_x
self.position.y = position.y self.position.y = position_y
gui.set_position(self.content_node, self.position) gui.set_position(self.content_node, self.position)
self.on_scroll:trigger(self:get_context(), self.position) self.on_scroll:trigger(self:get_context(), self.position)
@ -692,7 +719,7 @@ function Scroll._update_free_scroll(self, dt)
self:_check_soft_zone() self:_check_soft_zone()
if self.position.x ~= target.x or self.position.y ~= target.y then if self.position.x ~= target.x or self.position.y ~= target.y then
self:_set_scroll_position(target) self:_set_scroll_position(target.x, target.y)
end end
end end
@ -704,7 +731,7 @@ function Scroll._update_hand_scroll(self, dt)
self.inertion.x = (self.inertion.x + dx) * self.style.FRICT_HOLD self.inertion.x = (self.inertion.x + dx) * self.style.FRICT_HOLD
self.inertion.y = (self.inertion.y + dy) * self.style.FRICT_HOLD self.inertion.y = (self.inertion.y + dy) * self.style.FRICT_HOLD
self:_set_scroll_position(self.target_position) self:_set_scroll_position(self.target_position.x, self.target_position.y)
end end
@ -746,14 +773,14 @@ function Scroll._update_size(self)
content_border_extra.w = content_border_extra.w - stretch_size * sign_y content_border_extra.w = content_border_extra.w - stretch_size * sign_y
if not self.style.SMALL_CONTENT_SCROLL then if not self.style.SMALL_CONTENT_SCROLL then
self.drag.can_x = content_size.x > self.view_size.x self.drag.can_x = content_size.x > self.view_size.x and self._is_horizontal_scroll
self.drag.can_y = content_size.y > self.view_size.y self.drag.can_y = content_size.y > self.view_size.y and self._is_vertical_scroll
end end
self.available_pos_extra = get_border_vector(self.view_border - content_border_extra, self._offset) self.available_pos_extra = get_border_vector(self.view_border - content_border_extra, self._offset)
self.available_size_extra = get_size_vector(self.available_pos_extra) self.available_size_extra = get_size_vector(self.available_pos_extra)
self:_set_scroll_position(self.position) self:_set_scroll_position(self.position.x, self.position.y)
self.target_position.x = self.position.x self.target_position.x = self.position.x
self.target_position.y = self.position.y self.target_position.y = self.position.y
end end
@ -788,7 +815,7 @@ function Scroll._process_scroll_wheel(self, action_id, action)
self.inertion.x = 0 self.inertion.x = 0
end end
self:_set_scroll_position(self.target_position) self:_set_scroll_position(self.target_position.x, self.target_position.y)
end end
return true return true

View File

@ -171,8 +171,12 @@ end
-- @tparam vector3 pos The node position in the grid -- @tparam vector3 pos The node position in the grid
-- @treturn number The node index -- @treturn number The node index
function StaticGrid.get_index(self, pos) function StaticGrid.get_index(self, pos)
local col = pos.x / self.node_size.x + 1 -- Offset to left-top corner from node pivot
local row = -pos.y / self.node_size.y local node_offset_x = self.node_size.x * (-0.5 + self.node_pivot.x)
local node_offset_y = self.node_size.y * (0.5 - self.node_pivot.y)
local col = (pos.x + node_offset_x) / self.node_size.x + 1
local row = -(pos.y + node_offset_y) / self.node_size.y
col = helper.round(col) col = helper.round(col)
row = helper.round(row) row = helper.round(row)
@ -337,6 +341,7 @@ function StaticGrid.clear(self)
self:_update() self:_update()
self.on_clear:trigger(self:get_context()) self.on_clear:trigger(self:get_context())
self.on_change_items:trigger(self:get_context())
return self return self
end end

View File

@ -336,7 +336,7 @@ function BaseComponent.setup_component(self, druid_instance, context, style, ins
self:set_template("") self:set_template("")
if self._meta.parent then if self._meta.parent then
self._meta.parent:__add_children(self) self._meta.parent:__add_child(self)
end end
return self return self
@ -445,8 +445,8 @@ end
-- @tparam BaseComponent self @{BaseComponent} -- @tparam BaseComponent self @{BaseComponent}
-- @tparam component children The druid component instance -- @tparam component children The druid component instance
-- @local -- @local
function BaseComponent.__add_children(self, children) function BaseComponent.__add_child(self, child)
table.insert(self._meta.children, children) table.insert(self._meta.children, child)
end end
@ -454,10 +454,11 @@ end
-- @tparam BaseComponent self @{BaseComponent} -- @tparam BaseComponent self @{BaseComponent}
-- @tparam component children The druid component instance -- @tparam component children The druid component instance
-- @local -- @local
function BaseComponent.__remove_children(self, children) function BaseComponent.__remove_child(self, child)
for i = #self._meta.children, 1, -1 do for i = #self._meta.children, 1, -1 do
if self._meta.children[i] == children then if self._meta.children[i] == child then
table.remove(self._meta.children, i) table.remove(self._meta.children, i)
return true
end end
end end
end end

View File

@ -24,6 +24,7 @@
--- ---
local component = require("druid.component") local component = require("druid.component")
local input = require("druid.extended.input")
local RichInput = component.create("druid.rich_input") local RichInput = component.create("druid.rich_input")
@ -74,7 +75,7 @@ function RichInput.init(self, template, nodes)
self.druid = self:get_druid() self.druid = self:get_druid()
self.root = self:get_node(SCHEME.ROOT) self.root = self:get_node(SCHEME.ROOT)
self.input = self.druid:new_input(self:get_node(SCHEME.BUTTON), self:get_node(SCHEME.INPUT)) self.input = self.druid:new(input, self:get_node(SCHEME.BUTTON), self:get_node(SCHEME.INPUT))
self.cursor = self:get_node(SCHEME.CURSOR) self.cursor = self:get_node(SCHEME.CURSOR)
self.input:set_text("") self.input:set_text("")

View File

@ -227,9 +227,10 @@ end
--- Get all words, which has a passed tag. --- Get all words, which has a passed tag.
-- @tparam RichText self @{RichText}
-- @tparam string tag -- @tparam string tag
-- @treturn druid.rich_text.word[] words -- @treturn druid.rich_text.word[] words
function RichText:tagged(tag) function RichText.tagged(self, tag)
if not self._words then if not self._words then
return return
end end

View File

@ -102,7 +102,7 @@ function M.unsubscribe(self, callback, callback_context)
return false return false
end end
tremove(self, event_index --[[@as number]]) tremove(self, event_index)
return true return true
end end

View File

@ -15,12 +15,6 @@
--- The Druid Grid component --- The Druid Grid component
-- @tfield StaticGrid|DynamicGrid grid @{StaticGrid}, @{DynamicGrid} -- @tfield StaticGrid|DynamicGrid grid @{StaticGrid}, @{DynamicGrid}
--- The current visual top data index
-- @tfield number top_index
--- The current visual last data index
-- @tfield number last_index
--- The current progress of scroll posititon --- The current progress of scroll posititon
-- @tfield number scroll_progress -- @tfield number scroll_progress
@ -49,13 +43,11 @@ local DataList = component.create("data_list")
-- @tparam StaticGrid|DynamicGrid grid The @{StaticGrid} or @{DynamicGrid} instance for Data List component -- @tparam StaticGrid|DynamicGrid grid The @{StaticGrid} or @{DynamicGrid} instance for Data List component
-- @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])
function DataList.init(self, scroll, grid, create_function) function DataList.init(self, scroll, grid, create_function)
self.druid = self:get_druid()
self.scroll = scroll self.scroll = scroll
self.grid = grid self.grid = grid
if self.grid.style then if self.grid.style then
self.grid.style.IS_DYNAMIC_NODE_POSES = false self.grid.style.IS_DYNAMIC_NODE_POSES = false
end end
self.scroll:bind_grid(grid)
-- Current visual elements indexes -- Current visual elements indexes
self.top_index = 1 self.top_index = 1
@ -64,25 +56,21 @@ function DataList.init(self, scroll, grid, create_function)
self._create_function = create_function self._create_function = create_function
self._data = {} self._data = {}
self._data_first_index = false
self._data_last_index = false
self._data_length = 0
self._data_visual = {} self._data_visual = {}
self.scroll.on_scroll:subscribe(self._check_elements, self) self.scroll.on_scroll:subscribe(self._refresh, self)
self.on_scroll_progress_change = Event() self.on_scroll_progress_change = Event()
self.on_element_add = Event() self.on_element_add = Event()
self.on_element_remove = Event() self.on_element_remove = Event()
self:set_data()
end end
--- Druid System on_remove function --- Druid System on_remove function
-- @tparam DataList self @{DataList} -- @tparam DataList self @{DataList}
function DataList.on_remove(self) function DataList.on_remove(self)
self.scroll.on_scroll:unsubscribe(self._check_elements, self) self:clear()
self.scroll.on_scroll:unsubscribe(self._refresh, self)
end end
@ -92,7 +80,7 @@ end
-- @treturn druid.data_list Current DataList instance -- @treturn druid.data_list Current DataList instance
function DataList.set_data(self, data) function DataList.set_data(self, data)
self._data = data or {} self._data = data or {}
self:_update_data_info() self.scroll:set_size(self.grid:get_size_for(#self._data))
self:_refresh() self:_refresh()
return self return self
@ -118,8 +106,7 @@ function DataList.add(self, data, index, shift_policy)
shift_policy = shift_policy or const.SHIFT.RIGHT shift_policy = shift_policy or const.SHIFT.RIGHT
helper.insert_with_shift(self._data, data, index, shift_policy) helper.insert_with_shift(self._data, data, index, shift_policy)
self:_update_data_info() self:_refresh()
self:_check_elements()
end end
@ -129,10 +116,8 @@ end
-- @tparam number shift_policy The constant from const.SHIFT.* -- @tparam number shift_policy The constant from const.SHIFT.*
-- @local -- @local
function DataList.remove(self, index, shift_policy) function DataList.remove(self, index, shift_policy)
--self:_refresh()
helper.remove_with_shift(self._data, index, shift_policy) helper.remove_with_shift(self._data, index, shift_policy)
self:_update_data_info() self:_refresh()
end end
@ -145,7 +130,6 @@ function DataList.remove_by_data(self, data, shift_policy)
local index = helper.contains(self._data, data) local index = helper.contains(self._data, data)
if index then if index then
helper.remove_with_shift(self._data, index, shift_policy) helper.remove_with_shift(self._data, index, shift_policy)
self:_update_data_info()
self:_refresh() self:_refresh()
end end
end end
@ -155,32 +139,10 @@ end
-- @tparam DataList self @{DataList} -- @tparam DataList self @{DataList}
function DataList.clear(self) function DataList.clear(self)
self._data = {} self._data = {}
self:_update_data_info()
self:_refresh() self:_refresh()
end end
--- Return first index from data. It not always equals to 1
-- @tparam DataList self @{DataList}
function DataList.get_first_index(self)
return self._data_first_index
end
--- Return last index from data
-- @tparam DataList self @{DataList}
function DataList.get_last_index(self)
return self._data_last_index
end
--- Return amount of data
-- @tparam DataList self @{DataList}
function DataList.get_length(self)
return self._data_length
end
--- Return index for data value --- Return index for data value
-- @tparam DataList self @{DataList} -- @tparam DataList self @{DataList}
-- @tparam table data -- @tparam table data
@ -227,13 +189,8 @@ end
-- @tparam DataList self @{DataList} -- @tparam DataList self @{DataList}
-- @tparam number index -- @tparam number index
function DataList.scroll_to_index(self, index) function DataList.scroll_to_index(self, index)
local target = helper.clamp(index, self:get_first_index(), self:get_last_index()) local pos = self.grid:get_pos(index)
self.top_index = target self.scroll:scroll_to(pos)
self:_refresh()
if self._data_visual[target] then
self.scroll:scroll_to(gui.get_position(self._data_visual[target].node), true)
end
end end
@ -247,11 +204,11 @@ function DataList._add_at(self, index)
end end
local node, instance = self._create_function(self:get_context(), self._data[index], index, self) local node, instance = self._create_function(self:get_context(), self._data[index], index, self)
self.grid:add(node, index, const.SHIFT.NO_SHIFT)
self._data_visual[index] = { self._data_visual[index] = {
node = node, node = node,
component = instance component = instance,
} }
self.grid:add(node, index, const.SHIFT.NO_SHIFT)
self.on_element_add:trigger(self:get_context(), index, node, instance) self.on_element_add:trigger(self:get_context(), index, node, instance)
end end
@ -264,12 +221,14 @@ end
function DataList._remove_at(self, index) function DataList._remove_at(self, index)
self.grid:remove(index, const.SHIFT.NO_SHIFT) self.grid:remove(index, const.SHIFT.NO_SHIFT)
local node = self._data_visual[index].node local visual_data = self._data_visual[index]
gui.delete_node(node) local node = visual_data.node
local instance = visual_data.component
local instance = self._data_visual[index].component gui.delete_node(node)
if instance then if instance then
self.druid:remove(instance) --- We should remove instance from druid that spawned component
instance._meta.druid:remove(instance)
end end
self._data_visual[index] = nil self._data_visual[index] = nil
@ -281,102 +240,32 @@ end
-- @tparam DataList self @{DataList} -- @tparam DataList self @{DataList}
-- @local -- @local
function DataList._refresh(self) function DataList._refresh(self)
for index, _ in pairs(self._data_visual) do local start_pos = -self.scroll.position
local start_index = self.grid:get_index(start_pos)
start_index = math.max(1, start_index)
local pivot = helper.get_pivot_offset(gui.get_pivot(self.scroll.view_node))
local offset_x = self.scroll.view_size.x * (0.5 - pivot.x)
local offset_y = self.scroll.view_size.y * (0.5 + pivot.y)
local end_pos = vmath.vector3(start_pos.x + offset_x, start_pos.y - offset_y, 0)
local end_index = self.grid:get_index(end_pos)
end_index = math.min(#self._data, end_index)
self.top_index = start_index
self.last_index = end_index
-- Clear from non range elements
for index, data in pairs(self._data_visual) do
if index < start_index or index > end_index then
self:_remove_at(index) self:_remove_at(index)
end end
self:_check_elements()
end end
-- Add new elements
--- Check elements which should be created for index = start_index, end_index do
-- @tparam DataList self @{DataList} if not self._data_visual[index] and self._data[index] then
-- @local
function DataList._check_elements(self)
for index, data in pairs(self._data_visual) do
if self.scroll:is_node_in_view(data.node) then
self.top_index = index
self.last_index = index
end
end
self:_check_elements_from(self.top_index, -1)
self:_check_elements_from(self.top_index + 1, 1)
for index, data in pairs(self._data_visual) do
self.top_index = math.min(self.top_index or index, index)
self.last_index = math.max(self.last_index or index, index)
end
-- Progress report
local middle_index = (self.last_index + self.top_index) / 2
local progress = (middle_index - self._data_first_index) / (self._data_last_index - self._data_first_index)
progress = helper.clamp(progress, 0, 1)
if self.last_index == self:get_last_index() then
progress = 1
end
if self.top_index == self:get_first_index() then
progress = 0
end
if self.scroll_progress ~= progress then
self.scroll_progress = progress
self.on_scroll_progress_change:trigger(self:get_context(), progress)
end
end
--- Check elements which should be created.
-- Start from index with step until element is outside of scroll view
-- @tparam DataList self @{DataList}
-- @tparam number index
-- @tparam number step
-- @local
function DataList._check_elements_from(self, index, step)
local is_outside = false
while not is_outside do
if not self._data[index] then
break
end
if not self._data_visual[index] then
self:_add_at(index) self:_add_at(index)
end end
if not self.scroll:is_node_in_view(self._data_visual[index].node) then
is_outside = true
-- remove nexts:
-- We add one more element, which is not in view to
-- check what it's always outside to stop spawning
local remove_index = index + step
while self._data_visual[remove_index] do
self:_remove_at(remove_index)
remove_index = remove_index + step
end
end
index = index + step
end
end
--- Update actual data params
-- @tparam DataList self @{DataList}
-- @local
function DataList._update_data_info(self)
self._data_first_index = false
self._data_last_index = false
self._data_length = 0
for index, data in pairs(self._data) do
self._data_first_index = math.min(self._data_first_index or index, index)
self._data_last_index = math.max(self._data_last_index or index, index)
self._data_length = self._data_length + 1
end
if self._data_length == 0 then
self._data_first_index = 0
self._data_last_index = 0
end end
end end

View File

@ -6,21 +6,11 @@ local settings = require("druid.system.settings")
local M = {} local M = {}
local function button_hover_scale(node, target, time)
gui.animate(node, "scale", target, gui.EASING_OUTSINE, time)
end
local function button_tap_anim(node, tap_scale, start_scale)
gui.animate(node, gui.PROP_SCALE, tap_scale, gui.EASING_INSINE, 0.1, 0, function()
gui.animate(node, gui.PROP_SCALE, start_scale, gui.EASING_INSINE, 0.1)
end)
end
M["button"] = { M["button"] = {
HOVER_SCALE = vmath.vector3(0.02, 0.02, 1), HOVER_SCALE = vmath.vector3(0.08, 0.08, 1),
HOVER_MOUSE_SCALE = vmath.vector3(0.01, 0.01, 1), HOVER_MOUSE_SCALE = vmath.vector3(0.04, 0.04, 1),
HOVER_TIME = 0.04, HOVER_TIME = 0.05,
SCALE_CHANGE = vmath.vector3(0.035, 0.035, 1), SCALE_CHANGE = vmath.vector3(0.12, 0.12, 1),
BTN_SOUND = "click", BTN_SOUND = "click",
BTN_SOUND_DISABLED = "click", BTN_SOUND_DISABLED = "click",
DISABLED_COLOR = vmath.vector4(0, 0, 0, 1), DISABLED_COLOR = vmath.vector4(0, 0, 0, 1),
@ -33,19 +23,21 @@ M["button"] = {
local scale_to = self.start_scale + M.button.HOVER_SCALE local scale_to = self.start_scale + M.button.HOVER_SCALE
local target_scale = state and scale_to or self.start_scale local target_scale = state and scale_to or self.start_scale
button_hover_scale(node, target_scale, M.button.HOVER_TIME) gui.animate(node, "scale", target_scale, gui.EASING_OUTSINE, M.button.HOVER_TIME)
end, end,
on_mouse_hover = function(self, node, state) on_mouse_hover = function(self, node, state)
local scale_to = self.start_scale + M.button.HOVER_MOUSE_SCALE local scale_to = self.start_scale + M.button.HOVER_MOUSE_SCALE
local target_scale = state and scale_to or self.start_scale local target_scale = state and scale_to or self.start_scale
button_hover_scale(node, target_scale, M.button.HOVER_TIME) gui.animate(node, "scale", target_scale, gui.EASING_OUTSINE, M.button.HOVER_TIME)
end, end,
on_click = function(self, node) on_click = function(self, node)
local scale_to = self.start_scale + M.button.SCALE_CHANGE local scale_to = self.start_scale + M.button.SCALE_CHANGE
button_tap_anim(node, scale_to, self.start_scale) gui.set_scale(node, scale_to)
gui.animate(node, gui.PROP_SCALE, self.start_scale, gui.EASING_OUTBACK, 0.24)
settings.play_sound(M.button.BTN_SOUND) settings.play_sound(M.button.BTN_SOUND)
end, end,
@ -84,8 +76,8 @@ M["scroll"] = {
INERT_SPEED = 30, -- koef. of inert speed INERT_SPEED = 30, -- koef. of inert speed
EXTRA_STRETCH_SIZE = 100, -- extra size in pixels outside of scroll (stretch effect) EXTRA_STRETCH_SIZE = 100, -- extra size in pixels outside of scroll (stretch effect)
POINTS_DEADZONE = 20, -- Speed to check points of interests in no_inertion mode POINTS_DEADZONE = 20, -- Speed to check points of interests in no_inertion mode
WHEEL_SCROLL_SPEED = 0, -- Amount of pixels to scroll by one wheel event (0 to disable) WHEEL_SCROLL_SPEED = 20, -- Amount of pixels to scroll by one wheel event (0 to disable)
WHEEL_SCROLL_INVERTED = false, -- Boolean to invert wheel scroll side WHEEL_SCROLL_INVERTED = true, -- Boolean to invert wheel scroll side
WHEEL_SCROLL_BY_INERTION = false, -- If true, wheel will add inertion to scroll. Direct set position otherwise. WHEEL_SCROLL_BY_INERTION = false, -- If true, wheel will add inertion to scroll. Direct set position otherwise.
SMALL_CONTENT_SCROLL = false, -- If true, content node with size less than view node size can be scrolled SMALL_CONTENT_SCROLL = false, -- If true, content node with size less than view node size can be scrolled
} }

View File

@ -193,10 +193,6 @@ end
local function process_input(self, action_id, action, components) local function process_input(self, action_id, action, components)
local is_input_consumed = false local is_input_consumed = false
if #components == 0 then
return false
end
for i = #components, 1, -1 do for i = #components, 1, -1 do
local component = components[i] local component = components[i]
local meta = component._meta local meta = component._meta
@ -293,23 +289,27 @@ end
-- Component `on_remove` function will be invoked, if exist. -- Component `on_remove` function will be invoked, if exist.
-- @tparam DruidInstance self -- @tparam DruidInstance self
-- @tparam BaseComponent component Component instance -- @tparam BaseComponent component Component instance
-- @treturn boolean True if component was removed
function DruidInstance.remove(self, component) function DruidInstance.remove(self, component)
if self._is_late_remove_enabled then if self._is_late_remove_enabled then
table.insert(self._late_remove, component) table.insert(self._late_remove, component)
return return false
end end
-- Recursive remove all children of component -- Recursive remove all children of component
local children = component._meta.children local children = component._meta.children
for i = #children, 1, -1 do for i = #children, 1, -1 do
self:remove(children[i]) self:remove(children[i])
local parent = children[i]:get_parent_component()
if parent then
parent:__remove_children(children[i])
end
children[i] = nil children[i] = nil
end end
local parent = component:get_parent_component()
if parent then
parent:__remove_child(component)
end
local is_removed = false
local all_components = self.components_all local all_components = self.components_all
for i = #all_components, 1, -1 do for i = #all_components, 1, -1 do
if all_components[i] == component then if all_components[i] == component then
@ -317,6 +317,7 @@ function DruidInstance.remove(self, component)
component:on_remove() component:on_remove()
end end
table.remove(all_components, i) table.remove(all_components, i)
is_removed = true
end end
end end
@ -330,6 +331,8 @@ function DruidInstance.remove(self, component)
end end
end end
end end
return is_removed
end end
@ -556,7 +559,7 @@ end
-- @tparam DruidInstance self -- @tparam DruidInstance self
-- @tparam string|node node The node_id or gui.get_node(node_id) -- @tparam string|node node The node_id or gui.get_node(node_id)
-- @tparam function|nil callback Button callback -- @tparam function|nil callback Button callback
-- @tparam table|nil params Button callback params -- @tparam any|nil params Button callback params
-- @tparam node|string|nil anim_node Button anim node (node, if not provided) -- @tparam node|string|nil anim_node Button anim node (node, if not provided)
-- @treturn Button @{Button} component -- @treturn Button @{Button} component
function DruidInstance.new_button(self, node, callback, params, anim_node) function DruidInstance.new_button(self, node, callback, params, anim_node)

View File

@ -1,17 +1,17 @@
#!/bin/bash # Path to bob folder. It will find and save new bob.jar files inside
bob_folder=./
# If true, it will check and download latest bob version. It will ignore bob_sha param
use_latest_bob=false
# Set patch (last value after dot) game version value as total git commits count (1.2.0 -> 1.2.{commits_count})
# You allow to get SHA commit from version via: git rev-list --all --reverse | sed -n {N}p
enable_incremental_version=true
# You can point bob version for project in format "filename:sha" # You can point bob version for project in format "filename:sha"
bob_sha="1.6.0:d9e9c49ab946c058f29a8b688c862d70f30e9c43" bob_sha="181:fd1ad4c17bfdcd890ea7176f2672c35102384419"
# Select Defold channel. Values: stable, beta, alpha # Select Defold channel. Values: stable, beta, alpha
bob_channel="stable" bob_channel="stable"
# If true, it will check and download latest bob version. It will ignore bob_sha param
use_latest_bob=false
# Select Defold build server
build_server="https://build.defold.com"
# Is need to build html report # Is need to build html report
is_build_html_report=true is_build_html_report=true