diff --git a/.gitignore b/.gitignore index a32d29f..fb6af1f 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ Thumbs.db *.pyc .project .cproject -builtins \ No newline at end of file +builtins +dist diff --git a/.luacheckrc b/.luacheckrc new file mode 100644 index 0000000..a99ebbd --- /dev/null +++ b/.luacheckrc @@ -0,0 +1,51 @@ +std = "max" +files['.luacheckrc'].global = false +unused_args = false + +max_code_line_length = 90 +max_comment_line_length = false + +globals = { + "sys", + "go", + "gui", + "label", + "render", + "crash", + "sprite", + "sound", + "tilemap", + "spine", + "particlefx", + "physics", + "factory", + "collectionfactory", + "iac", + "msg", + "vmath", + "url", + "http", + "image", + "json", + "zlib", + "iap", + "push", + "facebook", + "hash", + "hash_to_hex", + "pprint", + "init", + "final", + "update", + "on_input", + "on_message", + "on_reload", + "socket", + "table", + "debug", + "timer", + "window", + "buffer", + "resource", + "defos", +} diff --git a/README.md b/README.md index dc028cf..72aa393 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,146 @@ -# druid -Defold UI library +[![](media/druid_logo.png)](https://insality.github.io/druid/) -This project was created from the "empty" project template. +**Druid** - powerful defold component UI library. Use basic druid components or make your own game-specific components to make amazing GUI in your games. -The settings in ["game.project"](defold://open?path=/game.project) are all the default. A bootstrap empty ["main.collection"](defold://open?path=/main/main.collection) is included. -Check out [the documentation pages](https://defold.com/learn) for examples, tutorials, manuals and API docs. +## Setup -If you run into trouble, help is available in [our forum](https://forum.defold.com). +### Dependency -Happy Defolding! +You can use the druid extension in your own project by adding this project as a [Defold library dependency](https://www.defold.com/manuals/libraries/). Open your game.project file and in the dependencies field under project add: ---- \ No newline at end of file +> [https://github.com/Insality/druid/archive/master.zip](https://github.com/Insality/druid/archive/master.zip) + +Or point to the ZIP file of a [specific release](https://github.com/Insality/druid/releases). + + +### Code + +Adjust druid settings, if needed: +```lua +local druid = require("druid.druid") + +-- Used for button component and custom components +druid.set_sound_function(callback) + +-- Used for lang_text component +druid.set_text_function(callback) + +-- Used for change default druid style +druid.set_default_style(your_style) +``` + + +## Components + +Druid provides next basic components: +- **Button** - Basic game button + +- **Text** - Wrap on text node with text size adjusting + +- **Blocker** - Block input in node zone + +- **Back Handler** - Handle back button (Android, backspace) + +- **Lang text** - Text component with handle localization system + +- **Timer** - Run timer on text node + +- **Progress** - Basic progress bar + +- **Scroll** - Basic scroll component + +- **Grid** - Component for manage node positions + +- **Slider** - Basic slider component + +- **Checkbox** - Basic checkbox component + +- **Checkbox group** - Several checkboxes in one group + +- **Radio group** - Several checkboxes in one group with single choice + +- **Hover** - Trigger component for check node hover state + +- **Input** - Component to process user text input + +Full info see on _components.md_ + + +## Creating components + +Any components creating via druid: +```lua +local druid = require("druid.druid") + +local function init(self) + self.druid = druid.new(self) + local button = self.druid:new_button(node_name, callback) + local text = self.druid:new_text(node_text_name) +end + +function update(self, dt) + self.druid:update(dt) +end + +function on_message(self, message_id, message, sender) + self.druid:on_message(message_id, message, sender) +end + +function on_input(self, action_id, action) + self.druid:on_input(action_id, action) +end +``` + + +## Examples + +See the [example folder](https://github.com/insality/druid/tree/develop/example/kenney) for examples of how to use Druid + +See the [druid-assets repository](https://github.com/insality/druid-assets) for examples of how to create custom components and styles + +Try the HTML5 version of the example app + + +## Documentation + +To learn druid better, read next documentation: +- Druid components +- Create custom components +- Druid asset store +- Druid Styles + +Full druid documentation you can find here: +https://insality.github.io/druid/ + + +## Games powered by Druid + +_Will fill later_ + + +## Future plans + +- Basic input component + +- Add on_layout_change support (to keep gui data between layout change) + +- Add on_change_language support (call single function to update all druid instance) + +- Better documentation and examples + +- Add more comfortable gamepad support for GUI (ability to select button with DPAD and other stuff) + + +## License + +Original idea by [AGulev](https://github.com/AGulev) + +Developed and supporting by [Insality](https://github.com/Insality) + +MIT License + + +## Issues and suggestions + +If you have any issues, questions or suggestions please [create an issue](https://github.com/Insality/druid/issues) or contact me: [insality@gmail.com](mailto:insality@gmail.com) diff --git a/alpha_todo.txt b/alpha_todo.txt new file mode 100644 index 0000000..62342af --- /dev/null +++ b/alpha_todo.txt @@ -0,0 +1,38 @@ +Simple to-do for Druid Alpha 0.2.0 + + +-- High ++ remove button event and match_event from druid ++ add hover component ++ add druid events/triggers? better callback system ++ better name for locale component? lang? lang_text? ++ better name for slider component? Slider is ok ++ Druid store assets - separate repository with rich components (progress_rich migrate) ++ refactor on_swipe. To on_scroll? Add input priority ++ separate custom data and predefined fields in components? Every component have their fields and events ++ How to set custom sprites for button states? ++ add druid settings (add auto_focus input and other stuff) (to game.project) + ++ button add key trigger ++ button and hover click restriction zone? ++ button polish, actions ++ better scroll size management, check different cases. So implicit now ++ better callbacks for every components + +- unify component api (get/set/to and other general stuff) +- better grid + scroll management +- better default style, add template for custom style +- compare with gooey +- add docs for all components +- add docs folder for every component with gifs? Solutions +- remove component autoremove all children component + + +-- Low +- add input_text component for alpha release +- add code template and example for user components +- custom input settings (name of touch, text, etc) +- add good examples with template and/or nodes (basic component no use any of them) +- try use final druid in real project (FI uses custom druid) (use in 4321?) +- ability to relocalize all locale text nodes +- ability to control buttons via controller. Select it by cursor (d-pad) diff --git a/config.ld b/config.ld new file mode 100644 index 0000000..273a754 --- /dev/null +++ b/config.ld @@ -0,0 +1,17 @@ +project='Druid' +title='Defold Druid UI Library' +description='Documentation for Druid Library' +file={"./druid", + exclude = { + "./druid/styles/", + "./druid/system/middleclass.lua" + } +} +package='druid' +sort=false +dir='./docs' +style='!fixed' +format='discount' +topics={"./docs_md", "README.md"} +use_markdown_titles=true +no_space_before_args=true \ No newline at end of file diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..c3f84fb --- /dev/null +++ b/docs/index.html @@ -0,0 +1,192 @@ + + + + + Defold Druid UI Library + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ + +

Documentation for Druid Library

+ +

Modules

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
druid.back_handlerComponent to handle back key (android, backspace)
druid.blockerComponent to block input on specify zone by node
druid.buttonComponent to handle basic GUI button
druid.checkboxDruid checkbox component
druid.checkbox_groupCheckbox group module
druid.gridComponent to handle placing components by row and columns.
druid.hoverComponent to handle hover node interaction
druid.inputDruid input text component.
druid.lang_textComponent to handle all GUI texts + Good working with localization system
druid.progressBasic progress bar component.
druid.radio_groupRadio group module
druid.scrollComponent to handle scroll content.
druid.sliderDruid slider component
druid.textComponent to handle all GUI texts.
druid.timerComponent to handle GUI timers.
componentBasic class for all Druid components.
druidDruid UI Library.
druid_eventLua event small library
druid.helperText node or icon node can be nil
druid_instanceDruid main class.
+

Topics

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

Module component

+

Basic class for all Druid components.

+

To create you component, use component.create

+ + +

Functions

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
get_style()Get current component style table
set_style(style)Set current component style table
get_template()Get current component template name
set_template(template)Set current component template name
get_nodes()Get current component nodes
set_nodes(nodes)Set current component nodes
get_context()Get current component context
set_context(context)Set current component context
get_interests()Get current component interests
get_druid()Return druid with context of calling component.
setup_component(table, table)Setup component context and his style table
Component.create(name, interest)Create new component.
+ +
+
+ + +

Functions

+ +
+
+ + get_style() +
+
+ Get current component style table + + + +

Returns:

+
    + + table + Component style table +
+ + + + +
+
+ + set_style(style) +
+
+ Set current component style table + + +

Parameters:

+
    +
  • style + table + Druid style module +
  • +
+ + + + + +
+
+ + get_template() +
+
+ Get current component template name + + + +

Returns:

+
    + + string + Component template name +
+ + + + +
+
+ + set_template(template) +
+
+ Set current component template name + + +

Parameters:

+
    +
  • template + string + Component template name +
  • +
+ + + + + +
+
+ + get_nodes() +
+
+ Get current component nodes + + + +

Returns:

+
    + + table + Component nodes table +
+ + + + +
+
+ + set_nodes(nodes) +
+
+ Set current component nodes + + +

Parameters:

+
    +
  • nodes + table + Component nodes table +
  • +
+ + + + + +
+
+ + get_context() +
+
+ Get current component context + + + +

Returns:

+
    + + table + Component context +
+ + + + +
+
+ + set_context(context) +
+
+ Set current component context + + +

Parameters:

+
    +
  • context + table + Druid context. Usually it is self of script +
  • +
+ + + + + +
+
+ + get_interests() +
+
+ Get current component interests + + + +

Returns:

+
    + + table + List of component interests +
+ + + + +
+
+ + get_druid() +
+
+ Return druid with context of calling component. + Use it to create component inside of other components. + + + +

Returns:

+
    + + Druid + Druid instance with component context +
+ + + + +
+
+ + setup_component(table, table) +
+
+ Setup component context and his style table + + +

Parameters:

+
    +
  • table + style + Druid style module +
  • +
  • table + style + Druid style module +
  • +
+ +

Returns:

+
    + + Component + Component itself +
+ + + + +
+
+ + Component.create(name, interest) +
+
+ Create new component. It will inheritance from basic + druid component. + + +

Parameters:

+
    +
  • name + string + Component name +
  • +
  • interest + table + List of component's interest +
  • +
+ + + + + +
+
+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-03-21 22:59:46 +
+
+ + diff --git a/docs/modules/druid.back_handler.html b/docs/modules/druid.back_handler.html new file mode 100644 index 0000000..f430074 --- /dev/null +++ b/docs/modules/druid.back_handler.html @@ -0,0 +1,222 @@ + + + + + Defold Druid UI Library + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module druid.back_handler

+

Component to handle back key (android, backspace)

+

+ +

+ + +

Functions

+ + + + + + + + + +
init(callback[, Callback])Component init function
on_input(action_id, action)Input handler for component
+

Tables

+ + + + + + + + + +
EventsComponent events
FieldsComponent fields
+ +
+
+ + +

Functions

+ +
+
+ + init(callback[, Callback]) +
+
+ Component init function + + +

Parameters:

+
    +
  • callback + callback + On back button +
  • +
  • Callback + params + argument + (optional) +
  • +
+ + + + + +
+
+ + on_input(action_id, action) +
+
+ Input handler for component + + +

Parameters:

+
    +
  • action_id + string + on_input action id +
  • +
  • action + table + on_input action +
  • +
+ + + + + +
+
+

Tables

+ +
+
+ + Events +
+
+ Component events + + +

Fields:

+
    +
  • on_back + druid_event + On back handler callback +
  • +
+ + + + + +
+
+ + Fields +
+
+ Component fields + + +

Fields:

+
    +
  • params + any + Params to click callbacks +
  • +
+ + + + + +
+
+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-03-21 22:59:46 +
+
+ + diff --git a/docs/modules/druid.blocker.html b/docs/modules/druid.blocker.html new file mode 100644 index 0000000..51202b1 --- /dev/null +++ b/docs/modules/druid.blocker.html @@ -0,0 +1,241 @@ + + + + + Defold Druid UI Library + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module druid.blocker

+

Component to block input on specify zone by node

+

+ +

+ + +

Functions

+ + + + + + + + + + + + + +
init(node)Component init function
set_enabled(state)Set enabled blocker component state
is_enabled()Return blocked enabled state
+

Tables

+ + + + + + + + + +
EventsComponent events
FieldsComponent fields
+ +
+
+ + +

Functions

+ +
+
+ + init(node) +
+
+ Component init function + + +

Parameters:

+
    +
  • node + node + Gui node +
  • +
+ + + + + +
+
+ + set_enabled(state) +
+
+ Set enabled blocker component state + + +

Parameters:

+
    +
  • state + bool + Enabled state +
  • +
+ + + + + +
+
+ + is_enabled() +
+
+ Return blocked enabled state + + + +

Returns:

+
    + + bool + True, if blocker is enabled +
+ + + + +
+
+

Tables

+ +
+
+ + Events +
+
+ Component events + + +

Fields:

+
    +
  • on_click + druid_event + On release button callback +
  • +
  • on_enable_change + druid_event + On enable/disable callback +
  • +
+ + + + + +
+
+ + Fields +
+
+ Component fields + + +

Fields:

+
    +
  • node + node + Trigger node +
  • +
+ + + + + +
+
+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-03-21 22:59:46 +
+
+ + diff --git a/docs/modules/druid.button.html b/docs/modules/druid.button.html new file mode 100644 index 0000000..9bc583a --- /dev/null +++ b/docs/modules/druid.button.html @@ -0,0 +1,412 @@ + + + + + Defold Druid UI Library + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module druid.button

+

Component to handle basic GUI button

+

+ +

+ + +

Functions

+ + + + + + + + + + + + + + + + + + + + + + + + + +
init(node, callback[, params[, anim_node[, event]]])Component init function
set_enabled(state)Set enabled button component state
is_enabled()Return button enabled state
set_click_zone(zone)Strict button click area.
set_key_trigger(key)Set key-code to trigger this button
get_key_trigger()Get key-code to trigger this button
+

Tables

+ + + + + + + + + + + + + +
EventsComponent events
FieldsComponent fields
StyleComponent style params
+ +
+
+ + +

Functions

+ +
+
+ + init(node, callback[, params[, anim_node[, event]]]) +
+
+ Component init function + + +

Parameters:

+
    +
  • node + node + Gui node +
  • +
  • callback + function + Button callback +
  • +
  • params + table + Button callback params + (optional) +
  • +
  • anim_node + node + Button anim node (node, if not provided) + (optional) +
  • +
  • event + string + Button react event, const.ACTION_TOUCH by default + (optional) +
  • +
+ + + + + +
+
+ + set_enabled(state) +
+
+ Set enabled button component state + + +

Parameters:

+
    +
  • state + bool + Enabled state +
  • +
+ + + + + +
+
+ + is_enabled() +
+
+ Return button enabled state + + + +

Returns:

+
    + + bool + True, if button is enabled +
+ + + + +
+
+ + set_click_zone(zone) +
+
+ Strict button click area. Useful for + no click events outside stencil node + + +

Parameters:

+
    +
  • zone + node + Gui node +
  • +
+ + + + + +
+
+ + set_key_trigger(key) +
+
+ Set key-code to trigger this button + + +

Parameters:

+
    +
  • key + hash + The action_id of the key +
  • +
+ + + + + +
+
+ + get_key_trigger() +
+
+ Get key-code to trigger this button + + + +

Returns:

+
    + + hash + The action_id of the key +
+ + + + +
+
+

Tables

+ +
+
+ + Events +
+
+ Component events + + +

Fields:

+
    +
  • on_click + druid_event + On release button callback +
  • +
  • on_repeated_click + druid_event + On repeated action button callback +
  • +
  • on_long_click + druid_event + On long tap button callback +
  • +
  • on_double_click + druid_event + On double tap button callback +
  • +
+ + + + + +
+
+ + Fields +
+
+ Component fields + + +

Fields:

+
    +
  • node + node + Trigger node +
  • +
  • anim_node + node + Animation node + (default node) +
  • +
  • scale_from + vector3 + Initial scale of anim_node +
  • +
  • pos + vector3 + Initial pos of anim_node +
  • +
  • params + any + Params to click callbacks +
  • +
  • hover + druid.hover + Druid hover logic component +
  • +
  • click_zone + node + Restriction zone + (optional) +
  • +
+ + + + + +
+
+ + Style +
+
+ Component style params + + +

Fields:

+
    +
  • on_click + function + (self, node) +
  • +
  • on_click_disabled + function + (self, node) +
  • +
  • on_hover + function + (self, node, hover_state) +
  • +
  • on_set_enabled + function + (self, node, enabled_state) +
  • +
  • IS_HOVER + bool + + + +
  • +
+ + + + + +
+
+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-03-21 22:59:46 +
+
+ + diff --git a/docs/modules/druid.checkbox.html b/docs/modules/druid.checkbox.html new file mode 100644 index 0000000..950cbaa --- /dev/null +++ b/docs/modules/druid.checkbox.html @@ -0,0 +1,284 @@ + + + + + Defold Druid UI Library + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module druid.checkbox

+

Druid checkbox component

+

+ +

+ + +

Functions

+ + + + + + + + + + + + + +
init(node, callback[, click=node])Component init function
set_state(state, is_silent)Set checkbox state
get_state()Return checkbox state
+

Tables

+ + + + + + + + + + + + + +
EventsComponent events
FieldsComponent fields
StyleComponent style params
+ +
+
+ + +

Functions

+ +
+
+ + init(node, callback[, click=node]) +
+
+ Component init function + + +

Parameters:

+
    +
  • node + node + Gui node +
  • +
  • callback + function + Checkbox callback +
  • +
  • click + node + node Trigger node, by default equals to node + (default node) +
  • +
+ + + + + +
+
+ + set_state(state, is_silent) +
+
+ Set checkbox state + + +

Parameters:

+
    +
  • state + bool + Checkbox state +
  • +
  • is_silent + bool + Don't trigger onchangestate if true +
  • +
+ + + + + +
+
+ + get_state() +
+
+ Return checkbox state + + + +

Returns:

+
    + + bool + Checkbox state +
+ + + + +
+
+

Tables

+ +
+
+ + Events +
+
+ Component events + + +

Fields:

+
    +
  • on_change_state + druid_event + On change state callback +
  • +
+ + + + + +
+
+ + Fields +
+
+ Component fields + + +

Fields:

+
    +
  • node + node + Visual node +
  • +
  • click_node + node + Button trigger node + (default node) +
  • +
  • button + druid.button + Button component from click_node +
  • +
+ + + + + +
+
+ + Style +
+
+ Component style params + + +

Fields:

+
    +
  • on_change_state + function + (self, node, state) +
  • +
+ + + + + +
+
+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-03-21 22:59:46 +
+
+ + diff --git a/docs/modules/druid.checkbox_group.html b/docs/modules/druid.checkbox_group.html new file mode 100644 index 0000000..25b2c0e --- /dev/null +++ b/docs/modules/druid.checkbox_group.html @@ -0,0 +1,246 @@ + + + + + Defold Druid UI Library + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module druid.checkbox_group

+

Checkbox group module

+

+ +

+ + +

Functions

+ + + + + + + + + + + + + +
init(node, callback[, click=node])Component init function
set_state(state)Set checkbox group state
get_state()Return checkbox group state
+

Tables

+ + + + + + + + + +
EventsComponent events
FieldsComponent fields
+ +
+
+ + +

Functions

+ +
+
+ + init(node, callback[, click=node]) +
+
+ Component init function + + +

Parameters:

+
    +
  • node + node[] + Array of gui node +
  • +
  • callback + function + Checkbox callback +
  • +
  • click + node[] + node Array of trigger nodes, by default equals to nodes + (default node) +
  • +
+ + + + + +
+
+ + set_state(state) +
+
+ Set checkbox group state + + +

Parameters:

+
    +
  • state + bool[] + Array of checkbox state +
  • +
+ + + + + +
+
+ + get_state() +
+
+ Return checkbox group state + + + +

Returns:

+
    + + bool[] + Array if checkboxes state +
+ + + + +
+
+

Tables

+ +
+
+ + Events +
+
+ Component events + + +

Fields:

+
    +
  • on_checkbox_click + druid_event + On any checkbox click +
  • +
+ + + + + +
+
+ + Fields +
+
+ Component fields + + +

Fields:

+
    +
  • checkboxes + table + Array of checkbox components +
  • +
+ + + + + +
+
+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-03-21 22:59:46 +
+
+ + diff --git a/docs/modules/druid.grid.html b/docs/modules/druid.grid.html new file mode 100644 index 0000000..34b19ac --- /dev/null +++ b/docs/modules/druid.grid.html @@ -0,0 +1,377 @@ + + + + + Defold Druid UI Library + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module druid.grid

+

Component to handle placing components by row and columns.

+

Grid can anchor your elements, get content size and other

+ + +

Functions

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
init(parent, element[, in_row=1])Component init function
set_offset(offset)Set grid items offset, the distance between items
set_anchor(acnhor)Set grid anchor
add(item[, index])Add new item to the grid
get_size()Return grid content size
get_all_pos()Return array of all node positions
clear()Clear all items from the grid
+

Tables

+ + + + + + + + + +
EventsComponent events
FieldsComponent fields
+ +
+
+ + +

Functions

+ +
+
+ + init(parent, element[, in_row=1]) +
+
+ Component init function + + +

Parameters:

+
    +
  • parent + node + The gui node parent, where items will be placed +
  • +
  • element + node + Element prefab. Need to get it size +
  • +
  • in_row + number + How many nodes in row can be placed + (default 1) +
  • +
+ + + + + +
+
+ + set_offset(offset) +
+
+ Set grid items offset, the distance between items + + +

Parameters:

+
    +
  • offset + vector3 + Offset +
  • +
+ + + + + +
+
+ + set_anchor(acnhor) +
+
+ Set grid anchor + + +

Parameters:

+
    +
  • acnhor + vector3 + Anchor +
  • +
+ + + + + +
+
+ + add(item[, index]) +
+
+ Add new item to the grid + + +

Parameters:

+
    +
  • item + node + Gui node +
  • +
  • index + number + The item position. By default add as last item + (optional) +
  • +
+ + + + + +
+
+ + get_size() +
+
+ Return grid content size + + + +

Returns:

+
    + + vector3 + The grid content size +
+ + + + +
+
+ + get_all_pos() +
+
+ Return array of all node positions + + + +

Returns:

+
    + + vector3[] + All grid node positions +
+ + + + +
+
+ + clear() +
+
+ Clear all items from the grid + + + + + + + +
+
+

Tables

+ +
+
+ + Events +
+
+ Component events + + +

Fields:

+
    +
  • on_add_item + druid_event + On item add callback +
  • +
  • on_remove_item + druid_event + On item remove callback +
  • +
  • on_clear + druid_event + On grid clear callback +
  • +
  • on_update_positions + druid_event + On update item positions callback +
  • +
+ + + + + +
+
+ + Fields +
+
+ Component fields + + +

Fields:

+
    +
  • parent + node + Parent gui node +
  • +
  • nodes + node[] + List of all grid nodes +
  • +
  • offset + vector3 + Item distance between each other items +
  • +
  • anchor + vector3 + Item anchor +
  • +
  • node_size + vector3 + Item size +
  • +
  • border + vector4 + The size of item content +
  • +
  • border_offer + vector3 + The border offset for correct anchor calculations +
  • +
+ + + + + +
+
+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-03-21 22:59:46 +
+
+ + diff --git a/docs/modules/druid.helper.html b/docs/modules/druid.helper.html new file mode 100644 index 0000000..7e0d187 --- /dev/null +++ b/docs/modules/druid.helper.html @@ -0,0 +1,243 @@ + + + + + Defold Druid UI Library + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module druid.helper

+

Text node or icon node can be nil

+

+ +

+ + +

Functions

+ + + + + + + + + + + + + + + + + +
centrate_text_with_icon([text_node][, icon_node], margin)Center two nodes.
centrate_icon_with_text([icon_node[, text_node[, margin=0]]])Center two nodes.
is_enabled(node)Check if node is enabled in gui hierarchy.
get_pivot_offset(pivot)Get node offset for given gui pivot
+ +
+
+ + +

Functions

+ +
+
+ + centrate_text_with_icon([text_node][, icon_node], margin) +
+
+ Center two nodes. + Nodes will be center around 0 x position + text_node will be first (at left side) + + +

Parameters:

+
    +
  • text_node + text + Gui text node + (optional) +
  • +
  • icon_node + box + Gui box node + (optional) +
  • +
  • margin + number + Offset between nodes +
  • +
+ + + + + +
+
+ + centrate_icon_with_text([icon_node[, text_node[, margin=0]]]) +
+
+ Center two nodes. + Nodes will be center around 0 x position + icon_node will be first (at left side) + + +

Parameters:

+
    +
  • icon_node + box + Gui box node + (optional) +
  • +
  • text_node + text + Gui text node + (optional) +
  • +
  • margin + number + Offset between nodes + (default 0) +
  • +
+ + + + + +
+
+ + is_enabled(node) +
+
+ Check if node is enabled in gui hierarchy. + Return false, if node or any his parent is disabled + + +

Parameters:

+
    +
  • node + node + Gui node +
  • +
+ +

Returns:

+
    + + bool + Is enabled in hierarchy +
+ + + + +
+
+ + get_pivot_offset(pivot) +
+
+ Get node offset for given gui pivot + + +

Parameters:

+
    +
  • pivot + gui.pivot + The node pivot +
  • +
+ +

Returns:

+
    + + vector3 + Vector offset with [-1..1] values +
+ + + + +
+
+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-03-21 22:59:46 +
+
+ + diff --git a/docs/modules/druid.hover.html b/docs/modules/druid.hover.html new file mode 100644 index 0000000..3aacad3 --- /dev/null +++ b/docs/modules/druid.hover.html @@ -0,0 +1,218 @@ + + + + + Defold Druid UI Library + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module druid.hover

+

Component to handle hover node interaction

+

+ +

+ + +

Functions

+ + + + + + + + + + + + + +
init(node, on_hover_callback)Component init function
set_hover(state)Set hover state
set_click_zone(zone)Strict button click area.
+

Tables

+ + + + + +
EventsComponent events
+ +
+
+ + +

Functions

+ +
+
+ + init(node, on_hover_callback) +
+
+ Component init function + + +

Parameters:

+
    +
  • node + node + Gui node +
  • +
  • on_hover_callback + function + Hover callback +
  • +
+ + + + + +
+
+ + set_hover(state) +
+
+ Set hover state + + +

Parameters:

+
    +
  • state + bool + The hover state +
  • +
+ + + + + +
+
+ + set_click_zone(zone) +
+
+ Strict button click area. Useful for + no click events outside stencil node + + +

Parameters:

+
    +
  • zone + node + Gui node +
  • +
+ + + + + +
+
+

Tables

+ +
+
+ + Events +
+
+ Component events + + +

Fields:

+
    +
  • on_hover + druid_event + On hover callback +
  • +
+ + + + + +
+
+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-03-21 22:59:46 +
+
+ + diff --git a/docs/modules/druid.html b/docs/modules/druid.html new file mode 100644 index 0000000..39e0d48 --- /dev/null +++ b/docs/modules/druid.html @@ -0,0 +1,188 @@ + + + + + Defold Druid UI Library + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module druid

+

Druid UI Library.

+

+ +

Powerful Defold component based UI library. Use standart + components or make your own game-specific components to + make amazing GUI in your games.

+ +

Contains the several basic components and examples + to how to do your custom complex components to + separate UI game logic to small files

+ + +
+require("druid.druid")
+function init(self)
+    self.druid = druid.new(self)
+end
+
+ +

+ + +

Functions

+ + + + + + + + + +
register(name, module)Register external druid component.
new(context[, style])Create Druid instance.
+ +
+
+ + +

Functions

+ +
+
+ + register(name, module) +
+
+ Register external druid component. + After register you can create the component with + druidinstance:new{name}. For example druid:new_button(...) + + +

Parameters:

+
    +
  • name + string + module name +
  • +
  • module + table + lua table with component +
  • +
+ + + + + +
+
+ + new(context[, style]) +
+
+ Create Druid instance. + + +

Parameters:

+
    +
  • context + table + Druid context. Usually it is self of script +
  • +
  • style + table + Druid style module + (optional) +
  • +
+ +

Returns:

+
    + + druid_instance + Druid instance +
+ + + + +
+
+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-03-21 22:59:46 +
+
+ + diff --git a/docs/modules/druid.input.html b/docs/modules/druid.input.html new file mode 100644 index 0000000..3db5add --- /dev/null +++ b/docs/modules/druid.input.html @@ -0,0 +1,93 @@ + + + + + Defold Druid UI Library + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module druid.input

+

Druid input text component.

+

Carry on user text input + UNIMPLEMENTED

+ + + +
+
+ + + + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-03-21 22:59:46 +
+
+ + diff --git a/docs/modules/druid.lang_text.html b/docs/modules/druid.lang_text.html new file mode 100644 index 0000000..7930f45 --- /dev/null +++ b/docs/modules/druid.lang_text.html @@ -0,0 +1,247 @@ + + + + + Defold Druid UI Library + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module druid.lang_text

+

Component to handle all GUI texts + Good working with localization system

+

+ +

+ + +

Functions

+ + + + + + + + + + + + + +
init(node, locale_id, no_adjust)Component init function
set_to(text)Setup raw text to lang_text component
translate(locale_id)Translate the text by locale_id
+

Tables

+ + + + + + + + + +
EventsComponent events
FieldsComponent fields
+ +
+
+ + +

Functions

+ +
+
+ + init(node, locale_id, no_adjust) +
+
+ Component init function + + +

Parameters:

+
    +
  • node + node + The text node +
  • +
  • locale_id + string + Default locale id +
  • +
  • no_adjust + bool + If true, will not correct text size +
  • +
+ + + + + +
+
+ + set_to(text) +
+
+ Setup raw text to lang_text component + + +

Parameters:

+
    +
  • text + string + Text for text node +
  • +
+ + + + + +
+
+ + translate(locale_id) +
+
+ Translate the text by locale_id + + +

Parameters:

+
    +
  • locale_id + string + Locale id +
  • +
+ + + + + +
+
+

Tables

+ +
+
+ + Events +
+
+ Component events + + +

Fields:

+
    +
  • on_change + druid_event + On change text callback +
  • +
+ + + + + +
+
+ + Fields +
+
+ Component fields + + +

Fields:

+
    +
  • text + druid.text + The text component +
  • +
+ + + + + +
+
+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-03-21 22:59:46 +
+
+ + diff --git a/docs/modules/druid.progress.html b/docs/modules/druid.progress.html new file mode 100644 index 0000000..b0750f0 --- /dev/null +++ b/docs/modules/druid.progress.html @@ -0,0 +1,385 @@ + + + + + Defold Druid UI Library + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module druid.progress

+

Basic progress bar component.

+

For correct progress bar init it should be in max size from gui

+ + +

Functions

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
init(node, key, init_value)Component init function
fill()Fill a progress bar and stop progress animation
empty()Empty a progress bar
set_to(to)Instant fill progress bar to value
get()Return current progress bar value
set_steps(steps, callback)Set points on progress bar to fire the callback
to(to[, callback])Start animation of a progress bar
+

Tables

+ + + + + + + + + + + + + +
EventsComponent events
FieldsComponent fields
StyleComponent style params
+ +
+
+ + +

Functions

+ +
+
+ + init(node, key, init_value) +
+
+ Component init function + + +

Parameters:

+
    +
  • node + string or node + Progress bar fill node or node name +
  • +
  • key + string + Progress bar direction: const.SIDE.X or const.SIDE.Y +
  • +
  • init_value + number + Initial value of progress bar +
  • +
+ + + + + +
+
+ + fill() +
+
+ Fill a progress bar and stop progress animation + + + + + + + +
+
+ + empty() +
+
+ Empty a progress bar + + + + + + + +
+
+ + set_to(to) +
+
+ Instant fill progress bar to value + + +

Parameters:

+
    +
  • to + number + Progress bar value, from 0 to 1 +
  • +
+ + + + + +
+
+ + get() +
+
+ Return current progress bar value + + + + + + + +
+
+ + set_steps(steps, callback) +
+
+ Set points on progress bar to fire the callback + + +

Parameters:

+
    +
  • steps + number[] + Array of progress bar values +
  • +
  • callback + function + Callback on intersect step value +
  • +
+ + + + +

Usage:

+
    +
    progress:set_steps({0, 0.3, 0.6, 1}, function(self, step) end)
    +
+ +
+
+ + to(to[, callback]) +
+
+ Start animation of a progress bar + + +

Parameters:

+
    +
  • to + number + value between 0..1 +
  • +
  • callback + function + Callback on animation ends + (optional) +
  • +
+ + + + + +
+
+

Tables

+ +
+
+ + Events +
+
+ Component events + + +

Fields:

+
    +
  • on_change + druid_event + On progress bar change callback +
  • +
+ + + + + +
+
+ + Fields +
+
+ Component fields + + +

Fields:

+
    +
  • node + node + Progress bar fill node +
  • +
  • key + string + The progress bar direction +
  • +
  • scale + vector3 + Current progress bar scale +
  • +
  • size + vector3 + Current progress bar size +
  • +
  • max_size + number + Maximum size of progress bar +
  • +
  • slice + vector4 + Progress bar slice9 settings +
  • +
+ + + + + +
+
+ + Style +
+
+ Component style params + + +

Fields:

+
    +
  • SPEED + number + Progress bas fill rate. More -> faster +
  • +
  • MIN_DELTA + number + Minimum step to fill progress bar +
  • +
+ + + + + +
+
+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-03-21 22:59:46 +
+
+ + diff --git a/docs/modules/druid.radio_group.html b/docs/modules/druid.radio_group.html new file mode 100644 index 0000000..d12b79e --- /dev/null +++ b/docs/modules/druid.radio_group.html @@ -0,0 +1,246 @@ + + + + + Defold Druid UI Library + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module druid.radio_group

+

Radio group module

+

+ +

+ + +

Functions

+ + + + + + + + + + + + + +
init(node, callback[, click=node])Component init function
set_state(state)Set radio group state
get_state()Return radio group state
+

Tables

+ + + + + + + + + +
EventsComponent events
FieldsComponent fields
+ +
+
+ + +

Functions

+ +
+
+ + init(node, callback[, click=node]) +
+
+ Component init function + + +

Parameters:

+
    +
  • node + node[] + Array of gui node +
  • +
  • callback + function + Radio callback +
  • +
  • click + node[] + node Array of trigger nodes, by default equals to nodes + (default node) +
  • +
+ + + + + +
+
+ + set_state(state) +
+
+ Set radio group state + + +

Parameters:

+
    +
  • state + bool[] + Array of checkbox state +
  • +
+ + + + + +
+
+ + get_state() +
+
+ Return radio group state + + + +

Returns:

+
    + + bool[] + Array if checkboxes state +
+ + + + +
+
+

Tables

+ +
+
+ + Events +
+
+ Component events + + +

Fields:

+
    +
  • on_radio_click + druid_event + On any checkbox click +
  • +
+ + + + + +
+
+ + Fields +
+
+ Component fields + + +

Fields:

+
    +
  • checkboxes + table + Array of checkbox components +
  • +
+ + + + + +
+
+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-03-21 22:59:46 +
+
+ + diff --git a/docs/modules/druid.scroll.html b/docs/modules/druid.scroll.html new file mode 100644 index 0000000..bcdbe27 --- /dev/null +++ b/docs/modules/druid.scroll.html @@ -0,0 +1,514 @@ + + + + + Defold Druid UI Library + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module druid.scroll

+

Component to handle scroll content.

+

Scroll consist from two nodes: scroll parent and scroll input + Scroll input the user input zone, it's static + Scroll parent the scroll moving part, it will change position. + Setup initial scroll size by changing scroll parent size. If scroll parent + size will be less than scroll_input size, no scroll is available. For scroll + parent size should be more than input size

+ + +

Functions

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
init(scroll_parent, input_zone)Component init function
scroll_to(vector3[, is_instant])Start scroll to target point
scroll_to_percent(vector3[, is_instant])Start scroll to target scroll percent
init(index[, skip_cb])Scroll to item in scroll by point index
set_points(points)Set points of interest.
set_inert(state)Enable or disable scroll inert.
on_point_move(callback)Set the callback on scrolling to point (if exist)
set_border(border)Set the scroll possibly area
get_scroll_percent()Return current scroll progress
+

Tables

+ + + + + + + + + + + + + +
EventsComponent events
FieldsComponent fields
StyleComponent style params
+ +
+
+ + +

Functions

+ +
+
+ + init(scroll_parent, input_zone) +
+
+ Component init function + + +

Parameters:

+
    +
  • scroll_parent + node + Gui node where placed scroll content. This node will change position +
  • +
  • input_zone + node + Gui node where input is catched +
  • +
+ + + + + +
+
+ + scroll_to(vector3[, is_instant]) +
+
+ Start scroll to target point + + +

Parameters:

+
    +
  • vector3 + point + target point +
  • +
  • is_instant + bool + instant scroll flag + (optional) +
  • +
+ + + + +

Usage:

+
    +
  • scroll:scroll_to(vmath.vector3(0, 50, 0))
  • +
  • scroll:scroll_to(vmath.vector3(0), true)
  • +
+ +
+
+ + scroll_to_percent(vector3[, is_instant]) +
+
+ Start scroll to target scroll percent + + +

Parameters:

+
    +
  • vector3 + point + target percent +
  • +
  • is_instant + bool + instant scroll flag + (optional) +
  • +
+ + + + +

Usage:

+
    +
    scroll:scroll_to_percent(vmath.vector3(0.5, 0, 0))
    +
+ +
+
+ + init(index[, skip_cb]) +
+
+ Scroll to item in scroll by point index + + +

Parameters:

+
    +
  • index + number + Point index +
  • +
  • skip_cb + bool + If true, skip the point callback + (optional) +
  • +
+ + + + + +
+
+ + set_points(points) +
+
+ Set points of interest. + Scroll will always centered on closer points + + +

Parameters:

+
    +
  • points + table + Array of vector3 points +
  • +
+ + + + + +
+
+ + set_inert(state) +
+
+ Enable or disable scroll inert. + If disabled, scroll through points (if exist) + If no points, just simple drag without inertion + + +

Parameters:

+
    +
  • state + bool + Inert scroll state +
  • +
+ + + + + +
+
+ + on_point_move(callback) +
+
+ Set the callback on scrolling to point (if exist) + + +

Parameters:

+
    +
  • callback + function + Callback on scroll to point of interest +
  • +
+ + + + + +
+
+ + set_border(border) +
+
+ Set the scroll possibly area + + +

Parameters:

+
    +
  • border + vector3 + Size of scrolling area +
  • +
+ + + + + +
+
+ + get_scroll_percent() +
+
+ Return current scroll progress + + + +

Returns:

+
    + + vector3 + Scroll progress +
+ + + + +
+
+

Tables

+ +
+
+ + Events +
+
+ Component events + + +

Fields:

+
    +
  • on_scroll + druid_event + On scroll move callback +
  • +
  • on_scroll_to + druid_event + On scroll_to function callback +
  • +
  • on_point_scroll + druid_event + On scrolltoindex function callbck +
  • +
+ + + + + +
+
+ + Fields +
+
+ Component fields + + +

Fields:

+
    +
  • node + node + Scroll parent node +
  • +
  • input_zone + node + Scroll input node +
  • +
  • zone_size + vector3 + Current scroll content size +
  • +
  • soft_size + number + Soft zone size from style table +
  • +
  • center_offset + vector3 + Distance from node to node's center +
  • +
  • is_inert + bool + Flag, if scroll now moving by inertion +
  • +
  • inert + vector3 + Current inert speed +
  • +
  • pos + vector3 + Current scroll posisition +
  • +
  • target + vector3 + Current scroll target position +
  • +
+ + + + + +
+
+ + Style +
+
+ Component style params + + +

Fields:

+
    +
  • FRICT_HOLD + number + Multiplier for inertion, while touching +
  • +
  • FRICT + number + Multiplier for free inertion +
  • +
  • INERT_THRESHOLD + number + Scroll speed to stop inertion +
  • +
  • INERT_SPEED + number + Multiplier for inertion speed +
  • +
  • DEADZONE + number + Deadzone for start scrol in pixels +
  • +
  • SOFT_ZONE_SIZE + number + Size of outside zone in pixels (for scroll back moving) +
  • +
  • BACK_SPEED + number + Scroll back returning lerp speed +
  • +
  • ANIM_SPEED + number + Scroll gui.animation speed for scroll_to function +
  • +
+ + + + + +
+
+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-03-21 22:59:46 +
+
+ + diff --git a/docs/modules/druid.slider.html b/docs/modules/druid.slider.html new file mode 100644 index 0000000..309e95b --- /dev/null +++ b/docs/modules/druid.slider.html @@ -0,0 +1,255 @@ + + + + + Defold Druid UI Library + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module druid.slider

+

Druid slider component

+

+ +

+ + +

Functions

+ + + + + + + + + +
init(node, end_pos[, callback])Component init function
set(value[, is_silent])Set value for slider
+

Tables

+ + + + + + + + + +
EventsComponent events
FieldsComponent fields
+ +
+
+ + +

Functions

+ +
+
+ + init(node, end_pos[, callback]) +
+
+ Component init function + + +

Parameters:

+
    +
  • node + node + Gui pin node +
  • +
  • end_pos + vector3 + The end position of slider +
  • +
  • callback + function + On slider change callback + (optional) +
  • +
+ + + + + +
+
+ + set(value[, is_silent]) +
+
+ Set value for slider + + +

Parameters:

+
    +
  • value + number + Value from 0 to 1 +
  • +
  • is_silent + bool + Don't trigger event if true + (optional) +
  • +
+ + + + + +
+
+

Tables

+ +
+
+ + Events +
+
+ Component events + + +

Fields:

+
    +
  • on_change_value + druid_event + On change value callback +
  • +
+ + + + + +
+
+ + Fields +
+
+ Component fields + + +

Fields:

+
    +
  • node + node + Slider pin node +
  • +
  • start_pos + vector3 + Start pin node position +
  • +
  • pos + vector3 + Current pin node position +
  • +
  • target_pos + vector3 + Targer pin node position +
  • +
  • end_pos + vector3 + End pin node position +
  • +
  • dist + number + Length between start and end position +
  • +
  • is_drag + bool + Current drag state +
  • +
  • value + number + Current slider value +
  • +
+ + + + + +
+
+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-03-21 22:59:46 +
+
+ + diff --git a/docs/modules/druid.text.html b/docs/modules/druid.text.html new file mode 100644 index 0000000..25ae2f4 --- /dev/null +++ b/docs/modules/druid.text.html @@ -0,0 +1,359 @@ + + + + + Defold Druid UI Library + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module druid.text

+

Component to handle all GUI texts.

+

Druid text can adjust itself for text node size + Text will never will be outside of his text size (even multiline)

+ + +

Functions

+ + + + + + + + + + + + + + + + + + + + + + + + + +
init(node[, value[, no_adjust]])Component init function
set_to(set_to)Set text to text field
set_color(color)Set color
set_alpha(alpha)Set alpha
set_scale(scale)Set scale
set_pivot(pivot)Set text pivot.
+

Tables

+ + + + + + + + + +
EventsComponent events
FieldsComponent fields
+ +
+
+ + +

Functions

+ +
+
+ + init(node[, value[, no_adjust]]) +
+
+ Component init function + + +

Parameters:

+
    +
  • node + node + Gui text node +
  • +
  • value + string + Initial text + (optional) +
  • +
  • no_adjust + bool + If true, text will be not auto-adjust size + (optional) +
  • +
+ + + + + +
+
+ + set_to(set_to) +
+
+ Set text to text field + + +

Parameters:

+
    +
  • set_to + string + Text for node +
  • +
+ + + + + +
+
+ + set_color(color) +
+
+ Set color + + +

Parameters:

+
    +
  • color + vector4 + Color for node +
  • +
+ + + + + +
+
+ + set_alpha(alpha) +
+
+ Set alpha + + +

Parameters:

+
    +
  • alpha + number + Alpha for node +
  • +
+ + + + + +
+
+ + set_scale(scale) +
+
+ Set scale + + +

Parameters:

+
    +
  • scale + vector3 + Scale for node +
  • +
+ + + + + +
+
+ + set_pivot(pivot) +
+
+ Set text pivot. Text will re-anchor inside + his text area + + +

Parameters:

+
    +
  • pivot + gui.pivot + Gui pivot constant +
  • +
+ + + + + +
+
+

Tables

+ +
+
+ + Events +
+
+ Component events + + +

Fields:

+
    +
  • on_set_text + druid_event + On set text callback +
  • +
  • on_update_text_scale + druid_event + On adjust text size callback +
  • +
  • on_set_pivot + druid_event + On change pivot callback +
  • +
+ + + + + +
+
+ + Fields +
+
+ Component fields + + +

Fields:

+
    +
  • node + node + Text node +
  • +
  • pos + vector3 + Current text position +
  • +
  • start_scale + vector3 + Initial text node scale +
  • +
  • scale + vector3 + Current text node scale +
  • +
  • start_size + vector3 + Initial text node size +
  • +
  • text_area + vector3 + Current text node available are +
  • +
  • is_no_adjust + bool + Current text size adjust settings +
  • +
  • color + vector3 + Current text color +
  • +
+ + + + + +
+
+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-03-21 22:59:46 +
+
+ + diff --git a/docs/modules/druid.timer.html b/docs/modules/druid.timer.html new file mode 100644 index 0000000..a65a18c --- /dev/null +++ b/docs/modules/druid.timer.html @@ -0,0 +1,314 @@ + + + + + Defold Druid UI Library + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module druid.timer

+

Component to handle GUI timers.

+

Timer updating by game delta time. If game is not focused - + timer will be not updated.

+ + +

Functions

+ + + + + + + + + + + + + + + + + +
init(node, seconds_from[, seconds_to=0[, callback]])Component init function
set_to(set_to)Set text to text field
set_state(is_on)Called when update
set_interval(from, to)Set time interval
+

Tables

+ + + + + + + + + +
EventsComponent events
FieldsComponent fields
+ +
+
+ + +

Functions

+ +
+
+ + init(node, seconds_from[, seconds_to=0[, callback]]) +
+
+ Component init function + + +

Parameters:

+
    +
  • node + node + Gui text node +
  • +
  • seconds_from + number + Start timer value in seconds +
  • +
  • seconds_to + number + End timer value in seconds + (default 0) +
  • +
  • callback + function + Function on timer end + (optional) +
  • +
+ + + + + +
+
+ + set_to(set_to) +
+
+ Set text to text field + + +

Parameters:

+
    +
  • set_to + number + Value in seconds +
  • +
+ + + + + +
+
+ + set_state(is_on) +
+
+ Called when update + + +

Parameters:

+
    +
  • is_on + bool + Timer enable state +
  • +
+ + + + + +
+
+ + set_interval(from, to) +
+
+ Set time interval + + +

Parameters:

+
    +
  • from + number + Start time in seconds +
  • +
  • to + number + Target time in seconds +
  • +
+ + + + + +
+
+

Tables

+ +
+
+ + Events +
+
+ Component events + + +

Fields:

+
    +
  • on_tick + druid_event + On timer tick callback. Fire every second +
  • +
  • on_set_enabled + druid_event + On timer change enabled state callback +
  • +
  • on_timer_end + druid_event + On timer end callback +
  • +
+ + + + + +
+
+ + Fields +
+
+ Component fields + + +

Fields:

+
    +
  • node + node + Trigger node +
  • +
  • anim_node + node + Animation node + (default node) +
  • +
  • scale_from + vector3 + Initial scale of anim_node +
  • +
  • pos + vector3 + Initial pos of anim_node +
  • +
  • params + any + Params to click callbacks +
  • +
  • hover + druid.hover + Druid hover logic component +
  • +
  • click_zone + node + Restriction zone + (optional) +
  • +
+ + + + + +
+
+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-03-21 22:59:46 +
+
+ + diff --git a/docs/modules/druid_event.html b/docs/modules/druid_event.html new file mode 100644 index 0000000..88500c8 --- /dev/null +++ b/docs/modules/druid_event.html @@ -0,0 +1,246 @@ + + + + + Defold Druid UI Library + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module druid_event

+

Lua event small library

+

+ +

+ + +

Functions

+ + + + + + + + + + + + + + + + + + + + + + + + + +
Event(initial_callback)Event constructur
event:subscribe(callback)Subscribe callback on event
event:unsubscribe(callback)Unsubscribe callback on event
event:is_exist()Return true, if event have at lease one handler
event:clear()Clear the all event handlers
event:trigger(...)Trigger the event and call all subscribed callbacks
+ +
+
+ + +

Functions

+ +
+
+ + Event(initial_callback) +
+
+ Event constructur + + +

Parameters:

+
    +
  • initial_callback + function + Subscribe the callback on new event, if callback exist +
  • +
+ + + + + +
+
+ + event:subscribe(callback) +
+
+ Subscribe callback on event + + +

Parameters:

+
    +
  • callback + function + Callback itself +
  • +
+ + + + + +
+
+ + event:unsubscribe(callback) +
+
+ Unsubscribe callback on event + + +

Parameters:

+
    +
  • callback + function + Callback itself +
  • +
+ + + + + +
+
+ + event:is_exist() +
+
+ Return true, if event have at lease one handler + + + +

Returns:

+
    + + bool + True if event have handlers +
+ + + + +
+
+ + event:clear() +
+
+ Clear the all event handlers + + + + + + + +
+
+ + event:trigger(...) +
+
+ Trigger the event and call all subscribed callbacks + + +

Parameters:

+
    +
  • ... + All event params +
  • +
+ + + + + +
+
+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-03-21 22:59:46 +
+
+ + diff --git a/docs/modules/druid_instance.html b/docs/modules/druid_instance.html new file mode 100644 index 0000000..6269911 --- /dev/null +++ b/docs/modules/druid_instance.html @@ -0,0 +1,757 @@ + + + + + Defold Druid UI Library + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

Module druid_instance

+

Druid main class.

+

Create instance of this + to start creating components

+

See also:

+ + + +

Functions

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
druid:initialize(table, table)Druid class constructor
druid:create(component, ...)Create new druid component
druid:remove(component)Remove component from druid instance.
druid:update(dt)Druid update function
druid:on_input(action_id, action)Druid on_input function
druid:on_message(message_id, message, sender)Druid on_message function
druid:new_button(...)Create button basic component
druid:new_blocker(...)Create blocker basic component
druid:new_back_handler(...)Create back_handler basic component
druid:new_hover(...)Create hover basic component
druid:new_text(...)Create text basic component
druid:new_lang_text(...)Create lang_text basic component
druid:new_timer(...)Create timer basic component
druid:new_progress(...)Create progress basic component
druid:new_grid(...)Create grid basic component
druid:new_scroll(...)Create scroll basic component
druid:new_slider(...)Create slider basic component
druid:new_checkbox(...)Create checkbox basic component
druid:new_input(...)Create input basic component
druid:new_checkbox_group(...)Create checkbox_group basic component
druid:new_radio_group(...)Create radio_group basic component
+ +
+
+ + +

Functions

+ +
+
+ + druid:initialize(table, table) +
+
+ Druid class constructor + + +

Parameters:

+
    +
  • table + style + Druid style module +
  • +
  • table + style + Druid style module +
  • +
+ + + + + +
+
+ + druid:create(component, ...) +
+
+ Create new druid component + + +

Parameters:

+
    +
  • component + Component + Component module +
  • +
  • ... + args + Other component params to pass it to component:init function +
  • +
+ + + + + +
+
+ + druid:remove(component) +
+
+ Remove component from druid instance. + Component on_remove function will be invoked, if exist. + + +

Parameters:

+
    +
  • component + Component + Component instance +
  • +
+ + + + + +
+
+ + druid:update(dt) +
+
+ Druid update function + + +

Parameters:

+
    +
  • dt + number + Delta time +
  • +
+ + + + + +
+
+ + druid:on_input(action_id, action) +
+
+ Druid on_input function + + +

Parameters:

+
    +
  • action_id + hash + Actionid from oninput +
  • +
  • action + table + Action from on_input +
  • +
+ + + + + +
+
+ + druid:on_message(message_id, message, sender) +
+
+ Druid on_message function + + +

Parameters:

+
    +
  • message_id + hash + Messageid from onmessage +
  • +
  • message + table + Message from on_message +
  • +
  • sender + hash + Sender from on_message +
  • +
+ + + + + +
+
+ + druid:new_button(...) +
+
+ Create button basic component + + +

Parameters:

+
    +
  • ... + args + button init args +
  • +
+ +

Returns:

+
    + + Component + button component +
+ + + + +
+
+ + druid:new_blocker(...) +
+
+ Create blocker basic component + + +

Parameters:

+
    +
  • ... + args + blocker init args +
  • +
+ +

Returns:

+
    + + Component + blocker component +
+ + + + +
+
+ + druid:new_back_handler(...) +
+
+ Create back_handler basic component + + +

Parameters:

+
    +
  • ... + args + back_handler init args +
  • +
+ +

Returns:

+
    + + Component + back_handler component +
+ + + + +
+
+ + druid:new_hover(...) +
+
+ Create hover basic component + + +

Parameters:

+
    +
  • ... + args + hover init args +
  • +
+ +

Returns:

+
    + + Component + hover component +
+ + + + +
+
+ + druid:new_text(...) +
+
+ Create text basic component + + +

Parameters:

+
    +
  • ... + args + text init args +
  • +
+ +

Returns:

+
    + + Component + text component +
+ + + + +
+
+ + druid:new_lang_text(...) +
+
+ Create lang_text basic component + + +

Parameters:

+
    +
  • ... + args + lang_text init args +
  • +
+ +

Returns:

+
    + + Component + lang_text component +
+ + + + +
+
+ + druid:new_timer(...) +
+
+ Create timer basic component + + +

Parameters:

+
    +
  • ... + args + timer init args +
  • +
+ +

Returns:

+
    + + Component + timer component +
+ + + + +
+
+ + druid:new_progress(...) +
+
+ Create progress basic component + + +

Parameters:

+
    +
  • ... + args + progress init args +
  • +
+ +

Returns:

+
    + + Component + progress component +
+ + + + +
+
+ + druid:new_grid(...) +
+
+ Create grid basic component + + +

Parameters:

+
    +
  • ... + args + grid init args +
  • +
+ +

Returns:

+
    + + Component + grid component +
+ + + + +
+
+ + druid:new_scroll(...) +
+
+ Create scroll basic component + + +

Parameters:

+
    +
  • ... + args + scroll init args +
  • +
+ +

Returns:

+
    + + Component + scroll component +
+ + + + +
+
+ + druid:new_slider(...) +
+
+ Create slider basic component + + +

Parameters:

+
    +
  • ... + args + slider init args +
  • +
+ +

Returns:

+
    + + Component + slider component +
+ + + + +
+
+ + druid:new_checkbox(...) +
+
+ Create checkbox basic component + + +

Parameters:

+
    +
  • ... + args + checkbox init args +
  • +
+ +

Returns:

+
    + + Component + checkbox component +
+ + + + +
+
+ + druid:new_input(...) +
+
+ Create input basic component + + +

Parameters:

+
    +
  • ... + args + input init args +
  • +
+ +

Returns:

+
    + + Component + input component +
+ + + + +
+
+ + druid:new_checkbox_group(...) +
+
+ Create checkbox_group basic component + + +

Parameters:

+
    +
  • ... + args + checkbox_group init args +
  • +
+ +

Returns:

+
    + + Component + checkbox_group component +
+ + + + +
+
+ + druid:new_radio_group(...) +
+
+ Create radio_group basic component + + +

Parameters:

+
    +
  • ... + args + radio_group init args +
  • +
+ +

Returns:

+
    + + Component + radio_group component +
+ + + + +
+
+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-03-21 22:59:46 +
+
+ + diff --git a/docs/topics/README.md.html b/docs/topics/README.md.html new file mode 100644 index 0000000..8f1bc1d --- /dev/null +++ b/docs/topics/README.md.html @@ -0,0 +1,241 @@ + + + + + Defold Druid UI Library + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ +

+ +

Druid - powerful defold component UI library. Use basic druid components or make your own game-specific components to make amazing GUI in your games.

+ + +

+

Setup

+ +

Dependency

+ +

You can use the druid extension in your own project by adding this project as a Defold library dependency. Open your game.project file and in the dependencies field under project add:

+ +
+

https://github.com/Insality/druid/archive/master.zip

+
+ +

Or point to the ZIP file of a specific release.

+ + +

Code

+ +

Adjust druid settings, if needed:

+ +
+local druid = require("druid.druid")
+
+-- Used for button component and custom components
+druid.set_sound_function(callback)
+
+-- Used for lang_text component
+druid.set_text_function(callback)
+
+-- Used for change default druid style
+druid.set_default_style(your_style)
+
+ + + +

+

Components

+ +

Druid provides next basic components: +- Button - Basic game button

+ +
    +
  • Text - Wrap on text node with text size adjusting

  • +
  • Blocker - Block input in node zone

  • +
  • Back Handler - Handle back button (Android, backspace)

  • +
  • Lang text - Text component with handle localization system

  • +
  • Timer - Run timer on text node

  • +
  • Progress - Basic progress bar

  • +
  • Scroll - Basic scroll component

  • +
  • Grid - Component for manage node positions

  • +
  • Slider - Basic slider component

  • +
  • Checkbox - Basic checkbox component

  • +
  • Checkbox group - Several checkboxes in one group

  • +
  • Radio group - Several checkboxes in one group with single choice

  • +
  • Hover - Trigger component for check node hover state

  • +
  • Input - Component to process user text input

  • +
+ +

Full info see on components.md

+ + +

+

Creating components

+ +

Any components creating via druid:

+ +
+local druid = require("druid.druid")
+
+local function init(self)
+    self.druid = druid.new(self)
+    local button = self.druid:new_button(node_name, callback)
+    local text = self.druid:new_text(node_text_name)
+end
+
+function update(self, dt)
+    self.druid:update(dt)
+end
+
+function on_message(self, message_id, message, sender)
+    self.druid:on_message(message_id, message, sender)
+end
+
+function on_input(self, action_id, action)
+    self.druid:on_input(action_id, action)
+end
+
+ + + +

+

Examples

+ +

See the example folder for examples of how to use Druid

+ +

See the druid-assets repository for examples of how to create custom components and styles

+ +

Try the HTML5 version of the example app

+ + +

+

Documentation

+ +

To learn druid better, read next documentation: +- Druid components +- Create custom components +- Druid asset store +- Druid Styles

+ +

Full druid documentation you can find here: +https://insality.github.io/druid/

+ + +

+

Games powered by Druid

+ +

Will fill later

+ + +

+

Future plans

+ +
    +
  • Basic input component

  • +
  • Add onlayoutchange support (to keep gui data between layout change)

  • +
  • Add onchangelanguage support (call single function to update all druid instance)

  • +
  • Better documentation and examples

  • +
  • Add more comfortable gamepad support for GUI (ability to select button with DPAD and other stuff)

  • +
+ + +

+

License

+ +

Original idea by AGulev

+ +

Developed and supporting by Insality

+ +

MIT License

+ + +

+

Issues and suggestions

+ +

If you have any issues, questions or suggestions please create an issue or contact me: insality@gmail.com + +

+
+
+generated by LDoc 1.4.6 +Last updated 2020-03-21 22:59:46 +
+
+ + diff --git a/docs/topics/components.md.html b/docs/topics/components.md.html new file mode 100644 index 0000000..a8c9c0a --- /dev/null +++ b/docs/topics/components.md.html @@ -0,0 +1,162 @@ + + + + + Defold Druid UI Library + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ + +

Druid components

+ +

+

Button

+

Basic game button

+ +

+

Text

+

Wrap on text node with text size adjusting

+ +

+

Blocker

+

Block input in node zone

+ +

+

Back Handler

+

Handle back button (Android, backspace)

+ +

+

Locale

+

Text component with handle localization system

+ +

+

Timer

+

Run timer on text node

+ +

+

Progress

+

Basic progress bar

+ +

+

Scroll

+

Basic scroll component

+ +

+

Grid

+

Component for manage node positions

+ +

+

Slider

+

Basic slider component

+ +

+

Checkbox

+

Basic checkbox component

+ +

+

Checkbox group

+

Several checkboxes in one group

+ +

+

Radio group

+

Several checkboxes in one group with single choice

+ +

+

Hover

+

Trigger component for check node hover state

+ +

+

Input

+

Component to process user text input

+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-03-21 22:59:46 +
+
+ + diff --git a/docs/topics/create_custom_components.md.html b/docs/topics/create_custom_components.md.html new file mode 100644 index 0000000..758f9c9 --- /dev/null +++ b/docs/topics/create_custom_components.md.html @@ -0,0 +1,172 @@ + + + + + Defold Druid UI Library + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ + +

Custom components

+ +

Add your custom components via druid.register

+ +
+local druid = require("druid.druid")
+local my_component = require("my.amazing.component")
+
+local function init(self)
+    druid.register("my_component", my_component)
+end
+
+ + +

Basic custom component template looks like this:

+ +
+local const = require("druid.const")
+
+local M = {}
+M.interest = { const.ON_INPUT }
+
+function M.init(self, ...)
+    -- Component constructor
+end
+
+-- Call only if exist interest: const.ON_UPDATE
+function M.update(self, dt)
+
+end
+
+-- Call only if exist interest: const.ON_INPUT or const.ON_SWIPE
+function M.on_input(self, action_id, action)
+
+end
+
+-- Call only if exist interest: const.ON_MESSAGE
+function M.on_message(self, message_id, message, sender)
+
+end
+
+-- Call only if swipe was started on another component (ex. scroll)
+function M.on_swipe(self)
+
+end
+
+return M
+
+ + + +

Best practice on custom components

+

On each component recomended describe component schema in next way:

+ + +
+-- Component module
+local helper = require("druid.helper")
+
+local M = {}
+
+local SCHEME = {
+    ROOT = "/root",
+    ITEM = "/item",
+    TITLE = "/title"
+}
+
+-- TODO: Rework self.template/self.nodes
+-- Make self._inner_data? { component_name, template, nodes }
+function M.init(self, template_name, node_table)
+    -- If component use template, setup it:
+ self.template = template_name
+
+    -- If component was cloned with gui.clone_tree, pass his nodes
+ self.nodes = node_table
+
+    -- helper can get node from gui/template/table
+ local root = helper.node(self, SCHEME.ROOT)
+
+    -- This component can spawn another druid components:
+ local druid = helper.get_druid(self)
+    -- Button self on callback is self of _this_ component
+ local button = druid:new_button(...)
+
+    -- helper can return you the component style
+ local my_style = helper.get_style(self, "component_name")
+end
+
+ + + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-03-21 22:00:04 +
+
+ + diff --git a/docs/topics/creating_custom_components.md.html b/docs/topics/creating_custom_components.md.html new file mode 100644 index 0000000..67d997b --- /dev/null +++ b/docs/topics/creating_custom_components.md.html @@ -0,0 +1,200 @@ + + + + + Defold Druid UI Library + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ + +

Creating custom components

+ +

+

Overview

+

Druid allows you to create your custom components from druid basic components or other custom components

+ + +

+

Custom components

+

Basic custom component template looks like this:

+ +
+local const = require("druid.const")
+local component = require("druid.component")
+
+local M = component.create("your_component")
+
+-- Component constructor
+function M.init(self, ...)
+end
+
+-- Call only if exist interest: const.ON_UPDATE
+function M.update(self, dt)
+end
+
+-- Call only if exist interest: const.ON_INPUT or const.ON_INPUT_HIGH
+function M.on_input(self, action_id, action)
+end
+
+-- Call only if exist interest: const.ON_MESSAGE
+function M.on_message(self, message_id, message, sender)
+end
+
+-- Call only if component with ON_CHANGE_LANGUAGE interest
+function M.on_change_language(self)
+end
+
+-- Call only if component with ON_LAYOUT_CHANGE interest
+function M.on_layout_change(self)
+end
+
+return M
+
+ + + +

Add your custom component to druid via druid.register

+ +
+local druid = require("druid.druid")
+local my_component = require("my.amazing.component")
+
+local function init(self)
+    druid.register("my_component", my_component)
+end
+
+ + +

Interest

+

Interest - is a way to indicate what events your component will respond to. +There is next interests in druid: +- ON_MESSAGE - component will receive messages from on_message

+ +
    +
  • ON_UPDATE - component will be updated from update

  • +
  • ONINPUTHIGH - component will receive input from oninput, before other components with ONINPUT

  • +
  • ON_INPUT - component will receive input from oninput, after other components with ONINPUT_HIGH

  • +
  • ONCHANGELANGUAGE - will call onchangelanguage function on language change trigger

  • +
  • ONLAYOUTCHANGED will call onlayoutchange function on layout change trigger

  • +
+ + +

+

Best practice on custom components

+

On each component recomended describe component scheme in next way:

+ + +
+-- Component module
+local component = require("druid.component")
+
+local M = component.create("your_component")
+
+local SCHEME = {
+    ROOT = "/root",
+    ITEM = "/item",
+    TITLE = "/title"
+}
+
+function M.init(self, template_name, node_table)
+    -- If component use template, setup it:
+ self:set_template(template_name)
+
+    -- If component was cloned with gui.clone_tree, pass his nodes
+ self:set_nodes(node_table)
+
+    -- helper can get node from gui/template/table
+ local root = self:get_node(SCHEME.ROOT)
+
+    -- This component can spawn another druid components:
+ local druid = self:get_druid()
+
+    -- Button self on callback is self of _this_ component
+ local button = druid:new_button(...)
+
+    -- helper can return you the component style for current component
+ -- It return by component name from
+ local my_style = self:get_style()
+end
+
+ + + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-03-21 22:59:46 +
+
+ + diff --git a/docs/topics/druid_assets.md.html b/docs/topics/druid_assets.md.html new file mode 100644 index 0000000..b45f35a --- /dev/null +++ b/docs/topics/druid_assets.md.html @@ -0,0 +1,96 @@ + + + + + Defold Druid UI Library + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ + +

Druid assets

+ +

+

Overview

+

I've created druid-assets repository to make a marketplace with custom styles and components.

+ +

Any of druid users can push their own components and styles to share it with the other users

+ +

Also, this marketplace is great example to how you can create your custom components

+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-03-21 22:59:46 +
+
+ + diff --git a/docs/topics/examples.md.html b/docs/topics/examples.md.html new file mode 100644 index 0000000..dafc08b --- /dev/null +++ b/docs/topics/examples.md.html @@ -0,0 +1,94 @@ + + + + + Defold Druid UI Library + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ + +

Examples

+ +

+

Overview

+

See the example folder for examples of how to use Druid

+ +

Try the HTML5 version of the example app

+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-03-21 22:59:46 +
+
+ + diff --git a/docs/topics/online_example.md.html b/docs/topics/online_example.md.html new file mode 100644 index 0000000..b057b31 --- /dev/null +++ b/docs/topics/online_example.md.html @@ -0,0 +1,86 @@ + + + + + Defold Druid UI Library + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ + +

Online example

+ +

Check druid --here-- (link)

+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-03-21 22:00:04 +
+
+ + diff --git a/docs/topics/styles.md.html b/docs/topics/styles.md.html new file mode 100644 index 0000000..9c680d6 --- /dev/null +++ b/docs/topics/styles.md.html @@ -0,0 +1,145 @@ + + + + + Defold Druid UI Library + + + + +
+ +
+ +
+
+
+ + +
+ + + + + + +
+ + +

Styles

+ +

+

Overview

+

Styles - set of functions and parameters for components to customize their behavior.

+ +

Styles is a table, where key is name of component, and value is style table for this component.

+ +

In component API documentation, you can find the style API for this component. Or just lookup for existing styles and modify them.

+ +

+

Usage

+

Setup default druid style for all druid instances via druid.set_default_style

+ +
+local druid = require("druid.druid")
+local my_style = require("my.amazing.style")
+
+local function init(self)
+    druid.set_default_style(my_style)
+end
+
+ + +

Setup custom style to specific druid instance:

+ +
+local druid = require("druid.druid")
+local my_style = require("my.amazing.style")
+
+local function init(self)
+    -- This druid instance will be use my_style as default
+ self.druid = druid.new(self, my_style)
+end
+
+ + +

Change component style with setstyle_ function

+ +
+local druid = require("druid.druid")
+local my_style = require("my.amazing.style")
+
+local function init(self)
+    self.druid = druid.new(self)
+    self.button = self.druid:new_button(self, "node")
+    -- Setup custom style for specific component
+ self.button:set_style(my_style)
+end
+
+ + +

+

Create custom components

+

Styles is just lua table, so it can be described in just one single file +TODO

+ + +
+
+
+generated by LDoc 1.4.6 +Last updated 2020-03-21 22:59:46 +
+
+ + diff --git a/docs_md/components.md b/docs_md/components.md new file mode 100644 index 0000000..ecfb132 --- /dev/null +++ b/docs_md/components.md @@ -0,0 +1,46 @@ +# Druid components + +## Button +Basic game button + +## Text +Wrap on text node with text size adjusting + +## Blocker +Block input in node zone + +## Back Handler +Handle back button (Android, backspace) + +## Locale +Text component with handle localization system + +## Timer +Run timer on text node + +## Progress +Basic progress bar + +## Scroll +Basic scroll component + +## Grid +Component for manage node positions + +## Slider +Basic slider component + +## Checkbox +Basic checkbox component + +## Checkbox group +Several checkboxes in one group + +## Radio group +Several checkboxes in one group with single choice + +## Hover +Trigger component for check node hover state + +## Input +Component to process user text input \ No newline at end of file diff --git a/docs_md/creating_custom_components.md b/docs_md/creating_custom_components.md new file mode 100644 index 0000000..197a2f3 --- /dev/null +++ b/docs_md/creating_custom_components.md @@ -0,0 +1,105 @@ +# Creating custom components + +## Overview +Druid allows you to create your custom components from druid basic components or other custom components + + +## Custom components +Basic custom component template looks like this: +```lua +local const = require("druid.const") +local component = require("druid.component") + +local M = component.create("your_component") + +-- Component constructor +function M.init(self, ...) +end + +-- Call only if exist interest: const.ON_UPDATE +function M.update(self, dt) +end + +-- Call only if exist interest: const.ON_INPUT or const.ON_INPUT_HIGH +function M.on_input(self, action_id, action) +end + +-- Call only if exist interest: const.ON_MESSAGE +function M.on_message(self, message_id, message, sender) +end + +-- Call only if component with ON_CHANGE_LANGUAGE interest +function M.on_change_language(self) +end + +-- Call only if component with ON_LAYOUT_CHANGE interest +function M.on_layout_change(self) +end + +return M +``` + + +Add your custom component to druid via `druid.register` +```lua +local druid = require("druid.druid") +local my_component = require("my.amazing.component") + +local function init(self) + druid.register("my_component", my_component) +end +``` + +### Interest +Interest - is a way to indicate what events your component will respond to. +There is next interests in druid: +- **ON_MESSAGE** - component will receive messages from on_message + +- **ON_UPDATE** - component will be updated from update + +- **ON_INPUT_HIGH** - component will receive input from on_input, before other components with ON_INPUT + +- **ON_INPUT** - component will receive input from on_input, after other components with ON_INPUT_HIGH + +- **ON_CHANGE_LANGUAGE** - will call _on_change_language_ function on language change trigger + +- **ON_LAYOUT_CHANGED** will call _on_layout_change_ function on layout change trigger + + +## Best practice on custom components +On each component recomended describe component scheme in next way: + +```lua +-- Component module +local component = require("druid.component") + +local M = component.create("your_component") + +local SCHEME = { + ROOT = "/root", + ITEM = "/item", + TITLE = "/title" +} + +function M.init(self, template_name, node_table) + -- If component use template, setup it: + self:set_template(template_name) + + -- If component was cloned with gui.clone_tree, pass his nodes + self:set_nodes(node_table) + + -- helper can get node from gui/template/table + local root = self:get_node(SCHEME.ROOT) + + -- This component can spawn another druid components: + local druid = self:get_druid() + + -- Button self on callback is self of _this_ component + local button = druid:new_button(...) + + -- helper can return you the component style for current component + -- It return by component name from + local my_style = self:get_style() +end + +``` \ No newline at end of file diff --git a/docs_md/druid_assets.md b/docs_md/druid_assets.md new file mode 100644 index 0000000..8a2c992 --- /dev/null +++ b/docs_md/druid_assets.md @@ -0,0 +1,8 @@ +# Druid assets + +## Overview +I've created [druid-assets repository](https://github.com/Insality/druid-assets) to make a _marketplace_ with custom styles and components. + +Any of druid users can push their own components and styles to share it with the other users + +Also, this marketplace is great example to how you can create your custom components \ No newline at end of file diff --git a/docs_md/examples.md b/docs_md/examples.md new file mode 100644 index 0000000..84a9c86 --- /dev/null +++ b/docs_md/examples.md @@ -0,0 +1,6 @@ +# Examples + +## Overview +See the [example folder](https://github.com/Insality/druid/tree/develop/example/kenney) for examples of how to use Druid + +Try the HTML5 version of the example app \ No newline at end of file diff --git a/docs_md/styles.md b/docs_md/styles.md new file mode 100644 index 0000000..a018c43 --- /dev/null +++ b/docs_md/styles.md @@ -0,0 +1,47 @@ +# Styles + +## Overview +Styles - set of functions and parameters for components to customize their behavior. + +Styles is a table, where key is name of component, and value is style table for this component. + +In component API documentation, you can find the style API for this component. Or just lookup for existing styles and modify them. + +## Usage +Setup default druid style for all druid instances via `druid.set_default_style` +```lua +local druid = require("druid.druid") +local my_style = require("my.amazing.style") + +local function init(self) + druid.set_default_style(my_style) +end +``` + +Setup custom style to specific druid instance: +```lua +local druid = require("druid.druid") +local my_style = require("my.amazing.style") + +local function init(self) + -- This druid instance will be use my_style as default + self.druid = druid.new(self, my_style) +end +``` + +Change component style with _set_style_ function +```lua +local druid = require("druid.druid") +local my_style = require("my.amazing.style") + +local function init(self) + self.druid = druid.new(self) + self.button = self.druid:new_button(self, "node") + -- Setup custom style for specific component + self.button:set_style(my_style) +end +``` + +## Create custom components +Styles is just lua table, so it can be described in just one single file +__TODO__ diff --git a/druid/base/back_handler.lua b/druid/base/back_handler.lua new file mode 100644 index 0000000..00414c8 --- /dev/null +++ b/druid/base/back_handler.lua @@ -0,0 +1,44 @@ +--- Component to handle back key (android, backspace) +-- @module druid.back_handler + +--- Component events +-- @table Events +-- @tfield druid_event on_back On back handler callback + +--- Component fields +-- @table Fields +-- @tfield any params Params to click callbacks + +local Event = require("druid.event") +local const = require("druid.const") +local component = require("druid.component") + +local M = component.create("back_handler", { const.ON_INPUT }) + + +--- Component init function +-- @function back_handler:init +-- @tparam callback callback On back button +-- @tparam[opt] params Callback argument +function M.init(self, callback, params) + self.params = params + + self.on_back = Event(callback) +end + + +--- Input handler for component +-- @function back_handler:on_input +-- @tparam string action_id on_input action id +-- @tparam table action on_input action +function M.on_input(self, action_id, action) + if action_id == const.ACTION_BACK and action[const.RELEASED] then + self.on_back:trigger(self:get_context(), self.params) + return true + end + + return false +end + + +return M \ No newline at end of file diff --git a/druid/base/blocker.lua b/druid/base/blocker.lua new file mode 100644 index 0000000..0b336bd --- /dev/null +++ b/druid/base/blocker.lua @@ -0,0 +1,64 @@ +--- Component to block input on specify zone by node +-- @module druid.blocker + +--- Component events +-- @table Events +-- @tfield druid_event on_click On release button callback +-- @tfield druid_event on_enable_change On enable/disable callback + +--- Component fields +-- @table Fields +-- @tfield node node Trigger node + +local Event = require("druid.event") +local const = require("druid.const") +local component = require("druid.component") + +local M = component.create("blocker", { const.ON_INPUT_HIGH }) + + +--- Component init function +-- @function blocker:init +-- @tparam node node Gui node +function M.init(self, node) + self.node = self:get_node(node) + + self.on_click = Event() + self.on_enable_change = Event() +end + + +function M.on_input(self, action_id, action) + if action_id ~= const.ACTION_TOUCH then + return false + end + + if not self:is_enabled(self.node) then + return false + end + + if gui.pick_node(self.node, action.x, action.y) then + return true + end + + return false +end + + +--- Set enabled blocker component state +-- @function blocker:set_enabled +-- @tparam bool state Enabled state +function M.set_enabled(self, state) + gui.set_enabled(self.node, state) +end + + +--- Return blocked enabled state +-- @function blocker:is_enabled +-- @treturn bool True, if blocker is enabled +function M.is_enabled(self, state) + return gui.is_enabled(self.node) +end + + +return M diff --git a/druid/base/button.lua b/druid/base/button.lua new file mode 100644 index 0000000..9c431f9 --- /dev/null +++ b/druid/base/button.lua @@ -0,0 +1,290 @@ +--- Component to handle basic GUI button +-- @module druid.button + +--- Component events +-- @table Events +-- @tfield druid_event on_click On release button callback +-- @tfield druid_event on_repeated_click On repeated action button callback +-- @tfield druid_event on_long_click On long tap button callback +-- @tfield druid_event on_double_click On double tap button callback + +--- Component fields +-- @table Fields +-- @tfield node node Trigger node +-- @tfield[opt=node] node anim_node Animation node +-- @tfield vector3 scale_from Initial scale of anim_node +-- @tfield vector3 pos Initial pos of anim_node +-- @tfield any params Params to click callbacks +-- @tfield druid.hover hover Druid hover logic component +-- @tfield[opt] node click_zone Restriction zone + +--- Component style params +-- @table Style +-- @tfield function on_click (self, node) +-- @tfield function on_click_disabled (self, node) +-- @tfield function on_hover (self, node, hover_state) +-- @tfield function on_set_enabled (self, node, enabled_state) +-- @tfield bool IS_HOVER + +local Event = require("druid.event") +local const = require("druid.const") +local helper = require("druid.helper") +local component = require("druid.component") + +local M = component.create("button", { const.ON_INPUT }) + + +local function is_input_match(self, action_id) + if action_id == const.ACTION_TOUCH then + return true + end + + if self.key_trigger and action_id == self.key_trigger then + return true + end + + return false +end + + +local function on_button_hover(self, hover_state) + if not self._style.on_hover then + return + end + + self._style.on_hover(self, self.anim_node, hover_state) +end + + +local function on_button_click(self) + if self._style.on_click then + self._style.on_click(self, self.anim_node) + end + self.click_in_row = 1 + self.on_click:trigger(self:get_context(), self.params, self) +end + + +local function on_button_repeated_click(self) + if not self.is_repeated_started then + self.click_in_row = 0 + self.is_repeated_started = true + end + + if self._style.on_click then + self._style.on_click(self, self.anim_node) + end + self.click_in_row = self.click_in_row + 1 + self.on_repeated_click:trigger(self:get_context(), self.params, self, self.click_in_row) +end + + +local function on_button_long_click(self) + if self._style.on_click then + self._style.on_click(self, self.anim_node) + end + self.click_in_row = 1 + local time = socket.gettime() - self.last_pressed_time + self.on_long_click:trigger(self:get_context(), self.params, self, time) +end + + +local function on_button_double_click(self) + if self._style.on_click then + self._style.on_click(self, self.anim_node) + end + self.click_in_row = self.click_in_row + 1 + self.on_double_click:trigger(self:get_context(), self.params, self, self.click_in_row) +end + + +local function on_button_hold(self, press_time) + self.on_hold_callback:trigger(self:get_context(), self.params, self, press_time) +end + + +local function on_button_release(self) + if self.is_repeated_started then + return false + end + + if not self.disabled then + if self.can_action then + self.can_action = false + + local time = socket.gettime() + local is_long_click = (time - self.last_pressed_time) > self._style.LONGTAP_TIME + is_long_click = is_long_click and self.on_long_click:is_exist() + + local is_double_click = (time - self.last_released_time) < self._style.DOUBLETAP_TIME + is_double_click = is_double_click and self.on_double_click:is_exist() + + if is_long_click then + on_button_long_click(self) + elseif is_double_click then + on_button_double_click(self) + else + on_button_click(self) + end + + self.last_released_time = time + end + return true + else + if self._style.on_click_disabled then + self._style.on_click_disabled(self, self.anim_node) + end + return false + end +end + + +--- Component init function +-- @function button:init +-- @tparam node node Gui node +-- @tparam function callback Button callback +-- @tparam[opt] table params Button callback params +-- @tparam[opt] node anim_node Button anim node (node, if not provided) +-- @tparam[opt] string event Button react event, const.ACTION_TOUCH by default +function M.init(self, node, callback, params, anim_node, event) + self.druid = self:get_druid() + self.node = self:get_node(node) + + self.anim_node = anim_node and helper:get_node(anim_node) or self.node + -- TODO: rename to start_scale + self.scale_from = gui.get_scale(self.anim_node) + self.params = params + self.hover = self.druid:new_hover(node, on_button_hover) + self.click_zone = nil + self.is_repeated_started = false + self.last_pressed_time = 0 + self.last_released_time = 0 + self.click_in_row = 0 + self.key_trigger = nil + + -- Event stubs + self.on_click = Event(callback) + self.on_repeated_click = Event() + self.on_long_click = Event() + self.on_double_click = Event() + self.on_hold_callback = Event() +end + + +function M.on_input(self, action_id, action) + if not is_input_match(self, action_id) then + return false + end + + if not helper.is_enabled(self.node) then + return false + end + + local is_pick = true + local is_key_trigger = (action_id == self.key_trigger) + if not is_key_trigger then + is_pick = gui.pick_node(self.node, action.x, action.y) + if self.click_zone then + is_pick = is_pick and gui.pick_node(self.click_zone, action.x, action.y) + end + end + + if not is_pick then + -- Can't interact, if touch outside of button + self.can_action = false + return false + end + + if is_key_trigger then + self.hover:set_hover(not action.released) + end + + if action.pressed then + -- Can interact if start touch on the button + self.can_action = true + self.is_repeated_started = false + self.last_pressed_time = socket.gettime() + return true + end + + -- While hold button, repeat rate pick from input.repeat_interval + if action.repeated then + if not self.disabled and self.on_repeated_click:is_exist() and self.can_action then + on_button_repeated_click(self) + return true + end + end + + if action.released then + return on_button_release(self) + end + + if not self.disabled and self.can_action and self.on_long_click:is_exist() then + local press_time = socket.gettime() - self.last_pressed_time + + if self._style.AUTOHOLD_TRIGGER and self._style.AUTOHOLD_TRIGGER <= press_time then + on_button_release(self) + return true + end + + if press_time >= self._style.LONGTAP_TIME then + on_button_hold(self, press_time) + return true + end + end + + return not self.disabled +end + + +function M.on_input_interrupt(self) + self.can_action = false +end + + +--- Set enabled button component state +-- @function button:set_enabled +-- @tparam bool state Enabled state +function M.set_enabled(self, state) + self.disabled = not state + if self._style.on_set_enabled then + self._style.on_set_enabled(self, self.node, state) + end +end + + +--- Return button enabled state +-- @function button:is_enabled +-- @treturn bool True, if button is enabled +function M.is_enabled(self) + return not self.disabled +end + + +--- Strict button click area. Useful for +-- no click events outside stencil node +-- @function button:set_click_zone +-- @tparam node zone Gui node +function M.set_click_zone(self, zone) + self.click_zone = self:get_node(zone) + self.hover:set_click_zone(zone) +end + + +--- Set key-code to trigger this button +-- @function button:set_key_trigger +-- @tparam hash key The action_id of the key +function M.set_key_trigger(self, key) + self.key_trigger = hash(key) +end + + +--- Get key-code to trigger this button +-- @function button:get_key_trigger +-- @treturn hash The action_id of the key +function M.get_key_trigger(self) + return self.key_trigger +end + + +return M diff --git a/druid/base/checkbox.lua b/druid/base/checkbox.lua new file mode 100644 index 0000000..7770be9 --- /dev/null +++ b/druid/base/checkbox.lua @@ -0,0 +1,75 @@ +--- Druid checkbox component +-- @module druid.checkbox + +--- Component events +-- @table Events +-- @tfield druid_event on_change_state On change state callback + +--- Component fields +-- @table Fields +-- @tfield node node Visual node +-- @tfield[opt=node] node click_node Button trigger node +-- @tfield druid.button button Button component from click_node + +--- Component style params +-- @table Style +-- @tfield function on_change_state (self, node, state) + +local Event = require("druid.event") +local component = require("druid.component") + +local M = component.create("checkbox") + + +local function on_click(self) + M.set_state(self, not self.state) +end + + +--- Component init function +-- @function checkbox:init +-- @tparam node node Gui node +-- @tparam function callback Checkbox callback +-- @tparam[opt=node] node click node Trigger node, by default equals to node +function M.init(self, node, callback, click_node) + self.style = self:get_style() + self.druid = self:get_druid() + self.node = self:get_node(node) + self.click_node = self:get_node(click_node) + + self.button = self.druid:new_button(self.click_node or self.node, on_click) + M.set_state(self, false, true) + + self.on_change_state = Event(callback) +end + + +--- Set checkbox state +-- @function checkbox:set_state +-- @tparam bool state Checkbox state +-- @tparam bool is_silent Don't trigger on_change_state if true +function M.set_state(self, state, is_silent) + if self.state == state then + return + end + + self.state = state + if self.style.on_change_state then + self.style.on_change_state(self, self.node, state) + end + + if not is_silent then + self.on_change_state:trigger(self:get_context(), state) + end +end + + +--- Return checkbox state +-- @function checkbox:get_state +-- @treturn bool Checkbox state +function M.get_state(self) + return self.state +end + + +return M diff --git a/druid/base/checkbox_group.lua b/druid/base/checkbox_group.lua new file mode 100644 index 0000000..dd9a94a --- /dev/null +++ b/druid/base/checkbox_group.lua @@ -0,0 +1,66 @@ +--- Checkbox group module +-- @module druid.checkbox_group + +--- Component events +-- @table Events +-- @tfield druid_event on_checkbox_click On any checkbox click + +--- Component fields +-- @table Fields +-- @tfield table checkboxes Array of checkbox components + +local Event = require("druid.event") +local component = require("druid.component") + +local M = component.create("checkbox_group") + + +--- Component init function +-- @function checkbox_group:init +-- @tparam node[] node Array of gui node +-- @tparam function callback Checkbox callback +-- @tparam[opt=node] node[] click node Array of trigger nodes, by default equals to nodes +function M.init(self, nodes, callback, click_nodes) + self.druid = self:get_druid() + self.checkboxes = {} + + self.on_checkbox_click = Event(callback) + + for i = 1, #nodes do + local click_node = click_nodes and click_nodes[i] or nil + local checkbox = self.druid:new_checkbox(nodes[i], function() + self.on_checkbox_click:trigger(self:get_context(), i) + end, click_node) + + table.insert(self.checkboxes, checkbox) + end +end + + +--- Set checkbox group state +-- @function checkbox_group:set_state +-- @tparam bool[] state Array of checkbox state +function M.set_state(self, indexes) + for i = 1, #indexes do + if self.checkboxes[i] then + self.checkboxes[i]:set_state(indexes[i], true) + end + end +end + + +--- Return checkbox group state +-- @function checkbox_group:get_state +-- @treturn bool[] Array if checkboxes state +function M.get_state(self) + local result = {} + + for i = 1, #self.checkboxes do + table.insert(result, self.checkboxes[i]:get_state()) + end + + return result +end + + +return M diff --git a/druid/base/grid.lua b/druid/base/grid.lua new file mode 100644 index 0000000..86e0444 --- /dev/null +++ b/druid/base/grid.lua @@ -0,0 +1,166 @@ +--- 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 Event = require("druid.event") +local component = require("druid.component") + +local M = component.create("grid") + + +--- 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) + self.anchor = vmath.vector3(0.5, 0, 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) + 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 + + + +--- 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 all items from the grid +-- @function grid:clear +function M.clear(self) + for i = 1, #self.nodes do + gui.delete_node(self.nodes[i]) + end + self.nodes = {} +end + + +return M diff --git a/druid/base/hover.lua b/druid/base/hover.lua new file mode 100644 index 0000000..0fb916a --- /dev/null +++ b/druid/base/hover.lua @@ -0,0 +1,82 @@ +--- Component to handle hover node interaction +-- @module druid.hover + +--- Component events +-- @table Events +-- @tfield druid_event on_hover On hover callback + +local Event = require("druid.event") +local const = require("druid.const") +local helper = require("druid.helper") +local component = require("druid.component") + +local M = component.create("hover", { const.ON_INPUT }) + + +--- Component init function +-- @function hover:init +-- @tparam node node Gui node +-- @tparam function on_hover_callback Hover callback +function M.init(self, node, on_hover_callback) + self.style = self:get_style() + self.node = self:get_node(node) + + self._is_hovered = false + + self.on_hover = Event(on_hover_callback) +end + + +function M.on_input(self, action_id, action) + if action_id ~= const.ACTION_TOUCH then + return + end + + if not helper.is_enabled(self.node) then + return false + end + + local is_pick = gui.pick_node(self.node, action.x, action.y) + if self.click_zone then + is_pick = is_pick and gui.pick_node(self.click_zone, action.x, action.y) + end + + if not is_pick then + M.set_hover(self, false) + return false + end + + if action.released then + M.set_hover(self, false) + else + M.set_hover(self, true) + end +end + + +function M.on_input_interrupt(self) + M.set_hover(self, false) +end + + +--- Set hover state +-- @function hover:set_hover +-- @tparam bool state The hover state +function M.set_hover(self, state) + if self._is_hovered ~= state then + self._is_hovered = state + self.on_hover:trigger(self:get_context(), state) + end +end + + +--- Strict button click area. Useful for +-- no click events outside stencil node +-- @function hover:set_click_zone +-- @tparam node zone Gui node +function M.set_click_zone(self, zone) + self.click_zone = self:get_node(zone) +end + + +return M diff --git a/druid/base/input.lua b/druid/base/input.lua new file mode 100644 index 0000000..603eb71 --- /dev/null +++ b/druid/base/input.lua @@ -0,0 +1,16 @@ +--- Druid input text component. +-- Carry on user text input +-- UNIMPLEMENTED +-- @module druid.input + +local component = require("druid.component") + +local M = component.create("input") + + +function M.init(self, node, callback, click_node) + self.style = self:get_style() +end + + +return M diff --git a/druid/base/lang_text.lua b/druid/base/lang_text.lua new file mode 100644 index 0000000..5c81c03 --- /dev/null +++ b/druid/base/lang_text.lua @@ -0,0 +1,64 @@ +--- Component to handle all GUI texts +-- Good working with localization system +-- @module druid.lang_text + +--- Component events +-- @table Events +-- @tfield druid_event on_change On change text callback + +--- Component fields +-- @table Fields +-- @tfield druid.text text The text component + +local Event = require("druid.event") +local const = require("druid.const") +local settings = require("druid.system.settings") +local component = require("druid.component") + +local M = component.create("lang_text", { const.ON_CHANGE_LANGUAGE }) + + +--- Component init function +-- @function lang_text:init +-- @tparam node node The text node +-- @tparam string locale_id Default locale id +-- @tparam bool no_adjust If true, will not correct text size +function M.init(self, node, locale_id, no_adjust) + self.druid = self:get_druid() + self.text = self.druid:new_text(node, locale_id, no_adjust) + + self.on_change = Event() + + self:translate(locale_id) + + return self +end + + +function M.on_change_language(self) + if self.last_locale then + M.translate(self) + end +end + + +--- Setup raw text to lang_text component +-- @function lang_text:set_to +-- @tparam string text Text for text node +function M.set_to(self, text) + self.last_locale = false + self.text:set_to(text) + self.on_change:trigger() +end + + +--- Translate the text by locale_id +-- @function lang_text:translate +-- @tparam string locale_id Locale id +function M.translate(self, locale_id) + self.last_locale = locale_id or self.last_locale + self.text:set_to(settings.get_text(self.last_locale)) +end + + +return M diff --git a/druid/base/progress.lua b/druid/base/progress.lua new file mode 100644 index 0000000..2aa21e8 --- /dev/null +++ b/druid/base/progress.lua @@ -0,0 +1,181 @@ +--- Basic progress bar component. +-- For correct progress bar init it should be in max size from gui +-- @module druid.progress + +--- Component events +-- @table Events +-- @tfield druid_event on_change On progress bar change callback + +--- Component fields +-- @table Fields +-- @tfield node node Progress bar fill node +-- @tfield string key The progress bar direction +-- @tfield vector3 scale Current progress bar scale +-- @tfield vector3 size Current progress bar size +-- @tfield number max_size Maximum size of progress bar +-- @tfield vector4 slice Progress bar slice9 settings + +--- Component style params +-- @table Style +-- @tfield number SPEED Progress bas fill rate. More -> faster +-- @tfield number MIN_DELTA Minimum step to fill progress bar + +local Event = require("druid.event") +local const = require("druid.const") +local helper = require("druid.helper") +local component = require("druid.component") + +local M = component.create("progress", { const.ON_UPDATE }) + + +local function check_steps(self, from, to, exactly) + if not self.steps then + return + end + + for i = 1, #self.steps do + local step = self.steps[i] + local v1, v2 = from, to + if v1 > v2 then + v1, v2 = v2, v1 + end + + if v1 < step and step < v2 then + self.step_callback(self:get_context(), step) + end + if exactly and exactly == step then + self.step_callback(self:get_context(), step) + end + end +end + + +local function set_bar_to(self, set_to, is_silent) + local prev_value = self.last_value + self.last_value = set_to + + local total_width = set_to * self.max_size + + local scale = math.min(total_width / self.slice_size, 1) + local size = math.max(total_width, self.slice_size) + + self.scale[self.key] = scale + gui.set_scale(self.node, self.scale) + self.size[self.key] = size + gui.set_size(self.node, self.size) + + if not is_silent then + check_steps(self, prev_value, set_to) + end +end + + +--- Component init function +-- @function progress:init +-- @tparam string|node node Progress bar fill node or node name +-- @tparam string key Progress bar direction: const.SIDE.X or const.SIDE.Y +-- @tparam number init_value Initial value of progress bar +function M.init(self, node, key, init_value) + assert(key == const.SIDE.X or const.SIDE.Y, "Progress bar key should be 'x' or 'y'") + + self.prop = hash("scale."..key) + self.key = key + + self.style = self:get_style() + self.node = self:get_node(node) + self.scale = gui.get_scale(self.node) + self.size = gui.get_size(self.node) + self.max_size = self.size[self.key] + self.slice = gui.get_slice9(self.node) + if key == const.SIDE.X then + self.slice_size = self.slice.x + self.slice.z + else + self.slice_size = self.slice.y + self.slice.w + end + + self.on_change = Event() + + self:set_to(init_value or 1) +end + + +function M.update(self, dt) + if self.target then + local prev_value = self.last_value + local step = math.abs(self.last_value - self.target) * (self.style.SPEED*dt) + step = math.max(step, self.style.MIN_DELTA) + self:set_to(helper.step(self.last_value, self.target, step)) + + if self.last_value == self.target then + check_steps(self, prev_value, self.target, self.target) + + if self.target_callback then + self.target_callback(self:get_context(), self.target) + end + + self.target = nil + end + end +end + + +--- Fill a progress bar and stop progress animation +-- @function progress:fill +function M.fill(self) + set_bar_to(self, 1, true) +end + + +--- Empty a progress bar +-- @function progress:empty +function M.empty(self) + set_bar_to(self, 0, true) +end + + +--- Instant fill progress bar to value +-- @function progress:set_to +-- @tparam number to Progress bar value, from 0 to 1 +function M.set_to(self, to) + set_bar_to(self, to) +end + + +--- Return current progress bar value +-- @function progress:get +function M.get(self) + return self.last_value +end + + +--- Set points on progress bar to fire the callback +-- @function progress:set_steps +-- @tparam number[] steps Array of progress bar values +-- @tparam function callback Callback on intersect step value +-- @usage progress:set_steps({0, 0.3, 0.6, 1}, function(self, step) end) +function M.set_steps(self, steps, callback) + self.steps = steps + self.step_callback = callback +end + + +--- Start animation of a progress bar +-- @function progress:to +-- @tparam number to value between 0..1 +-- @tparam[opt] function callback Callback on animation ends +function M.to(self, to, callback) + to = helper.clamp(to, 0, 1) + -- cause of float error + local value = helper.round(to, 5) + if value ~= self.last_value then + self.target = value + self.target_callback = callback + else + if callback then + callback(self:get_context(), to) + end + end +end + + +return M diff --git a/druid/base/radio_group.lua b/druid/base/radio_group.lua new file mode 100644 index 0000000..a446d02 --- /dev/null +++ b/druid/base/radio_group.lua @@ -0,0 +1,74 @@ +--- Radio group module +-- @module druid.radio_group + +--- Component events +-- @table Events +-- @tfield druid_event on_radio_click On any checkbox click + +--- Component fields +-- @table Fields +-- @tfield table checkboxes Array of checkbox components + +local Event = require("druid.event") +local component = require("druid.component") + +local M = component.create("radio_group") + + +local function on_checkbox_click(self, index) + for i = 1, #self.checkboxes do + self.checkboxes[i]:set_state(i == index, true) + end + + self.on_radio_click:trigger(self:get_context(), index) +end + + +--- Component init function +-- @function radio_group:init +-- @tparam node[] node Array of gui node +-- @tparam function callback Radio callback +-- @tparam[opt=node] node[] click node Array of trigger nodes, by default equals to nodes +function M.init(self, nodes, callback, click_nodes) + self.druid = self:get_druid() + self.checkboxes = {} + + self.on_radio_click = Event(callback) + + for i = 1, #nodes do + local click_node = click_nodes and click_nodes[i] or nil + local checkbox = self.druid:new_checkbox(nodes[i], function() + on_checkbox_click(self, i) + end, click_node) + + table.insert(self.checkboxes, checkbox) + end +end + + +--- Set radio group state +-- @function radio_group:set_state +-- @tparam bool[] state Array of checkbox state +function M.set_state(self, index) + on_checkbox_click(self, index) +end + + +--- Return radio group state +-- @function radio_group:get_state +-- @treturn bool[] Array if checkboxes state +function M.get_state(self) + local result = -1 + + for i = 1, #self.checkboxes do + if self.checkboxes[i]:get_state() then + result = i + break + end + end + + return result +end + + +return M diff --git a/druid/base/scroll.lua b/druid/base/scroll.lua new file mode 100644 index 0000000..4a437c2 --- /dev/null +++ b/druid/base/scroll.lua @@ -0,0 +1,538 @@ +--- Component to handle scroll content. +-- Scroll consist from two nodes: scroll parent and scroll input +-- Scroll input the user input zone, it's static +-- Scroll parent the scroll moving part, it will change position. +-- Setup initial scroll size by changing scroll parent size. If scroll parent +-- size will be less than scroll_input size, no scroll is available. For scroll +-- parent size should be more than input size +-- @module druid.scroll + +--- Component events +-- @table Events +-- @tfield druid_event on_scroll On scroll move callback +-- @tfield druid_event on_scroll_to On scroll_to function callback +-- @tfield druid_event on_point_scroll On scroll_to_index function callbck + +--- Component fields +-- @table Fields +-- @tfield node node Scroll parent node +-- @tfield node input_zone Scroll input node +-- @tfield vector3 zone_size Current scroll content size +-- @tfield number soft_size Soft zone size from style table +-- @tfield vector3 center_offset Distance from node to node's center +-- @tfield bool is_inert Flag, if scroll now moving by inertion +-- @tfield vector3 inert Current inert speed +-- @tfield vector3 pos Current scroll posisition +-- @tfield vector3 target Current scroll target position + +--- Component style params +-- @table Style +-- @tfield number FRICT_HOLD Multiplier for inertion, while touching +-- @tfield number FRICT Multiplier for free inertion +-- @tfield number INERT_THRESHOLD Scroll speed to stop inertion +-- @tfield number INERT_SPEED Multiplier for inertion speed +-- @tfield number DEADZONE Deadzone for start scrol in pixels +-- @tfield number SOFT_ZONE_SIZE Size of outside zone in pixels (for scroll back moving) +-- @tfield number BACK_SPEED Scroll back returning lerp speed +-- @tfield number ANIM_SPEED Scroll gui.animation speed for scroll_to function + +local Event = require("druid.event") +local helper = require("druid.helper") +local const = require("druid.const") +local component = require("druid.component") + +local M = component.create("scroll", { const.ON_UPDATE, const.ON_INPUT_HIGH }) + + +-- Global on all scrolls +-- TODO: remove it +M.current_scroll = nil + + +local function get_border(node) + local pivot = gui.get_pivot(node) + local pivot_offset = helper.get_pivot_offset(pivot) + local size = vmath.mul_per_elem(gui.get_size(node), gui.get_scale(node)) + return vmath.vector4( + -size.x*(0.5 + pivot_offset.x), + size.y*(0.5 + pivot_offset.y), + size.x*(0.5 - pivot_offset.x), + -size.y*(0.5 - pivot_offset.y) + ) +end + + +local function update_border(self) + local input_border = get_border(self.input_zone) + local content_border = get_border(self.node) + + -- border.x - min content.x node pos + -- border.y - min content.y node pos + -- border.z - max content.x node pos + -- border.w - max content.y node pos + self.border = vmath.vector4( + input_border.x - content_border.x, + -input_border.w + content_border.w, + input_border.z - content_border.z, + -input_border.y + content_border.y + ) + self.can_x = (self.border.x ~= self.border.z) + self.can_y = (self.border.y ~= self.border.w) +end + + + +local function set_pos(self, pos) + if self.pos.x ~= pos.x or self.pos.y ~= pos.y then + self.pos.x = pos.x + self.pos.y = pos.y + gui.set_position(self.node, self.pos) + + self.on_scroll:trigger(self:get_context(), self.pos) + end +end + + +--- Return scroll, if it outside of scroll area +-- Using the lerp with BACK_SPEED koef +local function check_soft_target(self) + local t = self.target + local b = self.border + + if t.y < b.y then + t.y = helper.step(t.y, b.y, math.abs(t.y - b.y) * self.style.BACK_SPEED) + end + if t.x > b.x then + t.x = helper.step(t.x, b.x, math.abs(t.x - b.x) * self.style.BACK_SPEED) + end + if t.y > b.w then + t.y = helper.step(t.y, b.w, math.abs(t.y - b.w) * self.style.BACK_SPEED) + end + if t.x < b.z then + t.x = helper.step(t.x, b.z, math.abs(t.x - b.z) * self.style.BACK_SPEED) + end +end + + +--- Free inert update function +local function update_hand_scroll(self, dt) + local inert = self.inert + local delta_x = self.target.x - self.pos.x + local delta_y = self.target.y - self.pos.y + + if helper.sign(delta_x) ~= helper.sign(inert.x) then + inert.x = 0 + end + if helper.sign(delta_y) ~= helper.sign(inert.y) then + inert.y = 0 + end + + inert.x = inert.x + delta_x + inert.y = inert.y + delta_y + + inert.x = math.abs(inert.x) * helper.sign(delta_x) + inert.y = math.abs(inert.y) * helper.sign(delta_y) + + inert.x = inert.x * self.style.FRICT_HOLD + inert.y = inert.y * self.style.FRICT_HOLD + + set_pos(self, self.target) +end + + +local function get_zone_center(self) + return self.pos + self.center_offset +end + + +--- Find closer point of interest +-- if no inert, scroll to next point by scroll direction +-- if inert, find next point by scroll director +-- @local +local function check_points(self) + if not self.points then + return + end + + local inert = self.inert + if not self.is_inert then + if math.abs(inert.x) > self.style.DEADZONE then + self:scroll_to_index(self.selected - helper.sign(inert.x)) + return + end + if math.abs(inert.y) > self.style.DEADZONE then + self:scroll_to_index(self.selected + helper.sign(inert.y)) + return + end + end + + -- Find closest point and point by scroll direction + -- Scroll to one of them (by scroll direction in priority) + local temp_dist = math.huge + local temp_dist_on_inert = math.huge + local index = false + local index_on_inert = false + local pos = get_zone_center(self) + for i = 1, #self.points do + local p = self.points[i] + local dist = helper.distance(pos.x, pos.y, p.x, p.y) + local on_inert = true + -- If inert ~= 0, scroll only by move direction + if inert.x ~= 0 and helper.sign(inert.x) ~= helper.sign(p.x - pos.x) then + on_inert = false + end + if inert.y ~= 0 and helper.sign(inert.y) ~= helper.sign(p.y - pos.y) then + on_inert = false + end + + if dist < temp_dist then + index = i + temp_dist = dist + end + if on_inert and dist < temp_dist_on_inert then + index_on_inert = i + temp_dist_on_inert = dist + end + end + + self:scroll_to_index(index_on_inert or index) +end + + +local function check_threshold(self) + local inert = self.inert + if not self.is_inert or vmath.length(inert) < self.style.INERT_THRESHOLD then + check_points(self) + inert.x = 0 + inert.y = 0 + end +end + + +local function update_free_inert(self, dt) + local inert = self.inert + if inert.x ~= 0 or inert.y ~= 0 then + self.target.x = self.pos.x + (inert.x * dt * self.style.INERT_SPEED) + self.target.y = self.pos.y + (inert.y * dt * self.style.INERT_SPEED) + + inert.x = inert.x * self.style.FRICT + inert.y = inert.y * self.style.FRICT + + -- Stop, when low inert speed and go to points + check_threshold(self) + end + + check_soft_target(self) + set_pos(self, self.target) +end + + +--- Cancel animation on other animation or input touch +local function cancel_animate(self) + if self.animate then + self.target = gui.get_position(self.node) + self.pos.x = self.target.x + self.pos.y = self.target.y + gui.cancel_animation(self.node, gui.PROP_POSITION) + self.animate = false + end +end + + +local function add_delta(self, dx, dy) + local t = self.target + local b = self.border + local soft = self.soft_size + + -- TODO: Can we calc it more easier? + -- A lot of calculations for every side of border + + -- Handle soft zones + -- Percent - multiplier for delta. Less if outside of scroll zone + local x_perc = 1 + local y_perc = 1 + + if t.x > b.x and dx < 0 then + x_perc = (soft - (b.x - t.x)) / soft + end + if t.x < b.z and dx > 0 then + x_perc = (soft - (t.x - b.z)) / soft + end + -- If disabled scroll by x + if not self.can_x then + x_perc = 0 + end + + if t.y < b.y and dy < 0 then + y_perc = (soft - (b.y - t.y)) / soft + end + if t.y > b.w and dy > 0 then + y_perc = (soft - (t.y - b.w)) / soft + end + -- If disabled scroll by y + if not self.can_y then + y_perc = 0 + end + + -- Reset inert if outside of scroll zone + if x_perc ~= 1 then + self.inert.x = 0 + end + if y_perc ~= 1 then + self.inert.y = 0 + end + + t.x = t.x + dx * x_perc + t.y = t.y + dy * y_perc +end + + +--- Component init function +-- @function scroll:init +-- @tparam node scroll_parent Gui node where placed scroll content. This node will change position +-- @tparam node input_zone Gui node where input is catched +function M.init(self, scroll_parent, input_zone) + self.style = self:get_style() + self.node = self:get_node(scroll_parent) + self.input_zone = self:get_node(input_zone) + + self.zone_size = gui.get_size(self.input_zone) + self.soft_size = self.style.SOFT_ZONE_SIZE + + -- Distance from node to node's center + local offset = helper.get_pivot_offset(gui.get_pivot(self.input_zone)) + self.center_offset = vmath.vector3(self.zone_size) + self.center_offset.x = self.center_offset.x * offset.x + self.center_offset.y = self.center_offset.y * offset.y + + self.is_inert = true + self.inert = vmath.vector3(0) + self.pos = gui.get_position(self.node) + self.target = vmath.vector3(self.pos) + + self.input = { + touch = false, + start_x = 0, + start_y = 0, + side = false, + } + + update_border(self) + + self.on_scroll = Event() + self.on_scroll_to = Event() + self.on_point_scroll = Event() +end + + +function M.update(self, dt) + if self.input.touch then + if M.current_scroll == self then + update_hand_scroll(self, dt) + end + else + update_free_inert(self, dt) + end +end + + +function M.on_input(self, action_id, action) + if action_id ~= const.ACTION_TOUCH then + return false + end + local inp = self.input + local inert = self.inert + local result = false + + if gui.pick_node(self.input_zone, action.x, action.y) then + if action.pressed then + inp.touch = true + inp.start_x = action.x + inp.start_y = action.y + inert.x = 0 + inert.y = 0 + self.target.x = self.pos.x + self.target.y = self.pos.y + else + local dist = helper.distance(action.x, action.y, inp.start_x, inp.start_y) + if not M.current_scroll and dist >= self.style.DEADZONE then + local dx = math.abs(inp.start_x - action.x) + local dy = math.abs(inp.start_y - action.y) + inp.side = (dx > dy) and const.SIDE.X or const.SIDE.Y + + -- Check scroll side if we can scroll + if (self.can_x and inp.side == const.SIDE.X or + self.can_y and inp.side == const.SIDE.Y) then + M.current_scroll = self + end + end + end + end + + if inp.touch and not action.pressed then + if M.current_scroll == self then + add_delta(self, action.dx, action.dy) + result = true + end + end + + if action.released then + inp.touch = false + inp.side = false + if M.current_scroll == self then + M.current_scroll = nil + result = true + end + + check_threshold(self) + end + + return result +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.border + local target = vmath.vector3(point) + target.x = helper.clamp(point.x - self.center_offset.x, b.x, b.z) + target.y = helper.clamp(point.y - self.center_offset.y, b.y, b.w) + + cancel_animate(self) + + self.animate = not is_instant + + if is_instant then + self.target = target + set_pos(self, target) + else + gui.animate(self.node, gui.PROP_POSITION, target, gui.EASING_OUTSINE, self.style.ANIM_SPEED, 0, function() + self.animate = false + self.target = target + set_pos(self, target) + end) + end + + self.on_scroll_to:trigger(self:get_context(), target, is_instant) +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.border + + local size_x = math.abs(border.z - border.x) + if size_x == 0 then + size_x = 1 + end + local size_y = math.abs(border.w - border.y) + if size_y == 0 then + size_y = 1 + end + + local pos = vmath.vector3( + -size_x * percent.x + border.x, + -size_y * percent.y + border.y, + 0) + M.scroll_to(self, pos, is_instant) +end + + +--- Scroll to item in scroll by point index +-- @function scroll:init +-- @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) + 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 + + +--- Set points of interest. +-- Scroll will always centered on closer points +-- @function scroll:set_points +-- @tparam table points Array of vector3 points +function M.set_points(self, points) + self.points = points + -- cause of parent move in other side by y + for i = 1, #self.points do + self.points[i].y = -self.points[i].y + end + + table.sort(self.points, function(a, b) + return a.x > b.x or a.y < b.y + end) + check_threshold(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 +function M.set_inert(self, state) + self.is_inert = state +end + + +--- Set the callback on scrolling to point (if exist) +-- @function scroll:on_point_move +-- @tparam function callback Callback on scroll to point of interest +function M.on_point_move(self, callback) + self.on_point_scroll:subscribe(callback) +end + + +--- Set the scroll possibly area +-- @function scroll:set_border +-- @tparam vector3 border Size of scrolling area +function M.set_border(self, content_size) + gui.set_size(self.node, content_size) + update_border(self) +end + + +--- Return current scroll progress +-- @function scroll:get_scroll_percent +-- @treturn vector3 Scroll progress +function M.get_scroll_percent(self) + local border = self.border + local size_x = math.abs(border.z - border.x) + if size_x == 0 then + size_x = 1 + end + + local size_y = math.abs(border.w - border.y) + if size_y == 0 then + size_y = 1 + end + local pos = self.pos + + return vmath.vector3( + (border.x - pos.x) / size_x, + (border.y - pos.y) / size_y, + 0 + ) +end + + +return M diff --git a/druid/base/slider.lua b/druid/base/slider.lua new file mode 100644 index 0000000..f5b8f13 --- /dev/null +++ b/druid/base/slider.lua @@ -0,0 +1,118 @@ +--- Druid slider component +-- @module druid.slider + +--- Component events +-- @table Events +-- @tfield druid_event on_change_value On change value callback + +--- Component fields +-- @table Fields +-- @tfield node node Slider pin node +-- @tfield vector3 start_pos Start pin node position +-- @tfield vector3 pos Current pin node position +-- @tfield vector3 target_pos Targer pin node position +-- @tfield vector3 end_pos End pin node position +-- @tfield number dist Length between start and end position +-- @tfield bool is_drag Current drag state +-- @tfield number value Current slider value + + +local Event = require("druid.event") +local helper = require("druid.helper") +local const = require("druid.const") +local component = require("druid.component") + +local M = component.create("slider", { const.ON_INPUT_HIGH }) + + +local function on_change_value(self) + self.on_change_value:trigger(self:get_context(), self.value) +end + + +--- Component init function +-- @function slider:init +-- @tparam node node Gui pin node +-- @tparam vector3 end_pos The end position of slider +-- @tparam[opt] function callback On slider change callback +function M.init(self, node, end_pos, callback) + self.node = self:get_node(node) + + self.start_pos = gui.get_position(self.node) + self.pos = gui.get_position(self.node) + self.target_pos = self.pos + self.end_pos = end_pos + + self.dist = self.end_pos - self.start_pos + self.is_drag = false + self.value = 0 + + self.on_change_value = Event(callback) + + assert(self.dist.x == 0 or self.dist.y == 0, "Slider for now can be only vertical or horizontal") +end + + +function M.on_input(self, action_id, action) + if action_id ~= const.ACTION_TOUCH then + return false + end + + if gui.pick_node(self.node, action.x, action.y) then + if action.pressed then + self.pos = gui.get_position(self.node) + self.is_drag = true + end + end + + if self.is_drag and not action.pressed then + -- move + self.pos.x = self.pos.x + action.dx + self.pos.y = self.pos.y + action.dy + + local prev_x = self.target_pos.x + local prev_y = self.target_pos.y + + self.target_pos.x = helper.clamp(self.pos.x, self.start_pos.x, self.end_pos.x) + self.target_pos.y = helper.clamp(self.pos.y, self.start_pos.y, self.end_pos.y) + + gui.set_position(self.node, self.target_pos) + + if prev_x ~= self.target_pos.x or prev_y ~= self.target_pos.y then + + if self.dist.x > 0 then + self.value = (self.target_pos.x - self.start_pos.x) / self.dist.x + end + + if self.dist.y > 0 then + self.value = (self.target_pos.y - self.start_pos.y) / self.dist.y + end + + on_change_value(self) + end + end + + if action.released then + self.is_drag = false + end + + return self.is_drag +end + + +--- Set value for slider +-- @function slider:set +-- @tparam number value Value from 0 to 1 +-- @tparam[opt] bool is_silent Don't trigger event if true +function M.set(self, value, is_silent) + value = helper.clamp(value, 0, 1) + + gui.set_position(self.node, self.start_pos + self.dist * value) + self.value = value + if not is_silent then + on_change_value(self) + end +end + + +return M diff --git a/druid/base/text.lua b/druid/base/text.lua new file mode 100644 index 0000000..436a4c7 --- /dev/null +++ b/druid/base/text.lua @@ -0,0 +1,162 @@ +--- Component to handle all GUI texts. +-- Druid text can adjust itself for text node size +-- Text will never will be outside of his text size (even multiline) +-- @module druid.text + +--- Component events +-- @table Events +-- @tfield druid_event on_set_text On set text callback +-- @tfield druid_event on_update_text_scale On adjust text size callback +-- @tfield druid_event on_set_pivot On change pivot callback + +--- Component fields +-- @table Fields +-- @tfield node node Text node +-- @tfield vector3 pos Current text position +-- @tfield vector3 start_scale Initial text node scale +-- @tfield vector3 scale Current text node scale +-- @tfield vector3 start_size Initial text node size +-- @tfield vector3 text_area Current text node available are +-- @tfield bool is_no_adjust Current text size adjust settings +-- @tfield vector3 color Current text color + +local Event = require("druid.event") +local const = require("druid.const") +local component = require("druid.component") + +local M = component.create("text") + + +local function update_text_size(self) + local size = vmath.vector3( + self.start_size.x * (self.start_scale.x / self.scale.x), + self.start_size.y * (self.start_scale.y / self.scale.y), + self.start_size.z + ) + gui.set_size(self.node, size) +end + + +--- Setup scale x, but can only be smaller, than start text scale +local function update_text_area_size(self) + gui.set_scale(self.node, self.start_scale) + gui.set_size(self.node, self.start_size) + + local max_width = self.text_area.x + local max_height = self.text_area.y + + local metrics = gui.get_text_metrics_from_node(self.node) + local cur_scale = gui.get_scale(self.node) + + local scale_modifier = max_width / metrics.width + scale_modifier = math.min(scale_modifier, self.start_scale.x) + + local scale_modifier_height = max_height / metrics.height + scale_modifier = math.min(scale_modifier, scale_modifier_height) + + local new_scale = vmath.vector3(scale_modifier, scale_modifier, cur_scale.z) + gui.set_scale(self.node, new_scale) + self.scale = new_scale + + update_text_size(self) + + self.on_update_text_scale:trigger(self:get_context(), new_scale) +end + + +--- Component init function +-- @function text:init +-- @tparam node node Gui text node +-- @tparam[opt] string value Initial text +-- @tparam[opt] bool no_adjust If true, text will be not auto-adjust size +function M.init(self, node, value, no_adjust) + self.node = self:get_node(node) + self.pos = gui.get_position(self.node) + + self.start_scale = gui.get_scale(self.node) + self.scale = gui.get_scale(self.node) + + self.start_size = gui.get_size(self.node) + self.text_area = gui.get_size(self.node) + self.text_area.x = self.text_area.x * self.start_scale.x + self.text_area.y = self.text_area.y * self.start_scale.y + + self.is_no_adjust = no_adjust + self.color = gui.get_color(self.node) + + self.on_set_text = Event() + self.on_update_text_scale = Event() + self.on_set_pivot = Event() + + self:set_to(value or 0) + return self +end + + +--- Set text to text field +-- @function text:set_to +-- @tparam string set_to Text for node +function M.set_to(self, set_to) + self.last_value = set_to + gui.set_text(self.node, set_to) + + self.on_set_text:trigger(self:get_context(), set_to) + + if not self.is_no_adjust then + update_text_area_size(self) + end +end + + +--- Set color +-- @function text:set_color +-- @tparam vector4 color Color for node +function M.set_color(self, color) + self.color = color + gui.set_color(self.node, color) +end + + +--- Set alpha +-- @function text:set_alpha +-- @tparam number alpha Alpha for node +function M.set_alpha(self, alpha) + self.color.w = alpha + gui.set_color(self.node, self.color) +end + + +--- Set scale +-- @function text:set_scale +-- @tparam vector3 scale Scale for node +function M.set_scale(self, scale) + self.last_scale = scale + gui.set_scale(self.node, scale) +end + + +--- Set text pivot. Text will re-anchor inside +-- his text area +-- @function text:set_pivot +-- @tparam gui.pivot pivot Gui pivot constant +function M.set_pivot(self, pivot) + local prev_pivot = gui.get_pivot(self.node) + local prev_offset = const.PIVOTS[prev_pivot] + + gui.set_pivot(self.node, pivot) + local cur_offset = const.PIVOTS[pivot] + + local pos_offset = vmath.vector3( + self.text_area.x * (cur_offset.x - prev_offset.x), + self.text_area.y * (cur_offset.y - prev_offset.y), + 0 + ) + + self.pos = self.pos + pos_offset + gui.set_position(self.node, self.pos) + + self.on_set_pivot:trigger(self:get_context(), pivot) +end + + +return M diff --git a/druid/base/timer.lua b/druid/base/timer.lua new file mode 100644 index 0000000..712dc93 --- /dev/null +++ b/druid/base/timer.lua @@ -0,0 +1,113 @@ +--- Component to handle GUI timers. +-- Timer updating by game delta time. If game is not focused - +-- timer will be not updated. +-- @module druid.timer + +--- Component events +-- @table Events +-- @tfield druid_event on_tick On timer tick callback. Fire every second +-- @tfield druid_event on_set_enabled On timer change enabled state callback +-- @tfield druid_event on_timer_end On timer end callback + +--- Component fields +-- @table Fields +-- @tfield node node Trigger node +-- @tfield[opt=node] node anim_node Animation node +-- @tfield vector3 scale_from Initial scale of anim_node +-- @tfield vector3 pos Initial pos of anim_node +-- @tfield any params Params to click callbacks +-- @tfield druid.hover hover Druid hover logic component +-- @tfield[opt] node click_zone Restriction zone + +local Event = require("druid.event") +local const = require("druid.const") +local formats = require("druid.helper.formats") +local helper = require("druid.helper") +local component = require("druid.component") + +local M = component.create("timer", { const.ON_UPDATE }) + + +--- Component init function +-- @function timer:init +-- @tparam node node Gui text node +-- @tparam number seconds_from Start timer value in seconds +-- @tparam[opt=0] number seconds_to End timer value in seconds +-- @tparam[opt] function callback Function on timer end +function M.init(self, node, seconds_from, seconds_to, callback) + self.node = self:get_node(node) + seconds_from = math.max(seconds_from, 0) + seconds_to = math.max(seconds_to or 0, 0) + + self.on_tick = Event() + self.on_set_enabled = Event() + self.on_timer_end = Event(callback) + + self:set_to(seconds_from) + self:set_interval(seconds_from, seconds_to) + + if seconds_to - seconds_from == 0 then + self:set_state(false) + self.on_timer_end:trigger(self:get_context(), self) + end + + return self +end + + +function M.update(self, dt) + if not self.is_on then + return + end + + self.temp = self.temp + dt + local dist = math.min(1, math.abs(self.value - self.target)) + + if self.temp > dist then + self.temp = self.temp - dist + self.value = helper.step(self.value, self.target, 1) + M.set_to(self, self.value) + + self.on_tick:trigger(self:get_context(), self.value) + + if self.value == self.target then + self:set_state(false) + self.on_timer_end:trigger(self:get_context(), self) + end + end +end + +--- Set text to text field +-- @function timer:set_to +-- @tparam number set_to Value in seconds +function M.set_to(self, set_to) + self.last_value = set_to + gui.set_text(self.node, formats.second_string_min(set_to)) +end + + +--- Called when update +-- @function timer:set_state +-- @tparam bool is_on Timer enable state +function M.set_state(self, is_on) + self.is_on = is_on + + self.on_set_enabled:trigger(self:get_context(), is_on) +end + + +--- Set time interval +-- @function timer:set_interval +-- @tparam number from Start time in seconds +-- @tparam number to Target time in seconds +function M.set_interval(self, from, to) + self.from = from + self.value = from + self.temp = 0 + self.target = to + M.set_state(self, true) + M.set_to(self, from) +end + + +return M \ No newline at end of file diff --git a/druid/component.lua b/druid/component.lua new file mode 100644 index 0000000..0e17adf --- /dev/null +++ b/druid/component.lua @@ -0,0 +1,171 @@ +--- Basic class for all Druid components. +-- To create you component, use `component.create` +-- @module component + +local const = require("druid.const") +local class = require("druid.system.middleclass") + +-- @classmod Component +local Component = class("druid.component") + + +--- Get current component style table +-- @function component:get_style +-- @treturn table Component style table +function Component.get_style(self) + if not self._meta.style then + return const.EMPTY_TABLE + end + + return self._meta.style[self._component.name] or const.EMPTY_TABLE +end + + +--- Set current component style table +-- @function component:set_style +-- @tparam table style Druid style module +function Component.set_style(self, druid_style) + self._meta.style = druid_style + self._style = self:get_style() +end + + +--- Get current component template name +-- @function component:get_template +-- @treturn string Component template name +function Component.get_template(self) + return self._meta.template +end + + +--- Set current component template name +-- @function component:set_template +-- @tparam string template Component template name +function Component.set_template(self, template) + self._meta.template = template +end + + +--- Get current component nodes +-- @function component:get_nodes +-- @treturn table Component nodes table +function Component.get_nodes(self) + return self._meta.nodes +end + + +--- Set current component nodes +-- @function component:set_nodes +-- @tparam table nodes Component nodes table +function Component.set_nodes(self, nodes) + self._meta.nodes = nodes +end + + +--- Get current component context +-- @function component:get_context +-- @treturn table Component context +function Component.get_context(self, context) + return self._meta.context +end + + +--- Set current component context +-- @function component:set_context +-- @tparam table context Druid context. Usually it is self of script +function Component.set_context(self, context) + self._meta.context = context +end + + +--- Get current component interests +-- @function component:get_interests +-- @treturn table List of component interests +function Component.get_interests(self) + return self._component.interest +end + + +-- TODO: Определиться с get_node и node +-- get_node - берет ноду по ноде или строке +-- node - может брать ноду у компонента по схеме (если есть +-- template или таблица нод после gui.clone_tree) +function Component.get_node(self, node_or_name) + local template_name = self:get_template() or const.EMPTY_STRING + local nodes = self:get_nodes() + + if nodes then + assert(type(node_or_name) == "strings", "You should pass node name instead of node") + return nodes[template_name .. node_or_name] + else + if type(node_or_name) == const.STRING then + return gui.get_node(template_name .. node_or_name) + else + return node_or_name + end + end +end + + +--- Return druid with context of calling component. +-- Use it to create component inside of other components. +-- @function component:get_druid +-- @treturn Druid Druid instance with component context +function Component.get_druid(self) + local context = { _context = self } + return setmetatable(context, { __index = self:get_context().druid }) +end + + +--- Setup component context and his style table +-- @function component:setup_component +-- @tparam context table Druid context. Usually it is self of script +-- @tparam style table Druid style module +-- @treturn Component Component itself +function Component.setup_component(self, context, style) + self._meta = { + template = nil, + context = nil, + nodes = nil, + style = nil, + } + + self:set_context(context) + self:set_style(style) + + return self +end + + +--- Basic constructor of component. It will call automaticaly +-- by `Component.static.create` +-- @function component:initialize +-- @tparam string name Component name +-- @tparam table interest List of component's interest +-- @local +function Component.initialize(self, name, interest) + self._component = { + name = name, + interest = interest + } +end + + +--- Create new component. It will inheritance from basic +-- druid component. +-- @function Component.create +-- @tparam string name Component name +-- @tparam table interest List of component's interest +function Component.static.create(name, interest) + -- Yea, inheritance here + local new_class = class(name, Component) + + new_class.initialize = function(self) + Component.initialize(self, name, interest) + end + + return new_class +end + + +return Component diff --git a/druid/components/andr_back_btn.lua b/druid/components/andr_back_btn.lua deleted file mode 100644 index a237675..0000000 --- a/druid/components/andr_back_btn.lua +++ /dev/null @@ -1,11 +0,0 @@ -local M = {} - ---- input handler --- @param action_id - input action id --- @param action - input action -function M.on_input(instance, action_id, action) - instance.callback(instance.parent.parent) - return true -end - -return M diff --git a/druid/components/button.lua b/druid/components/button.lua deleted file mode 100644 index d57f388..0000000 --- a/druid/components/button.lua +++ /dev/null @@ -1,119 +0,0 @@ -local M = {} - -local ui_animate = require "druid.help_modules.druid_animate" - -M.DEFAULT_SCALE_CHANGE = vmath.vector3(-0.05, - 0.1, 1) -M.DEFAULT_POS_CHANGE = vmath.vector3(0, - 10, 0) -M.DEFAULT_MOVE_SPEED = 5 -M.DEFAULT_ALPHA_DOWN = 0.8 -M.DEFAULT_TIME_ANIM = 0.1 -M.DEFAULT_DEACTIVATE_COLOR = vmath.vector4(0, 0, 0, 0) -M.DEFAULT_DEACTIVATE_SCALE = vmath.vector3(0.8, 0.9, 1) -M.DEFAULT_ACTIVATE_SCALE = vmath.vector3(1, 1, 1) -M.DEFAUL_ACTIVATION_TIME = 0.2 - ---- Set text to text field --- @param action_id - input action id --- @param action - input action -function M.on_input(instance, action_id, action) - if gui.is_enabled(instance.node) and gui.pick_node(instance.node, action.x, action.y) then - if not instance.disabled then - instance.tap_anim(instance) - return true - else - instance.sound_disable() - return false - end - end - return false -end - -function M.tap_scale_animation(instance) - ui_animate.scale_to(instance, instance.anim_node, instance.scale_to, - function() - if instance.back_anim then - instance.back_anim(instance) - end - instance.sound() - instance.callback(instance.parent.parent, instance.params, instance) - end - ) -end - -function M.back_scale_animation(instance) - ui_animate.scale_to(instance, instance.anim_node, instance.scale_from) -end - -function M.tap_tab_animation(instance, force) - ui_animate.alpha(instance, instance.anim_node, M.DEFAULT_ALPHA_DOWN, nil, M.DEFAULT_TIME_ANIM) - ui_animate.fly_to(instance, instance.anim_node, instance.pos + M.DEFAULT_POS_CHANGE, M.DEFAULT_MOVE_SPEED) - ui_animate.scale_to(instance, instance.anim_node, instance.scale_to, - function() - if instance.back_anim then - instance.back_anim(instance) - end - instance.callback(instance.parent.parent, instance.params, force) - end - ) -end - -function M.back_tab_animation(instance) - ui_animate.alpha(instance, instance.anim_node, 1, nil, M.DEFAULT_TIME_ANIM) - ui_animate.fly_to(instance, instance.anim_node, instance.pos, M.DEFAULT_MOVE_SPEED) - ui_animate.scale_to(instance, instance.anim_node, instance.scale_from) -end - -function M.deactivate(instance, is_animate, callback) - instance.disabled = true - if is_animate then - local counter = 0 - local clbk = function() - counter = counter + 1 - if counter == 3 and callback then - callback(instance.parent.parent) - end - end - ui_animate.color(instance, instance.node, M.DEFAULT_DEACTIVATE_COLOR, clbk, M.DEFAUL_ACTIVATION_TIME, 0, - gui.EASING_OUTBOUNCE) - ui_animate.scale_y_from_to(instance, instance.node, M.DEFAULT_ACTIVATE_SCALE.x, M.DEFAULT_DEACTIVATE_SCALE.x, clbk, - M.DEFAUL_ACTIVATION_TIME, gui.EASING_OUTBOUNCE) - ui_animate.scale_x_from_to(instance, instance.node, M.DEFAULT_ACTIVATE_SCALE.y, M.DEFAULT_DEACTIVATE_SCALE.y, clbk, - M.DEFAUL_ACTIVATION_TIME, gui.EASING_OUTBOUNCE) - else - gui.set_color(instance.node, M.DEFAULT_DEACTIVATE_COLOR) - gui.set_scale(instance.node, M.DEFAULT_DEACTIVATE_SCALE) - if callback then - callback(instance.parent.parent) - end - end -end - -function M.activate(instance, is_animate, callback) - if is_animate then - local counter = 0 - local clbk = function() - counter = counter + 1 - if counter == 3 then - instance.disabled = false - if callback then - callback(instance.parent.parent) - end - end - end - ui_animate.color(instance, instance.node, ui_animate.TINT_SHOW, clbk, M.DEFAUL_ACTIVATION_TIME, 0, - gui.EASING_OUTBOUNCE) - ui_animate.scale_y_from_to(instance, instance.node, M.DEFAULT_DEACTIVATE_SCALE.x, M.DEFAULT_ACTIVATE_SCALE.x, clbk, - M.DEFAUL_ACTIVATION_TIME, gui.EASING_OUTBOUNCE) - ui_animate.scale_x_from_to(instance, instance.node, M.DEFAULT_DEACTIVATE_SCALE.y, M.DEFAULT_ACTIVATE_SCALE.y, clbk, - M.DEFAUL_ACTIVATION_TIME, gui.EASING_OUTBOUNCE) - else - gui.set_color(instance.node, ui_animate.TINT_SHOW) - gui.set_scale(instance.node, M.DEFAULT_ACTIVATE_SCALE) - instance.disabled = false - if callback then - callback(instance.parent.parent) - end - end -end - -return M diff --git a/druid/components/counter.lua b/druid/components/counter.lua deleted file mode 100644 index 35b1389..0000000 --- a/druid/components/counter.lua +++ /dev/null @@ -1,67 +0,0 @@ -local M = {} - -local text_field = require "druid.components.text_field" - -local FRAMES = 60 - ---- Bounce text field -M.bounce = text_field.bounce - ---- Set text to text field --- @param set_to - set value to text field -M.set_to = text_field.set_to - ---- Set color --- @param color -M.set_color = text_field.set_color - ---- Set scale --- @param scale -M.set_scale = text_field.set_scale - ---- Called when layout updated (rotate for example) -function M.on_layout_updated(instance) - text_field.on_layout_updated(instance) - if instance.last_value then - text_field.set_to(instance, instance.last_value) - end -end - ---- Animate counter --- @param set_to - set value to text field -function M.animate_to(instance, set_to, frames) - if set_to == instance.last_value then - text_field.set_to(instance, set_to) - elseif not instance.is_animate then - frames = frames or FRAMES - instance.end_anim_value = set_to - local diff = set_to - instance.last_value - instance.anim_step = math.floor((set_to - instance.last_value) / frames) - if diff ~= 0 and instance.anim_step == 0 then - instance.anim_step = diff > 0 and 1 or - 1 - end - instance.is_animate = true - else - instance.end_anim_value = set_to - end -end - ---- Called when update --- @param dt - delta time -function M.on_updated(instance, dt) - if instance.is_animate then - instance.last_value = instance.last_value + instance.anim_step - text_field.set_to(instance, instance.last_value) - if not instance.is_in_bounce then - instance.is_in_bounce = true - text_field.bounce(instance, function() instance.is_in_bounce = false end) - end - if instance.anim_step > 0 and instance.last_value >= instance.end_anim_value or - instance.anim_step < 0 and instance.last_value <= instance.end_anim_value then - instance.is_animate = false - text_field.set_to(instance, instance.end_anim_value) - end - end -end - -return M diff --git a/druid/components/flying_particles.lua b/druid/components/flying_particles.lua deleted file mode 100644 index 0c0edcd..0000000 --- a/druid/components/flying_particles.lua +++ /dev/null @@ -1,52 +0,0 @@ -local M = {} - -local ui_animate = require "druid.help_modules.druid_animate" - -local function fly_to(instance, pos_from, speed, callback) - local pos_to = instance.get_pos_func() - instance.last_speed = speed - instance.last_callback = callback - - instance.last_particle = instance.last_particle + 1 - if instance.last_particle > #instance.fly_particles then - instance.last_particle = 1 - end - local fly_particle = instance.fly_particles[instance.last_particle] - if pos_from then - gui.set_position(fly_particle, pos_from) - end - gui.play_particlefx(fly_particle) - instance.is_anim = true - ui_animate.fly_to(nil, fly_particle, pos_to, speed, - function() - instance.is_anim = false - gui.stop_particlefx(fly_particle) - if callback then - callback(instance.parent.parent) - instance.last_callback = nil - end - end, - 0, gui.EASING_INSINE) -end - ---- Start animation of a flying particles --- @param pos_from - fly from this position --- @param speed - speed of flying --- @param callback - callback when progress ended if need -function M.fly_to(instance, pos_from, speed, callback) - fly_to(instance, pos_from, speed, callback) -end - ---- Called when layout updated (rotate for example) -function M.on_layout_updated(instance) - if instance.is_anim then - instance.last_particle = instance.last_particle - 1 - if instance.last_particle < 1 then - instance.last_particle = #instance.fly_particles - end - fly_to(instance, nil, instance.last_speed, instance.last_callback) - end -end - - -return M diff --git a/druid/components/image.lua b/druid/components/image.lua deleted file mode 100644 index 97af383..0000000 --- a/druid/components/image.lua +++ /dev/null @@ -1,46 +0,0 @@ -local M = {} - -local ui_animate = require "druid.help_modules.druid_animate" - ---- Bounce image -function M.bounce(instance) - gui.set_scale(instance.node, instance.scale_from) - ui_animate.bounce(nil, instance.node, instance.scale_to) -end - ---- Set image anim --- @param set_to - index of animation or animation name -function M.set_to(instance, set_to) - instance.last_value = set_to - gui.play_flipbook(instance.node, instance.anim_table and instance.anim_table[set_to] or set_to) -end - ---- Set position to the image --- @param pos - set position of the image -function M.set_pos(instance, pos) - instance.last_pos = pos - gui.set_position(instance.node, pos) -end - ---- Set tint to the image --- @param color - set color of the image -function M.set_color(instance, color) - instance.last_color = color - gui.set_color(instance.node, color) -end - ---- Called when layout updated (rotate for example) -function M.on_layout_updated(instance) - if instance.last_value then - M.set_to(instance, instance.last_value) - end - if instance.last_pos then - M.set_pos(instance, instance.last_pos) - end - if instance.last_color then - M.set_color(instance, instance.last_color) - end -end - - -return M diff --git a/druid/components/pie_progress_bar.lua b/druid/components/pie_progress_bar.lua deleted file mode 100644 index ec8533b..0000000 --- a/druid/components/pie_progress_bar.lua +++ /dev/null @@ -1,51 +0,0 @@ -local M = {} - -local FULL_FILL = 360 - -local function set_bar_to(instance, set_to) - instance.last_value = set_to - gui.cancel_animation(instance.node, gui.PROP_FILL_ANGLE) - gui.set_fill_angle(instance.node, FULL_FILL * set_to) -end - ---- Fill a pie progress bar and stop progress animation -function M.fill_bar(instance) - set_bar_to(instance, 1) -end - ---- To empty a pie progress bar -function M.empty_bar(instance) - set_bar_to(instance, 0) -end - ---- Set fill a pie progress bar to value --- @param to - value between 0..1 -function M.set_to(instance, to) - set_bar_to(instance, to) -end - ---- Start animation of a pie progress bar --- @param to - value between 0..1 --- @param duration - time of animation --- @param callback - callback when progress ended if need -function M.start_progress_to(instance, to, duration, callback) - instance.is_anim = true - instance.last_value = to - gui.animate(instance.node, gui.PROP_FILL_ANGLE, FULL_FILL * to, gui.EASING_LINEAR, duration, 0, - function() - instance.is_anim = false - if callback then - callback(instance.parent.parent) - end - end - ) -end - ---- Called when layout updated (rotate for example) -function M.on_layout_updated(instance) - if not instance.is_anim then - set_bar_to(instance, instance.last_value) - end -end - -return M diff --git a/druid/components/progress_bar.lua b/druid/components/progress_bar.lua deleted file mode 100644 index bb60693..0000000 --- a/druid/components/progress_bar.lua +++ /dev/null @@ -1,93 +0,0 @@ -local M = {} - -local function set_bar_to(instance, set_to) - instance.last_value = set_to - gui.cancel_animation(instance.node, instance.prop) - instance.scale[instance.key] = set_to - gui.set_scale(instance.node, instance.scale) -end - -local function circle_anim(instance, full, steps, num, full_duration, callback) - local duration = (math.abs(steps[num - 1] - steps[num]) / full) * full_duration - local to = steps[num] - gui.animate(instance.node, instance.prop, to, gui.EASING_LINEAR, duration, 0, - function() - callback(num, callback) - end - ) -end - ---- Fill a progress bar and stop progress animation -function M.fill_bar(instance) - set_bar_to(instance, 1) -end - ---- To empty a progress bar -function M.empty_bar(instance) - set_bar_to(instance, 0) -end - ---- Set fill a progress bar to value --- @param to - value between 0..1 -function M.set_to(instance, to) - set_bar_to(instance, to) -end - ---- Start animation of a progress bar --- @param to - value between 0..1 --- @param duration - time of animation --- @param callback - callback when progress ended if need --- @param callback_values - whitch values should callback -function M.start_progress_to(instance, to, duration, callback, callback_values) - instance.is_anim = true - local steps - if callback_values then - steps = {instance.last_value} - if instance.last_value > to then - table.sort(callback_values, function(a, b) return a > b end) - else - table.sort(callback_values, function(a, b) return a < b end) - end - for i, v in ipairs(callback_values) do - if (instance.last_value > v and to < v) or (instance.last_value < v and to > v) then - steps[#steps + 1] = v - end - end - steps[#steps + 1] = to - end - if not steps then - gui.animate(instance.node, instance.prop, to, gui.EASING_LINEAR, duration, 0, - function() - set_bar_to(instance, to) - instance.is_anim = false - if callback then - callback(instance.parent.parent, to) - end - end - ) - else - local full = math.abs(steps[1] - steps[#steps]) - local _callback = function (num, _callback) - if num == #steps then - set_bar_to(instance, steps[num]) - instance.is_anim = false - callback(instance.parent.parent, steps[num]) - else - callback(instance.parent.parent, steps[num]) - num = num + 1 - circle_anim(instance, full, steps, num, duration, _callback) - end - end - circle_anim(instance, full, steps, 2, duration, _callback) - end -end - ---- Called when layout updated (rotate for example) -function M.on_layout_updated(instance) - if not instance.is_anim then - set_bar_to(instance, instance.last_value) - end -end - - -return M diff --git a/druid/components/scrolling_box.lua b/druid/components/scrolling_box.lua deleted file mode 100644 index 8618335..0000000 --- a/druid/components/scrolling_box.lua +++ /dev/null @@ -1,241 +0,0 @@ -local M = {} - -local druid_input = require "druid.help_modules.druid_input" -local ui_animate = require "druid.help_modules.druid_animate" - -M.START = hash("START") -M.FINISH = hash("FINISH") - -M.SCROLLING = hash("SCROLLING") -M.INTEREST_MOVE = hash("INTEREST_MOVE") -M.OUT_OF_ZONE_MOVE = hash("OUT_OF_ZONE_MOVE") - -M.BACK_TIME = 0.2 -M.ANIM_TIME = 0.4 - -local function callback(instance, event, type, param) - if instance.callback then - instance.callback(instance.parent.parent, event, type, param) - end -end - -local function checkSwipeDirection(swipe, action) - swipe.xDistance = math.abs(swipe.endX - swipe.beginX) - swipe.yDistance = math.abs(swipe.endY - swipe.beginY) - if swipe.is_x and swipe.xDistance > swipe.yDistance then - if swipe.beginX > swipe.endX then - swipe.totalSwipeDistanceLeft = swipe.beginX - swipe.endX - if swipe.totalSwipeDistanceLeft > swipe.minSwipeDistance then - swipe.speed.x = action.dx * swipe.speed_up_coef.x * swipe.end_move_coef_x - return true - else - return false - end - else - swipe.totalSwipeDistanceRight = swipe.endX - swipe.beginX - if swipe.totalSwipeDistanceRight > swipe.minSwipeDistance then - swipe.speed.x = action.dx * swipe.speed_up_coef.x * swipe.end_move_coef_x - return true - else - return false - end - end - elseif swipe.is_y and swipe.xDistance < swipe.yDistance then - if swipe.beginY > swipe.endY then - swipe.totalSwipeDistanceUp = swipe.beginY - swipe.endY - if swipe.totalSwipeDistanceUp > swipe.minSwipeDistance then - swipe.speed.y = action.dy * swipe.speed_up_coef.y * swipe.end_move_coef_y - return true - else - return false - end - else - swipe.totalSwipeDistanceDown = swipe.endY - swipe.beginY - if swipe.totalSwipeDistanceDown > swipe.minSwipeDistance then - swipe.speed.y = action.dy * swipe.speed_up_coef.y * swipe.end_move_coef_y - return true - else - return false - end - end - end -end - -function lenght(x1, y1, x2, y2) - local a, b = x1 - x2, y1 - y2 - return math.sqrt(a * a + b * b) -end - -local function back_move(instance) - if not instance.swipe.end_position_x and not instance.swipe.end_position_y then - if instance.points_of_interest then - local min_index, min_lenght = 0, math.huge - local len - for k, v in pairs(instance.points_of_interest) do - len = lenght(instance.pos.x, instance.pos.y, v.x, v.y) - if len < min_lenght then - min_lenght = len - min_index = k - end - end - instance.swipe.speed.x = 0 - instance.swipe.speed.y = 0 - gui.cancel_animation(instance.node, gui.PROP_POSITION) - instance.swipe.special_move = true - callback(instance, M.START, M.INTEREST_MOVE, instance.points_of_interest[min_index]) - gui.animate(instance.node, gui.PROP_POSITION, instance.points_of_interest[min_index], - gui.EASING_LINEAR, M.ANIM_TIME, 0, - function() - instance.swipe.special_move = false - instance.pos.x = instance.points_of_interest[min_index].x - instance.pos.y = instance.points_of_interest[min_index].y - callback(instance, M.FINISH, M.SCROLLING, instance.pos) - callback(instance, M.FINISH, M.INTEREST_MOVE, instance.pos) - end - ) - else - callback(instance, M.FINISH, M.SCROLLING, instance.pos) - end - end - - if instance.swipe.end_position_x then - local swipe = instance.swipe - swipe.speed.x = 0 - instance.pos.x = swipe.end_position_x - swipe.special_move = true - callback(instance, M.START, M.OUT_OF_ZONE_MOVE, instance.pos) - gui.animate(instance.node, ui_animate.PROP_POS_X, swipe.end_position_x, gui.EASING_INSINE, M.BACK_TIME, 0, - function() - swipe.special_move = false - callback(instance, M.FINISH, M.SCROLLING, instance.pos) - callback(instance, M.FINISH, M.OUT_OF_ZONE_MOVE, instance.pos) - end - ) - swipe.end_position_x = nil - end - if instance.swipe.end_position_y then - local swipe = instance.swipe - swipe.speed.y = 0 - instance.pos.y = swipe.end_position_y - swipe.special_move = true - callback(instance, M.START, M.OUT_OF_ZONE_MOVE, instance.pos) - gui.animate(instance.node, ui_animate.PROP_POS_Y, swipe.end_position_y, gui.EASING_INSINE, M.BACK_TIME, 0, - function() - swipe.special_move = false - callback(instance, M.FINISH, M.SCROLLING, instance.pos) - callback(instance, M.FINISH, M.OUT_OF_ZONE_MOVE, instance.pos) - end - ) - swipe.end_position_y = nil - end -end - ---- Set text to text field --- @param action_id - input action id --- @param action - input action -function M.on_input(instance, action_id, action) - if action_id == druid_input.A_CLICK then - if gui.pick_node(instance.scrolling_zone, action.x, action.y) then - local swipe = instance.swipe - if action.pressed then - swipe.pressed = true - swipe.beginX = action.x - swipe.beginY = action.y - druid_input.is_swipe = false - swipe.end_move_coef_x = 1 - elseif not action.released and not action.pressed and not swipe.special_move then - swipe.endX = action.x - swipe.endY = action.y - local before = swipe.is_swipe - swipe.is_swipe = checkSwipeDirection(swipe, action) - if not before and swipe.is_swipe and not swipe.special_move and not swipe.waiting_for_back_move then - callback(instance, M.START, M.SCROLLING, instance.pos) - end - return swipe.is_swipe or swipe.special_move - elseif action.released then - swipe.beginX = 0 - swipe.beginY = 0 - swipe.endX = 0 - swipe.endY = 0 - swipe.pressed = false - if swipe.waiting_for_back_move then - back_move(instance) - swipe.waiting_for_back_move = false - end - return swipe.is_swipe or swipe.special_move - end - elseif action.released then - instance.swipe.pressed = false - if instance.swipe.waiting_for_back_move then - back_move(instance) - instance.swipe.waiting_for_back_move = false - end - end - end -end - ---- Called when update --- @param dt - delta time -function M.on_updated(instance, dt) - if instance.swipe.speed.x ~= 0 or instance.swipe.speed.y ~= 0 then - local swipe = instance.swipe - instance.pos.x = instance.pos.x + swipe.speed.x - instance.pos.y = instance.pos.y + swipe.speed.y - if instance.pos.x < instance.start_pos.x then - swipe.end_move_coef_x = swipe.back_slow_coef - swipe.end_position_x = instance.start_pos.x - elseif instance.pos.x > instance.maximum.x then - swipe.end_move_coef_x = swipe.back_slow_coef - swipe.end_position_x = instance.maximum.x - else - swipe.end_move_coef_x = 1 - swipe.end_position_x = nil - end - if instance.pos.y < instance.start_pos.y then - swipe.end_move_coef_y = swipe.back_slow_coef - swipe.end_position_y = instance.start_pos.y - elseif instance.pos.y > instance.maximum.y then - swipe.end_move_coef_y = swipe.back_slow_coef - swipe.end_position_y = instance.maximum.y - else - swipe.end_move_coef_y = 1 - swipe.end_position_y = nil - end - gui.set_position(instance.node, instance.pos) - swipe.speed.x = swipe.speed.x / swipe.speed_down_coef * swipe.end_move_coef_x - swipe.speed.y = swipe.speed.y / swipe.speed_down_coef * swipe.end_move_coef_y - if swipe.speed.x < swipe.min_speed and swipe.speed.x > - swipe.min_speed then - swipe.speed.x = 0 - if not swipe.pressed then - back_move(instance) - else - swipe.waiting_for_back_move = true - end - if swipe.speed.y < swipe.min_speed and swipe.speed.y > - swipe.min_speed then - swipe.speed.y = 0 - end - if swipe.speed.y == 0 and swipe.speed.x == 0 then - swipe.is_swipe = false - end - end - end -end - ---- Scroll position to --- @param pos - positon for set --- @param is_animate - is animated set -function M.scroll_to(instance, pos, is_animate, cb, time_scrolling) - local time = is_animate and M.ANIM_TIME or 0 - time = time_scrolling or time - instance.pos.x = pos.x - instance.pos.y = pos.y - gui.animate(instance.node, gui.PROP_POSITION, instance.pos, gui.EASING_INSINE, time, 0, - function() - if cb then - cb(instance.parent.parent) - end - end - ) -end - -return M diff --git a/druid/components/spine_anim.lua b/druid/components/spine_anim.lua deleted file mode 100644 index 40861c2..0000000 --- a/druid/components/spine_anim.lua +++ /dev/null @@ -1,62 +0,0 @@ -local M = {} - ---- Set animation scene --- @param scene - animations scene -function M.set_scene(instance, scene) - instance.last_scene = scene - gui.set_spine_scene(instance.node, scene) -end - - ---- Set idle animation --- @param anim - idle animation name or index in idle table --- @param properties - properties of the animation -function M.play_idle(instance, anim, properties) - if not anim then - return - end - anim = (instance.idle_table and instance.idle_table[anim]) and instance.idle_table[anim] or anim - instance.last_value = anim - properties = properties or {} - gui.play_spine_anim(instance.node, anim, gui.PLAYBACK_LOOP_FORWARD, properties) -end - ---- Set active animation --- @param anim - active animation name or index in active table --- @param callback - call when animation done --- @param idle_after - set idle after active anim -function M.play_active(instance, anim, callback, idle_after) - instance.is_play_now = true - anim = instance.active_table and instance.active_table[anim] or anim - instance.last_value = anim - instance.callback = callback - M.play_idle(instance, idle_after) - gui.play_spine_anim(instance.node, anim, gui.PLAYBACK_ONCE_FORWARD, {}, - function() - M.play_idle(instance, idle_after) - instance.is_play_now = false - if callback then - callback(instance.parent.parent) - end - end - ) -end - ---- Called when layout updated (rotate for example) -function M.on_layout_updated(instance) - if instance.last_scene then - M.set_scene(instance, instance.last_scene) - end - if instance.last_value then - M.play_idle(instance, instance.last_value) - end - if instance.is_play_now then - instance.is_play_now = false - if instance.callback then - instance.callback(instance.parent.parent) - end - end -end - - -return M diff --git a/druid/components/tab_page.lua b/druid/components/tab_page.lua deleted file mode 100644 index f6771ce..0000000 --- a/druid/components/tab_page.lua +++ /dev/null @@ -1,92 +0,0 @@ ---[[ Single tab screen module. Assumed to be used with Tab Bar module. - --- Create tab screen with parental gui node: -self.tab = tab.create "tab_bkg" - --- Show and hide tab manually: -self.tab.slide_in(self, vmath.vector3(0, -540, 0)) -self.tab.slide_out(self, vmath.vector3(0, -540, 0)) - --- Or receive show and hide messages: -function on_message(self, message_id, message, sender) - self.tab.on_message(self, message_id, message) -end - -]] - -local M = {} -M.T_SLIDE_IN = hash("t_slide_in") -M.T_SLIDE_OUT = hash("t_slide_out") - -M.STATE_START = hash("state_start") -M.STATE_FINISH = hash("state_finish") - -local ENABLE = hash("enable") -local DISABLE = hash("disable") -local PATH_COMP = "#" - -M.DEFAULT_EASING = gui.EASING_INOUTQUAD -M.DEFAULT_DURATION = 0.3 - -function M.slide_in(instance, out_pos, is_force) - msg.post(PATH_COMP, ENABLE) - if instance.callback then - instance.callback(instance.parent.parent, instance, M.T_SLIDE_IN, M.STATE_START) - end - if is_force then - gui.set_position(instance.node, instance.in_pos) - if instance.callback then - instance.callback(instance.parent.parent, instance, M.T_SLIDE_IN, M.STATE_FINISH) - end - else - instance.in_action = true - out_pos = out_pos or instance.put_pos - gui.set_position(instance.node, out_pos or instance.out_pos) - gui.animate(instance.node, gui.PROP_POSITION, instance.in_pos, instance.easing, instance.duration, 0, - function() - instance.in_action = false - if instance.callback then - instance.callback(instance.parent.parent, instance, M.T_SLIDE_IN, M.STATE_FINISH) - end - end - ) - end -end - -function M.slide_out(instance, out_pos, is_force) - out_pos = out_pos or instance.put_pos - if instance.callback then - instance.callback(instance.parent.parent, instance, M.T_SLIDE_OUT, M.STATE_START) - end - if is_force then - gui.set_position(instance.node, out_pos) - if instance.callback then - instance.callback(instance.parent.parent, instance, M.T_SLIDE_OUT, M.STATE_FINISH) - end - msg.post(PATH_COMP, DISABLE) - else - instance.in_action = true - gui.set_position(instance.node, instance.in_pos) - gui.animate(instance.node, gui.PROP_POSITION, out_pos, instance.easing, instance.duration, 0, - function() - instance.in_action = false - if instance.callback then - instance.callback(instance.parent.parent, instance, M.T_SLIDE_OUT, M.STATE_FINISH) - end - msg.post(PATH_COMP, DISABLE) - end - ) - end -end - -function M.on_message(instance, message_id, message, sender) - if message_id == M.T_SLIDE_IN then - M.slide_in(instance, message.out_pos, message.is_force) - return true - elseif message_id == M.T_SLIDE_OUT then - M.slide_out(instance, message.out_pos, message.is_force) - return true - end -end - -return M diff --git a/druid/components/tabs_container.lua b/druid/components/tabs_container.lua deleted file mode 100644 index 4a45820..0000000 --- a/druid/components/tabs_container.lua +++ /dev/null @@ -1,50 +0,0 @@ -local M = {} - ---local helper = require "modules.render.helper" -local tab_page = require "druid.components.tab_page" - -local DISABLE = hash("disable") - -function M.update_sizes(instance, width) - -- width = width or helper.config_x - instance.left = vmath.vector3(width * - 1, 0, 0) - instance.right = vmath.vector3(width * 1, 0, 0) -end - ---- Called when layout updated (rotate for example) -function M.on_layout_updated(instance, message) - -- local width = helper.settings_x - M.update_sizes(instance, width) -end - -function M.switch_tab(instance, params, force) - if instance.current == params then - return - end - if instance.current then - instance.btns[instance.current.index]:manual_back() - end - local out_pos - if instance.current and instance.current.url then - out_pos = (instance.current and instance.current.index < params.index) and instance.left or instance.right - msg.post(instance.current.url, tab_page.T_SLIDE_OUT, { out_pos = out_pos, is_force = force }) - end - if params and params.url then - out_pos = (instance.current and instance.current.index > params.index) and instance.left or instance.right - msg.post(params.url, tab_page.T_SLIDE_IN, { out_pos = out_pos, is_force = force }) - instance.current = params - end -end - ---- Select current tab manually -function M.select(instance, node_name) - for k, v in pairs(instance.btns) do - if k == instance[node_name].index then - v:tap_anim(true) - else - msg.post(instance[v.name].url, DISABLE) - end - end -end - -return M diff --git a/druid/components/text_field.lua b/druid/components/text_field.lua deleted file mode 100644 index fbc6eb1..0000000 --- a/druid/components/text_field.lua +++ /dev/null @@ -1,42 +0,0 @@ -local M = {} - -local ui_animate = require "druid.help_modules.druid_animate" - ---- Bounce text field -function M.bounce(instance, callback) - gui.set_scale(instance.node, instance.scale_from) - ui_animate.bounce(nil, instance.node, instance.scale_to, callback) -end - ---- Set text to text field --- @param set_to - set value to text field -function M.set_to(instance, set_to) - instance.last_value = set_to - gui.set_text(instance.node, set_to) -end - ---- Set color --- @param color -function M.set_color(instance, color) - instance.last_color = color - gui.set_color(instance.node, color) -end - ---- Set scale --- @param scale -function M.set_scale(instance, scale) - instance.last_scale = scale - gui.set_scale(instance.node, scale) -end - ---- Called when layout updated (rotate for example) -function M.on_layout_updated(instance) - if instance.last_color then - M.set_color(instance, instance.last_color) - end - if instance.last_scale then - M.set_scale(instance, instance.last_scale) - end -end - -return M diff --git a/druid/components/timer.lua b/druid/components/timer.lua deleted file mode 100644 index 3f7329d..0000000 --- a/druid/components/timer.lua +++ /dev/null @@ -1,53 +0,0 @@ -local M = {} - -local formats = require "druid.help_modules.formats" - ---- Set text to text field --- @param set_to - set value in seconds -function M.set_to(instance, set_to) - instance.last_value = set_to - gui.set_text(instance.node, formats.second_string_min(set_to)) -end - ---- Called when layout updated (rotate for example) -function M.on_layout_updated(instance) - M.set_to(instance, instance.last_value) -end - ---- Called when update --- @param is_on - boolean is timer on -function M.set_work_mode(instance, is_on) - instance.is_on = is_on -end - ---- Set time interval --- @param from - "from" time in seconds --- @param to - "to" time in seconds -function M.set_interval(instance, from, to) - instance.second_from = from - instance.seconds_counter = from - instance.seconds_temp = 0 - instance.seconds_to = to - instance.second_step = from < to and 1 or - 1 - M.set_work_mode(instance, true) - M.set_to(instance, from) -end - ---- Called when update --- @param dt - delta time -function M.on_updated(instance, dt) - if instance.is_on then - instance.seconds_temp = instance.seconds_temp + dt - if instance.seconds_temp > 1 then - instance.seconds_temp = instance.seconds_temp - 1 - instance.seconds_counter = instance.seconds_counter + instance.second_step - M.set_to(instance, instance.seconds_counter) - if instance.seconds_counter == instance.seconds_to then - instance.is_on = false - instance.callback(instance) - end - end - end -end - -return M diff --git a/druid/const.lua b/druid/const.lua new file mode 100644 index 0000000..fd2c121 --- /dev/null +++ b/druid/const.lua @@ -0,0 +1,67 @@ +--- Druid constants +-- @local +-- @module const + +local M = {} + +M.ACTION_TOUCH = hash("touch") +M.ACTION_TEXT = hash("text") +M.ACTION_BACKSPACE = hash("backspace") +M.ACTION_ENTER = hash("enter") +M.ACTION_BACK = hash("back") + + +M.RELEASED = "released" +M.PRESSED = "pressed" +M.STRING = "string" +M.TABLE = "table" +M.ZERO = "0" +M.ALL = "all" + + +--- Component Interests +M.ON_MESSAGE = hash("on_message") +M.ON_UPDATE = hash("on_update") +M.ON_INPUT_HIGH = hash("on_input_high") +M.ON_INPUT = hash("on_input") +M.ON_CHANGE_LANGUAGE = hash("on_change_language") +M.ON_LAYOUT_CHANGED = hash("on_layout_changed") + + +M.PIVOTS = { + [gui.PIVOT_CENTER] = vmath.vector3(0), + [gui.PIVOT_N] = vmath.vector3(0, 0.5, 0), + [gui.PIVOT_NE] = vmath.vector3(0.5, 0.5, 0), + [gui.PIVOT_E] = vmath.vector3(0.5, 0, 0), + [gui.PIVOT_SE] = vmath.vector3(0.5, -0.5, 0), + [gui.PIVOT_S] = vmath.vector3(0, -0.5, 0), + [gui.PIVOT_SW] = vmath.vector3(-0.5, -0.5, 0), + [gui.PIVOT_W] = vmath.vector3(-0.5, 0, 0), + [gui.PIVOT_NW] = vmath.vector3(-0.5, 0.5, 0), +} + + +M.SPECIFIC_UI_MESSAGES = { + [M.ON_CHANGE_LANGUAGE] = "on_change_language", + [M.ON_LAYOUT_CHANGED] = "on_layout_changed" +} + + +M.UI_INPUT = { + [M.ON_INPUT_HIGH] = true, + [M.ON_INPUT] = true +} + + +M.SIDE = { + X = "x", + Y = "y" +} + + +M.EMPTY_FUNCTION = function() end +M.EMPTY_STRING = "" +M.EMPTY_TABLE = {} + + +return M \ No newline at end of file diff --git a/druid/druid.lua b/druid/druid.lua index 4c34465..3a05c83 100644 --- a/druid/druid.lua +++ b/druid/druid.lua @@ -1,420 +1,83 @@ +--- Druid UI Library. +-- Powerful Defold component based UI library. Use standart +-- components or make your own game-specific components to +-- make amazing GUI in your games. +-- +-- Contains the several basic components and examples +-- to how to do your custom complex components to +-- separate UI game logic to small files +-- +-- require("druid.druid") +-- function init(self) +-- self.druid = druid.new(self) +-- end +-- +-- @module druid + +local const = require("druid.const") +local druid_instance = require("druid.system.druid_instance") +local settings = require("druid.system.settings") + local M = {} -local druid_input = require "druid.help_modules.druid_input" -M.input = druid_input -local pie_progress_bar = require "druid.components.pie_progress_bar" -local progress_bar = require "druid.components.progress_bar" -local flying_particles = require "druid.components.flying_particles" -local text_field = require "druid.components.text_field" -local counter = require "druid.components.counter" -local image = require "druid.components.image" -local button = require "druid.components.button" -local timer = require "druid.components.timer" -local tab_page = require "druid.components.tab_page" -local tabs_container = require "druid.components.tabs_container" -local spine_anim = require "druid.components.spine_anim" -local scrolling_box = require "druid.components.scrolling_box" +--- Register external druid component. +-- After register you can create the component with +-- druid_instance:new_{name}. For example `druid:new_button(...)` +-- @function druid:register +-- @tparam string name module name +-- @tparam table module lua table with component +function M.register(name, module) + -- TODO: Find better solution to creating elements? + -- Possibly: druid.new(druid.BUTTON, etc?) + -- Current way is very implicit + druid_instance["new_" .. name] = function(self, ...) + return druid_instance.create(self, module, ...) + end -local andr_back_btn = require "druid.components.andr_back_btn" - -local LAYOUT_CHANGED = hash("layout_changed") -local ON_MESSAGE = hash("on_message") -local ON_INPUT = hash("on_input") -local ON_SWIPE = hash("on_swipe") -local ON_UPDATE = hash("on_update") -M.TRANSLATABLE = hash("TRANSLATABLE") - -local STRING = "string" - ---- Call this method when you need to update translations. -function M.translate(factory) - if factory[M.TRANSLATABLE] then - local key, result - for i, v in ipairs(factory[M.TRANSLATABLE]) do - key = v.lang_key or v.name - if key then - if v.lang_params then - result = lang.txp(key, v.lang_params) - else - result = lang.txt(key) - end - if result then - lang.set_node_properties(v.node, key) - end - result = result or v.last_value - v:set_to(result) - end - end - end + -- print("Register component", name) end ---- Called on_message -function M.on_message(factory, message_id, message, sender) - if message_id == LAYOUT_CHANGED then - if factory[LAYOUT_CHANGED] then - M.translate(factory) - for i, v in ipairs(factory[LAYOUT_CHANGED]) do - v:on_layout_updated(message) - end - end - elseif message_id == M.TRANSLATABLE then - M.translate(factory) - else - if factory[ON_MESSAGE] then - for i, v in ipairs(factory[ON_MESSAGE]) do - v:on_message(message_id, message, sender) - end - end - end + +--- Create Druid instance. +-- @function druid.new +-- @tparam table context Druid context. Usually it is self of script +-- @tparam[opt] table style Druid style module +-- @treturn druid_instance Druid instance +function M.new(context, style) + return druid_instance(context, style) end ---- Called ON_INPUT -function M.on_input(factory, action_id, action) - if factory[ON_SWIPE] then - local v, result - local len = #factory[ON_SWIPE] - for i = 1, len do - v = factory[ON_SWIPE][i] - result = result or v:on_input(action_id, action) - end - if result then - return true - end - end - if factory[ON_INPUT] then - local v - local len = #factory[ON_INPUT] - for i = 1, len do - v = factory[ON_INPUT][i] - if action_id == v.event and action[v.action] and v:on_input(action_id, action) then - return true - end - end - return false - end - return false + +-- Set new default style. +-- @function druid.set_default_style +-- @tparam table style Druid style module +function M.set_default_style(style) + settings.default_style = style end ---- Called on_update -function M.on_update(factory, dt) - if factory[ON_UPDATE] then - for i, v in ipairs(factory[ON_UPDATE]) do - v:on_updated(dt) - end - end + +-- Set text function. +-- Druid locale component will call this function +-- to get translated text. After set_text_funtion +-- all existing locale component will be updated +-- @function druid.set_text_function(callback) +-- @tparam function callback Get localized text function +function M.set_text_function(callback) + settings.get_text = callback or const.EMPTY_FUNCTION + -- TODO: Update all localized text + -- Need to store all current druid instances to iterate over it? end ---- Create UI instance for ui elements --- @return instance with all ui components -function M.new(self) - local factory = setmetatable({}, {__index = M}) - factory.parent = self - return factory + +-- Set sound function. +-- Component will call this function to +-- play sound by sound_id +-- @function druid.set_sound_function +-- @tparam function callback Sound play callback +function M.set_sound_function(callback) + settings.play_sound = callback or const.EMPTY_FUNCTION end -local function input_init(factory) - if not factory.input_inited then - factory.input_inited = true - druid_input.focus() - end -end - --------------------------------------------------------------------------------- - -local function create(meta, factory, name, ...) - local instance = setmetatable({}, {__index = meta}) - instance.parent = factory - if name then - if type(name) == STRING then - instance.name = name - instance.node = gui.get_node(name) - else - --name already is node - instance.name = nil - instance.node = name - end - end - factory[#factory + 1] = instance - local register_to = {...} - for i, v in ipairs(register_to) do - if not factory[v] then - factory[v] = {} - end - factory[v][#factory[v] + 1] = instance - end - return instance -end - ---- Create new instance of a text_field --- @param factory - parent factory --- @param name - name of text node --- @param init_value - init ui object with this value --- @return instance of a text_field -function M.new_text_field(factory, name, init_value, bounce_in) - local instance = create(text_field, factory, name, M.TRANSLATABLE, LAYOUT_CHANGED) - instance.scale_from = gui.get_scale(instance.node) - instance.scale_to = bounce_in and vmath.mul_per_elem(instance.scale_from, bounce_in) or instance.scale_from - instance:set_to(init_value or 0) - return instance -end - ---- Create new instance of a counter --- @param factory - parent factory --- @param name - name of text node --- @param init_value - init ui object with this value --- @return instance of a text_field -function M.new_counter(factory, name, init_value) - local instance = create(counter, factory, name, LAYOUT_CHANGED, ON_UPDATE) - instance.scale_from = gui.get_scale(instance.node) - instance.scale_to = instance.scale_from * 1.2 - instance:set_to(init_value or 0) - return instance -end - ---- Create new instance of an image --- @param factory - parent factory --- @param name - name of image node --- @param anim_table - table with animations or frames --- @param init_frame - init with this frame --- @return instance of an image -function M.new_image(factory, name, anim_table, init_frame, bounce_in) - local instance = create(image, factory, name, LAYOUT_CHANGED) - instance.scale_from = gui.get_scale(instance.node) - instance.scale_to = bounce_in and vmath.mul_per_elem(instance.scale_from, bounce_in) or instance.scale_from - instance.anim_table = anim_table - if init_frame then - instance:set_to(init_frame) - elseif anim_table then - instance:set_to(1) - end - return instance -end - ---- Create new instance of a timer --- @param factory - parent factory --- @param name - name of image node --- @param second_from - start time --- @param seconds_to - end time --- @param callback - call when timer finished --- @return instance of a timer -function M.new_timer(factory, name, second_from, seconds_to, callback) - local instance = create(timer, factory, name, LAYOUT_CHANGED, ON_UPDATE) - instance:set_to(second_from) - instance:set_interval(second_from, seconds_to) - instance.is_on = true - instance.callback = callback - return instance -end - ---- Add new pie progress component for handling --- @param factory - parent factory --- @param name - a node name for a pie progress instance --- @param init_value - init ui object with this value --- @return instance with pie_progress -function M.new_pie_progress(factory, name, init_value) - local instance = create(pie_progress_bar, factory, name, LAYOUT_CHANGED) - instance:set_to(init_value or 1) - return instance -end - ---- Add new progress bar component for handling --- @param factory - parent factory --- @param name - name of the fill node --- @param key - x or y - key for scale --- @param init_value - init ui object with this value --- @return instance with pie_progress -function M.new_progress_bar(factory, name, key, init_value) - local instance = create(progress_bar, factory, name, LAYOUT_CHANGED) - instance.prop = hash("scale."..key) - instance.key = key - instance.node = gui.get_node(name) - instance.scale = gui.get_scale(instance.node) - instance:set_to(init_value or 1) - return instance -end - ---- Create new instance of a flying particles --- @param factory - parent factory --- @param name - name of prototype --- @param count - how many particles need to cache --- @param get_pos_func - function that returns target pos for flying --- @return instance of a flying particles -function M.new_flying_particles(factory, name, count, get_pos_func) - local instance = create(flying_particles, factory, name, LAYOUT_CHANGED) - instance.get_pos_func = get_pos_func - local node = instance.node - instance.node = node - instance.fly_particles = {} - instance.fly_particles[1] = node - for i = 2, count do - instance.fly_particles[i] = gui.clone(node) - end - instance.scale = gui.get_scale(node) - instance.last_particle = 0 - return instance -end - -M.BTN_SOUND_FUNC = function() end -M.BTN_SOUND_DISABLE_FUNC = function()end - ---- Add new button component for handling --- @param factory - parent factory --- @param name - a node name for a button instance --- @param callback - click button callback --- @param params - callback parameters, will be returned with self callback(self, params) --- @param animate_node_name - node for animation, if it's not a main node --- @return instance of button -function M.new_button(factory, name, callback, params, animate_node_name, event, action, sound, sound_disable) - input_init(factory) - local instance = create(button, factory, name, ON_INPUT) - instance.event = event or druid_input.A_CLICK - instance.action = action or druid_input.RELEASED - instance.anim_node = animate_node_name and gui.get_node(animate_node_name) or instance.node - instance.scale_from = gui.get_scale(instance.anim_node) - instance.scale_to = instance.scale_from + button.DEFAULT_SCALE_CHANGE - instance.pos = gui.get_position(instance.anim_node) - instance.callback = callback - instance.params = params - instance.tap_anim = button.tap_scale_animation - instance.back_anim = button.back_scale_animation - instance.sound = sound or M.BTN_SOUND_FUNC - instance.sound_disable = sound_disable or M.BTN_SOUND_DISABLE_FUNC - return instance -end - ---- Add reaction for back btn (on Android for example) --- @param factory - parent factory --- @param callback - tap button callback -function M.new_back_handler(factory, callback) - input_init(factory) - local instance = create(andr_back_btn, factory, nil, ON_INPUT) - instance.event = druid_input.A_ANDR_BACK - instance.action = druid_input.RELEASED - instance.callback = callback - return instance -end - ---- Create new tab page instance --- @param factory - parent factory --- @param name - name of parental node that represents tab page content --- @param easing - easing for tab page --- @param duration - duration of animation for tab page --- @param callback - call when change page --- @return instance that represents the tab page -function M.new_tab_page(factory, name, easing, duration, callback) - local instance = create(tab_page, factory, name, M.EVENTS.ON_MESSAGE) - instance.in_pos = gui.get_position(instance.node) - instance.out_pos = gui.get_position(instance.node) - instance.easing = easing or tab_page.DEFAULT_EASING - instance.duration = duration or tab_page.DEFAULT_DURATION - instance.callback = callback - return instance -end - ---- Create new tab btns container instance --- @param factory - parent factory --- @param name - name of parental node that represents tab btns container --- @return instance that represents the tab btns container -function M.new_tabs_container(factory, name, callback) - local instance = create(tabs_container, factory, name, LAYOUT_CHANGED) - instance:update_sizes() - instance.url = msg.url() - --- Create new tab btn instance - -- @param name - name of parental node that represents tab btn - -- @return instance that represents the tab btn - function instance.new_tab_btn(_instance, _name, url, index) - local params = {url = url, index = index, name = _name} - local btn = M.new_button(factory, _name, nil, params) - btn.back_anim = nil - btn.manual_back = button.back_tab_animation - btn.tap_anim = button.tap_tab_animation - btn.callback = function(_, _, force) - instance.switch_tab(instance, params, force) - if callback then - callback(factory.parent, index, force) - end - end - instance[_name] = params - if not instance.btns then - instance.btns = {} - end - instance.btns[index] = btn - return btn - end - - return instance -end - ---- Add new spine animation --- @param factory - parent factory --- @param name - a node name for a spine anim --- @param idle_table - table with idle animations --- @param active_table - table with active animations --- @param init_idle - init idle animation name or index in idle table --- @return instance with spine anim -function M.new_spine_anim(factory, name, idle_table, active_table, init_idle) - local instance = create(spine_anim, factory, name, LAYOUT_CHANGED) - instance.idle_table = idle_table - instance.active_table = active_table - instance:play_idle(init_idle) - return instance -end - ---- Add new scrolling box --- @param factory - parent factory --- @param name - a node name for a spine anim --- @param zone_name - node name of zone for tap --- @param speed_coef - vector3 coef. of speed for scrolling --- @param maximum - vector3 maximum position for scrolling --- @param points_of_interest - table with vector3 point of interes --- @param callback - scrolling events callback --- @return instance with scrolling box -function M.new_scrolling_box(factory, name, zone_name, speed_coef, maximum, points_of_interest, callback) - local instance = create(scrolling_box, factory, name, ON_UPDATE, ON_SWIPE) - instance.pos = gui.get_position(instance.node) - instance.start_pos = vmath.vector3(instance.pos) - instance.maximum = maximum - instance.points_of_interest = points_of_interest - instance.callback = callback - if instance.start_pos.x > instance.maximum.x then - instance.start_pos.x, instance.maximum.x = instance.maximum.x, instance.start_pos.x - end - if instance.start_pos.y > instance.maximum.y then - instance.start_pos.y, instance.maximum.y = instance.maximum.y, instance.start_pos.y - end - if type(name) == STRING then - instance.scrolling_zone = gui.get_node(zone_name) - else - instance.scrolling_zone = zone_name - end - instance.swipe = { - minSwipeDistance = 40, - speed_down_coef = 1.1, - speed_up_coef = speed_coef or vmath.vector3(1.1, 1.1, 0), - speed = vmath.vector3(0, 0, 0), - maximum = vmath.vector3(0, 0, 0), - min_speed = 2, - beginX = 0, - beginY = 0, - endX = 0, - endY = 0, - xDistance = nil, - yDistance = nil, - totalSwipeDistanceLeft = nil, - totalSwipeDistanceRight = nil, - totalSwipeDistanceUp = nil, - totalSwipeDistanceDown = nil, - is_swipe = nil, - end_move_coef_x = 1, - end_move_coef_y = 1, - back_slow_coef = 0.4, - end_position_x = nil, - end_position_y = nil, - is_x = instance.start_pos.x ~= instance.maximum.x, - is_y = instance.start_pos.y ~= instance.maximum.y - } - return instance -end return M diff --git a/druid/event.lua b/druid/event.lua new file mode 100644 index 0000000..b4bc110 --- /dev/null +++ b/druid/event.lua @@ -0,0 +1,71 @@ +--- Lua event small library +-- @module druid_event + +local class = require("druid.system.middleclass") + +-- @class DruidEvent +local M = class("druid.event") + + +--- Event constructur +-- @function Event +-- @tparam function initial_callback Subscribe the callback on new event, if callback exist +function M.initialize(self, initial_callback) + self._callbacks = {} + + if initial_callback then + self:subscribe(initial_callback) + end +end + + +--- Subscribe callback on event +-- @function event:subscribe +-- @tparam function callback Callback itself +function M.subscribe(self, callback) + assert(type(self) == "table", "You should subscribe to event with : syntax") + assert(type(callback) == "function", "Callback should be function") + + table.insert(self._callbacks, callback) +end + + +--- Unsubscribe callback on event +-- @function event:unsubscribe +-- @tparam function callback Callback itself +function M.unsubscribe(self, callback) + for i = 1, #self._callbacks do + if self._callbacks[i] == callback then + table.remove(self._callbacks, i) + return + end + end +end + + +--- Return true, if event have at lease one handler +-- @function event:is_exist +-- @treturn bool True if event have handlers +function M.is_exist(self) + return #self._callbacks > 0 +end + + +--- Clear the all event handlers +-- @function event:clear +function M.clear(self) + self._callbacks = {} +end + + +--- Trigger the event and call all subscribed callbacks +-- @function event:trigger +-- @param ... All event params +function M.trigger(self, ...) + for i = 1, #self._callbacks do + self._callbacks[i](...) + end +end + + +return M diff --git a/druid/help_modules/druid_animate.lua b/druid/help_modules/druid_animate.lua deleted file mode 100644 index 394c153..0000000 --- a/druid/help_modules/druid_animate.lua +++ /dev/null @@ -1,169 +0,0 @@ -local M = {} - -local PROP_SCALE = gui.PROP_SCALE -local PROP_POSITION = gui.PROP_POSITION -M.PROP_POS_X = hash("position.x") -M.PROP_POS_Y = hash("position.y") -M.PROP_ALPHA = hash("color.w") -local PROP_COLOR = hash("color") -local PROP_SCALE_X = "scale.x" -local PROP_SCALE_Y = "scale.y" - -M.TINT_HIDE = vmath.vector4(1, 1, 1, 0) -M.TINT_SHOW = vmath.vector4(1, 1, 1, 1) - -M.V3_ONE = vmath.vector3(1, 1, 1) -M.V3_ZERO = vmath.vector3(0, 0, 1) - -M.SCALE_ANIMATION_TIME = 0.1 -M.BOUNCE_ANIMATION_TIME = 0.25 -M.ALPHA_ANIMATION_TIME = 0.25 - -function M.alpha(self, node, alpha, callback, time, delay, easing, playback) - time = time or M.ALPHA_ANIMATION_TIME - delay = delay or 0 - easing = easing or gui.EASING_LINEAR - playback = playback or gui.PLAYBACK_ONCE_FORWARD - gui.animate(node, M.PROP_ALPHA, alpha, easing, time, delay, - function() - if callback then - callback(self, node) - end - end, - playback) -end - -function M.color(self, node, color, callback, time, delay, easing, playback) - time = time or M.ALPHA_ANIMATION_TIME - delay = delay or 0 - easing = easing or gui.EASING_LINEAR - playback = playback or gui.PLAYBACK_ONCE_FORWARD - gui.animate(node, PROP_COLOR, color, easing, time, delay, - function() - if callback then - callback(self, node) - end - end, - playback) -end - -function M.shake(self, node, callback, str, time) - str = str or - 30 - time = time or 0.25 - local pos = gui.get_position(node) - pos.x = pos.x + str - gui.animate(node, PROP_POSITION, pos, gui.EASING_INELASTIC, time, - 0, - function() - if callback then - callback(self) - end - end, - gui.PLAYBACK_ONCE_BACKWARD - ) -end - -function M.bounce(self, node, change_to, callback, time, easing, playback, delaly) - time = time or M.BOUNCE_ANIMATION_TIME - delaly = delaly or 0 - easing = easing or gui.EASING_OUTSINE - playback = playback or gui.PLAYBACK_ONCE_PINGPONG - gui.animate(node, PROP_SCALE, change_to, easing, time, delaly, - function() - if callback then - callback(self) - end - end, - playback) -end - -function M.fly_to(self, node, to_pos, speed, callback, delay, easing) - easing = easing or gui.EASING_OUTSINE - delay = delay or 0 - local time = vmath.length(to_pos - gui.get_position(node)) / 100 / speed - gui.animate(node, gui.PROP_POSITION, to_pos, easing, time, delay, - function() - if callback then - callback(self, node) - end - end) -end - -function M.fly_by_x(self, node, to_x, time, callback, delay, easing, playback) - playback = playback or gui.PLAYBACK_ONCE_FORWARD - easing = easing or gui.EASING_OUTSINE - delay = delay or 0 - gui.animate(node, M.PROP_POS_X, to_x, easing, time, delay, - function() - if callback then - callback(self, node) - end - end, - playback) -end - -function M.fly_by_y(self, node, to_y, time, callback, delay, easing, playback) - playback = playback or gui.PLAYBACK_ONCE_FORWARD - easing = easing or gui.EASING_OUTSINE - delay = delay or 0 - time = time or 0.25 - gui.animate(node, M.PROP_POS_Y, to_y, easing, time, delay, - function() - if callback then - callback(self, node) - end - end, - playback) -end - -function M.scale_to(self, node, to, callback, time, delay, easing) - easing = easing or gui.EASING_INSINE - time = time or M.SCALE_ANIMATION_TIME - delay = delay or 0 - time = time or 0.25 - gui.animate(node, PROP_SCALE, to, easing, time, delay, - function() - if callback then - callback(self, node) - end - end - ) -end - -function M.scale_x_from_to(self, node, from, to, callback, time, easing, delay, playback) - easing = easing or gui.EASING_INSINE - time = time or M.SCALE_ANIMATION_TIME - delay = delay or 0 - playback = playback or gui.PLAYBACK_ONCE_FORWARD - local scale = gui.get_scale(node) - scale.x = from - gui.set_scale(node, scale) - gui.animate(node, PROP_SCALE_X, to, easing, time, delay, - function() - if callback then - callback(self) - end - end, - playback - ) -end - -function M.scale_y_from_to(self, node, from, to, callback, time, easing, delay, playback) - easing = easing or gui.EASING_INSINE - time = time or M.SCALE_ANIMATION_TIME - delay = delay or 0 - playback = playback or gui.PLAYBACK_ONCE_FORWARD - local scale = gui.get_scale(node) - scale.y = from - gui.set_scale(node, scale) - gui.animate(node, PROP_SCALE_Y, to, easing, time, delay, - function() - if callback then - callback(self) - end - end, - playback - ) -end - -return M diff --git a/druid/help_modules/druid_input.lua b/druid/help_modules/druid_input.lua deleted file mode 100644 index 21ac43a..0000000 --- a/druid/help_modules/druid_input.lua +++ /dev/null @@ -1,24 +0,0 @@ -local M = {} - -local ADD_FOCUS = hash("acquire_input_focus") -local REMOVE_FOCUS = hash("release_input_focus") -local PATH_OBJ = "." - -M.A_CLICK = hash("click") -M.A_TEXT = hash("text") -M.A_BACKSPACE = hash("backspace") -M.A_ENTER = hash("enter") -M.A_ANDR_BACK = hash("back") - -M.RELEASED = "released" -M.PRESSED = "pressed" - -function M.focus() - msg.post(PATH_OBJ, ADD_FOCUS) -end - -function M.remove() - msg.post(PATH_OBJ, REMOVE_FOCUS) -end - -return M diff --git a/druid/help_modules/formats.lua b/druid/help_modules/formats.lua deleted file mode 100644 index b32ffaa..0000000 --- a/druid/help_modules/formats.lua +++ /dev/null @@ -1,34 +0,0 @@ -local M = {} - -local ZERO = "0" - --- Return number with zero number prefix --- @param num - number for conversion --- @param count - count of numerals --- @return string with need count of zero (1,3) -> 001 -function M.add_prefix_zeros(num, count) - local result = tostring(num) - for i = string.len(result), count - 1 do - result = ZERO..result - end - return result -end - --- Convert seconds to string minutes:seconds --- @param num - number of seconds --- @return string minutes:seconds -function M.second_string_min(sec) - local mins = math.floor(sec / 60) - local seconds = math.floor(sec - mins * 60) - return string.format("%.2d:%.2d", mins, seconds) -end - --- Interpolate string with named Parameters in Table --- @param s - string for interpolate --- @param tab - table with parameters --- @return string with replaced parameters -function M.interpolate_strinng(s, tab) - return (s:gsub('($%b{})', function(w) return tab[w:sub(3, -2)] or w end)) -end - -return M diff --git a/druid/help_modules/ui_helper.lua b/druid/help_modules/ui_helper.lua deleted file mode 100644 index 89e5aa5..0000000 --- a/druid/help_modules/ui_helper.lua +++ /dev/null @@ -1,16 +0,0 @@ -local M = {} - -function M.centrate_text_with_icon(text_node, icon_node) - local metr = gui.get_text_metrics_from_node(text_node) - local scl = gui.get_scale(text_node).x - local scl_i = gui.get_scale(icon_node).x - local pos_i = gui.get_position(icon_node) - local pos = gui.get_position(text_node) - local w = metr.width * scl * scl_i - local icon_w = gui.get_size(icon_node).x * scl_i - local width = w + icon_w + (math.abs(pos.x) - icon_w / 2) * scl_i - pos_i.x = width / 2 - (icon_w / 2) - gui.set_position(icon_node, pos_i) -end - -return M diff --git a/druid/helper.lua b/druid/helper.lua new file mode 100644 index 0000000..ccd08a4 --- /dev/null +++ b/druid/helper.lua @@ -0,0 +1,155 @@ +-- Druid helper module for gui layouts +-- @module helper + +local const = require("druid.const") + +local M = {} + + +--- Text node or icon node can be nil +local function get_text_width(text_node) + if text_node then + local text_metrics = gui.get_text_metrics_from_node(text_node) + local text_scale = gui.get_scale(text_node).x + return text_metrics.width * text_scale + end + + return 0 +end + + +local function get_icon_width(icon_node) + if icon_node then + local icon_scale_x = gui.get_scale(icon_node).x + return gui.get_size(icon_node).x * icon_scale_x -- icon width + end + + return 0 +end + + +--- Center two nodes. +-- Nodes will be center around 0 x position +-- text_node will be first (at left side) +-- @function helper.centrate_text_with_icon +-- @tparam[opt] text text_node Gui text node +-- @tparam[opt] box icon_node Gui box node +-- @tparam number margin Offset between nodes +function M.centrate_text_with_icon(text_node, icon_node, margin) + margin = margin or 0 + local text_width = get_text_width(text_node) + local icon_width = get_icon_width(icon_node) + local width = text_width + icon_width + + if text_node then + local pos = gui.get_position(text_node) + pos.x = -width/2 + text_width - margin/2 + gui.set_position(text_node, pos) + end + + if icon_node then + local icon_pos = gui.get_position(icon_node) + icon_pos.x = width/2 - icon_width + margin/2 + gui.set_position(icon_node, icon_pos) + end +end + + +--- Center two nodes. +-- Nodes will be center around 0 x position +-- icon_node will be first (at left side) +-- @function helper.centrate_icon_with_text +-- @tparam[opt] box icon_node Gui box node +-- @tparam[opt] text text_node Gui text node +-- @tparam[opt=0] number margin Offset between nodes +function M.centrate_icon_with_text(icon_node, text_node, margin) + margin = margin or 0 + local icon_width = get_icon_width(icon_node) + local text_width = get_text_width(text_node) + local width = text_width + icon_width + + if text_node then + local pos = gui.get_position(text_node) + pos.x = width/2 - text_width + margin/2 + gui.set_position(text_node, pos) + end + + if icon_node then + local icon_pos = gui.get_position(icon_node) + icon_pos.x = -width/2 + icon_width - margin/2 + gui.set_position(icon_node, icon_pos) + end +end + + +function M.step(current, target, step) + if current < target then + return math.min(current + step, target) + else + return math.max(target, current - step) + end +end + + +function M.clamp(a, min, max) + if min > max then + min, max = max, min + end + + if a >= min and a <= max then + return a + elseif a < min then + return min + else + return max + end +end + + +function M.distance(x1, y1, x2, y2) + return math.sqrt((x2 - x1) ^ 2 + (y2 - y1) ^ 2) +end + + +function M.sign(val) + if val == 0 then + return 0 + end + + return (val < 0) and -1 or 1 +end + + +function M.round(num, numDecimalPlaces) + local mult = 10^(numDecimalPlaces or 0) + return math.floor(num * mult + 0.5) / mult +end + + +--- Check if node is enabled in gui hierarchy. +-- Return false, if node or any his parent is disabled +-- @function helper.is_enabled +-- @tparam node node Gui node +-- @treturn bool Is enabled in hierarchy +function M.is_enabled(node) + local is_enabled = gui.is_enabled(node) + local parent = gui.get_parent(node) + while parent and is_enabled do + is_enabled = is_enabled and gui.is_enabled(parent) + parent = gui.get_parent(parent) + end + + return is_enabled +end + + +--- Get node offset for given gui pivot +-- @function helper.get_pivot_offset +-- @tparam gui.pivot pivot The node pivot +-- @treturn vector3 Vector offset with [-1..1] values +function M.get_pivot_offset(pivot) + return const.PIVOTS[pivot] +end + + +return M diff --git a/druid/helper/druid_input.lua b/druid/helper/druid_input.lua new file mode 100644 index 0000000..6a447c8 --- /dev/null +++ b/druid/helper/druid_input.lua @@ -0,0 +1,22 @@ +--- Druid inner module to acquire/release input +-- @module helper.input +-- @local + +local M = {} + +local ADD_FOCUS = hash("acquire_input_focus") +local REMOVE_FOCUS = hash("release_input_focus") +local PATH_OBJ = "." + + +function M.focus() + msg.post(PATH_OBJ, ADD_FOCUS) +end + + +function M.remove() + msg.post(PATH_OBJ, REMOVE_FOCUS) +end + + +return M diff --git a/druid/helper/formats.lua b/druid/helper/formats.lua new file mode 100644 index 0000000..626ee52 --- /dev/null +++ b/druid/helper/formats.lua @@ -0,0 +1,45 @@ +--- Druid module with utils on string formats +-- @local +-- @module helper.formats + +local const = require("druid.const") + +local M = {} + + +--- Return number with zero number prefix +-- @function formats.add_prefix_zeros +-- @tparam number num Number for conversion +-- @tparam number count Count of numerals +-- @return string with need count of zero (1,3) -> 001 +function M.add_prefix_zeros(num, count) + local result = tostring(num) + for i = string.len(result), count - 1 do + result = const.ZERO .. result + end + return result +end + + +--- Convert seconds to string minutes:seconds +-- @function formats.second_string_min +-- @tparam number sec Seconds +-- @return string minutes:seconds +function M.second_string_min(sec) + local mins = math.floor(sec / 60) + local seconds = math.floor(sec - mins * 60) + return string.format("%.2d:%.2d", mins, seconds) +end + + +--- Interpolate string with named Parameters in Table +-- @function formats.second_string_min +-- @tparam string s Target string +-- @tparam table tab Table with parameters +-- @return string with replaced parameters +function M.interpolate_string(s, tab) + return (s:gsub('($%b{})', function(w) return tab[w:sub(3, -2)] or w end)) +end + + +return M \ No newline at end of file diff --git a/druid/styles/default/anims.lua b/druid/styles/default/anims.lua new file mode 100644 index 0000000..ae56a40 --- /dev/null +++ b/druid/styles/default/anims.lua @@ -0,0 +1,38 @@ +local M = {} + + +local function scale_to(self, node, to, callback, time, delay, easing) + easing = easing or gui.EASING_INSINE + time = time or M.SCALE_ANIMATION_TIME + delay = delay or 0 + time = time or 0.25 + gui.animate(node, gui.PROP_SCALE, to, easing, time, delay, + function() + if callback then + callback(self, node) + end + end + ) +end + + +function M.back_scale_animation(self, node, target_scale) + scale_to(self, node, target_scale) +end + + +function M.tap_scale_animation(self, node, target_scale) + scale_to(self, node, target_scale, + function() + M.back_scale_animation(self, node, self.scale_from) + end + ) +end + + +function M.hover_scale(self, target, time) + gui.animate(self.anim_node, "scale", target, gui.EASING_OUTSINE, time) +end + + +return M diff --git a/druid/styles/default/style.lua b/druid/styles/default/style.lua new file mode 100644 index 0000000..f34558f --- /dev/null +++ b/druid/styles/default/style.lua @@ -0,0 +1,78 @@ +local settings = require("druid.system.settings") +local anims = require("druid.styles.default.anims") + +local M = {} + + +M["button"] = { + HOVER_SCALE = vmath.vector3(-0.025, -0.025, 1), + HOVER_TIME = 0.05, + SCALE_CHANGE = vmath.vector3(-0.05, -0.05, 1), + BTN_SOUND = "click", + BTN_SOUND_DISABLED = "click", + DISABLED_COLOR = vmath.vector4(0, 0, 0, 1), + ENABLED_COLOR = vmath.vector4(1), + LONGTAP_TIME = 0.4, + AUTOHOLD_TRIGGER = 0.8, + DOUBLETAP_TIME = 0.4, + IS_HOVER = true, + + on_hover = function(self, node, state) + local scale_to = self.scale_from + M.button.HOVER_SCALE + + local target_scale = state and scale_to or self.scale_from + anims.hover_scale(self, target_scale, M.button.HOVER_TIME) + end, + + on_click = function(self, node) + local scale_to = self.scale_from + M.button.SCALE_CHANGE + anims.tap_scale_animation(self, node, scale_to) + settings.play_sound(M.button.BTN_SOUND) + end, + + on_click_disabled = function(self, node) + settings.play_sound(M.button.BTN_SOUND_DISABLED) + end, + + on_set_enabled = function(self, node, state) + if state then + gui.set_color(node, M.button.ENABLED_COLOR) + else + gui.set_color(node, M.button.DISABLED_COLOR) + end + end +} + + +M["scroll"] = { + FRICT_HOLD = 0.8, -- mult. for inert, while touching + FRICT = 0.93, -- mult for free inert + INERT_THRESHOLD = 2, -- speed to stop inertion + INERT_SPEED = 25, -- koef. of inert speed + DEADZONE = 6, -- in px + SOFT_ZONE_SIZE = 160, -- size of outside zone (back move) + BACK_SPEED = 0.2, -- lerp speed + ANIM_SPEED = 0.3, -- gui.animation speed to point +} + + +M["progress"] = { + SPEED = 5, -- progress bar fill rate, more faster + MIN_DELTA = 0.005 +} + + +M["progress_rich"] = { + DELAY = 1, -- delay in seconds before main fill +} + + +M["checkbox"] = { + on_change_state = function(self, node, state) + local target = state and 1 or 0 + gui.animate(node, "color.w", target, gui.EASING_OUTSINE, 0.1) + end +} + + +return M diff --git a/druid/styles/empty/style.lua b/druid/styles/empty/style.lua new file mode 100644 index 0000000..27a9a43 --- /dev/null +++ b/druid/styles/empty/style.lua @@ -0,0 +1,45 @@ +local M = {} + + +M["button"] = { + BTN_SOUND = "click", + BTN_SOUND_DISABLED = "click", + DISABLED_COLOR = vmath.vector4(0, 0, 0, 1), + ENABLED_COLOR = vmath.vector4(1), + LONGTAP_TIME = 0.4, + DOUBLETAP_TIME = 0.4, + IS_HOVER = false, +} + + +M["scroll"] = { + FRICT_HOLD = 0, -- mult. for inert, while touching + FRICT = 0, -- mult for free inert + INERT_THRESHOLD = 2, -- speed to stop inertion + INERT_SPEED = 0, -- koef. of inert speed + DEADZONE = 6, -- in px + SOFT_ZONE_SIZE = 20, -- size of outside zone (back move) + BACK_SPEED = 0, -- lerp speed + ANIM_SPEED = 0, -- gui.animation speed to point +} + + +M["progress"] = { + SPEED = 5, -- progress bar fill rate, more faster + MIN_DELTA = 1 +} + + +M["progress_rich"] = { + DELAY = 0, -- delay in seconds before main fill +} + + +M["checkbox"] = { + on_change_state = function(self, node, state) + gui.set_enabled(node, state) + end +} + + +return M diff --git a/druid/styles/sprites/style.lua b/druid/styles/sprites/style.lua new file mode 100644 index 0000000..b99409d --- /dev/null +++ b/druid/styles/sprites/style.lua @@ -0,0 +1,52 @@ +local M = {} + + +M["button"] = { + BTN_SOUND = "click", + BTN_SOUND_DISABLED = "click", + DISABLED_COLOR = vmath.vector4(0, 0, 0, 1), + ENABLED_COLOR = vmath.vector4(1), + LONGTAP_TIME = 0.4, + DOUBLETAP_TIME = 0.4, + HOVER_IMAGE = "button_yellow", + DEFAULT_IMAGE = "button_blue", + CLICK_IMAGE = "button_red", + + on_hover = function(self, node, state) + local anim = state and M.button.HOVER_IMAGE or M.button.DEFAULT_IMAGE + gui.play_flipbook(node, anim) + end +} + + +M["scroll"] = { + FRICT_HOLD = 0, -- mult. for inert, while touching + FRICT = 0, -- mult for free inert + INERT_THRESHOLD = 2, -- speed to stop inertion + INERT_SPEED = 0, -- koef. of inert speed + DEADZONE = 6, -- in px + SOFT_ZONE_SIZE = 20, -- size of outside zone (back move) + BACK_SPEED = 0, -- lerp speed + ANIM_SPEED = 0, -- gui.animation speed to point +} + + +M["progress"] = { + SPEED = 5, -- progress bar fill rate, more faster + MIN_DELTA = 1 +} + + +M["progress_rich"] = { + DELAY = 0, -- delay in seconds before main fill +} + + +M["checkbox"] = { + on_change_state = function(self, node, state) + gui.set_enabled(node, state) + end +} + + +return M diff --git a/druid/system/druid_instance.lua b/druid/system/druid_instance.lua new file mode 100644 index 0000000..f06f10b --- /dev/null +++ b/druid/system/druid_instance.lua @@ -0,0 +1,350 @@ +--- Druid main class. Create instance of this +-- to start creating components +-- @module druid_instance +-- @see druid.button +-- @see druid.blocker +-- @see druid.back_handler +-- @see druid.input +-- @see druid.text +-- @see druid.lang_text +-- @see druid.timer +-- @see druid.progress +-- @see druid.grid +-- @see druid.scroll +-- @see druid.slider +-- @see druid.checkbox +-- @see druid.checkbox_group +-- @see druid.radio_group + +local const = require("druid.const") +local druid_input = require("druid.helper.druid_input") +local settings = require("druid.system.settings") +local class = require("druid.system.middleclass") + +local button = require("druid.base.button") +local blocker = require("druid.base.blocker") +local back_handler = require("druid.base.back_handler") +local hover = require("druid.base.hover") +local text = require("druid.base.text") +local lang_text = require("druid.base.lang_text") +local timer = require("druid.base.timer") +local progress = require("druid.base.progress") +local grid = require("druid.base.grid") +local scroll = require("druid.base.scroll") +local slider = require("druid.base.slider") +local checkbox = require("druid.base.checkbox") +local checkbox_group = require("druid.base.checkbox_group") +local radio_group = require("druid.base.radio_group") +local input = require("druid.base.input") +-- local infinity_scroll = require("druid.base.infinity_scroll") + +-- @classmod Druid +local Druid = class("druid.druid_instance") + + +local function input_init(self) + if not sys.get_config("druid.auto_focus") == "1" then + return + end + + if not self.input_inited then + self.input_inited = true + druid_input.focus() + end +end + + +-- Create the component itself +local function create(self, instance_class) + local instance = instance_class() + instance:setup_component(self._context, self._style) + + self.components[const.ALL] = self.components[const.ALL] or {} + table.insert(self.components[const.ALL], instance) + + local register_to = instance:get_interests() + if register_to then + for i = 1, #register_to do + local interest = register_to[i] + if not self.components[interest] then + self.components[interest] = {} + end + table.insert(self.components[interest], instance) + + if const.UI_INPUT[interest] then + input_init(self) + end + end + end + + return instance +end + + +local function process_input(action_id, action, components, is_input_consumed) + if not components then + return is_input_consumed + end + + for i = #components, 1, -1 do + local component = components[i] + + if not is_input_consumed then + is_input_consumed = component:on_input(action_id, action) + else + if component.on_input_interrupt then + component:on_input_interrupt() + end + end + end + + return is_input_consumed +end + + +--- Druid class constructor +-- @function druid:initialize +-- @tparam context table Druid context. Usually it is self of script +-- @tparam style table Druid style module +function Druid.initialize(self, context, style) + self._context = context + self._style = style or settings.default_style + self.components = {} +end + + +--- Create new druid component +-- @function druid:create +-- @tparam Component component Component module +-- @tparam args ... Other component params to pass it to component:init function +function Druid.create(self, component, ...) + local instance = create(self, component) + + if instance.init then + instance:init(...) + end + + return instance +end + + +--- Remove component from druid instance. +-- Component `on_remove` function will be invoked, if exist. +-- @function druid:remove +-- @tparam Component component Component instance +function Druid.remove(self, component) + local all_components = self.components[const.ALL] + for i = #all_components, 1, -1 do + if all_components[i] == component then + if component.on_remove then + component:on_remove() + end + table.remove(self, i) + end + end + + local interests = component:get_interests() + if interests then + for i = 1, #interests do + local interest = interests[i] + local components = self.components[interest] + for j = #components, 1, -1 do + if components[j] == component then + table.remove(components, j) + end + end + end + end +end + + +--- Druid update function +-- @function druid:update +-- @tparam number dt Delta time +function Druid.update(self, dt) + local components = self.components[const.ON_UPDATE] + if components then + for i = 1, #components do + components[i]:update(dt) + end + end +end + + +--- Druid on_input function +-- @function druid:on_input +-- @tparam hash action_id Action_id from on_input +-- @tparam table action Action from on_input +function Druid.on_input(self, action_id, action) + local is_input_consumed = false + + is_input_consumed = process_input(action_id, action, + self.components[const.ON_INPUT_HIGH], is_input_consumed) + + is_input_consumed = process_input(action_id, action, + self.components[const.ON_INPUT], is_input_consumed) + + return is_input_consumed +end + + +--- Druid on_message function +-- @function druid:on_message +-- @tparam hash message_id Message_id from on_message +-- @tparam table message Message from on_message +-- @tparam hash sender Sender from on_message +function Druid.on_message(self, message_id, message, sender) + local specific_ui_message = const.SPECIFIC_UI_MESSAGES[message_id] + if specific_ui_message then + local components = self.components[message_id] + if components then + for i = 1, #components do + local component = components[i] + component[specific_ui_message](component, message, sender) + end + end + else + local components = self.components[const.ON_MESSAGE] or const.EMPTY_TABLE + for i = 1, #components do + components[i]:on_message(message_id, message, sender) + end + end +end + + +--- Create button basic component +-- @function druid:new_button +-- @tparam args ... button init args +-- @treturn Component button component +function Druid.new_button(self, ...) + return Druid.create(self, button, ...) +end + + +--- Create blocker basic component +-- @function druid:new_blocker +-- @tparam args ... blocker init args +-- @treturn Component blocker component +function Druid.new_blocker(self, ...) + return Druid.create(self, blocker, ...) +end + + +--- Create back_handler basic component +-- @function druid:new_back_handler +-- @tparam args ... back_handler init args +-- @treturn Component back_handler component +function Druid.new_back_handler(self, ...) + return Druid.create(self, back_handler, ...) +end + + +--- Create hover basic component +-- @function druid:new_hover +-- @tparam args ... hover init args +-- @treturn Component hover component +function Druid.new_hover(self, ...) + return Druid.create(self, hover, ...) +end + + +--- Create text basic component +-- @function druid:new_text +-- @tparam args ... text init args +-- @treturn Component text component +function Druid.new_text(self, ...) + return Druid.create(self, text, ...) +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 timer basic component +-- @function druid:new_timer +-- @tparam args ... timer init args +-- @treturn Component timer component +function Druid.new_timer(self, ...) + return Druid.create(self, timer, ...) +end + + +--- Create progress basic component +-- @function druid:new_progress +-- @tparam args ... progress init args +-- @treturn Component progress component +function Druid.new_progress(self, ...) + return Druid.create(self, progress, ...) +end + + +--- Create grid basic component +-- @function druid:new_grid +-- @tparam args ... grid init args +-- @treturn Component grid component +function Druid.new_grid(self, ...) + return Druid.create(self, grid, ...) +end + + +--- Create scroll basic component +-- @function druid:new_scroll +-- @tparam args ... scroll init args +-- @treturn Component scroll component +function Druid.new_scroll(self, ...) + return Druid.create(self, scroll, ...) +end + + +--- Create slider basic component +-- @function druid:new_slider +-- @tparam args ... slider init args +-- @treturn Component slider component +function Druid.new_slider(self, ...) + return Druid.create(self, slider, ...) +end + + +--- Create checkbox basic component +-- @function druid:new_checkbox +-- @tparam args ... checkbox init args +-- @treturn Component checkbox component +function Druid.new_checkbox(self, ...) + return Druid.create(self, checkbox, ...) +end + + +--- Create input basic component +-- @function druid:new_input +-- @tparam args ... input init args +-- @treturn Component input component +function Druid.new_input(self, ...) + return Druid.create(self, input, ...) +end + + +--- Create checkbox_group basic component +-- @function druid:new_checkbox_group +-- @tparam args ... checkbox_group init args +-- @treturn Component checkbox_group component +function Druid.new_checkbox_group(self, ...) + return Druid.create(self, checkbox_group, ...) +end + + +--- Create radio_group basic component +-- @function druid:new_radio_group +-- @tparam args ... radio_group init args +-- @treturn Component radio_group component +function Druid.new_radio_group(self, ...) + return Druid.create(self, radio_group, ...) +end + + +return Druid diff --git a/druid/system/middleclass.lua b/druid/system/middleclass.lua new file mode 100644 index 0000000..7e36bcd --- /dev/null +++ b/druid/system/middleclass.lua @@ -0,0 +1,183 @@ +local middleclass = { + _VERSION = 'middleclass v4.1.1', + _DESCRIPTION = 'Object Orientation for Lua', + _URL = 'https://github.com/kikito/middleclass', + _LICENSE = [[ + MIT LICENSE + + Copyright (c) 2011 Enrique García Cota + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ]] +} + +local function _createIndexWrapper(aClass, f) + if f == nil then + return aClass.__instanceDict + else + return function(self, name) + local value = aClass.__instanceDict[name] + + if value ~= nil then + return value + elseif type(f) == "function" then + return (f(self, name)) + else + return f[name] + end + end + end +end + +local function _propagateInstanceMethod(aClass, name, f) + f = name == "__index" and _createIndexWrapper(aClass, f) or f + aClass.__instanceDict[name] = f + + for subclass in pairs(aClass.subclasses) do + if rawget(subclass.__declaredMethods, name) == nil then + _propagateInstanceMethod(subclass, name, f) + end + end +end + +local function _declareInstanceMethod(aClass, name, f) + aClass.__declaredMethods[name] = f + + if f == nil and aClass.super then + f = aClass.super.__instanceDict[name] + end + + _propagateInstanceMethod(aClass, name, f) +end + +local function _tostring(self) return "class " .. self.name end +local function _call(self, ...) return self:new(...) end + +local function _createClass(name, super) + local dict = {} + dict.__index = dict + + local aClass = { name = name, super = super, static = {}, + __instanceDict = dict, __declaredMethods = {}, + subclasses = setmetatable({}, {__mode='k'}) } + + if super then + setmetatable(aClass.static, { + __index = function(_,k) + local result = rawget(dict,k) + if result == nil then + return super.static[k] + end + return result + end + }) + else + setmetatable(aClass.static, { __index = function(_,k) return rawget(dict,k) end }) + end + + setmetatable(aClass, { __index = aClass.static, __tostring = _tostring, + __call = _call, __newindex = _declareInstanceMethod }) + + return aClass +end + +local function _includeMixin(aClass, mixin) + assert(type(mixin) == 'table', "mixin must be a table") + + for name,method in pairs(mixin) do + if name ~= "included" and name ~= "static" then aClass[name] = method end + end + + for name,method in pairs(mixin.static or {}) do + aClass.static[name] = method + end + + if type(mixin.included)=="function" then mixin:included(aClass) end + return aClass +end + +local DefaultMixin = { + __tostring = function(self) return "instance of " .. tostring(self.class) end, + + initialize = function(self, ...) end, + + isInstanceOf = function(self, aClass) + return type(aClass) == 'table' + and type(self) == 'table' + and (self.class == aClass + or type(self.class) == 'table' + and type(self.class.isSubclassOf) == 'function' + and self.class:isSubclassOf(aClass)) + end, + + static = { + allocate = function(self) + assert(type(self) == 'table', "Make sure that you are using 'Class:allocate' instead of 'Class.allocate'") + return setmetatable({ class = self }, self.__instanceDict) + end, + + new = function(self, ...) + assert(type(self) == 'table', "Make sure that you are using 'Class:new' instead of 'Class.new'") + local instance = self:allocate() + instance:initialize(...) + return instance + end, + + subclass = function(self, name) + assert(type(self) == 'table', "Make sure that you are using 'Class:subclass' instead of 'Class.subclass'") + assert(type(name) == "string", "You must provide a name(string) for your class") + + local subclass = _createClass(name, self) + + for methodName, f in pairs(self.__instanceDict) do + _propagateInstanceMethod(subclass, methodName, f) + end + subclass.initialize = function(instance, ...) return self.initialize(instance, ...) end + + self.subclasses[subclass] = true + self:subclassed(subclass) + + return subclass + end, + + subclassed = function(self, other) end, + + isSubclassOf = function(self, other) + return type(other) == 'table' and + type(self.super) == 'table' and + ( self.super == other or self.super:isSubclassOf(other) ) + end, + + include = function(self, ...) + assert(type(self) == 'table', "Make sure you that you are using 'Class:include' instead of 'Class.include'") + for _,mixin in ipairs({...}) do _includeMixin(self, mixin) end + return self + end + } +} + +function middleclass.class(name, super) + assert(type(name) == 'string', "A name (string) is needed for the new class") + return super and super:subclass(name) or _includeMixin(_createClass(name), DefaultMixin) +end + +setmetatable(middleclass, { __call = function(_, ...) return middleclass.class(...) end }) + +return middleclass diff --git a/druid/system/settings.lua b/druid/system/settings.lua new file mode 100644 index 0000000..0dfe1a1 --- /dev/null +++ b/druid/system/settings.lua @@ -0,0 +1,18 @@ +--- Druid settings file +-- @module settings +-- @local + +local M = {} + +M.default_style = nil + +function M.get_text(name) + return "[Druid]: locales not inited" +end + + +function M.play_sound(name) +end + + +return M diff --git a/example/assets/fonts/exo2.ttf b/example/assets/fonts/exo2.ttf new file mode 100755 index 0000000..49f0b49 Binary files /dev/null and b/example/assets/fonts/exo2.ttf differ diff --git a/example/assets/fonts/game.font b/example/assets/fonts/game.font new file mode 100644 index 0000000..fda7575 --- /dev/null +++ b/example/assets/fonts/game.font @@ -0,0 +1,17 @@ +font: "/example/assets/fonts/exo2.ttf" +material: "/builtins/fonts/font-df.material" +size: 40 +antialias: 1 +alpha: 1.0 +outline_alpha: 1.0 +outline_width: 2.0 +shadow_alpha: 1.0 +shadow_blur: 0 +shadow_x: 3.0 +shadow_y: -4.0 +extra_characters: "" +output_format: TYPE_DISTANCE_FIELD +all_chars: true +cache_width: 0 +cache_height: 0 +render_mode: MODE_MULTI_LAYER diff --git a/example/assets/images/back/back_blue.png b/example/assets/images/back/back_blue.png new file mode 100755 index 0000000..73dcfd0 Binary files /dev/null and b/example/assets/images/back/back_blue.png differ diff --git a/example/assets/images/back/back_gray.png b/example/assets/images/back/back_gray.png new file mode 100755 index 0000000..3d6f82c Binary files /dev/null and b/example/assets/images/back/back_gray.png differ diff --git a/example/assets/images/back/back_green.png b/example/assets/images/back/back_green.png new file mode 100755 index 0000000..8ec8548 Binary files /dev/null and b/example/assets/images/back/back_green.png differ diff --git a/example/assets/images/back/back_red.png b/example/assets/images/back/back_red.png new file mode 100755 index 0000000..e771c39 Binary files /dev/null and b/example/assets/images/back/back_red.png differ diff --git a/example/assets/images/buttons/button_blue.png b/example/assets/images/buttons/button_blue.png new file mode 100755 index 0000000..5ec356a Binary files /dev/null and b/example/assets/images/buttons/button_blue.png differ diff --git a/example/assets/images/buttons/button_green.png b/example/assets/images/buttons/button_green.png new file mode 100755 index 0000000..1da6a09 Binary files /dev/null and b/example/assets/images/buttons/button_green.png differ diff --git a/example/assets/images/buttons/button_red.png b/example/assets/images/buttons/button_red.png new file mode 100755 index 0000000..7263038 Binary files /dev/null and b/example/assets/images/buttons/button_red.png differ diff --git a/example/assets/images/buttons/button_yellow.png b/example/assets/images/buttons/button_yellow.png new file mode 100755 index 0000000..e00e7e3 Binary files /dev/null and b/example/assets/images/buttons/button_yellow.png differ diff --git a/example/assets/images/empty.png b/example/assets/images/empty.png new file mode 100755 index 0000000..5da9ec0 Binary files /dev/null and b/example/assets/images/empty.png differ diff --git a/example/assets/images/kenney.atlas b/example/assets/images/kenney.atlas new file mode 100644 index 0000000..622bf0d --- /dev/null +++ b/example/assets/images/kenney.atlas @@ -0,0 +1,79 @@ +images { + image: "/example/assets/images/back/back_blue.png" + sprite_trim_mode: SPRITE_TRIM_MODE_OFF +} +images { + image: "/example/assets/images/back/back_gray.png" + sprite_trim_mode: SPRITE_TRIM_MODE_OFF +} +images { + image: "/example/assets/images/back/back_green.png" + sprite_trim_mode: SPRITE_TRIM_MODE_OFF +} +images { + image: "/example/assets/images/back/back_red.png" + sprite_trim_mode: SPRITE_TRIM_MODE_OFF +} +images { + image: "/example/assets/images/buttons/button_green.png" + sprite_trim_mode: SPRITE_TRIM_MODE_OFF +} +images { + image: "/example/assets/images/buttons/button_red.png" + sprite_trim_mode: SPRITE_TRIM_MODE_OFF +} +images { + image: "/example/assets/images/buttons/button_yellow.png" + sprite_trim_mode: SPRITE_TRIM_MODE_OFF +} +images { + image: "/example/assets/images/empty.png" + sprite_trim_mode: SPRITE_TRIM_MODE_OFF +} +images { + image: "/example/assets/images/progress/progress_back.png" + sprite_trim_mode: SPRITE_TRIM_MODE_OFF +} +images { + image: "/example/assets/images/progress/progress_fill_green.png" + sprite_trim_mode: SPRITE_TRIM_MODE_OFF +} +images { + image: "/example/assets/images/progress/progress_fill_red.png" + sprite_trim_mode: SPRITE_TRIM_MODE_OFF +} +images { + image: "/example/assets/images/progress/progress_fill_yellow.png" + sprite_trim_mode: SPRITE_TRIM_MODE_OFF +} +images { + image: "/example/assets/images/radio/check_back_circle.png" + sprite_trim_mode: SPRITE_TRIM_MODE_OFF +} +images { + image: "/example/assets/images/radio/check_back_square.png" + sprite_trim_mode: SPRITE_TRIM_MODE_OFF +} +images { + image: "/example/assets/images/radio/checkmark.png" + sprite_trim_mode: SPRITE_TRIM_MODE_OFF +} +images { + image: "/example/assets/images/radio/tick.png" + sprite_trim_mode: SPRITE_TRIM_MODE_OFF +} +images { + image: "/example/assets/images/slider/slider_back.png" + sprite_trim_mode: SPRITE_TRIM_MODE_OFF +} +images { + image: "/example/assets/images/slider/slider_move.png" + sprite_trim_mode: SPRITE_TRIM_MODE_OFF +} +images { + image: "/example/assets/images/buttons/button_blue.png" + sprite_trim_mode: SPRITE_TRIM_MODE_OFF +} +margin: 0 +extrude_borders: 2 +inner_padding: 0 diff --git a/example/assets/images/progress/progress_back.png b/example/assets/images/progress/progress_back.png new file mode 100755 index 0000000..662b16a Binary files /dev/null and b/example/assets/images/progress/progress_back.png differ diff --git a/example/assets/images/progress/progress_fill_green.png b/example/assets/images/progress/progress_fill_green.png new file mode 100755 index 0000000..195fa3b Binary files /dev/null and b/example/assets/images/progress/progress_fill_green.png differ diff --git a/example/assets/images/progress/progress_fill_red.png b/example/assets/images/progress/progress_fill_red.png new file mode 100755 index 0000000..32f5f4c Binary files /dev/null and b/example/assets/images/progress/progress_fill_red.png differ diff --git a/example/assets/images/progress/progress_fill_yellow.png b/example/assets/images/progress/progress_fill_yellow.png new file mode 100755 index 0000000..70ee720 Binary files /dev/null and b/example/assets/images/progress/progress_fill_yellow.png differ diff --git a/example/assets/images/radio/check_back_circle.png b/example/assets/images/radio/check_back_circle.png new file mode 100755 index 0000000..877a3e0 Binary files /dev/null and b/example/assets/images/radio/check_back_circle.png differ diff --git a/example/assets/images/radio/check_back_square.png b/example/assets/images/radio/check_back_square.png new file mode 100755 index 0000000..f17bb8f Binary files /dev/null and b/example/assets/images/radio/check_back_square.png differ diff --git a/example/assets/images/radio/checkmark.png b/example/assets/images/radio/checkmark.png new file mode 100755 index 0000000..3ad298e Binary files /dev/null and b/example/assets/images/radio/checkmark.png differ diff --git a/example/assets/images/radio/tick.png b/example/assets/images/radio/tick.png new file mode 100755 index 0000000..f0c945d Binary files /dev/null and b/example/assets/images/radio/tick.png differ diff --git a/example/assets/images/slider/slider_back.png b/example/assets/images/slider/slider_back.png new file mode 100755 index 0000000..d28abb9 Binary files /dev/null and b/example/assets/images/slider/slider_back.png differ diff --git a/example/assets/images/slider/slider_move.png b/example/assets/images/slider/slider_move.png new file mode 100755 index 0000000..04018c5 Binary files /dev/null and b/example/assets/images/slider/slider_move.png differ diff --git a/example/assets/sounds/click.ogg b/example/assets/sounds/click.ogg new file mode 100755 index 0000000..4f17f1c Binary files /dev/null and b/example/assets/sounds/click.ogg differ diff --git a/example/example.collection b/example/example.collection deleted file mode 100644 index 42fe135..0000000 --- a/example/example.collection +++ /dev/null @@ -1,37 +0,0 @@ -name: "main" -scale_along_z: 0 -embedded_instances { - id: "go" - data: "components {\n" - " id: \"example\"\n" - " component: \"/example/example.gui\"\n" - " position {\n" - " x: 0.0\n" - " y: 0.0\n" - " z: 0.0\n" - " }\n" - " rotation {\n" - " x: 0.0\n" - " y: 0.0\n" - " z: 0.0\n" - " w: 1.0\n" - " }\n" - "}\n" - "" - position { - x: 0.0 - y: 0.0 - z: 0.0 - } - rotation { - x: 0.0 - y: 0.0 - z: 0.0 - w: 1.0 - } - scale3 { - x: 1.0 - y: 1.0 - z: 1.0 - } -} diff --git a/example/example.gui b/example/example.gui deleted file mode 100644 index 12b1015..0000000 --- a/example/example.gui +++ /dev/null @@ -1,10 +0,0 @@ -script: "/example/example.gui.gui_script" -background_color { - x: 0.0 - y: 0.0 - z: 0.0 - w: 0.0 -} -material: "/builtins/materials/gui.material" -adjust_reference: ADJUST_REFERENCE_PARENT -max_nodes: 512 diff --git a/example/example.gui.gui_script b/example/example.gui.gui_script deleted file mode 100644 index ce07f4f..0000000 --- a/example/example.gui.gui_script +++ /dev/null @@ -1,29 +0,0 @@ -local druid = require "druid.druid" - -function init(self) -end - -function final(self) - -- Add finalization code here - -- Remove this function if not needed -end - -function update(self, dt) - -- Add update code here - -- Remove this function if not needed -end - -function on_message(self, message_id, message, sender) - -- Add message-handling code here - -- Remove this function if not needed -end - -function on_input(self, action_id, action) - -- Add input-handling code here - -- Remove this function if not needed -end - -function on_reload(self) - -- Add input-handling code here - -- Remove this function if not needed -end diff --git a/example/gui/main/main.gui b/example/gui/main/main.gui new file mode 100644 index 0000000..4e3a5ec --- /dev/null +++ b/example/gui/main/main.gui @@ -0,0 +1,6411 @@ +script: "/example/gui/main/main.gui_script" +fonts { + name: "game" + font: "/example/assets/fonts/game.font" +} +textures { + name: "kenney" + texture: "/example/assets/images/kenney.atlas" +} +background_color { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 +} +nodes { + position { + x: 300.0 + y: 450.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 1.0 + y: 1.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/empty" + id: "root" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_STRETCH + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_AUTO +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 1.0 + y: 1.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/empty" + id: "C_Anchor" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "root" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_AUTO +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 600.0 + y: 900.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/empty" + id: "main_page" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_STRETCH + parent: "C_Anchor" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 0.0 + y: 450.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 600.0 + y: 1250.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/empty" + id: "scroll_content" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_N + adjust_mode: ADJUST_MODE_FIT + parent: "main_page" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 0.0 + y: -170.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 1.0 + y: 1.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/empty" + id: "section_button" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "scroll_content" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_AUTO +} +nodes { + position { + x: -250.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Button:" + font: "game" + id: "text_button" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_W + outline { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "section_button" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 0.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: 6.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEMPLATE + id: "button_template" + parent: "section_button" + layer: "" + inherit_alpha: true + alpha: 1.0 + template: "/example/templates/button.gui" + template_node_child: false +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 130.0 + y: 60.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/button_blue" + id: "button_template/button" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "button_template" + layer: "" + inherit_alpha: true + slice9 { + x: 15.0 + y: 15.0 + z: 15.0 + w: 15.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: true + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 0.0 + y: 7.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Tap me!" + font: "game" + id: "button_template/text" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + shadow { + x: 0.101960786 + y: 0.2 + z: 0.6 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "button_template/button" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 0.0 + shadow_alpha: 0.78 + template_node_child: true + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: 156.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 130.0 + y: 60.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/button_green" + id: "button_simple" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "section_button" + layer: "" + inherit_alpha: true + slice9 { + x: 15.0 + y: 15.0 + z: 15.0 + w: 5.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 0.0 + y: 5.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 150.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Change Lang" + font: "game" + id: "text_button_lang" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: true + parent: "button_simple" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 0.0 + template_node_child: false + text_leading: 0.8 + text_tracking: 0.0 +} +nodes { + position { + x: 0.0 + y: -260.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 1.0 + y: 1.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/empty" + id: "section_text" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "scroll_content" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_AUTO +} +nodes { + position { + x: -250.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Text:" + font: "game" + id: "text_text" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_W + outline { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "section_text" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 0.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: 150.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Translated" + font: "game" + id: "text_translated" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "section_text" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 0.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Simple" + font: "game" + id: "text_simple" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "section_text" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 0.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: 0.0 + y: -350.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 1.0 + y: 1.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/empty" + id: "section_timer" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "scroll_content" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_AUTO +} +nodes { + position { + x: -250.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Timer:" + font: "game" + id: "text_timer" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_W + outline { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "section_timer" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 0.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: 220.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "12:20" + font: "game" + id: "timer" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_E + outline { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "section_timer" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 0.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: 0.0 + y: -440.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 1.0 + y: 1.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/empty" + id: "section_progress" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "scroll_content" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_AUTO +} +nodes { + position { + x: -250.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 300.0 + y: 60.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Progress bar:" + font: "game" + id: "text_progress" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_W + outline { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "section_progress" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 0.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: 30.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 190.0 + y: 45.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/progress_back" + id: "progress_back" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_W + adjust_mode: ADJUST_MODE_FIT + parent: "section_progress" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_AUTO +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 190.0 + y: 45.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/progress_fill_yellow" + id: "progress_fill" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_W + adjust_mode: ADJUST_MODE_FIT + parent: "progress_back" + layer: "" + inherit_alpha: true + slice9 { + x: 10.0 + y: 0.0 + z: 10.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 95.0 + y: 2.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 100.0 + y: 60.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "15%" + font: "game" + id: "text_progress_amount" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "progress_back" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 0.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: 0.0 + y: -530.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 1.0 + y: 1.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/empty" + id: "section_slider" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "scroll_content" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_AUTO +} +nodes { + position { + x: -250.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 300.0 + y: 60.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Slider:" + font: "game" + id: "text_slider" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_W + outline { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "section_slider" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 0.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: 130.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 190.0 + y: 4.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/slider_back" + id: "slider_back" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "section_slider" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_AUTO +} +nodes { + position { + x: -95.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 36.0 + y: 36.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/slider_move" + id: "slider_pin" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "slider_back" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_AUTO +} +nodes { + position { + x: 0.0 + y: 20.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 100.0 + y: 60.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "50%" + font: "game" + id: "text_progress_slider" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "slider_back" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 0.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: 0.0 + y: -620.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 1.0 + y: 1.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/empty" + id: "section_radio" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "scroll_content" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_AUTO +} +nodes { + position { + x: -250.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 300.0 + y: 60.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Radio:" + font: "game" + id: "text_radio" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_W + outline { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "section_radio" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 0.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: 60.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 1.0 + y: 1.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/empty" + id: "radio_group" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "section_radio" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_AUTO +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEMPLATE + id: "radio1" + parent: "radio_group" + layer: "" + inherit_alpha: true + alpha: 1.0 + template: "/example/templates/radio.gui" + template_node_child: false +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 36.0 + y: 36.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/check_back_circle" + id: "radio1/back" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "radio1" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: true + size_mode: SIZE_MODE_AUTO +} +nodes { + position { + x: -0.5 + y: 0.5 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 17.0 + y: 17.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/tick" + id: "radio1/check" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "radio1/back" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: true + size_mode: SIZE_MODE_AUTO +} +nodes { + position { + x: 70.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEMPLATE + id: "radio2" + parent: "radio_group" + layer: "" + inherit_alpha: true + alpha: 1.0 + template: "/example/templates/radio.gui" + template_node_child: false +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 36.0 + y: 36.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/check_back_circle" + id: "radio2/back" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "radio2" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: true + size_mode: SIZE_MODE_AUTO +} +nodes { + position { + x: -0.5 + y: 0.5 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 17.0 + y: 17.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/tick" + id: "radio2/check" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "radio2/back" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: true + size_mode: SIZE_MODE_AUTO +} +nodes { + position { + x: 140.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEMPLATE + id: "radio3" + parent: "radio_group" + layer: "" + inherit_alpha: true + alpha: 1.0 + template: "/example/templates/radio.gui" + template_node_child: false +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 36.0 + y: 36.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/check_back_circle" + id: "radio3/back" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "radio3" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: true + size_mode: SIZE_MODE_AUTO +} +nodes { + position { + x: -0.5 + y: 0.5 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 17.0 + y: 17.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/tick" + id: "radio3/check" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "radio3/back" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: true + size_mode: SIZE_MODE_AUTO +} +nodes { + position { + x: 0.0 + y: -710.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 1.0 + y: 1.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/empty" + id: "section_checkbox" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "scroll_content" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_AUTO +} +nodes { + position { + x: -250.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 300.0 + y: 60.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Checkbox:" + font: "game" + id: "text_checkbox" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_W + outline { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "section_checkbox" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 0.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: 60.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 1.0 + y: 1.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/empty" + id: "checkbox_group" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "section_checkbox" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_AUTO +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEMPLATE + id: "checkbox1" + parent: "checkbox_group" + layer: "" + inherit_alpha: true + alpha: 1.0 + template: "/example/templates/checkbox.gui" + template_node_child: false +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 38.0 + y: 36.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/check_back_square" + id: "checkbox1/back" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "checkbox1" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: true + size_mode: SIZE_MODE_AUTO +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 21.0 + y: 20.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/checkmark" + id: "checkbox1/check" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "checkbox1/back" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: true + size_mode: SIZE_MODE_AUTO +} +nodes { + position { + x: 70.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEMPLATE + id: "checkbox2" + parent: "checkbox_group" + layer: "" + inherit_alpha: true + alpha: 1.0 + template: "/example/templates/checkbox.gui" + template_node_child: false +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 38.0 + y: 36.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/check_back_square" + id: "checkbox2/back" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "checkbox2" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: true + size_mode: SIZE_MODE_AUTO +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 21.0 + y: 20.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/checkmark" + id: "checkbox2/check" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "checkbox2/back" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: true + size_mode: SIZE_MODE_AUTO +} +nodes { + position { + x: 140.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEMPLATE + id: "checkbox3" + parent: "checkbox_group" + layer: "" + inherit_alpha: true + alpha: 1.0 + template: "/example/templates/checkbox.gui" + template_node_child: false +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 38.0 + y: 36.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/check_back_square" + id: "checkbox3/back" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "checkbox3" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: true + size_mode: SIZE_MODE_AUTO +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 21.0 + y: 20.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/checkmark" + id: "checkbox3/check" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "checkbox3/back" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: true + size_mode: SIZE_MODE_AUTO +} +nodes { + position { + x: 0.0 + y: -800.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 1.0 + y: 1.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/empty" + id: "section_grid" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "scroll_content" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_AUTO +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 400.0 + y: 400.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/empty" + id: "grid" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_N + adjust_mode: ADJUST_MODE_FIT + parent: "section_grid" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 600.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 1.0 + y: 1.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/empty" + id: "text_page" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_STRETCH + parent: "C_Anchor" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_AUTO +} +nodes { + position { + x: -250.0 + y: 280.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Inline:" + font: "game" + id: "inline" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_W + outline { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "text_page" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 0.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: -250.0 + y: 190.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Multiline:" + font: "game" + id: "multiline" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_W + outline { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "text_page" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 0.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: -250.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Anchoring:" + font: "game" + id: "anchoring" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_W + outline { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "text_page" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 0.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: -250.0 + y: 10.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "No adjust:" + font: "game" + id: "no_adjust" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_W + outline { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "text_page" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 0.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: -250.0 + y: -80.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Locale:" + font: "game" + id: "locale" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_W + outline { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "text_page" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 0.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: -250.0 + y: -170.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Max Width:" + font: "game" + id: "max_width" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_W + outline { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "text_page" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 0.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: -250.0 + y: -260.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Max height:" + font: "game" + id: "max_height" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_W + outline { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "text_page" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 0.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: 150.0 + y: 280.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 300.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Inline:" + font: "game" + id: "text_inline" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "text_page" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 0.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: 150.0 + y: 190.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 300.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Multiline" + font: "game" + id: "text_multiline" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: true + parent: "text_page" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 0.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: 150.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 300.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 0.7019608 + y: 0.8 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "" + id: "anchoring_zone_visual" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "text_page" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 150.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 300.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Anchoring:" + font: "game" + id: "text_anchoring" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "text_page" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 0.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: 150.0 + y: 10.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 300.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "No adjust:" + font: "game" + id: "text_no_adjust" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "text_page" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 0.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: 150.0 + y: -80.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 300.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Locale:" + font: "game" + id: "text_locale" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "text_page" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 0.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: 150.0 + y: -170.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 300.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Max Width:" + font: "game" + id: "text_max_width" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "text_page" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 0.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: 150.0 + y: -260.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 300.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 0.7019608 + y: 0.7019608 + z: 0.7019608 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "" + id: "text_max_height_visual" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "text_page" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 0.7 + template_node_child: false + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 150.0 + y: -260.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 300.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Max height:" + font: "game" + id: "text_max_height" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: true + parent: "text_page" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 0.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: 1200.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 1.0 + y: 1.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/empty" + id: "button_page" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_STRETCH + parent: "C_Anchor" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_AUTO +} +nodes { + position { + x: -200.0 + y: 280.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEMPLATE + id: "button_usual" + parent: "button_page" + layer: "" + inherit_alpha: true + alpha: 1.0 + template: "/example/templates/button.gui" + template_node_child: false +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 130.0 + y: 60.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/button_blue" + id: "button_usual/button" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "button_usual" + layer: "" + inherit_alpha: true + slice9 { + x: 15.0 + y: 15.0 + z: 15.0 + w: 15.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: true + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 0.0 + y: 7.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Usual" + font: "game" + id: "button_usual/text" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + shadow { + x: 0.101960786 + y: 0.2 + z: 0.6 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "button_usual/button" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 0.0 + shadow_alpha: 0.78 + overridden_fields: 8 + template_node_child: true + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: -200.0 + y: 180.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEMPLATE + id: "button_custom_style" + parent: "button_page" + layer: "" + inherit_alpha: true + alpha: 1.0 + template: "/example/templates/button.gui" + template_node_child: false +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 130.0 + y: 60.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/button_blue" + id: "button_custom_style/button" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "button_custom_style" + layer: "" + inherit_alpha: true + slice9 { + x: 15.0 + y: 15.0 + z: 15.0 + w: 15.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: true + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 0.0 + y: 7.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.5 + y: 0.5 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Custom Style" + font: "game" + id: "button_custom_style/text" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + shadow { + x: 0.101960786 + y: 0.2 + z: 0.6 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "button_custom_style/button" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 0.0 + shadow_alpha: 0.78 + overridden_fields: 3 + overridden_fields: 8 + template_node_child: true + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: -200.0 + y: 80.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEMPLATE + id: "button_long_tap" + parent: "button_page" + layer: "" + inherit_alpha: true + alpha: 1.0 + template: "/example/templates/button.gui" + template_node_child: false +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 130.0 + y: 60.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/button_blue" + id: "button_long_tap/button" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "button_long_tap" + layer: "" + inherit_alpha: true + slice9 { + x: 15.0 + y: 15.0 + z: 15.0 + w: 15.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: true + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 0.0 + y: 7.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.5 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Long tap" + font: "game" + id: "button_long_tap/text" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + shadow { + x: 0.101960786 + y: 0.2 + z: 0.6 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "button_long_tap/button" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 0.0 + shadow_alpha: 0.78 + overridden_fields: 3 + overridden_fields: 8 + template_node_child: true + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: -200.0 + y: -20.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEMPLATE + id: "button_repeated_tap" + parent: "button_page" + layer: "" + inherit_alpha: true + alpha: 1.0 + template: "/example/templates/button.gui" + template_node_child: false +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 130.0 + y: 60.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/button_blue" + id: "button_repeated_tap/button" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "button_repeated_tap" + layer: "" + inherit_alpha: true + slice9 { + x: 15.0 + y: 15.0 + z: 15.0 + w: 15.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: true + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 0.0 + y: 7.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.5 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Repeated" + font: "game" + id: "button_repeated_tap/text" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + shadow { + x: 0.101960786 + y: 0.2 + z: 0.6 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "button_repeated_tap/button" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 0.0 + shadow_alpha: 0.78 + overridden_fields: 3 + overridden_fields: 8 + template_node_child: true + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: -200.0 + y: -120.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEMPLATE + id: "button_double_tap" + parent: "button_page" + layer: "" + inherit_alpha: true + alpha: 1.0 + template: "/example/templates/button.gui" + template_node_child: false +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 130.0 + y: 60.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/button_blue" + id: "button_double_tap/button" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "button_double_tap" + layer: "" + inherit_alpha: true + slice9 { + x: 15.0 + y: 15.0 + z: 15.0 + w: 15.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: true + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 0.0 + y: 7.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.5 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Double tap" + font: "game" + id: "button_double_tap/text" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + shadow { + x: 0.101960786 + y: 0.2 + z: 0.6 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "button_double_tap/button" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 0.0 + shadow_alpha: 0.78 + overridden_fields: 3 + overridden_fields: 8 + template_node_child: true + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: -200.0 + y: -220.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEMPLATE + id: "button_key_trigger" + parent: "button_page" + layer: "" + inherit_alpha: true + alpha: 1.0 + template: "/example/templates/button.gui" + template_node_child: false +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 130.0 + y: 60.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/button_blue" + id: "button_key_trigger/button" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "button_key_trigger" + layer: "" + inherit_alpha: true + slice9 { + x: 15.0 + y: 15.0 + z: 15.0 + w: 15.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: true + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 0.0 + y: 7.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.5 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Press Space" + font: "game" + id: "button_key_trigger/text" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + shadow { + x: 0.101960786 + y: 0.2 + z: 0.6 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "button_key_trigger/button" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 0.0 + shadow_alpha: 0.78 + overridden_fields: 3 + overridden_fields: 8 + template_node_child: true + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: 1800.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 600.0 + y: 900.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/empty" + id: "scroll_page" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_STRETCH + parent: "C_Anchor" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_STENCIL + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 0.0 + y: 450.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 600.0 + y: 1900.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/empty" + id: "scroll_page_content" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_N + adjust_mode: ADJUST_MODE_FIT + parent: "scroll_page" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 0.0 + y: -300.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 600.0 + y: 300.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/empty" + id: "simple_scroll_input" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "scroll_page_content" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_STENCIL + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 300.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 1200.0 + y: 300.0 + z: 0.0 + w: 1.0 + } + color { + x: 0.5019608 + y: 0.4 + z: 0.8 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "" + id: "simple_scroll_content" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "simple_scroll_input" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: -489.0 + y: 91.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Hello!" + font: "game" + id: "content1" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "simple_scroll_content" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 1.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: 220.0 + y: 71.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Hello!" + font: "game" + id: "content4" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "simple_scroll_content" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 1.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: -128.0 + y: -54.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Hello!" + font: "game" + id: "content2" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "simple_scroll_content" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 1.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: 466.0 + y: -73.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Hello!" + font: "game" + id: "content3" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "simple_scroll_content" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 1.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: 0.0 + y: -630.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 600.0 + y: 300.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/empty" + id: "scroll_with_grid_size" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "scroll_page_content" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_STENCIL + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: -300.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 600.0 + y: 300.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 0.6 + z: 0.4 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "" + id: "grid_content" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_W + adjust_mode: ADJUST_MODE_FIT + parent: "scroll_with_grid_size" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 0.0 + y: -780.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 600.0 + y: 4.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/slider_back" + id: "grid_scroll_slider" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "scroll_page_content" + layer: "" + inherit_alpha: true + slice9 { + x: 10.0 + y: 0.0 + z: 10.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: -300.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 36.0 + y: 36.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/slider_move" + id: "grid_scroll_pin" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "grid_scroll_slider" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_AUTO +} +nodes { + position { + x: -180.0 + y: -550.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 240.0 + y: 150.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/empty" + id: "grid_prefab" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "scroll_page_content" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/button_blue" + id: "grid_button" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "grid_prefab" + layer: "" + inherit_alpha: true + slice9 { + x: 10.0 + y: 10.0 + z: 10.0 + w: 10.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 0.0 + y: 5.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 180.0 + y: 50.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Just text" + font: "game" + id: "grid_prefab_text" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 0.2 + y: 0.3019608 + z: 0.7019608 + w: 1.0 + } + shadow { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "grid_button" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 0.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: 0.0 + y: 450.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 1.0 + y: 1.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/empty" + id: "N_Anchor" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_STRETCH + parent: "root" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_AUTO +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 600.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/back_gray" + id: "panel_top" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_N + adjust_mode: ADJUST_MODE_STRETCH + parent: "N_Anchor" + layer: "" + inherit_alpha: true + slice9 { + x: 20.0 + y: 20.0 + z: 20.0 + w: 20.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: -200.0 + y: -50.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEMPLATE + id: "button_left" + parent: "panel_top" + layer: "" + inherit_alpha: true + alpha: 1.0 + template: "/example/templates/button.gui" + template_node_child: false +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 130.0 + y: 60.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/button_blue" + id: "button_left/button" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "button_left" + layer: "" + inherit_alpha: true + slice9 { + x: 15.0 + y: 15.0 + z: 15.0 + w: 15.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: true + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 0.0 + y: 7.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "<<<" + font: "game" + id: "button_left/text" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + shadow { + x: 0.101960786 + y: 0.2 + z: 0.6 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "button_left/button" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 0.0 + shadow_alpha: 0.78 + overridden_fields: 8 + template_node_child: true + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: 200.0 + y: -50.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEMPLATE + id: "button_right" + parent: "panel_top" + layer: "" + inherit_alpha: true + alpha: 1.0 + template: "/example/templates/button.gui" + template_node_child: false +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 130.0 + y: 60.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/button_blue" + id: "button_right/button" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "button_right" + layer: "" + inherit_alpha: true + slice9 { + x: 15.0 + y: 15.0 + z: 15.0 + w: 15.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: true + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 0.0 + y: 7.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: ">>>" + font: "game" + id: "button_right/text" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + shadow { + x: 0.101960786 + y: 0.2 + z: 0.6 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "button_right/button" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 0.0 + shadow_alpha: 0.78 + overridden_fields: 8 + template_node_child: true + text_leading: 1.0 + text_tracking: 0.0 +} +nodes { + position { + x: 0.0 + y: -43.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Title" + font: "game" + id: "text_header" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + shadow { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "panel_top" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 1.0 + shadow_alpha: 1.0 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} +material: "/builtins/materials/gui.material" +adjust_reference: ADJUST_REFERENCE_PARENT +max_nodes: 512 diff --git a/example/gui/main/main.gui_script b/example/gui/main/main.gui_script new file mode 100644 index 0000000..26f3d8a --- /dev/null +++ b/example/gui/main/main.gui_script @@ -0,0 +1,68 @@ +local druid = require("druid.druid") + +local empty_style = require("druid.styles.empty.style") +local default_style = require("druid.styles.default.style") + +local main_page = require("example.page.main") +local text_page = require("example.page.texts") +local button_page = require("example.page.button") +local scroll_page = require("example.page.scroll") + +local pages = { + "main_page", + "texts_page", + "button_page", + "scroll_page", +} + +local function on_control_button(self, delta) + self.page = self.page + delta + if self.page < 1 then + self.page = #pages + end + if self.page > #pages then + self.page = 1 + end + + self.header:translate(pages[self.page]) + local node = gui.get_node("C_Anchor") + gui.animate(node, "position.x", (self.page-1) * -600, gui.EASING_OUTSINE, 0.2) +end + + +local function init_top_panel(self) + self.druid:new_button("button_left/button", on_control_button, -1) + self.druid:new_button("button_right/button", on_control_button, 1) + self.header = self.druid:new_lang_text("text_header", "main_page") +end + + +function init(self) + druid.set_default_style(default_style) + self.druid = druid.new(self) + + init_top_panel(self) + self.page = 1 + main_page.setup_page(self) + text_page.setup_page(self) + button_page.setup_page(self) + scroll_page.setup_page(self) + + -- Refresh state + on_control_button(self, 0) +end + + +function update(self, dt) + self.druid:update(dt) +end + + +function on_message(self, message_id, message, sender) + self.druid:on_message(message_id, message, sender) +end + + +function on_input(self, action_id, action) + self.druid:on_input(action_id, action) +end diff --git a/example/init.script b/example/init.script new file mode 100644 index 0000000..87e6385 --- /dev/null +++ b/example/init.script @@ -0,0 +1,24 @@ +local druid = require("druid.druid") +local const = require("druid.const") +local lang = require("example.lang") + + +local function setup_druid() + druid.set_sound_function(function(name) + sound.play("kenney:/sound#" .. name) + end) + + druid.set_text_function(function(lang_id) + return lang.get_locale(lang_id) + end) + + -- TODO: Call druid.finish_setup? + -- Need to update all gui, in case, when gui init was befure this init + msg.post("/gui#main", const.ON_CHANGE_LANGUAGE) +end + + +function init(self) + setup_druid() + msg.post("@render:", "clear_color", { color = vmath.vector4(0.8, 0.9, 0.85, 1) }) +end diff --git a/example/kenney.collection b/example/kenney.collection new file mode 100644 index 0000000..a8b3693 --- /dev/null +++ b/example/kenney.collection @@ -0,0 +1,114 @@ +name: "kenney" +scale_along_z: 0 +embedded_instances { + id: "gui" + data: "components {\n" + " id: \"main\"\n" + " component: \"/example/gui/main/main.gui\"\n" + " position {\n" + " x: 0.0\n" + " y: 0.0\n" + " z: 0.0\n" + " }\n" + " rotation {\n" + " x: 0.0\n" + " y: 0.0\n" + " z: 0.0\n" + " w: 1.0\n" + " }\n" + "}\n" + "" + position { + x: 0.0 + y: 0.0 + z: 0.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale3 { + x: 1.0 + y: 1.0 + z: 1.0 + } +} +embedded_instances { + id: "system" + data: "components {\n" + " id: \"init\"\n" + " component: \"/example/init.script\"\n" + " position {\n" + " x: 0.0\n" + " y: 0.0\n" + " z: 0.0\n" + " }\n" + " rotation {\n" + " x: 0.0\n" + " y: 0.0\n" + " z: 0.0\n" + " w: 1.0\n" + " }\n" + "}\n" + "" + position { + x: 0.0 + y: 0.0 + z: 0.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale3 { + x: 1.0 + y: 1.0 + z: 1.0 + } +} +embedded_instances { + id: "sound" + data: "embedded_components {\n" + " id: \"click\"\n" + " type: \"sound\"\n" + " data: \"sound: \\\"/example/assets/sounds/click.ogg\\\"\\n" + "looping: 0\\n" + "group: \\\"master\\\"\\n" + "gain: 1.0\\n" + "pan: 0.0\\n" + "speed: 1.0\\n" + "\"\n" + " position {\n" + " x: 0.0\n" + " y: 0.0\n" + " z: 0.0\n" + " }\n" + " rotation {\n" + " x: 0.0\n" + " y: 0.0\n" + " z: 0.0\n" + " w: 1.0\n" + " }\n" + "}\n" + "" + position { + x: 0.0 + y: 0.0 + z: 0.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale3 { + x: 1.0 + y: 1.0 + z: 1.0 + } +} diff --git a/example/lang.lua b/example/lang.lua new file mode 100644 index 0000000..c7eb298 --- /dev/null +++ b/example/lang.lua @@ -0,0 +1,51 @@ +local const = require("druid.const") + +local M = {} + +local en = { + main_page = "Main page", + texts_page = "Text page", + button_page = "Button page", + scroll_page = "Scroll page", + ui_section_button = "Button", + ui_section_text = "Text", + ui_section_timer = "Timer", + ui_section_progress = "Progress", + ui_section_slider = "Slider", + ui_section_radio = "Radio", + ui_section_checkbox = "Checkbox", + ui_text_example = "Translated", + ui_text_change_lang = "Change lang", +} + +local ru = { + main_page = "Основное", + texts_page = "Текст", + button_page = "Кнопки", + scroll_page = "Скролл", + ui_section_button = "Кнопка", + ui_section_text = "Текст", + ui_section_timer = "Таймер", + ui_section_progress = "Прогресс", + ui_section_slider = "Слайдер", + ui_section_radio = "Выбор", + ui_section_checkbox = "Мн. выбор", + ui_text_example = "Переведен", + ui_text_change_lang = "Сменить язык", +} + + +local data = en + + +function M.get_locale(lang_id) + return data[lang_id] or lang_id +end + + +function M.toggle_locale() + data = data == en and ru or en + msg.post("/gui#main", const.ON_CHANGE_LANGUAGE) +end + +return M \ No newline at end of file diff --git a/example/page/button.lua b/example/page/button.lua new file mode 100644 index 0000000..e736935 --- /dev/null +++ b/example/page/button.lua @@ -0,0 +1,53 @@ +local sprite_style = require("druid.styles.sprites.style") + +local M = {} + + +local function usual_callback() + print("Usual callback") +end + +local function long_tap_callback(self, params, button, hold_time) + print("Long tap callback", hold_time) +end + +local function hold_callback(self, params, button, hold_time) + print("On hold callback", hold_time) +end + +local function repeated_callback(self, params, button, click_in_row) + print("Repeated callback", click_in_row) +end + +local function double_tap_callback(self, params, button, click_in_row) + print("Double tap callback", click_in_row) +end + + +local function setup_buttons(self) + self.druid:new_button("button_usual/button", usual_callback) + + local custom_style = self.druid:new_button("button_custom_style/button", usual_callback) + custom_style:set_style(sprite_style) + + local long_button = self.druid:new_button("button_long_tap/button", usual_callback) + long_button.on_hold_callback:subscribe(hold_callback) + long_button.on_long_click:subscribe(long_tap_callback) + self.druid:new_button("button_repeated_tap/button", usual_callback) + .on_repeated_click:subscribe(repeated_callback) + self.druid:new_button("button_double_tap/button", usual_callback) + .on_double_click:subscribe(double_tap_callback) + + local button_space = self.druid:new_button("button_key_trigger/button", usual_callback) + button_space:set_key_trigger("key_space") + button_space.on_long_click:subscribe(long_tap_callback) + button_space.on_double_click:subscribe(double_tap_callback) +end + + +function M.setup_page(self) + setup_buttons(self) +end + + +return M diff --git a/example/page/main.lua b/example/page/main.lua new file mode 100644 index 0000000..5702deb --- /dev/null +++ b/example/page/main.lua @@ -0,0 +1,123 @@ +local lang = require("example.lang") + +local M = {} + + +local function empty_callback(self, param) + print("Empty callback. Param", param) +end + + +local function random_progress(progress, text) + local rnd = math.random() + + gui.set_text(text, math.ceil(rnd * 100) .. "%") + progress:to(rnd) +end + + +local function setup_button(self) + local b = self.druid:new_button("button_simple", lang.toggle_locale, "button_param") + self.druid:new_button("button_template/button", function() + print(b:is_enabled()) + b:set_enabled(not b:is_enabled()) + end, "button_param") +end + + +local function setup_texts(self) + self.druid:new_lang_text("text_button", "ui_section_button") + self.druid:new_lang_text("text_text", "ui_section_text") + self.druid:new_lang_text("text_timer", "ui_section_timer") + self.druid:new_lang_text("text_progress", "ui_section_progress") + self.druid:new_lang_text("text_slider", "ui_section_slider") + self.druid:new_lang_text("text_radio", "ui_section_radio") + self.druid:new_lang_text("text_checkbox", "ui_section_checkbox") + + self.druid:new_lang_text("text_translated", "ui_text_example") + self.druid:new_lang_text("text_button_lang", "ui_text_change_lang") + self.druid:new_text("text_simple", "Simple") +end + + +local function setup_progress(self) + self.progress = self.druid:new_progress("progress_fill", "x", 0.4) + random_progress(self.progress, gui.get_node("text_progress_amount")) + timer.delay(2, true, function() + random_progress(self.progress, gui.get_node("text_progress_amount")) + end) +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) .. "%") + end) + + slider:set(0.2) +end + + +local function setup_checkbox(self) + local radio_group = self.druid:new_radio_group( + {"radio1/check", "radio2/check", "radio3/check"}, + nil, + {"radio1/back", "radio2/back", "radio3/back"}) + + local checkbox_group = self.druid:new_checkbox_group( + {"checkbox1/check", "checkbox2/check", "checkbox3/check"}, + nil, + {"checkbox1/back", "checkbox2/back", "checkbox3/back"}) + + radio_group:set_state(2) + checkbox_group:set_state({true, false, true}) +end + + +local function setup_timer(self) + self.timer = self.druid:new_timer("timer", 300, 0, empty_callback) +end + + +local function setup_scroll(self) + self.scroll = self.druid:new_scroll("scroll_content", "main_page") +end + + +local function setup_back_handler(self) + self.druid:new_back_handler(empty_callback, "back button") +end + + + +function M.setup_page(self) + setup_texts(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) +end + + +return M diff --git a/example/page/scroll.lua b/example/page/scroll.lua new file mode 100644 index 0000000..6557082 --- /dev/null +++ b/example/page/scroll.lua @@ -0,0 +1,46 @@ +local M = {} + + +local function init_grid(self) + local prefab = gui.get_node("grid_prefab") + + local grid_scroll = self.druid:new_scroll("grid_content", "scroll_with_grid_size") + local grid = self.druid:new_grid("grid_content", "grid_prefab", 20) + grid:set_anchor(vmath.vector3(0, 0.5, 0)) + + for i = 1, 40 do + local clone_prefab = gui.clone_tree(prefab) + + grid:add(clone_prefab["grid_prefab"]) + gui.set_text(clone_prefab["grid_prefab_text"], "Node " .. i) + + local button = self.druid:new_button(clone_prefab["grid_button"], function() + local position = gui.get_position(clone_prefab["grid_prefab"]) + position.x = -position.x + grid_scroll:scroll_to(position) + end) + button:set_click_zone(gui.get_node("scroll_with_grid_size")) + end + + gui.set_enabled(prefab, false) + + grid_scroll:set_border(grid:get_size()) + + local scroll_slider = self.druid:new_slider("grid_scroll_pin", vmath.vector3(300, 0, 0), function(_, value) + grid_scroll:scroll_to_percent(vmath.vector3(value, 0, 0), true) + end) + + grid_scroll.on_scroll:subscribe(function(_, point) + scroll_slider:set(grid_scroll:get_scroll_percent().x, true) + end) +end + + +function M.setup_page(self) + self.druid:new_scroll("scroll_page_content", "scroll_page") + self.druid:new_scroll("simple_scroll_content", "simple_scroll_input") + init_grid(self) +end + + +return M diff --git a/example/page/texts.lua b/example/page/texts.lua new file mode 100644 index 0000000..0a91534 --- /dev/null +++ b/example/page/texts.lua @@ -0,0 +1,53 @@ +local M = {} + +local pivots = { + gui.PIVOT_CENTER, + gui.PIVOT_N, + gui.PIVOT_NE, + gui.PIVOT_E, + gui.PIVOT_SE, + gui.PIVOT_S, + gui.PIVOT_SW, + gui.PIVOT_W, + gui.PIVOT_NW +} + +local function setup_texts(self) + self.druid:new_text("text_inline", "Simple inline text") + self.druid:new_text("text_multiline", "Simple multiline text with smth") + local anchoring = self.druid:new_text("text_anchoring", "Anchoring") + self.druid:new_text("text_no_adjust", "Without adjust size", true) + self.druid:new_lang_text("text_locale", "ui_text_example") + + local big_text = "Check max size" + local width = self.druid:new_text("text_max_width", big_text) + local height = self.druid:new_text("text_max_height", big_text) + + local pivot_index = 1 + timer.delay(0.3, true, function() + anchoring:set_pivot(pivots[pivot_index]) + + pivot_index = pivot_index + 1 + if pivot_index > #pivots then + pivot_index = 1 + end + end) + + timer.delay(0.2, true, function() + big_text = big_text .. " max" + width:set_to(big_text) + height:set_to(big_text) + + if #big_text > 50 then + big_text = "Check max size" + end + end) +end + + +function M.setup_page(self) + setup_texts(self) +end + + +return M diff --git a/example/templates/button.gui b/example/templates/button.gui new file mode 100644 index 0000000..0e17ab9 --- /dev/null +++ b/example/templates/button.gui @@ -0,0 +1,135 @@ +script: "" +fonts { + name: "game" + font: "/example/assets/fonts/game.font" +} +textures { + name: "kenney" + texture: "/example/assets/images/kenney.atlas" +} +background_color { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 130.0 + y: 60.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/button_blue" + id: "button" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + layer: "" + inherit_alpha: true + slice9 { + x: 15.0 + y: 15.0 + z: 15.0 + w: 15.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_MANUAL +} +nodes { + position { + x: 0.0 + y: 7.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 0.7 + y: 0.7 + z: 1.0 + w: 1.0 + } + size { + x: 200.0 + y: 100.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_TEXT + blend_mode: BLEND_MODE_ALPHA + text: "Tap me!" + font: "game" + id: "text" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + outline { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + shadow { + x: 0.101960786 + y: 0.2 + z: 0.6 + w: 1.0 + } + adjust_mode: ADJUST_MODE_FIT + line_break: false + parent: "button" + layer: "" + inherit_alpha: true + alpha: 1.0 + outline_alpha: 0.0 + shadow_alpha: 0.78 + template_node_child: false + text_leading: 1.0 + text_tracking: 0.0 +} +material: "/builtins/materials/gui.material" +adjust_reference: ADJUST_REFERENCE_PARENT +max_nodes: 512 diff --git a/example/templates/checkbox.gui b/example/templates/checkbox.gui new file mode 100644 index 0000000..53c2069 --- /dev/null +++ b/example/templates/checkbox.gui @@ -0,0 +1,123 @@ +script: "" +textures { + name: "kenney" + texture: "/example/assets/images/kenney.atlas" +} +background_color { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 38.0 + y: 36.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/check_back_square" + id: "back" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_AUTO +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 21.0 + y: 20.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/checkmark" + id: "check" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "back" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_AUTO +} +material: "/builtins/materials/gui.material" +adjust_reference: ADJUST_REFERENCE_PARENT +max_nodes: 512 diff --git a/example/templates/radio.gui b/example/templates/radio.gui new file mode 100644 index 0000000..75ed7d1 --- /dev/null +++ b/example/templates/radio.gui @@ -0,0 +1,123 @@ +script: "" +textures { + name: "kenney" + texture: "/example/assets/images/kenney.atlas" +} +background_color { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 +} +nodes { + position { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 36.0 + y: 36.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/check_back_circle" + id: "back" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_AUTO +} +nodes { + position { + x: -0.5 + y: 0.5 + z: 0.0 + w: 1.0 + } + rotation { + x: 0.0 + y: 0.0 + z: 0.0 + w: 1.0 + } + scale { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + size { + x: 17.0 + y: 17.0 + z: 0.0 + w: 1.0 + } + color { + x: 1.0 + y: 1.0 + z: 1.0 + w: 1.0 + } + type: TYPE_BOX + blend_mode: BLEND_MODE_ALPHA + texture: "kenney/tick" + id: "check" + xanchor: XANCHOR_NONE + yanchor: YANCHOR_NONE + pivot: PIVOT_CENTER + adjust_mode: ADJUST_MODE_FIT + parent: "back" + layer: "" + inherit_alpha: true + slice9 { + x: 0.0 + y: 0.0 + z: 0.0 + w: 0.0 + } + clipping_mode: CLIPPING_MODE_NONE + clipping_visible: true + clipping_inverted: false + alpha: 1.0 + template_node_child: false + size_mode: SIZE_MODE_AUTO +} +material: "/builtins/materials/gui.material" +adjust_reference: ADJUST_REFERENCE_PARENT +max_nodes: 512 diff --git a/game.project b/game.project index a165b1d..f9d7075 100644 --- a/game.project +++ b/game.project @@ -1,12 +1,12 @@ [bootstrap] -main_collection = /example/example.collectionc +main_collection = /example/kenney.collectionc [script] shared_state = 1 [display] -width = 960 -height = 640 +width = 600 +height = 900 [project] title = druid @@ -14,3 +14,9 @@ title = druid [library] include_dirs = druid +[input] +gamepads = /builtins/input/default.gamepadsc + +[druid] +autofocus = 1 + diff --git a/input/game.input_binding b/input/game.input_binding index 8ed1d4e..8700888 100644 --- a/input/game.input_binding +++ b/input/game.input_binding @@ -1,3 +1,15 @@ +key_trigger { + input: KEY_BACKSPACE + action: "back" +} +key_trigger { + input: KEY_BACK + action: "back" +} +key_trigger { + input: KEY_SPACE + action: "key_space" +} mouse_trigger { input: MOUSE_BUTTON_1 action: "touch" diff --git a/liveupdate.settings b/liveupdate.settings new file mode 100644 index 0000000..10a44f5 --- /dev/null +++ b/liveupdate.settings @@ -0,0 +1,6 @@ +[liveupdate] +mode = Zip +zip-filepath = /Users/insality/code/defold/defold-eva/dist +supported-versions = "1.0.0" +publickey = /Users/insality/code/provisions/liveupdate/public.der +privatekey = /Users/insality/code/provisions/liveupdate/private.der \ No newline at end of file diff --git a/media/druid_logo.png b/media/druid_logo.png new file mode 100644 index 0000000..84d44b9 Binary files /dev/null and b/media/druid_logo.png differ diff --git a/settings_deployer b/settings_deployer new file mode 100644 index 0000000..5625ee7 --- /dev/null +++ b/settings_deployer @@ -0,0 +1,2 @@ +#!/bin/bash +use_latest_bob=true