mirror of
https://github.com/Insality/druid
synced 2025-06-27 10:27:48 +02:00
Merge pull request #90 from Insality/feature/77-grid-update
Feature/77 grid update
This commit is contained in:
commit
b9f8fc95f5
60
README.md
60
README.md
@ -1,11 +1,8 @@
|
||||
|
||||
|
||||
|
||||
[](https://insality.github.io/druid/)
|
||||
|
||||
[](https://github.com/Insality/druid/releases)
|
||||
|
||||
**Druid** - powerful Defold component UI library. Use basic **Druid** components or make your own game-specific components to make amazing GUI in your games.
|
||||
**Druid** - powerful Defold component UI library. Use basic and extended **Druid** components or make your own game-specific components to make amazing GUI in your games.
|
||||
|
||||
|
||||
## Setup
|
||||
@ -35,7 +32,7 @@ For **Druid** to work requires next input bindings:
|
||||
|
||||
### Input capturing [optional]
|
||||
|
||||
By default, **Druid** will auto-capture input focus, if any input component will be created. So you don't need to call `msg.post(".", "acquire_input_focus)"`
|
||||
By default, **Druid** will auto-capture input focus, if any input component will be created. So you don't need to call `msg.post(".", "acquire_input_focus")`
|
||||
|
||||
If you not need this behaviour, you can disable it by settings `druid.no_auto_input` field in _game.project_:
|
||||
```
|
||||
@ -76,35 +73,19 @@ druid.on_window_callback(event)
|
||||
|
||||
## Components
|
||||
|
||||
**Druid** provides next basic components:
|
||||
**Druid** provides next *basic* components:
|
||||
|
||||
- **[Button](https://github.com/Insality/druid/blob/master/docs_md/01-components.md#button)** - Basic Druid input component
|
||||
- **[Button](https://github.com/Insality/druid/blob/master/docs_md/01-components.md#button)** - Basic Druid button input component. Handles all types of interaction (tap, long-tap, hold-tap, double-tap, simple key triggers, etc)
|
||||
|
||||
- **[Text](https://github.com/Insality/druid/blob/master/docs_md/01-components.md#text)** - Basic Druid text component
|
||||
|
||||
- **[Lang text](https://github.com/Insality/druid/blob/master/docs_md/01-components.md#lang-text)** - Wrap on Text component to handle localization
|
||||
- **[Text](https://github.com/Insality/druid/blob/master/docs_md/01-components.md#text)** - Basic Druid text component. Wrap on gui text node, handle text size adjusting.
|
||||
|
||||
- **[Scroll](https://github.com/Insality/druid/blob/master/docs_md/01-components.md#scroll)** - Basic Druid scroll component
|
||||
|
||||
- **[Progress](https://github.com/Insality/druid/blob/master/docs_md/01-components.md#progress)** - Basic Druid progress bar component
|
||||
|
||||
- **[Slider](https://github.com/Insality/druid/blob/master/docs_md/01-components.md#slider)** - Basic Druid slider component
|
||||
|
||||
- **[Input](https://github.com/Insality/druid/blob/master/docs_md/01-components.md#input)** - Basic Druid text input component (unimplemented)
|
||||
|
||||
- **[Checkbox](https://github.com/Insality/druid/blob/master/docs_md/01-components.md#checkbox)** - Basic Druid checkbox component
|
||||
|
||||
- **[Checkbox group](https://github.com/Insality/druid/blob/master/docs_md/01-components.md#checkbox-group)** - Several checkboxes in one group
|
||||
|
||||
- **[Radio group](https://github.com/Insality/druid/blob/master/docs_md/01-components.md#radio-group)** - Several checkboxes in one group with single choice
|
||||
|
||||
- **[Blocker](https://github.com/Insality/druid/blob/master/docs_md/01-components.md#blocker)** - Block input in node zone component
|
||||
|
||||
- **[Back Handler](https://github.com/Insality/druid/blob/master/docs_md/01-components.md#back-handler)** - Handle back button (Android back, backspace)
|
||||
- **[Back Handler](https://github.com/Insality/druid/blob/master/docs_md/01-components.md#back-handler)** - Handle back button (Android back button, backspace key)
|
||||
|
||||
- **[Timer](https://github.com/Insality/druid/blob/master/docs_md/01-components.md#timer)** - Handle timer work on gui text node
|
||||
|
||||
- **[Grid](https://github.com/Insality/druid/blob/master/docs_md/01-components.md#grid)** - Component for manage node positions
|
||||
- **[Static Grid](https://github.com/Insality/druid/blob/master/docs_md/01-components.md#static-grid)** - Component for manage node positions with equal sizes
|
||||
|
||||
- **[Hover](https://github.com/Insality/druid/blob/master/docs_md/01-components.md#hover)** - System Druid component, handle hover node state
|
||||
|
||||
@ -112,6 +93,28 @@ druid.on_window_callback(event)
|
||||
|
||||
- **[Drag](https://github.com/Insality/druid/blob/master/docs_md/01-components.md#drag)** - System Druid component, handle drag input on node
|
||||
|
||||
**Druid** also provides next *extended* components:
|
||||
|
||||
***Note**: In future, to use extended components, you should register them first. It's required for make **Druid** modular - to exclude unused components from build*
|
||||
|
||||
- **[Checkbox](https://github.com/Insality/druid/blob/master/docs_md/01-components.md#checkbox)** - Checkbox component
|
||||
|
||||
- **[Checkbox group](https://github.com/Insality/druid/blob/master/docs_md/01-components.md#checkbox-group)** - Several checkboxes in one group
|
||||
|
||||
- **[Dynamic Grid](https://github.com/Insality/druid/blob/master/docs_md/01-components.md#dynamic-grid)** - Component for manage node positions with different sizes. Only in one row or column
|
||||
|
||||
- **[Input](https://github.com/Insality/druid/blob/master/docs_md/01-components.md#input)** - User text input component
|
||||
|
||||
- **[Lang text](https://github.com/Insality/druid/blob/master/docs_md/01-components.md#lang-text)** - Wrap on Text component to handle localization
|
||||
|
||||
- **[Progress](https://github.com/Insality/druid/blob/master/docs_md/01-components.md#progress)** - Progress bar component
|
||||
|
||||
- **[Radio group](https://github.com/Insality/druid/blob/master/docs_md/01-components.md#radio-group)** - Several checkboxes in one group with single choice
|
||||
|
||||
- **[Slider](https://github.com/Insality/druid/blob/master/docs_md/01-components.md#slider)** - Slider component
|
||||
|
||||
- **[Timer](https://github.com/Insality/druid/blob/master/docs_md/01-components.md#timer)** - Handle timer work on gui text node
|
||||
|
||||
Full info see on _[components.md](https://github.com/Insality/druid/blob/master/docs_md/01-components.md)_
|
||||
|
||||
|
||||
@ -157,7 +160,7 @@ Any **Druid** components as callbacks uses [Druid Events](https://insality.githu
|
||||
Any events can handle several callbacks, if needed.
|
||||
|
||||
|
||||
## Druid lifecycle
|
||||
## Druid Lifecycle
|
||||
|
||||
Here is full druid lifecycle setup in your ***.gui_script** file:
|
||||
```lua
|
||||
@ -213,6 +216,7 @@ Try the [HTML5 version](https://insality.github.io/druid/druid/) of the example
|
||||
To learn **Druid** better, read next documentation:
|
||||
- [Druid components](https://github.com/Insality/druid/blob/master/docs_md/01-components.md)
|
||||
- [Create custom components](https://github.com/Insality/druid/blob/master/docs_md/02-creating_custom_components.md)
|
||||
- [See FAQ article](https://github.com/Insality/druid/blob/master/docs_md/FAQ.md)
|
||||
- [Druid styles](https://github.com/Insality/druid/blob/master/docs_md/03-styles.md)
|
||||
- [Druid asset store](https://github.com/Insality/druid/blob/master/docs_md/04-druid_assets.md)
|
||||
|
||||
@ -227,8 +231,8 @@ _Will fill later_
|
||||
|
||||
## License
|
||||
|
||||
- Original created by [AGulev](https://github.com/AGulev)
|
||||
- Developed and supporting by [Insality](https://github.com/Insality)
|
||||
- Original idea by [AGulev](https://github.com/AGulev)
|
||||
- Assets from [Kenney](http://www.kenney.nl/)
|
||||
|
||||
**MIT** License
|
||||
|
@ -13,6 +13,9 @@ Basic Druid input component. Handle input on node and provide different callback
|
||||
Create button with druid: `button = druid:new_button(node_name, callback, [params], [animation_node])`
|
||||
Where node name is name of node from GUI scene. You can use `node_name` as input trigger zone and point another node for animation via `animation_node`
|
||||
|
||||
### Usecase
|
||||
_fill example usecases_
|
||||
|
||||
### Notes
|
||||
- Button callback have next params: (self, params, button_instance)
|
||||
- **self** - Druid self context
|
||||
@ -146,6 +149,7 @@ Key is value from druid const: const.SIDE.X (or just "x") or const.SIDE.Y (or ju
|
||||
- Progress bar can fill only by vertical or horizontal size. If you want make diagonal progress bar, just rotate node in GUI scene
|
||||
- If you have glitchy or dark texture bug with progress bar, try to disable mipmaps in your texture profiles
|
||||
|
||||
|
||||
## Slider
|
||||
[Slider API here](https://insality.github.io/druid/modules/druid.slider.html)
|
||||
|
||||
@ -161,11 +165,12 @@ Pin node (node_name in params) should be placed in zero position (initial). It w
|
||||
- You can setup points of interests on slider via `slider:set_steps`. If steps are exist, slider values will be only from this steps (notched slider)
|
||||
- For now, start pos and end pos should be on vertical or horizontal line (their x or y value should be equal)
|
||||
|
||||
|
||||
## Input
|
||||
[Input API here](https://insality.github.io/druid/modules/druid.input.html)
|
||||
|
||||
### Overview
|
||||
Basic Druid text input component (unimplemented)
|
||||
Basic Druid text input component
|
||||
|
||||
### Setup
|
||||
Create input component with druid: `input = druid:new_input(button_node_name, text_node_name, keyboard_type)`
|
||||
@ -194,6 +199,7 @@ Create checkbox component with druid: `checkbox = druid:new_checkbox(node, callb
|
||||
- Checkbox uses button to handle click
|
||||
- You can setup another node to handle input with click_node arg in component init: `druid:new_checkbox(node, callback, [click_node])`
|
||||
|
||||
|
||||
## Checkbox group
|
||||
[Checkbox group API here](https://insality.github.io/druid/modules/druid.checkbox_group.html)
|
||||
|
||||
@ -222,6 +228,7 @@ Create radio_group component with druid: `group = druid:new_radio_group(nodes[],
|
||||
- You can get/set radio_group state with `group:set_state()` and `group:get_state()`
|
||||
- Only different from checkbox_group: on click another checkboxes in this group will be unchecked
|
||||
|
||||
|
||||
## Timer
|
||||
[Timer API here](https://insality.github.io/druid/modules/druid.timer.html)
|
||||
|
||||
@ -236,21 +243,51 @@ Create timer component with druid: `timer = druid:new_timer(text_node, from_seco
|
||||
- Timer will setup text node with current timer value
|
||||
- Timer uses update function to handle time
|
||||
|
||||
## Grid
|
||||
[Grid API here](https://insality.github.io/druid/modules/druid.grid.html)
|
||||
|
||||
## Static Grid
|
||||
[Static Grid API here](https://insality.github.io/druid/modules/druid.static_grid.html)
|
||||
|
||||
### Overview
|
||||
Component for manage node positions. Very simple implementation for nodes with equal size
|
||||
Component for manage node positions.
|
||||
Static grid have constant node size, so it possible to calculate node positions before placement. Nodes can be placed with gaps.
|
||||
Static grid can shift elements on add/remove functions.
|
||||
|
||||
### Setup
|
||||
Create component with druid: `grid = druid:new_grid(parent_node, prefab_node, max_in_row_elements)`
|
||||
Create component with druid: `grid = druid:new_static_grid(parent_node, prefab_node, max_in_row_elements)`
|
||||
|
||||
### Notes
|
||||
- Grid on _adding elements_ will setup parent to _parent_node_
|
||||
- On _add node_ grid will set node parent to _parent_node_
|
||||
- You can get array of position of every element for setup points of interest in scroll component
|
||||
- You can get size of all elements for setup size in scroll component
|
||||
- You can adjust anchor and border between elements
|
||||
- _Prefab node_ in component init used to get grid item size
|
||||
- You can also bind the grid to the scroll component for auto resize scroll content size
|
||||
- Pivot of parent_node matter for node placement
|
||||
- _Prefab node_ used to get node size and anchor
|
||||
- You can point *position_function* for animations with _static_grid:set_position_function(node, pos)_ callback. Default - *gui.set_position()*
|
||||
|
||||
|
||||
## Dynamic Grid
|
||||
[Dynamic Grid API here](https://insality.github.io/druid/modules/druid.dynamic_grid.html)
|
||||
|
||||
### Overview
|
||||
Component for manage node positions with different node sizes.
|
||||
Unlike Static Grid, Dynamic Grid can place nodes only in one row or in one column.
|
||||
Dynamic Grid can't have gaps between elements - you will get error, if try spawn element far away from others.
|
||||
Dynamic Grid should have __West__, __East__, __South__ or __North__ pivot (vertical or horizontal element placement)
|
||||
|
||||
### Setup
|
||||
Create component with druid: `grid = druid:new_dynamic_grid(parent_node)`
|
||||
|
||||
Check the _parent_node_ have correct pivot point. You will get the error otherwise.
|
||||
|
||||
### Notes
|
||||
- On _add node_ grid will set node parent to _parent_node_
|
||||
- You can get array of position of every element for setup points of interest in scroll component
|
||||
- You can get size of all elements for setup size in scroll component
|
||||
- You can also bind the grid to the scroll component for auto resize scroll content size
|
||||
- Pivot of parent_node matter for node placement
|
||||
- You can point *position_function* for animations with _static_grid:set_position_function(node, pos)_ callback. Default - *gui.set_position()*
|
||||
- First node placed at Grid pivot point. Other nodes placed nearby of other nodes.
|
||||
- On *add/remove* nodes always shifted. You can point the shift side in this functions (*is_shift_left* boolean argument)
|
||||
|
||||
## Hover
|
||||
[Hover API here](https://insality.github.io/druid/modules/druid.hover.html)
|
||||
|
@ -94,14 +94,34 @@ Druid 0.4.0:
|
||||
|
||||
|
||||
Druid 0.5.0:
|
||||
- **#77** Grid update:
|
||||
- The _grid_ component now is __deprecated__. Use _static_grid_ instead. Druid will show you deprecated message, if you still using _grid_ component
|
||||
- __[BREAKING]__ Remove the _grid:set_offset_ grid functions. To adjust the distance between nodes inside grid - setup correct node sizes
|
||||
- Add _static_grid_ component
|
||||
- The behaviour like previous _grid_ component
|
||||
- Have constant element size, so have ability to precalculate positions, indexes and size of content
|
||||
- By default, not shifting elements on removing element. Add _is_shift_ flag to _static_grid:remove_ function
|
||||
- This grid can spawn elements with several rows and collumns
|
||||
- Add _dynamic_grid_ component
|
||||
- Have dynamic element size. So have no ability to precalculate stuff like _static_grid_
|
||||
- This grid can't have gaps between elements. You will get the error, if spawn element far away from other elements
|
||||
- The grid can spawn elements only in row or in collumn
|
||||
- The grid node should have __West__, __East__, __South__ or __North__ pivot (vertical or horizontal element placement)
|
||||
- Able to shift nodes left or right on _grid:add_/_grid:remove_ functions
|
||||
- Scroll update:
|
||||
- Add _scroll:set_vertical_scroll_ and _scroll:set_horizontal_scroll_ for disable scroll sides
|
||||
- Add _scroll:bind_grid_ function. It's allow bind grid component (static or dynamic) to the scroll for auto refresh the scroll size on grid nodes changing
|
||||
- **#37** Add _on_layout_change_ support. Druid will keep and restore GUI component data between changing game layout. Override function _on_layout_change_ in your custom components to do stuff you need.
|
||||
- **#85** Move several components from `base` folder to `extended`. In future to use them, you have to register them manually. This is done for decrease build size by excluding unused components
|
||||
- **Fix #61:** Button component: fix button animation node creation
|
||||
- **Fix #64:** Hover component: wrong mouse_hover default state
|
||||
- **Fix #71:** Blocker: blocker now correct block mouse hover event
|
||||
- **Fix #72:** Fix `return nil` in some `on_input` functions
|
||||
- **Fix #74:** Fix typo: strech -> stretch. Scroll function `set_extra_stretch_size` renamed
|
||||
- **Fix #74:** __[BREAKING]__ Fix typo: strech -> stretch. Scroll function `set_extra_stretch_size` renamed
|
||||
- **Fix #76:** Add params for lang text localization component
|
||||
- **Fix #79:** Fix druid:remove inside on_input callback
|
||||
- **Fix #80:** Fix hover set_enable typo function call
|
||||
- **FIX #88:** Add _component:set_input_enabled_ function to enable/disable input for druid component
|
||||
- **Fix #79:** Fix _druid:remove_ inside on_input callback
|
||||
- **Fix #80:** Fix _hover:set_enable_ typo function call
|
||||
- **Fix #88:** Add _component:set_input_enabled_ function to enable/disable input for druid component. Now you can disable input of any druid component, even complex (with other components inside)
|
||||
- Add `component.tempalte.lua` as template for Druid custom component
|
||||
>>>>>>> develop
|
||||
|
||||
|
59
docs_md/faq.md
Normal file
59
docs_md/faq.md
Normal file
@ -0,0 +1,59 @@
|
||||
|
||||
# Druid FAQ
|
||||
|
||||
### Q: Why I want use Druid?
|
||||
**A:** ---
|
||||
|
||||
|
||||
### Q: How to remove the Druid component instance?
|
||||
**A:** Any created **Druid** component can be removed with _druid:remove_. [API reference link](https://insality.github.io/druid/modules/druid_instance.html#druid:remove).
|
||||
|
||||
|
||||
### Q: How to make scroll work?
|
||||
**A:** ---
|
||||
|
||||
|
||||
### Q: How the input is processing?
|
||||
**A:**
|
||||
*SImply*: the **Druid** has a LIFO queue to check input. Last added buttons have more priority than first. Placing your buttons from behind to the front is correct in most cases.
|
||||
|
||||
|
||||
### Q: For what purpose Blocker component is exist?
|
||||
**A:** Component explanation [here](https://github.com/Insality/druid/blob/master/docs_md/01-components.md#notes-2).
|
||||
With Blocker you can block input in some zone. It is useful for make unclickable zone in buttons or kind of buttons panel on other big button (ex. close windows on window background click)
|
||||
|
||||
|
||||
### Q: Which stuff can I do with custom components?
|
||||
**A:** Any of you can imagine! There is a lot of examples, but in general: custom components allow you place component and some game logic separately from other stuff. It will be reusable, easier for testing and developing.
|
||||
|
||||
For example it can be element in scroll with buttons, your custom GUI widget or even component with your game logic. Usually custom components going with templates. You can do several templates for single component module (for different visuals!)
|
||||
|
||||
Some examples of custom components you can find [here](https://github.com/Insality/druid-assets).
|
||||
|
||||
|
||||
### Q: How *self:get_node()* is working?
|
||||
**A:** The node can be placed in gui directly or can be cloned via *gui.clone_tree()*. Also nodes can be placed as templates, so full node id will be composed from template name and node name (in cloned nodes too).
|
||||
|
||||
**Druid** component *self:get_node()* trying to search in all of this places. Use *self:set_template()* and *self:set_component_nodes()* for correct setup component nodes before any call of *self:get_node()*.
|
||||
|
||||
Remember, usually you should pass *__string name__ of the node*, not gui node itself. It's better and more druid-way.
|
||||
|
||||
|
||||
### Q: My button in scroll is clickable outside the stencil node
|
||||
**A:** Since **Druid** checking click node with _gui.pick_node_, stencil is not prevent this. You can setup additional click zone on your buttons with _button:set_click_zone_.
|
||||
|
||||
The usual Druid way after add button to the scroll do:
|
||||
```lua
|
||||
-- Scroll view node usually is stencil node
|
||||
button:set_click_zone(scroll.view_node)
|
||||
```
|
||||
|
||||
### Q: How to use EmmyLua annotations?
|
||||
**A:** Since the dependencies can't be processed by external editors, for use generated EmmyLua annotations you should copy the _annotations.lua_ to your project. For EmmyLua it will be enough. Remember you can _restart emmylua server_ for refresh the changes, if something goes wrong.
|
||||
After the annotations is processed, you should point the type of druid in requires:
|
||||
```lua
|
||||
---@type druid
|
||||
local druid = require("druid.druid")
|
||||
|
||||
-- Now the autocomplete is working
|
||||
```
|
@ -1,178 +0,0 @@
|
||||
--- Component to handle placing components by row and columns.
|
||||
-- Grid can anchor your elements, get content size and other
|
||||
-- @module druid.grid
|
||||
|
||||
--- Component events
|
||||
-- @table Events
|
||||
-- @tfield druid_event on_add_item On item add callback
|
||||
-- @tfield druid_event on_remove_item On item remove callback
|
||||
-- @tfield druid_event on_clear On grid clear callback
|
||||
-- @tfield druid_event on_update_positions On update item positions callback
|
||||
|
||||
--- Component fields
|
||||
-- @table Fields
|
||||
-- @tfield node parent Parent gui node
|
||||
-- @tfield node[] nodes List of all grid nodes
|
||||
-- @tfield vector3 offset Item distance between each other items
|
||||
-- @tfield vector3 anchor Item anchor
|
||||
-- @tfield vector3 node_size Item size
|
||||
-- @tfield vector4 border The size of item content
|
||||
-- @tfield vector3 border_offer The border offset for correct anchor calculations
|
||||
|
||||
local const = require("druid.const")
|
||||
local Event = require("druid.event")
|
||||
local helper = require("druid.helper")
|
||||
local component = require("druid.component")
|
||||
|
||||
local M = component.create("grid", { const.ON_LAYOUT_CHANGE })
|
||||
|
||||
|
||||
--- Component init function
|
||||
-- @function grid:init
|
||||
-- @tparam node parent The gui node parent, where items will be placed
|
||||
-- @tparam node element Element prefab. Need to get it size
|
||||
-- @tparam[opt=1] number in_row How many nodes in row can be placed
|
||||
function M.init(self, parent, element, in_row)
|
||||
self.parent = self:get_node(parent)
|
||||
self.nodes = {}
|
||||
|
||||
self.offset = vmath.vector3(0)
|
||||
|
||||
local pivot = helper.get_pivot_offset(gui.get_pivot(self.parent))
|
||||
self.anchor = vmath.vector3(0.5 + pivot.x, 0.5 - pivot.y, 0)
|
||||
|
||||
self.in_row = in_row or 1
|
||||
self.node_size = gui.get_size(self:get_node(element))
|
||||
self.border = vmath.vector4(0)
|
||||
self.border_offset = vmath.vector3(0)
|
||||
|
||||
self.on_add_item = Event()
|
||||
self.on_remove_item = Event()
|
||||
self.on_clear = Event()
|
||||
self.on_update_positions = Event()
|
||||
end
|
||||
|
||||
|
||||
local function check_border(self, pos)
|
||||
local border = self.border
|
||||
local size = self.node_size
|
||||
|
||||
local W = pos.x - size.x/2 + self.border_offset.x
|
||||
local E = pos.x + size.x/2 + self.border_offset.x
|
||||
local N = pos.y + size.y/2 + self.border_offset.y
|
||||
local S = pos.y - size.y/2 + self.border_offset.y
|
||||
|
||||
border.x = math.min(border.x, W)
|
||||
border.y = math.max(border.y, N)
|
||||
border.z = math.max(border.z, E)
|
||||
border.w = math.min(border.w, S)
|
||||
|
||||
self.border_offset = vmath.vector3(
|
||||
(border.x + (border.z - border.x) * self.anchor.x),
|
||||
(border.y + (border.w - border.y) * self.anchor.y),
|
||||
0
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
local temp_pos = vmath.vector3(0)
|
||||
local function get_pos(self, index)
|
||||
local row = math.ceil(index / self.in_row) - 1
|
||||
local col = (index - row * self.in_row) - 1
|
||||
|
||||
temp_pos.x = col * (self.node_size.x + self.offset.x) - self.border_offset.x
|
||||
temp_pos.y = -row * (self.node_size.y + self.offset.y) - self.border_offset.y
|
||||
temp_pos.z = 0
|
||||
|
||||
return temp_pos
|
||||
end
|
||||
|
||||
|
||||
local function update_pos(self, is_instant)
|
||||
for i = 1, #self.nodes do
|
||||
local node = self.nodes[i]
|
||||
gui.set_position(node, get_pos(self, i))
|
||||
end
|
||||
|
||||
self.on_update_positions:trigger(self:get_context())
|
||||
end
|
||||
|
||||
|
||||
function M.on_layout_change(self)
|
||||
update_pos(self, true)
|
||||
end
|
||||
|
||||
|
||||
--- Set grid items offset, the distance between items
|
||||
-- @function grid:set_offset
|
||||
-- @tparam vector3 offset Offset
|
||||
function M.set_offset(self, offset)
|
||||
self.offset = offset
|
||||
update_pos(self)
|
||||
end
|
||||
|
||||
|
||||
--- Set grid anchor
|
||||
-- @function grid:set_anchor
|
||||
-- @tparam vector3 acnhor Anchor
|
||||
function M.set_anchor(self, anchor)
|
||||
self.anchor = anchor
|
||||
update_pos(self)
|
||||
end
|
||||
|
||||
|
||||
--- Add new item to the grid
|
||||
-- @function grid:add
|
||||
-- @tparam node item Gui node
|
||||
-- @tparam[opt] number index The item position. By default add as last item
|
||||
function M.add(self, item, index)
|
||||
index = index or (#self.nodes + 1)
|
||||
table.insert(self.nodes, index, item)
|
||||
gui.set_parent(item, self.parent)
|
||||
|
||||
local pos = get_pos(self, index)
|
||||
check_border(self, pos)
|
||||
update_pos(self)
|
||||
|
||||
self.on_add_item:trigger(self:get_context(), item, index)
|
||||
end
|
||||
|
||||
|
||||
--- Return grid content size
|
||||
-- @function grid:get_size
|
||||
-- @treturn vector3 The grid content size
|
||||
function M.get_size(self)
|
||||
return vmath.vector3(
|
||||
self.border.z - self.border.x,
|
||||
self.border.y - self.border.w,
|
||||
0)
|
||||
end
|
||||
|
||||
|
||||
--- Return array of all node positions
|
||||
-- @function grid:get_all_pos
|
||||
-- @treturn vector3[] All grid node positions
|
||||
function M.get_all_pos(self)
|
||||
local result = {}
|
||||
for i = 1, #self.nodes do
|
||||
table.insert(result, gui.get_position(self.nodes[i]))
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
--- Clear grid nodes array. GUI nodes will be not deleted!
|
||||
-- If you want to delete GUI nodes, use grid.nodes array before grid:clear
|
||||
-- @function grid:clear
|
||||
function M.clear(self)
|
||||
self.border.x = 0
|
||||
self.border.y = 0
|
||||
self.border.w = 0
|
||||
self.border.z = 0
|
||||
|
||||
self.nodes = {}
|
||||
end
|
||||
|
||||
|
||||
return M
|
@ -32,7 +32,7 @@ local const = require("druid.const")
|
||||
local helper = require("druid.helper")
|
||||
local component = require("druid.component")
|
||||
|
||||
local M = component.create("scroll", { const.ON_UPDATE, const.ON_LAYOUT_CHANGE })
|
||||
local Scroll = component.create("scroll", { const.ON_UPDATE, const.ON_LAYOUT_CHANGE })
|
||||
|
||||
|
||||
local function inverse_lerp(min, max, current)
|
||||
@ -61,7 +61,300 @@ local function get_size_vector(vector)
|
||||
end
|
||||
|
||||
|
||||
local function on_scroll_drag(self, dx, dy)
|
||||
--- Component style params.
|
||||
-- You can override this component styles params in druid styles table
|
||||
-- or create your own style
|
||||
-- @table Style
|
||||
-- @tfield[opt=0] number FRICT Multiplier for free inertion
|
||||
-- @tfield[opt=0] number FRICT_HOLD Multiplier for inertion, while touching
|
||||
-- @tfield[opt=3] number INERT_THRESHOLD Scroll speed to stop inertion
|
||||
-- @tfield[opt=30] number INERT_SPEED Multiplier for inertion speed
|
||||
-- @tfield[opt=20] number POINTS_DEADZONE Speed to check points of interests in no_inertion mode
|
||||
-- @tfield[opt=0.35] number BACK_SPEED Scroll back returning lerp speed
|
||||
-- @tfield[opt=0.2] number ANIM_SPEED Scroll gui.animation speed for scroll_to function
|
||||
-- @tfield[opt=0] number EXTRA_STRETCH_SIZE extra size in pixels outside of scroll (stretch effect)
|
||||
-- @tfield[opt=false] bool SMALL_CONTENT_SCROLL If true, content node with size less than view node size can be scrolled
|
||||
function Scroll:on_style_change(style)
|
||||
self.style = {}
|
||||
self.style.EXTRA_STRETCH_SIZE = style.EXTRA_STRETCH_SIZE or 0
|
||||
self.style.ANIM_SPEED = style.ANIM_SPEED or 0.2
|
||||
self.style.BACK_SPEED = style.BACK_SPEED or 0.35
|
||||
|
||||
self.style.FRICT = style.FRICT or 0
|
||||
self.style.FRICT_HOLD = style.FRICT_HOLD or 0
|
||||
|
||||
self.style.INERT_THRESHOLD = style.INERT_THRESHOLD or 3
|
||||
self.style.INERT_SPEED = style.INERT_SPEED or 30
|
||||
self.style.POINTS_DEADZONE = style.POINTS_DEADZONE or 20
|
||||
self.style.SMALL_CONTENT_SCROLL = style.SMALL_CONTENT_SCROLL or false
|
||||
|
||||
self._is_inert = not (self.style.FRICT == 0 or
|
||||
self.style.FRICT_HOLD == 0 or
|
||||
self.style.INERT_SPEED == 0)
|
||||
end
|
||||
|
||||
|
||||
--- Scroll constructor.
|
||||
-- @function scroll:init
|
||||
-- @tparam node view_node GUI view scroll node
|
||||
-- @tparam node content_node GUI content scroll node
|
||||
function Scroll:init(view_node, content_node)
|
||||
self.druid = self:get_druid()
|
||||
|
||||
self.view_node = self:get_node(view_node)
|
||||
self.content_node = self:get_node(content_node)
|
||||
|
||||
self.position = gui.get_position(self.content_node)
|
||||
self.target_position = vmath.vector3(self.position)
|
||||
self.inertion = vmath.vector3(0)
|
||||
|
||||
self.drag = self.druid:new_drag(view_node, self._on_scroll_drag)
|
||||
self.drag.on_touch_start:subscribe(self._on_touch_start)
|
||||
self.drag.on_touch_end:subscribe(self._on_touch_end)
|
||||
|
||||
self.on_scroll = Event()
|
||||
self.on_scroll_to = Event()
|
||||
self.on_point_scroll = Event()
|
||||
|
||||
self.selected = nil
|
||||
self.is_animate = false
|
||||
|
||||
self._is_horizontal_scroll = true
|
||||
self._is_vertical_scroll = true
|
||||
self._grid_on_change = nil
|
||||
self._grid_on_change_callback = nil
|
||||
|
||||
self:_update_size()
|
||||
end
|
||||
|
||||
|
||||
function Scroll:on_layout_change()
|
||||
gui.set_position(self.content_node, self.position)
|
||||
end
|
||||
|
||||
|
||||
function Scroll:update(dt)
|
||||
if self.drag.is_drag then
|
||||
self:_update_hand_scroll(dt)
|
||||
else
|
||||
self:_update_free_scroll(dt)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function Scroll:on_remove()
|
||||
self:bind_grid(nil)
|
||||
end
|
||||
|
||||
|
||||
--- Start scroll to target point.
|
||||
-- @function scroll:scroll_to
|
||||
-- @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 Scroll:scroll_to(point, is_instant)
|
||||
local b = self.available_pos
|
||||
local target = vmath.vector3(-point.x, -point.y, 0)
|
||||
target.x = helper.clamp(target.x, b.x, b.z)
|
||||
target.y = helper.clamp(target.y, b.y, b.w)
|
||||
|
||||
self:_cancel_animate()
|
||||
|
||||
self.is_animate = not is_instant
|
||||
|
||||
if is_instant then
|
||||
self.target_position = target
|
||||
self:_set_scroll_position(target)
|
||||
else
|
||||
gui.animate(self.content_node, gui.PROP_POSITION, target, gui.EASING_OUTSINE, self.style.ANIM_SPEED, 0, function()
|
||||
self.is_animate = false
|
||||
self.target_position = target
|
||||
self:_set_scroll_position(target)
|
||||
end)
|
||||
end
|
||||
|
||||
self.on_scroll_to:trigger(self:get_context(), target, is_instant)
|
||||
end
|
||||
|
||||
|
||||
--- Scroll to item in scroll by point index.
|
||||
-- @function scroll:scroll_to_index
|
||||
-- @tparam number index Point index
|
||||
-- @tparam[opt] bool skip_cb If true, skip the point callback
|
||||
function Scroll:scroll_to_index(index, skip_cb)
|
||||
if not self.points then
|
||||
return
|
||||
end
|
||||
|
||||
index = helper.clamp(index, 1, #self.points)
|
||||
|
||||
if self.selected ~= index then
|
||||
self.selected = index
|
||||
|
||||
if not skip_cb then
|
||||
self.on_point_scroll:trigger(self:get_context(), index, self.points[index])
|
||||
end
|
||||
end
|
||||
|
||||
self:scroll_to(self.points[index])
|
||||
end
|
||||
|
||||
|
||||
--- Start scroll to target scroll percent
|
||||
-- @function scroll:scroll_to_percent
|
||||
-- @tparam point vector3 target percent
|
||||
-- @tparam[opt] bool is_instant instant scroll flag
|
||||
-- @usage scroll:scroll_to_percent(vmath.vector3(0.5, 0, 0))
|
||||
function Scroll:scroll_to_percent(percent, is_instant)
|
||||
local border = self.available_pos
|
||||
|
||||
local pos = vmath.vector3(
|
||||
-helper.lerp(border.x, border.z, 1 - percent.x),
|
||||
-helper.lerp(border.w, border.y, 1 - percent.y),
|
||||
0
|
||||
)
|
||||
|
||||
self:scroll_to(pos, is_instant)
|
||||
end
|
||||
|
||||
|
||||
--- Return current scroll progress status.
|
||||
-- Values will be in [0..1] interval
|
||||
-- @function scroll:get_percent
|
||||
-- @treturn vector3 New vector with scroll progress values
|
||||
function Scroll:get_percent()
|
||||
local x_perc = 1 - inverse_lerp(self.available_pos.x, self.available_pos.z, self.position.x)
|
||||
local y_perc = inverse_lerp(self.available_pos.w, self.available_pos.y, self.position.y)
|
||||
|
||||
return vmath.vector3(x_perc, y_perc, 0)
|
||||
end
|
||||
|
||||
|
||||
--- Set scroll content size.
|
||||
-- It will change content gui node size
|
||||
-- @function scroll:set_size
|
||||
-- @tparam vector3 size The new size for content node
|
||||
-- @treturn druid.scroll Current scroll instance
|
||||
function Scroll:set_size(size)
|
||||
gui.set_size(self.content_node, size)
|
||||
self:_update_size()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Enable or disable scroll inert.
|
||||
-- If disabled, scroll through points (if exist)
|
||||
-- If no points, just simple drag without inertion
|
||||
-- @function scroll:set_inert
|
||||
-- @tparam bool state Inert scroll state
|
||||
-- @treturn druid.scroll Current scroll instance
|
||||
function Scroll:set_inert(state)
|
||||
self._is_inert = state
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Return if scroll have inertion.
|
||||
-- @function scroll:is_inert
|
||||
-- @treturn bool If scroll have inertion
|
||||
function Scroll:is_inert()
|
||||
return self._is_inert
|
||||
end
|
||||
|
||||
|
||||
--- Set extra size for scroll stretching.
|
||||
-- Set 0 to disable stretching effect
|
||||
-- @function scroll:set_extra_stretch_size
|
||||
-- @tparam[opt=0] number stretch_size Size in pixels of additional scroll area
|
||||
-- @treturn druid.scroll Current scroll instance
|
||||
function Scroll:set_extra_stretch_size(stretch_size)
|
||||
self.style.EXTRA_STRETCH_SIZE = stretch_size or 0
|
||||
self:_update_size()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Return vector of scroll size with width and height.
|
||||
-- @function scroll:get_scroll_size
|
||||
-- @treturn vector3 Available scroll size
|
||||
function Scroll:get_scroll_size()
|
||||
return self.available_size
|
||||
end
|
||||
|
||||
|
||||
--- Set points of interest.
|
||||
-- Scroll will always centered on closer points
|
||||
-- @function scroll:set_points
|
||||
-- @tparam table points Array of vector3 points
|
||||
-- @treturn druid.scroll Current scroll instance
|
||||
function Scroll:set_points(points)
|
||||
self.points = points
|
||||
|
||||
table.sort(self.points, function(a, b)
|
||||
return a.x > b.x or a.y < b.y
|
||||
end)
|
||||
|
||||
self:_check_threshold()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Lock or unlock horizontal scroll
|
||||
-- @function scroll:set_horizontal_scroll
|
||||
-- @tparam bool state True, if horizontal scroll is enabled
|
||||
-- @treturn druid.scroll Current scroll instance
|
||||
function Scroll:set_horizontal_scroll(state)
|
||||
self._is_horizontal_scroll = state
|
||||
self.drag.can_x = self.available_size.x > 0 and state
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Lock or unlock vertical scroll
|
||||
-- @function scroll:set_vertical_scroll
|
||||
-- @tparam bool state True, if vertical scroll is enabled
|
||||
-- @treturn druid.scroll Current scroll instance
|
||||
function Scroll:set_vertical_scroll(state)
|
||||
self._is_vertical_scroll = state
|
||||
self.drag.can_y = self.available_size.y > 0 and state
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Bind the grid component (Static or Dynamic) to recalculate
|
||||
-- scroll size on grid changes
|
||||
-- @function scroll:bind_grid
|
||||
-- @tparam druid.static_grid|druid.dynamic_grid Druid grid component
|
||||
-- @treturn druid.scroll Current scroll instance
|
||||
function Scroll:bind_grid(grid)
|
||||
if self._grid_on_change then
|
||||
self._grid_on_change:unsubscribe(self._grid_on_change_callback)
|
||||
|
||||
self._grid_on_change = nil
|
||||
self._grid_on_change_callback = nil
|
||||
end
|
||||
|
||||
if not grid then
|
||||
return
|
||||
end
|
||||
|
||||
self._grid_on_change = grid.on_change_items
|
||||
self._grid_on_change_callback = self._grid_on_change:subscribe(function()
|
||||
self:set_size(grid:get_size())
|
||||
end)
|
||||
self:set_size(grid:get_size())
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
function Scroll:_on_scroll_drag(dx, dy)
|
||||
local t = self.target_position
|
||||
local b = self.available_pos
|
||||
local eb = self.available_pos_extra
|
||||
@ -102,7 +395,7 @@ local function on_scroll_drag(self, dx, dy)
|
||||
end
|
||||
|
||||
|
||||
local function check_soft_zone(self)
|
||||
function Scroll:_check_soft_zone()
|
||||
local target = self.target_position
|
||||
local border = self.available_pos
|
||||
local speed = self.style.BACK_SPEED
|
||||
@ -127,7 +420,7 @@ end
|
||||
|
||||
|
||||
--- Cancel animation on other animation or input touch
|
||||
local function cancel_animate(self)
|
||||
function Scroll:_cancel_animate()
|
||||
if self.is_animate then
|
||||
self.target_position = gui.get_position(self.content_node)
|
||||
self.position.x = self.target_position.x
|
||||
@ -138,8 +431,7 @@ local function cancel_animate(self)
|
||||
end
|
||||
|
||||
|
||||
|
||||
local function set_scroll_position(self, position)
|
||||
function Scroll:_set_scroll_position(position)
|
||||
local available_extra = self.available_pos_extra
|
||||
position.x = helper.clamp(position.x, available_extra.x, available_extra.z)
|
||||
position.y = helper.clamp(position.y, available_extra.w, available_extra.y)
|
||||
@ -158,7 +450,7 @@ end
|
||||
-- if no inert, scroll to next point by scroll direction
|
||||
-- if inert, find next point by scroll director
|
||||
-- @local
|
||||
local function check_points(self)
|
||||
function Scroll:_check_points()
|
||||
if not self.points then
|
||||
return
|
||||
end
|
||||
@ -210,7 +502,7 @@ local function check_points(self)
|
||||
end
|
||||
|
||||
|
||||
local function check_threshold(self)
|
||||
function Scroll:_check_threshold()
|
||||
local is_stopped = false
|
||||
|
||||
if self.inertion.x ~= 0 and math.abs(self.inertion.x) < self.style.INERT_THRESHOLD then
|
||||
@ -223,12 +515,12 @@ local function check_threshold(self)
|
||||
end
|
||||
|
||||
if is_stopped or not self._is_inert then
|
||||
check_points(self)
|
||||
self:_check_points()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function update_free_scroll(self, dt)
|
||||
function Scroll:_update_free_scroll(dt)
|
||||
local target = self.target_position
|
||||
|
||||
if self._is_inert and (self.inertion.x ~= 0 or self.inertion.y ~= 0) then
|
||||
@ -236,31 +528,31 @@ local function update_free_scroll(self, dt)
|
||||
target.x = self.position.x + self.inertion.x * self.style.INERT_SPEED * dt
|
||||
target.y = self.position.y + self.inertion.y * self.style.INERT_SPEED * dt
|
||||
|
||||
check_threshold(self)
|
||||
self:_check_threshold()
|
||||
end
|
||||
|
||||
-- Inertion friction
|
||||
self.inertion = self.inertion * self.style.FRICT
|
||||
|
||||
check_soft_zone(self)
|
||||
self:_check_soft_zone()
|
||||
if self.position.x ~= target.x or self.position.y ~= target.y then
|
||||
set_scroll_position(self, target)
|
||||
self:_set_scroll_position(target)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function update_hand_scroll(self, dt)
|
||||
function Scroll:_update_hand_scroll(dt)
|
||||
local dx = self.target_position.x - self.position.x
|
||||
local dy = self.target_position.y - self.position.y
|
||||
|
||||
self.inertion.x = (self.inertion.x + dx) * self.style.FRICT_HOLD
|
||||
self.inertion.y = (self.inertion.y + dy) * self.style.FRICT_HOLD
|
||||
|
||||
set_scroll_position(self, self.target_position)
|
||||
self:_set_scroll_position(self.target_position)
|
||||
end
|
||||
|
||||
|
||||
local function on_touch_start(self)
|
||||
function Scroll:_on_touch_start()
|
||||
self.inertion.x = 0
|
||||
self.inertion.y = 0
|
||||
self.target_position.x = self.position.x
|
||||
@ -268,12 +560,12 @@ local function on_touch_start(self)
|
||||
end
|
||||
|
||||
|
||||
local function on_touch_end(self)
|
||||
check_threshold(self)
|
||||
function Scroll:_on_touch_end()
|
||||
self:_check_threshold()
|
||||
end
|
||||
|
||||
|
||||
local function update_size(self)
|
||||
function Scroll:_update_size()
|
||||
local view_border = helper.get_border(self.view_node)
|
||||
local view_size = vmath.mul_per_elem(gui.get_size(self.view_node), gui.get_scale(self.view_node))
|
||||
|
||||
@ -283,8 +575,8 @@ local function update_size(self)
|
||||
self.available_pos = get_border_vector(view_border - content_border)
|
||||
self.available_size = get_size_vector(self.available_pos)
|
||||
|
||||
self.drag.can_x = self.available_size.x > 0
|
||||
self.drag.can_y = self.available_size.y > 0
|
||||
self.drag.can_x = self.available_size.x > 0 and self._is_horizontal_scroll
|
||||
self.drag.can_y = self.available_size.y > 0 and self._is_vertical_scroll
|
||||
|
||||
-- Extra content size calculation
|
||||
-- We add extra size only if scroll is available
|
||||
@ -314,237 +606,4 @@ local function update_size(self)
|
||||
end
|
||||
|
||||
|
||||
--- Component style params.
|
||||
-- You can override this component styles params in druid styles table
|
||||
-- or create your own style
|
||||
-- @table Style
|
||||
-- @tfield[opt=0] number FRICT Multiplier for free inertion
|
||||
-- @tfield[opt=0] number FRICT_HOLD Multiplier for inertion, while touching
|
||||
-- @tfield[opt=3] number INERT_THRESHOLD Scroll speed to stop inertion
|
||||
-- @tfield[opt=30] number INERT_SPEED Multiplier for inertion speed
|
||||
-- @tfield[opt=20] number POINTS_DEADZONE Speed to check points of interests in no_inertion mode
|
||||
-- @tfield[opt=0.35] number BACK_SPEED Scroll back returning lerp speed
|
||||
-- @tfield[opt=0.2] number ANIM_SPEED Scroll gui.animation speed for scroll_to function
|
||||
-- @tfield[opt=0] number EXTRA_STRETCH_SIZE extra size in pixels outside of scroll (stretch effect)
|
||||
-- @tfield[opt=false] bool SMALL_CONTENT_SCROLL If true, content node with size less than view node size can be scrolled
|
||||
function M.on_style_change(self, style)
|
||||
self.style = {}
|
||||
self.style.EXTRA_STRETCH_SIZE = style.EXTRA_STRETCH_SIZE or 0
|
||||
self.style.ANIM_SPEED = style.ANIM_SPEED or 0.2
|
||||
self.style.BACK_SPEED = style.BACK_SPEED or 0.35
|
||||
|
||||
self.style.FRICT = style.FRICT or 0
|
||||
self.style.FRICT_HOLD = style.FRICT_HOLD or 0
|
||||
|
||||
self.style.INERT_THRESHOLD = style.INERT_THRESHOLD or 3
|
||||
self.style.INERT_SPEED = style.INERT_SPEED or 30
|
||||
self.style.POINTS_DEADZONE = style.POINTS_DEADZONE or 20
|
||||
self.style.SMALL_CONTENT_SCROLL = style.SMALL_CONTENT_SCROLL or false
|
||||
|
||||
self._is_inert = not (self.style.FRICT == 0 or
|
||||
self.style.FRICT_HOLD == 0 or
|
||||
self.style.INERT_SPEED == 0)
|
||||
end
|
||||
|
||||
|
||||
--- Scroll constructor.
|
||||
-- @function scroll:init
|
||||
-- @tparam node view_node GUI view scroll node
|
||||
-- @tparam node content_node GUI content scroll node
|
||||
function M.init(self, view_node, content_node)
|
||||
self.druid = self:get_druid()
|
||||
|
||||
self.view_node = self:get_node(view_node)
|
||||
self.content_node = self:get_node(content_node)
|
||||
|
||||
self.position = gui.get_position(self.content_node)
|
||||
self.target_position = vmath.vector3(self.position)
|
||||
self.inertion = vmath.vector3(0)
|
||||
|
||||
self.drag = self.druid:new_drag(view_node, on_scroll_drag)
|
||||
self.drag.on_touch_start:subscribe(on_touch_start)
|
||||
self.drag.on_touch_end:subscribe(on_touch_end)
|
||||
|
||||
self.on_scroll = Event()
|
||||
self.on_scroll_to = Event()
|
||||
self.on_point_scroll = Event()
|
||||
|
||||
self.selected = nil
|
||||
self.is_animate = false
|
||||
|
||||
update_size(self)
|
||||
end
|
||||
|
||||
|
||||
function M.on_layout_change(self)
|
||||
gui.set_position(self.content_node, self.position)
|
||||
end
|
||||
|
||||
|
||||
function M.update(self, dt)
|
||||
if self.drag.is_drag then
|
||||
update_hand_scroll(self, dt)
|
||||
else
|
||||
update_free_scroll(self, dt)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Start scroll to target point.
|
||||
-- @function scroll:scroll_to
|
||||
-- @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.available_pos
|
||||
local target = vmath.vector3(-point.x, -point.y, 0)
|
||||
target.x = helper.clamp(target.x, b.x, b.z)
|
||||
target.y = helper.clamp(target.y, b.y, b.w)
|
||||
|
||||
cancel_animate(self)
|
||||
|
||||
self.is_animate = not is_instant
|
||||
|
||||
if is_instant then
|
||||
self.target_position = target
|
||||
set_scroll_position(self, target)
|
||||
else
|
||||
gui.animate(self.content_node, gui.PROP_POSITION, target, gui.EASING_OUTSINE, self.style.ANIM_SPEED, 0, function()
|
||||
self.is_animate = false
|
||||
self.target_position = target
|
||||
set_scroll_position(self, target)
|
||||
end)
|
||||
end
|
||||
|
||||
self.on_scroll_to:trigger(self:get_context(), target, is_instant)
|
||||
end
|
||||
|
||||
|
||||
--- Scroll to item in scroll by point index.
|
||||
-- @function scroll:scroll_to_index
|
||||
-- @tparam number index Point index
|
||||
-- @tparam[opt] bool skip_cb If true, skip the point callback
|
||||
function M.scroll_to_index(self, index, skip_cb)
|
||||
if not self.points then
|
||||
return
|
||||
end
|
||||
|
||||
index = helper.clamp(index, 1, #self.points)
|
||||
|
||||
if self.selected ~= index then
|
||||
self.selected = index
|
||||
|
||||
if not skip_cb then
|
||||
self.on_point_scroll:trigger(self:get_context(), index, self.points[index])
|
||||
end
|
||||
end
|
||||
|
||||
self:scroll_to(self.points[index])
|
||||
end
|
||||
|
||||
|
||||
--- Start scroll to target scroll percent
|
||||
-- @function scroll:scroll_to_percent
|
||||
-- @tparam point vector3 target percent
|
||||
-- @tparam[opt] bool is_instant instant scroll flag
|
||||
-- @usage scroll:scroll_to_percent(vmath.vector3(0.5, 0, 0))
|
||||
function M.scroll_to_percent(self, percent, is_instant)
|
||||
local border = self.available_pos
|
||||
|
||||
local pos = vmath.vector3(
|
||||
-helper.lerp(border.x, border.z, 1 - percent.x),
|
||||
-helper.lerp(border.w, border.y, 1 - percent.y),
|
||||
0
|
||||
)
|
||||
|
||||
M.scroll_to(self, pos, is_instant)
|
||||
end
|
||||
|
||||
|
||||
--- Return current scroll progress status.
|
||||
-- Values will be in [0..1] interval
|
||||
-- @function scroll:get_percent
|
||||
-- @treturn vector3 New vector with scroll progress values
|
||||
function M.get_percent(self)
|
||||
local x_perc = 1 - inverse_lerp(self.available_pos.x, self.available_pos.z, self.position.x)
|
||||
local y_perc = inverse_lerp(self.available_pos.w, self.available_pos.y, self.position.y)
|
||||
|
||||
return vmath.vector3(x_perc, y_perc, 0)
|
||||
end
|
||||
|
||||
|
||||
--- Set scroll content size.
|
||||
-- It will change content gui node size
|
||||
-- @function scroll:set_size
|
||||
-- @tparam vector3 size The new size for content node
|
||||
-- @treturn druid.scroll Current scroll instance
|
||||
function M.set_size(self, size)
|
||||
gui.set_size(self.content_node, size)
|
||||
update_size(self)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Enable or disable scroll inert.
|
||||
-- If disabled, scroll through points (if exist)
|
||||
-- If no points, just simple drag without inertion
|
||||
-- @function scroll:set_inert
|
||||
-- @tparam bool state Inert scroll state
|
||||
-- @treturn druid.scroll Current scroll instance
|
||||
function M.set_inert(self, state)
|
||||
self._is_inert = state
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Return if scroll have inertion.
|
||||
-- @function scroll:is_inert
|
||||
-- @treturn bool If scroll have inertion
|
||||
function M.is_inert(self)
|
||||
return self._is_inert
|
||||
end
|
||||
|
||||
|
||||
--- Set extra size for scroll stretching.
|
||||
-- Set 0 to disable stretching effect
|
||||
-- @function scroll:set_extra_stretch_size
|
||||
-- @tparam[opt=0] number stretch_size Size in pixels of additional scroll area
|
||||
-- @treturn druid.scroll Current scroll instance
|
||||
function M.set_extra_stretch_size(self, stretch_size)
|
||||
self.style.EXTRA_STRETCH_SIZE = stretch_size or 0
|
||||
update_size(self)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Return vector of scroll size with width and height.
|
||||
-- @function scroll:get_scroll_size
|
||||
-- @treturn vector3 Available scroll size
|
||||
function M.get_scroll_size(self)
|
||||
return self.available_size
|
||||
end
|
||||
|
||||
|
||||
--- Set points of interest.
|
||||
-- Scroll will always centered on closer points
|
||||
-- @function scroll:set_points
|
||||
-- @tparam table points Array of vector3 points
|
||||
-- @treturn druid.scroll Current scroll instance
|
||||
function M.set_points(self, points)
|
||||
self.points = points
|
||||
|
||||
table.sort(self.points, function(a, b)
|
||||
return a.x > b.x or a.y < b.y
|
||||
end)
|
||||
|
||||
check_threshold(self)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
return M
|
||||
return Scroll
|
||||
|
323
druid/base/static_grid.lua
Normal file
323
druid/base/static_grid.lua
Normal file
@ -0,0 +1,323 @@
|
||||
--- Component to handle placing components by row and columns.
|
||||
-- Grid can anchor your elements, get content size and other
|
||||
-- @module druid.static_grid
|
||||
|
||||
--- Component events
|
||||
-- @table Events
|
||||
-- @tfield druid_event on_add_item On item add callback
|
||||
-- @tfield druid_event on_remove_item On item remove callback
|
||||
-- @tfield druid_event on_change_items On item add or remove callback
|
||||
-- @tfield druid_event on_clear On grid clear callback
|
||||
-- @tfield druid_event on_update_positions On update item positions callback
|
||||
|
||||
--- Component fields
|
||||
-- @table Fields
|
||||
-- @tfield node parent Parent gui node
|
||||
-- @tfield node[] nodes List of all grid nodes
|
||||
-- @tfield number first_index The first index of node in grid
|
||||
-- @tfield number last_index The last index of node in grid
|
||||
-- @tfield vector3 anchor Item anchor
|
||||
-- @tfield vector3 node_size Item size
|
||||
-- @tfield vector4 border The size of item content
|
||||
|
||||
local const = require("druid.const")
|
||||
local Event = require("druid.event")
|
||||
local helper = require("druid.helper")
|
||||
local component = require("druid.component")
|
||||
|
||||
local StaticGrid = component.create("static_grid", { const.ON_LAYOUT_CHANGE })
|
||||
|
||||
|
||||
--- Component init function
|
||||
-- @function static_grid:init
|
||||
-- @tparam node parent The gui node parent, where items will be placed
|
||||
-- @tparam node element Element prefab. Need to get it size
|
||||
-- @tparam[opt=1] number in_row How many nodes in row can be placed
|
||||
function StaticGrid:init(parent, element, in_row)
|
||||
self.parent = self:get_node(parent)
|
||||
self.nodes = {}
|
||||
|
||||
self.pivot = helper.get_pivot_offset(gui.get_pivot(self.parent))
|
||||
self.anchor = vmath.vector3(0.5 + self.pivot.x, 0.5 - self.pivot.y, 0)
|
||||
|
||||
self.in_row = in_row or 1
|
||||
|
||||
self._prefab = self:get_node(element)
|
||||
self.node_size = gui.get_size(self._prefab)
|
||||
self.node_pivot = const.PIVOTS[gui.get_pivot(self._prefab)]
|
||||
|
||||
self.border = vmath.vector4(0) -- Current grid content size
|
||||
|
||||
self.on_add_item = Event()
|
||||
self.on_remove_item = Event()
|
||||
self.on_change_items = Event()
|
||||
self.on_clear = Event()
|
||||
self.on_update_positions = Event()
|
||||
|
||||
self._set_position_function = gui.set_position
|
||||
end
|
||||
|
||||
|
||||
local _temp_pos = vmath.vector3(0)
|
||||
--- Return pos for grid node index
|
||||
-- @function static_grid:get_pos
|
||||
-- @tparam number index The grid element index
|
||||
-- @treturn vector3 Node position
|
||||
function StaticGrid:get_pos(index)
|
||||
local row = math.ceil(index / self.in_row) - 1
|
||||
local col = (index - row * self.in_row) - 1
|
||||
|
||||
_temp_pos.x = col * self.node_size.x
|
||||
_temp_pos.y = -row * self.node_size.y
|
||||
_temp_pos.z = 0
|
||||
|
||||
return _temp_pos
|
||||
end
|
||||
|
||||
|
||||
--- Return index for grid pos
|
||||
-- @function static_grid:get_index
|
||||
-- @tparam vector3 pos The node position in the grid
|
||||
-- @treturn number The node index
|
||||
function StaticGrid:get_index(pos)
|
||||
local col = pos.x / self.node_size.x + 1
|
||||
local row = -pos.y / self.node_size.y
|
||||
|
||||
col = helper.round(col)
|
||||
row = helper.round(row)
|
||||
|
||||
local index = col + (row * self.in_row)
|
||||
return math.ceil(index)
|
||||
end
|
||||
|
||||
|
||||
--- Return grid index by node
|
||||
-- @function static_grid:get_index_by_node
|
||||
-- @tparam node node The gui node in the grid
|
||||
-- @treturn number The node index
|
||||
function StaticGrid:get_index_by_node(node)
|
||||
for index, grid_node in pairs(self.nodes) do
|
||||
if node == grid_node then
|
||||
return index
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
function StaticGrid:on_layout_change()
|
||||
self:_update(true)
|
||||
end
|
||||
|
||||
|
||||
--- Set grid anchor. Default anchor is equal to anchor of grid parent node
|
||||
-- @function static_grid:set_anchor
|
||||
-- @tparam vector3 anchor Anchor
|
||||
function StaticGrid:set_anchor(anchor)
|
||||
self.anchor = anchor
|
||||
self:_update()
|
||||
end
|
||||
|
||||
|
||||
--- Add new item to the grid
|
||||
-- @function static_grid:add
|
||||
-- @tparam node item Gui node
|
||||
-- @tparam[opt] number index The item position. By default add as last item
|
||||
function StaticGrid:add(item, index)
|
||||
index = index or ((self.last_index or 0) + 1)
|
||||
|
||||
if self.nodes[index] then
|
||||
-- Move nodes to right
|
||||
for i = self.last_index, index, -1 do
|
||||
self.nodes[i + 1] = self.nodes[i]
|
||||
end
|
||||
end
|
||||
|
||||
self.nodes[index] = item
|
||||
|
||||
gui.set_parent(item, self.parent)
|
||||
|
||||
-- Add new item instantly in new pos. Break update function for correct positioning
|
||||
self:_update_indexes()
|
||||
self:_update_borders()
|
||||
|
||||
gui.set_position(item, self:get_pos(index) + self:_get_zero_offset())
|
||||
|
||||
self:_update_pos()
|
||||
|
||||
self.on_add_item:trigger(self:get_context(), item, index)
|
||||
self.on_change_items:trigger(self:get_context(), index)
|
||||
end
|
||||
|
||||
|
||||
--- Remove the item from the grid. Note that gui node will be not deleted
|
||||
-- @function static_grid:remove
|
||||
-- @tparam number index The grid node index to remove
|
||||
-- @tparam bool is_shift_nodes If true, will shift nodes left after index
|
||||
function StaticGrid:remove(index, is_shift_nodes)
|
||||
assert(self.nodes[index], "No grid item at given index " .. index)
|
||||
|
||||
self.nodes[index] = nil
|
||||
|
||||
if is_shift_nodes then
|
||||
for i = index, self.last_index do
|
||||
self.nodes[i] = self.nodes[i + 1]
|
||||
end
|
||||
end
|
||||
|
||||
self:_update()
|
||||
|
||||
self.on_add_item:trigger(self:get_context(), index)
|
||||
self.on_change_items:trigger(self:get_context(), index)
|
||||
end
|
||||
|
||||
|
||||
--- Return grid content size
|
||||
-- @function static_grid:get_size
|
||||
-- @treturn vector3 The grid content size
|
||||
function StaticGrid:get_size()
|
||||
return vmath.vector3(
|
||||
self.border.z - self.border.x,
|
||||
self.border.y - self.border.w,
|
||||
0)
|
||||
end
|
||||
|
||||
|
||||
--- Return array of all node positions
|
||||
-- @function static_grid:get_all_pos
|
||||
-- @treturn vector3[] All grid node positions
|
||||
function StaticGrid:get_all_pos()
|
||||
local result = {}
|
||||
for i, node in pairs(self.nodes) do
|
||||
table.insert(result, gui.get_position(node))
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
--- Change set position function for grid nodes. It will call on
|
||||
-- update poses on grid elements. Default: gui.set_position
|
||||
-- @function static_grid:set_position_function
|
||||
-- @tparam function callback Function on node set position
|
||||
-- @treturn druid.static_grid Current grid instance
|
||||
function StaticGrid:set_position_function(callback)
|
||||
self._set_position_function = callback or gui.set_position
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Clear grid nodes array. GUI nodes will be not deleted!
|
||||
-- If you want to delete GUI nodes, use static_grid.nodes array before grid:clear
|
||||
-- @function static_grid:clear
|
||||
-- @treturn druid.static_grid Current grid instance
|
||||
function StaticGrid:clear()
|
||||
self.border.x = 0
|
||||
self.border.y = 0
|
||||
self.border.w = 0
|
||||
self.border.z = 0
|
||||
|
||||
self.nodes = {}
|
||||
self:_update()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Return elements offset for correct posing nodes. Correct posing at
|
||||
-- parent pivot node (0:0) with adjusting of node sizes and anchoring
|
||||
-- @function static_grid:_get_zero_offset
|
||||
-- @treturn vector3 The offset vector
|
||||
-- @local
|
||||
function StaticGrid:_get_zero_offset()
|
||||
-- zero offset: center pos - border size * anchor
|
||||
return vmath.vector3(
|
||||
-((self.border.x + self.border.z)/2 + (self.border.z - self.border.x) * self.pivot.x),
|
||||
-((self.border.y + self.border.w)/2 + (self.border.y - self.border.w) * self.pivot.y),
|
||||
0
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
--- Update grid inner state
|
||||
-- @function static_grid:_update
|
||||
-- @tparam bool is_instant If true, node position update instantly, otherwise with set_position_function callback
|
||||
-- @local
|
||||
function StaticGrid:_update(is_instant)
|
||||
self:_update_indexes()
|
||||
self:_update_borders()
|
||||
self:_update_pos(is_instant)
|
||||
end
|
||||
|
||||
|
||||
--- Update first and last indexes of grid nodes
|
||||
-- @function static_grid:_update_indexes
|
||||
-- @local
|
||||
function StaticGrid:_update_indexes()
|
||||
self.first_index = nil
|
||||
self.last_index = nil
|
||||
for index in pairs(self.nodes) do
|
||||
self.first_index = self.first_index or index
|
||||
self.last_index = self.last_index or index
|
||||
|
||||
self.first_index = math.min(self.first_index, index)
|
||||
self.last_index = math.max(self.last_index, index)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Update grid content borders, recalculate min and max values
|
||||
-- @function static_grid:_update_borders
|
||||
-- @local
|
||||
function StaticGrid:_update_borders()
|
||||
if not self.first_index then
|
||||
self.border = vmath.vector4(0)
|
||||
return
|
||||
end
|
||||
|
||||
self.border = vmath.vector4(math.huge, -math.huge, -math.huge, math.huge)
|
||||
|
||||
local size = self.node_size
|
||||
local pivot = self.node_pivot
|
||||
for index, node in pairs(self.nodes) do
|
||||
local pos = self:get_pos(index)
|
||||
|
||||
local left = pos.x - size.x/2 - (size.x * pivot.x)
|
||||
local right = pos.x + size.x/2 - (size.x * pivot.x)
|
||||
local top = pos.y + size.y/2 - (size.y * pivot.y)
|
||||
local bottom = pos.y - size.y/2 - (size.y * pivot.y)
|
||||
|
||||
self.border.x = math.min(self.border.x, left)
|
||||
self.border.y = math.max(self.border.y, top)
|
||||
self.border.z = math.max(self.border.z, right)
|
||||
self.border.w = math.min(self.border.w, bottom)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Update grid nodes position
|
||||
-- @function static_grid:_update_indexes
|
||||
-- @tparam bool is_instant If true, node position update instantly, otherwise with set_position_function callback
|
||||
-- @local
|
||||
function StaticGrid:_update_pos(is_instant)
|
||||
local zero_offset = self:_get_zero_offset()
|
||||
|
||||
for i, node in pairs(self.nodes) do
|
||||
local pos = self:get_pos(i)
|
||||
pos.x = pos.x + zero_offset.x
|
||||
pos.y = pos.y + zero_offset.y
|
||||
|
||||
if is_instant then
|
||||
gui.set_position(node, pos)
|
||||
else
|
||||
self._set_position_function(node, pos)
|
||||
end
|
||||
end
|
||||
|
||||
self.on_update_positions:trigger(self:get_context())
|
||||
end
|
||||
|
||||
|
||||
return StaticGrid
|
@ -102,6 +102,11 @@ M.SWIPE = {
|
||||
}
|
||||
|
||||
|
||||
M.ERRORS = {
|
||||
GRID_DYNAMIC_ANCHOR = "The pivot of dynamic grid node should be West, East, South or North"
|
||||
}
|
||||
|
||||
|
||||
M.EMPTY_FUNCTION = function() end
|
||||
M.EMPTY_STRING = ""
|
||||
M.SPACE_STRING = " "
|
||||
|
@ -27,6 +27,8 @@ function M.subscribe(self, callback)
|
||||
assert(type(callback) == "function", "Callback should be function")
|
||||
|
||||
table.insert(self._callbacks, callback)
|
||||
|
||||
return callback
|
||||
end
|
||||
|
||||
|
||||
|
386
druid/extended/dynamic_grid.lua
Normal file
386
druid/extended/dynamic_grid.lua
Normal file
@ -0,0 +1,386 @@
|
||||
--- Component to handle placing components in row
|
||||
-- @module druid.dynamic_grid
|
||||
|
||||
--- Component events
|
||||
-- @table Events
|
||||
-- @tfield druid_event on_add_item On item add callback
|
||||
-- @tfield druid_event on_remove_item On item remove callback
|
||||
-- @tfield druid_event on_change_items On item add or remove callback
|
||||
-- @tfield druid_event on_clear On grid clear callback
|
||||
-- @tfield druid_event on_update_positions On update item positions callback
|
||||
|
||||
--- Component fields
|
||||
-- @table Fields
|
||||
-- @tfield node parent Parent gui node
|
||||
-- @tfield node[] nodes List of all grid nodes
|
||||
-- @tfield number first_index The first index of node in grid
|
||||
-- @tfield number last_index The last index of node in grid
|
||||
-- @tfield vector3 node_size Item size
|
||||
-- @tfield vector4 border The size of item content
|
||||
|
||||
local const = require("druid.const")
|
||||
local Event = require("druid.event")
|
||||
local helper = require("druid.helper")
|
||||
local component = require("druid.component")
|
||||
|
||||
local DynamicGrid = component.create("dynamic_grid", { const.ON_LAYOUT_CHANGE })
|
||||
|
||||
local SIDE_VECTORS = {
|
||||
LEFT = vmath.vector3(-1, 0, 0),
|
||||
RIGHT = vmath.vector3(1, 0, 0),
|
||||
TOP = vmath.vector3(0, -1, 0),
|
||||
BOT = vmath.vector3(0, 1, 0),
|
||||
}
|
||||
|
||||
local AVAILABLE_PIVOTS = {
|
||||
gui.PIVOT_N,
|
||||
gui.PIVOT_S,
|
||||
gui.PIVOT_W,
|
||||
gui.PIVOT_E,
|
||||
}
|
||||
|
||||
|
||||
--- Component init function
|
||||
-- @function dynamic_grid:init
|
||||
-- @tparam node parent The gui node parent, where items will be placed
|
||||
function DynamicGrid:init(parent)
|
||||
self.parent = self:get_node(parent)
|
||||
|
||||
local parent_pivot = gui.get_pivot(self.parent)
|
||||
self.pivot = helper.get_pivot_offset(parent_pivot)
|
||||
|
||||
assert(helper.contains(AVAILABLE_PIVOTS, parent_pivot), const.ERRORS.GRID_DYNAMIC_ANCHOR)
|
||||
self.side = ((parent_pivot == gui.PIVOT_W or parent_pivot == gui.PIVOT_E)
|
||||
and const.SIDE.X or const.SIDE.Y)
|
||||
|
||||
self.nodes = {}
|
||||
self.border = vmath.vector4(0) -- Current grid content size
|
||||
|
||||
self.on_add_item = Event()
|
||||
self.on_remove_item = Event()
|
||||
self.on_change_items = Event()
|
||||
self.on_clear = Event()
|
||||
self.on_update_positions = Event()
|
||||
|
||||
self._set_position_function = gui.set_position
|
||||
end
|
||||
|
||||
|
||||
function DynamicGrid:on_layout_change()
|
||||
self:_update(true)
|
||||
end
|
||||
|
||||
|
||||
--- Return pos for grid node index
|
||||
-- @function dynamic_grid:get_pos
|
||||
-- @tparam number index The grid element index
|
||||
-- @tparam node node The node to be placed
|
||||
-- @treturn vector3 Node position
|
||||
function DynamicGrid:get_pos(index, node, origin_index)
|
||||
local origin_node = self.nodes[origin_index]
|
||||
|
||||
-- If anchor node is not exist, check around nodes
|
||||
if not origin_node then
|
||||
if self.nodes[index + 1] then
|
||||
origin_index = index + 1
|
||||
end
|
||||
if self.nodes[index - 1] then
|
||||
origin_index = index - 1
|
||||
end
|
||||
origin_node = self.nodes[origin_index]
|
||||
end
|
||||
|
||||
if not origin_node then
|
||||
assert(not self.first_index, "Dynamic Grid can't have gaps between nodes. Error on grid:add")
|
||||
|
||||
-- If not origin node, so it should be first element in the grid
|
||||
local size = self:_get_node_size(node)
|
||||
local pivot = const.PIVOTS[gui.get_pivot(node)]
|
||||
return vmath.vector3(
|
||||
size.x * pivot.x - size.x * self.pivot.x,
|
||||
size.y * pivot.y - size.y * self.pivot.y,
|
||||
0)
|
||||
end
|
||||
|
||||
if origin_node then
|
||||
-- Other nodes spawn from other side of the origin node
|
||||
local is_forward = origin_index < index
|
||||
local delta = is_forward and 1 or -1
|
||||
return self:_get_next_node_pos(index - delta, node, self:_get_side_vector(self.side, is_forward))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Add new node to the grid
|
||||
-- @function dynamic_grid:add
|
||||
-- @tparam node node Gui node
|
||||
-- @tparam[opt] number index The node position. By default add as last node
|
||||
-- @tparam[opt=false] bool is_shift_left If true, shift all nodes to the left, otherwise shift nodes to the right
|
||||
function DynamicGrid:add(node, index, is_shift_left)
|
||||
local delta = is_shift_left and -1 or 1
|
||||
|
||||
-- By default add node at end
|
||||
index = index or ((self.last_index or 0) + 1)
|
||||
|
||||
-- If node exist at index place, shifting them
|
||||
local is_shift = self.nodes[index]
|
||||
if is_shift then
|
||||
-- We need to iterate from index to start or end grid, depends of shift side
|
||||
local start_index = is_shift_left and self.first_index or self.last_index
|
||||
for i = start_index, index, -delta do
|
||||
self.nodes[i + delta] = self.nodes[i]
|
||||
end
|
||||
end
|
||||
|
||||
self:_add_node(node, index, index - delta)
|
||||
|
||||
-- After shifting we should recalc node poses
|
||||
if is_shift then
|
||||
-- We need to iterate from placed node to start or end grid, depends of shift side
|
||||
local target_index = is_shift_left and self.first_index or self.last_index
|
||||
for i = index + delta, target_index + delta, delta do
|
||||
local move_node = self.nodes[i]
|
||||
move_node.pos = self:get_pos(i, move_node.node, i - delta)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Sync grid data
|
||||
self:_update()
|
||||
|
||||
self.on_add_item:trigger(self:get_context(), node, index)
|
||||
self.on_change_items:trigger(self:get_context(), index)
|
||||
end
|
||||
|
||||
|
||||
--- Remove the item from the grid. Note that gui node will be not deleted
|
||||
-- @function dynamic_grid:remove
|
||||
-- @tparam number index The grid node index to remove
|
||||
-- @tparam[opt=false] bool is_shift_left If true, shift all nodes to the left, otherwise shift nodes to the right
|
||||
function DynamicGrid:remove(index, is_shift_left)
|
||||
local delta = is_shift_left and -1 or 1
|
||||
|
||||
assert(self.nodes[index], "No grid item at given index " .. index)
|
||||
|
||||
-- Just set nil for delete node data
|
||||
self.nodes[index] = nil
|
||||
|
||||
-- After delete node, we should shift nodes and recalc their poses, depends from is_shift_left
|
||||
local target_index = is_shift_left and self.first_index or self.last_index
|
||||
for i = index, target_index, delta do
|
||||
self.nodes[i] = self.nodes[i + delta]
|
||||
if self.nodes[i] then
|
||||
self.nodes[i].pos = self:get_pos(i, self.nodes[i].node, i - delta)
|
||||
end
|
||||
end
|
||||
|
||||
-- Sync grid data
|
||||
self:_update()
|
||||
|
||||
self.on_add_item:trigger(self:get_context(), index)
|
||||
self.on_change_items:trigger(self:get_context(), index)
|
||||
end
|
||||
|
||||
|
||||
--- Return grid content size
|
||||
-- @function dynamic_grid:get_size
|
||||
-- @treturn vector3 The grid content size
|
||||
function DynamicGrid:get_size(border)
|
||||
border = border or self.border
|
||||
return vmath.vector3(
|
||||
border.z - border.x,
|
||||
border.y - border.w,
|
||||
0)
|
||||
end
|
||||
|
||||
|
||||
--- Return grid index by node
|
||||
-- @function dynamic_grid:get_index_by_node
|
||||
-- @tparam node node The gui node in the grid
|
||||
-- @treturn number The node index
|
||||
function DynamicGrid:get_index_by_node(node)
|
||||
for index, node_info in pairs(self.nodes) do
|
||||
if node == node_info.node then
|
||||
return index
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Return array of all node positions
|
||||
-- @function dynamic_grid:get_all_pos
|
||||
-- @treturn vector3[] All grid node positions
|
||||
function DynamicGrid:get_all_pos()
|
||||
local result = {}
|
||||
for i, node in pairs(self.nodes) do
|
||||
table.insert(result, gui.get_position(node))
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
--- Change set position function for grid nodes. It will call on
|
||||
-- update poses on grid elements. Default: gui.set_position
|
||||
-- @function dynamic_grid:set_position_function
|
||||
-- @tparam function callback Function on node set position
|
||||
-- @treturn druid.dynamic_grid Current grid instance
|
||||
function DynamicGrid:set_position_function(callback)
|
||||
self._set_position_function = callback or gui.set_position
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Clear grid nodes array. GUI nodes will be not deleted!
|
||||
-- If you want to delete GUI nodes, use dynamic_grid.nodes array before grid:clear
|
||||
-- @function dynamic_grid:clear
|
||||
-- @treturn druid.dynamic_grid Current grid instance
|
||||
function DynamicGrid:clear()
|
||||
self.nodes = {}
|
||||
self:_update()
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
function DynamicGrid:_add_node(node, index, origin_index)
|
||||
self.nodes[index] = {
|
||||
node = node,
|
||||
pos = self:get_pos(index, node, origin_index),
|
||||
size = self:_get_node_size(node),
|
||||
pivot = const.PIVOTS[gui.get_pivot(node)]
|
||||
}
|
||||
|
||||
-- Add new item instantly in new pos
|
||||
gui.set_parent(node, self.parent)
|
||||
gui.set_position(node, self.nodes[index].pos + self:_get_zero_offset())
|
||||
end
|
||||
|
||||
|
||||
--- Update grid inner state
|
||||
-- @function dynamic_grid:_update
|
||||
-- @tparam bool is_instant If true, node position update instantly, otherwise with set_position_function callback
|
||||
-- @local
|
||||
function DynamicGrid:_update(is_instant)
|
||||
self:_update_indexes()
|
||||
self:_update_borders()
|
||||
self:_update_pos(is_instant)
|
||||
end
|
||||
|
||||
|
||||
--- Update first and last indexes of grid nodes
|
||||
-- @function dynamic_grid:_update_indexes
|
||||
-- @local
|
||||
function DynamicGrid:_update_indexes()
|
||||
self.first_index = nil
|
||||
self.last_index = nil
|
||||
for index in pairs(self.nodes) do
|
||||
self.first_index = self.first_index or index
|
||||
self.last_index = self.last_index or index
|
||||
|
||||
self.first_index = math.min(self.first_index, index)
|
||||
self.last_index = math.max(self.last_index, index)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Update grid content borders, recalculate min and max values
|
||||
-- @function dynamic_grid:_update_borders
|
||||
-- @local
|
||||
function DynamicGrid:_update_borders()
|
||||
if not self.first_index then
|
||||
self.border = vmath.vector4(0)
|
||||
return
|
||||
end
|
||||
|
||||
self.border = vmath.vector4(math.huge, -math.huge, -math.huge, math.huge)
|
||||
|
||||
for index, node in pairs(self.nodes) do
|
||||
local pos = node.pos
|
||||
local size = node.size
|
||||
local pivot = node.pivot
|
||||
|
||||
local left = pos.x - size.x/2 - (size.x * pivot.x)
|
||||
local right = pos.x + size.x/2 - (size.x * pivot.x)
|
||||
local top = pos.y + size.y/2 - (size.y * pivot.y)
|
||||
local bottom = pos.y - size.y/2 - (size.y * pivot.y)
|
||||
|
||||
self.border.x = math.min(self.border.x, left)
|
||||
self.border.y = math.max(self.border.y, top)
|
||||
self.border.z = math.max(self.border.z, right)
|
||||
self.border.w = math.min(self.border.w, bottom)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Update grid nodes position
|
||||
-- @function dynamic_grid:_update_indexes
|
||||
-- @tparam bool is_instant If true, node position update instantly, otherwise with set_position_function callback
|
||||
-- @local
|
||||
function DynamicGrid:_update_pos(is_instant)
|
||||
local offset = self:_get_zero_offset()
|
||||
|
||||
for index, node in pairs(self.nodes) do
|
||||
if is_instant then
|
||||
gui.set_position(node.node, node.pos + offset)
|
||||
else
|
||||
self._set_position_function(node.node, node.pos + offset)
|
||||
end
|
||||
end
|
||||
|
||||
self.on_update_positions:trigger(self:get_context())
|
||||
end
|
||||
|
||||
|
||||
function DynamicGrid:_get_next_node_pos(origin_node_index, new_node, place_side)
|
||||
local node = self.nodes[origin_node_index]
|
||||
|
||||
local new_node_size = self:_get_node_size(new_node)
|
||||
local new_pivot = const.PIVOTS[gui.get_pivot(new_node)]
|
||||
|
||||
local dist_x = (node.size.x/2 + new_node_size.x/2) * place_side.x
|
||||
local dist_y = (node.size.y/2 + new_node_size.y/2) * place_side.y
|
||||
local node_center_x = node.pos.x - node.size.x * node.pivot.x
|
||||
local node_center_y = node.pos.y - node.size.y * node.pivot.y
|
||||
|
||||
return vmath.vector3(
|
||||
node_center_x + dist_x + new_node_size.x * new_pivot.x,
|
||||
node_center_y - dist_y + new_node_size.y * new_pivot.y,
|
||||
0
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
function DynamicGrid:_get_node_size(node)
|
||||
return vmath.mul_per_elem(gui.get_size(node), gui.get_scale(node))
|
||||
end
|
||||
|
||||
|
||||
--- Return elements offset for correct posing nodes. Correct posing at
|
||||
-- parent pivot node (0:0) with adjusting of node sizes and anchoring
|
||||
-- @function dynamic_grid:_get_zero_offset
|
||||
-- @treturn vector3 The offset vector
|
||||
-- @local
|
||||
function DynamicGrid:_get_zero_offset()
|
||||
-- zero offset: center pos - border size * anchor
|
||||
return vmath.vector3(
|
||||
-((self.border.x + self.border.z)/2 + (self.border.z - self.border.x) * self.pivot.x),
|
||||
-((self.border.y + self.border.w)/2 + (self.border.y - self.border.w) * self.pivot.y),
|
||||
0)
|
||||
end
|
||||
|
||||
|
||||
--- Return side vector to correct node shifting
|
||||
function DynamicGrid:_get_side_vector(side, is_forward)
|
||||
if side == const.SIDE.X then
|
||||
return is_forward and SIDE_VECTORS.RIGHT or SIDE_VECTORS.LEFT
|
||||
end
|
||||
|
||||
if side == const.SIDE.Y then
|
||||
return is_forward and SIDE_VECTORS.BOT or SIDE_VECTORS.TOP
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return DynamicGrid
|
@ -131,6 +131,16 @@ function M.lerp(a, b, t)
|
||||
end
|
||||
|
||||
|
||||
function M.contains(t, value)
|
||||
for i = 1, #t do
|
||||
if t[i] == value then
|
||||
return i
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
--- Check if node is enabled in gui hierarchy.
|
||||
-- Return false, if node or any his parent is disabled
|
||||
-- @function helper.is_enabled
|
||||
@ -189,6 +199,20 @@ function M.get_border(node)
|
||||
end
|
||||
|
||||
|
||||
--- Show deprecated message. Once time per message
|
||||
-- @function helper.deprecated
|
||||
-- @tparam string message The deprecated message
|
||||
local _deprecated_messages = {}
|
||||
function M.deprecated(message)
|
||||
if _deprecated_messages[message] then
|
||||
return
|
||||
end
|
||||
|
||||
print("[Druid]: " .. message)
|
||||
_deprecated_messages[message] = true
|
||||
end
|
||||
|
||||
|
||||
-- Show message to require extended component
|
||||
function M.extended_component(component_name)
|
||||
print(string.format("[Druid]: The component %s is extended component. You have to register it via druid.register to use it", component_name))
|
||||
@ -197,4 +221,5 @@ function M.extended_component(component_name)
|
||||
print(string.format('druid.register("%s", %s)', component_name, component_name))
|
||||
end
|
||||
|
||||
|
||||
return M
|
||||
|
@ -9,7 +9,8 @@
|
||||
-- @see druid.lang_text
|
||||
-- @see druid.timer
|
||||
-- @see druid.progress
|
||||
-- @see druid.grid
|
||||
-- @see druid.static_grid
|
||||
-- @see druid.dynamic_grid
|
||||
-- @see druid.scroll
|
||||
-- @see druid.slider
|
||||
-- @see druid.checkbox
|
||||
@ -28,16 +29,17 @@ local back_handler = require("druid.base.back_handler")
|
||||
local blocker = require("druid.base.blocker")
|
||||
local button = require("druid.base.button")
|
||||
local drag = require("druid.base.drag")
|
||||
local grid = require("druid.base.grid")
|
||||
local hover = require("druid.base.hover")
|
||||
local lang_text = require("druid.base.lang_text")
|
||||
local scroll = require("druid.base.scroll")
|
||||
local static_grid = require("druid.base.static_grid")
|
||||
local swipe = require("druid.base.swipe")
|
||||
local text = require("druid.base.text")
|
||||
|
||||
local checkbox = require("druid.extended.checkbox")
|
||||
local checkbox_group = require("druid.extended.checkbox_group")
|
||||
local dynamic_grid = require("druid.extended.dynamic_grid")
|
||||
local input = require("druid.extended.input")
|
||||
local lang_text = require("druid.extended.lang_text")
|
||||
local progress = require("druid.extended.progress")
|
||||
local radio_group = require("druid.extended.radio_group")
|
||||
local slider = require("druid.extended.slider")
|
||||
@ -381,21 +383,23 @@ function Druid.new_text(self, ...)
|
||||
end
|
||||
|
||||
|
||||
--- Create lang_text basic component
|
||||
-- @function druid:new_lang_text
|
||||
-- @tparam args ... lang_text init args
|
||||
-- @treturn Component lang_text component
|
||||
function Druid.new_lang_text(self, ...)
|
||||
return Druid.create(self, lang_text, ...)
|
||||
end
|
||||
|
||||
|
||||
--- Create grid basic component
|
||||
-- @function druid:new_grid
|
||||
-- @tparam args ... grid init args
|
||||
-- @treturn Component grid component
|
||||
-- @deprecated
|
||||
function Druid.new_grid(self, ...)
|
||||
return Druid.create(self, grid, ...)
|
||||
helper.deprecated("The druid:new_grid is deprecated. Please use druid:new_static_grid instead")
|
||||
return Druid.create(self, static_grid, ...)
|
||||
end
|
||||
|
||||
|
||||
--- Create static grid basic component
|
||||
-- @function druid:new_static_grid
|
||||
-- @tparam args ... grid init args
|
||||
-- @treturn Component grid component
|
||||
function Druid.new_static_grid(self, ...)
|
||||
return Druid.create(self, static_grid, ...)
|
||||
end
|
||||
|
||||
|
||||
@ -426,7 +430,27 @@ function Druid.new_drag(self, ...)
|
||||
end
|
||||
|
||||
|
||||
--- Create slider basic component
|
||||
--- Create dynamic grid component
|
||||
-- @function druid:new_dynamic_grid
|
||||
-- @tparam args ... grid init args
|
||||
-- @treturn Component grid component
|
||||
function Druid.new_dynamic_grid(self, ...)
|
||||
-- return helper.extended_component("dynamic_grid")
|
||||
return Druid.create(self, dynamic_grid, ...)
|
||||
end
|
||||
|
||||
|
||||
--- Create lang_text component
|
||||
-- @function druid:new_lang_text
|
||||
-- @tparam args ... lang_text init args
|
||||
-- @treturn Component lang_text component
|
||||
function Druid.new_lang_text(self, ...)
|
||||
-- return helper.extended_component("lang_text")
|
||||
return Druid.create(self, lang_text, ...)
|
||||
end
|
||||
|
||||
|
||||
--- Create slider component
|
||||
-- @function druid:new_slider
|
||||
-- @tparam args ... slider init args
|
||||
-- @treturn Component slider component
|
||||
@ -436,7 +460,7 @@ function Druid.new_slider(self, ...)
|
||||
end
|
||||
|
||||
|
||||
--- Create checkbox basic component
|
||||
--- Create checkbox component
|
||||
-- @function druid:new_checkbox
|
||||
-- @tparam args ... checkbox init args
|
||||
-- @treturn Component checkbox component
|
||||
@ -446,7 +470,7 @@ function Druid.new_checkbox(self, ...)
|
||||
end
|
||||
|
||||
|
||||
--- Create input basic component
|
||||
--- Create input component
|
||||
-- @function druid:new_input
|
||||
-- @tparam args ... input init args
|
||||
-- @treturn Component input component
|
||||
@ -456,7 +480,7 @@ function Druid.new_input(self, ...)
|
||||
end
|
||||
|
||||
|
||||
--- Create checkbox_group basic component
|
||||
--- Create checkbox_group component
|
||||
-- @function druid:new_checkbox_group
|
||||
-- @tparam args ... checkbox_group init args
|
||||
-- @treturn Component checkbox_group component
|
||||
@ -466,7 +490,7 @@ function Druid.new_checkbox_group(self, ...)
|
||||
end
|
||||
|
||||
|
||||
--- Create radio_group basic component
|
||||
--- Create radio_group component
|
||||
-- @function druid:new_radio_group
|
||||
-- @tparam args ... radio_group init args
|
||||
-- @treturn Component radio_group component
|
||||
@ -476,7 +500,7 @@ function Druid.new_radio_group(self, ...)
|
||||
end
|
||||
|
||||
|
||||
--- Create timer basic component
|
||||
--- Create timer component
|
||||
-- @function druid:new_timer
|
||||
-- @tparam args ... timer init args
|
||||
-- @treturn Component timer component
|
||||
@ -486,7 +510,7 @@ function Druid.new_timer(self, ...)
|
||||
end
|
||||
|
||||
|
||||
--- Create progress basic component
|
||||
--- Create progress component
|
||||
-- @function druid:new_progress
|
||||
-- @tparam args ... progress init args
|
||||
-- @treturn Component progress component
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,26 +1,49 @@
|
||||
local M = {}
|
||||
|
||||
|
||||
local function add_node(self)
|
||||
local function simple_animate(node, pos)
|
||||
gui.animate(node, "position", pos, gui.EASING_OUTSINE, 0.2)
|
||||
end
|
||||
|
||||
|
||||
local function remove_node(self, button, is_shift)
|
||||
gui.delete_node(button.node)
|
||||
|
||||
self.druid:remove(button)
|
||||
local index = self.grid_static_grid:get_index_by_node(button.node)
|
||||
self.grid_static_grid:remove(index, is_shift)
|
||||
for i = 1, #self.grid_node_buttons do
|
||||
if self.grid_node_buttons[i] == button then
|
||||
table.remove(self.grid_node_buttons, i)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function add_node(self, index)
|
||||
local prefab = gui.get_node("grid_nodes_prefab")
|
||||
local cloned = gui.clone_tree(prefab)
|
||||
gui.set_enabled(cloned["grid_nodes_prefab"], true)
|
||||
local index = #self.grid_nodes + 1
|
||||
gui.set_text(cloned["grid_nodes_text"], index)
|
||||
|
||||
local button = self.druid:new_button(cloned["grid_nodes_prefab"], function()
|
||||
print(index)
|
||||
local button = self.druid:new_button(cloned["grid_nodes_prefab"], function(_, params, button)
|
||||
remove_node(self, button, true)
|
||||
end)
|
||||
button.on_long_click:subscribe(function()
|
||||
remove_node(self, button)
|
||||
end)
|
||||
button:set_click_zone(self.grid_static_scroll.view_node)
|
||||
|
||||
table.insert(self.grid_node_buttons, button)
|
||||
|
||||
self.grid_nodes:add(cloned["grid_nodes_prefab"])
|
||||
self.grid_static_grid:add(cloned["grid_nodes_prefab"], index)
|
||||
end
|
||||
|
||||
|
||||
local function clear_nodes(self)
|
||||
local nodes = self.grid_nodes.nodes
|
||||
for i = 1, #nodes do
|
||||
gui.delete_node(nodes[i])
|
||||
local nodes = self.grid_static_grid.nodes
|
||||
for i, node in pairs(nodes) do
|
||||
gui.delete_node(node)
|
||||
end
|
||||
|
||||
for i = 1, #self.grid_node_buttons do
|
||||
@ -28,29 +51,125 @@ local function clear_nodes(self)
|
||||
end
|
||||
self.grid_node_buttons = {}
|
||||
|
||||
self.grid_nodes:clear()
|
||||
self.grid_static_grid:clear()
|
||||
end
|
||||
|
||||
|
||||
local function remove_node(self)
|
||||
-- Remove is not implemented yet
|
||||
end
|
||||
|
||||
|
||||
function M.setup_page(self)
|
||||
self.grid_nodes = self.druid:new_grid("grid_nodes", "grid_nodes_prefab", 5)
|
||||
local function init_static_grid(self)
|
||||
self.grid_node_buttons = {}
|
||||
gui.set_enabled(gui.get_node("grid_nodes_prefab"), false)
|
||||
|
||||
for i = 1, 15 do
|
||||
add_node(self)
|
||||
add_node(self, i)
|
||||
end
|
||||
|
||||
self.druid:new_button("button_add/button", add_node)
|
||||
self.druid:new_button("button_clear/button", clear_nodes)
|
||||
self.druid:new_button("button_add/button", function()
|
||||
add_node(self)
|
||||
end)
|
||||
self.druid:new_button("button_clear/button", function()
|
||||
clear_nodes(self)
|
||||
end)
|
||||
end
|
||||
|
||||
local remove_button = self.druid:new_button("button_remove/button", remove_node)
|
||||
gui.set_enabled(remove_button.node, false)
|
||||
|
||||
local function remove_dynamic_node(self, button, is_shift_left)
|
||||
gui.delete_node(button.node)
|
||||
|
||||
self.druid:remove(button)
|
||||
local index = self.grid_dynamic_grid:get_index_by_node(button.node)
|
||||
self.grid_dynamic_grid:remove(index, is_shift_left)
|
||||
for i = 1, #self.dynamic_node_buttons do
|
||||
if self.dynamic_node_buttons[i] == button then
|
||||
table.remove(self.dynamic_node_buttons, i)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function add_node_dynamic(self, index, is_shift_left)
|
||||
local node = gui.clone(self.prefab_dynamic)
|
||||
gui.set_enabled(node, true)
|
||||
gui.set_size(node, vmath.vector3(250, math.random(60, 150), 0))
|
||||
self.grid_dynamic_grid:add(node, index, is_shift_left)
|
||||
|
||||
local button = self.druid:new_button(node, function(_, params, button)
|
||||
remove_dynamic_node(self, button)
|
||||
end)
|
||||
button.on_long_click:subscribe(function()
|
||||
remove_dynamic_node(self, button, true)
|
||||
end)
|
||||
button:set_click_zone(self.grid_dynamic_scroll.view_node)
|
||||
table.insert(self.dynamic_node_buttons, button)
|
||||
end
|
||||
|
||||
|
||||
local function add_node_dynamic_hor(self, index)
|
||||
local node = gui.clone(self.prefab_hor_dynamic)
|
||||
gui.set_enabled(node, true)
|
||||
gui.set_size(node, vmath.vector3(80 + math.random(0, 80), 80, 0))
|
||||
self.grid_dynamic_hor_grid:add(node, index)
|
||||
end
|
||||
|
||||
|
||||
local function init_dynamic_grid(self)
|
||||
-- Vertical horizontal grid
|
||||
self.dynamic_node_buttons = {}
|
||||
|
||||
self.prefab_dynamic = gui.get_node("grid_dynamic_prefab")
|
||||
gui.set_enabled(self.prefab_dynamic, false)
|
||||
|
||||
for i = 1, 10 do
|
||||
add_node_dynamic(self, i)
|
||||
end
|
||||
self.druid:new_button("button_add_start_dynamic/button", function()
|
||||
local start_index = (self.grid_dynamic_grid.first_index or 2) - 1
|
||||
add_node_dynamic(self, start_index)
|
||||
end)
|
||||
self.druid:new_button("button_add_end_dynamic/button", function()
|
||||
add_node_dynamic(self)
|
||||
end)
|
||||
|
||||
-- Horizontal dynamic grid
|
||||
self.prefab_hor_dynamic = gui.get_node("grid_dynamic_hor_prefab")
|
||||
gui.set_enabled(self.prefab_hor_dynamic, false)
|
||||
|
||||
for i = 1, 10 do
|
||||
add_node_dynamic_hor(self, i)
|
||||
end
|
||||
|
||||
self.druid:new_button("button_add_start_dynamic_hor/button", function()
|
||||
add_node_dynamic_hor(self, 1)
|
||||
end)
|
||||
self.druid:new_button("button_add_end_dynamic_hor/button", function()
|
||||
add_node_dynamic_hor(self)
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
function M.setup_page(self)
|
||||
self.grid_page_scroll = self.druid:new_scroll("grid_page", "grid_page_content")
|
||||
|
||||
self.grid_static_grid = self.druid:new_static_grid("grid_nodes", "grid_nodes_prefab", 5)
|
||||
:set_position_function(simple_animate)
|
||||
self.grid_static_scroll = self.druid:new_scroll("grid_nodes_view", "grid_nodes")
|
||||
:set_horizontal_scroll(false)
|
||||
:bind_grid(self.grid_static_grid)
|
||||
|
||||
self.grid_dynamic_grid = self.druid:new_dynamic_grid("grid_dynamic_nodes")
|
||||
:set_position_function(simple_animate)
|
||||
self.grid_dynamic_scroll = self.druid:new_scroll("grid_dynamic_view", "grid_dynamic_nodes")
|
||||
:set_horizontal_scroll(false)
|
||||
:bind_grid(self.grid_dynamic_grid)
|
||||
|
||||
self.grid_dynamic_hor_grid = self.druid:new_dynamic_grid("grid_dynamic_hor_nodes")
|
||||
:set_position_function(simple_animate)
|
||||
self.grid_dynamic_hor_scroll = self.druid:new_scroll("grid_dynamic_hor_view", "grid_dynamic_hor_nodes")
|
||||
:set_vertical_scroll(false)
|
||||
:bind_grid(self.grid_dynamic_hor_grid)
|
||||
|
||||
init_static_grid(self)
|
||||
init_dynamic_grid(self)
|
||||
end
|
||||
|
||||
|
||||
|
@ -50,22 +50,6 @@ local function setup_progress(self)
|
||||
end
|
||||
|
||||
|
||||
local function setup_grid(self)
|
||||
local grid = self.druid:new_grid("grid", "button_template/button", 3)
|
||||
|
||||
for i = 1, 12 do
|
||||
local nodes = gui.clone_tree(gui.get_node("button_template/button"))
|
||||
|
||||
local root = nodes["button_template/button"]
|
||||
self.druid:new_button(root, function(context, param)
|
||||
grid:set_offset(vmath.vector3(param))
|
||||
end, i)
|
||||
self.druid:new_text(nodes["button_template/text"], "Grid"..i)
|
||||
grid:add(root)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function setup_slider(self)
|
||||
local slider = self.druid:new_slider("slider_pin", vmath.vector3(95, 0, 0), function(_, value)
|
||||
gui.set_text(gui.get_node("text_progress_slider"), math.ceil(value * 100) .. "%")
|
||||
@ -96,11 +80,6 @@ local function setup_timer(self)
|
||||
end
|
||||
|
||||
|
||||
local function setup_scroll(self)
|
||||
self.druid:new_scroll("main_page", "scroll_content")
|
||||
end
|
||||
|
||||
|
||||
local function setup_back_handler(self)
|
||||
self.druid:new_back_handler(empty_callback, "back button")
|
||||
end
|
||||
@ -117,10 +96,8 @@ function M.setup_page(self)
|
||||
|
||||
setup_button(self)
|
||||
setup_progress(self)
|
||||
setup_grid(self)
|
||||
setup_timer(self)
|
||||
setup_checkbox(self)
|
||||
setup_scroll(self)
|
||||
setup_slider(self)
|
||||
setup_back_handler(self)
|
||||
setup_input(self)
|
||||
|
@ -5,7 +5,7 @@ local function init_scroll_with_grid(self)
|
||||
local prefab = gui.get_node("grid_prefab")
|
||||
|
||||
local grid_scroll = self.druid:new_scroll("scroll_with_grid_size", "grid_content")
|
||||
local grid = self.druid:new_grid("grid_content", "grid_prefab", 20)
|
||||
local grid = self.druid:new_static_grid("grid_content", "grid_prefab", 20)
|
||||
|
||||
for i = 1, 40 do
|
||||
local clone_prefab = gui.clone_tree(prefab)
|
||||
|
@ -27,7 +27,7 @@ local function setup_texts(self)
|
||||
timer.delay(0.3, true, function()
|
||||
anchoring:set_pivot(pivots[pivot_index])
|
||||
|
||||
pivot_index = pivot_index + 1
|
||||
pivot_index = pivot_index + 1
|
||||
if pivot_index > #pivots then
|
||||
pivot_index = 1
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user