diff --git a/.vscode/settings.json b/.vscode/settings.json index 125c6f08..fc31eb25 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -27,6 +27,11 @@ "test/tests/*.lua", "utils/annotations_manual.lua" ], + "Lua.runtime.pathStrict": true, "Lua.diagnostics.libraryFiles": "Enable", - "Lua.runtime.version": "Lua 5.1" + "Lua.runtime.version": "Lua 5.1", + "Lua.workspace.library": [ + "~/Library/Application Support/Code/User/globalStorage/astronachos.defold", + "~/Library/Application Support/Code/User/workspaceStorage/72e25b7e0fdc873ee6f7baa61edbd6b1/astronachos.defold" + ] } \ No newline at end of file diff --git a/README.md b/README.md index 84f0cb52..75a3ae53 100644 --- a/README.md +++ b/README.md @@ -17,9 +17,9 @@ Try the [**HTML5 version**](https://insality.github.io/druid/druid/) of the **Dr To integrate the **Druid** extension into your own project, add this project as a [dependency](https://www.defold.com/manuals/libraries/) in your **Defold** game. Open your `game.project` file and add the following line to the dependencies field under the project section: -**Druid v0.11.0** +**Druid v0.12.0** -> [https://github.com/Insality/druid/archive/refs/tags/0.11.0.zip](https://github.com/Insality/druid/archive/refs/tags/0.11.0.zip) +> [https://github.com/Insality/druid/archive/refs/tags/0.12.0.zip](https://github.com/Insality/druid/archive/refs/tags/0.12.0.zip) Here is a list of [all releases](https://github.com/Insality/druid/releases). diff --git a/config.ld b/config.ld index 1a295b1b..a487ec90 100644 --- a/config.ld +++ b/config.ld @@ -4,7 +4,6 @@ description='Documentation for Druid Framework' file={"./druid", exclude = { "./druid/styles/", - "./druid/system/middleclass.lua", "./druid/templates/", "./druid/annotations.lua", "./druid/custom/rich_text/module", diff --git a/docs/modules/BaseComponent.html b/docs/modules/BaseComponent.html index 1c816176..4d4d649c 100644 --- a/docs/modules/BaseComponent.html +++ b/docs/modules/BaseComponent.html @@ -106,7 +106,7 @@

Functions

Context used as first arg in all Druid events - get_druid(self) + get_druid(self, template, nodes) Get Druid instance for inner component creation. @@ -226,7 +226,7 @@

Returns:

- get_druid(self) + get_druid(self, template, nodes)
Get Druid instance for inner component creation. @@ -238,6 +238,14 @@

Parameters:

BaseComponent BaseComponent +
  • template + string or nil + The template name +
  • +
  • nodes + table or nil + The nodes table +
  • Returns:

    diff --git a/docs/modules/DataList.html b/docs/modules/DataList.html index 8003f364..7dcbdbe6 100644 --- a/docs/modules/DataList.html +++ b/docs/modules/DataList.html @@ -100,22 +100,10 @@

    Functions

    Return current data from DataList component - get_first_index(self) - Return first index from data. - - get_index(self, data) Return index for data value - get_last_index(self) - Return last index from data - - - get_length(self) - Return amount of data - - init(self, scroll, grid, create_function) The DataList constructor @@ -131,6 +119,10 @@

    Functions

    set_data(self, data) Set new data set for DataList component + + set_use_cache(self, is_use_cache) + Set refresh function for DataList component +

    Fields

    @@ -140,7 +132,7 @@

    Fields

    - + @@ -164,7 +156,7 @@

    Fields

    - +
    last_indexThe current visual last data indexThe current last index of visual elements
    on_element_add
    top_indexThe current visual top data indexThe current top index of visual elements
    @@ -276,27 +268,6 @@

    Returns:

    -
    -
    - - get_first_index(self) -
    -
    - Return first index from data. It not always equals to 1 - - -

    Parameters:

    - - - - - -
    @@ -322,48 +293,6 @@

    Parameters:

    - -
    - - get_last_index(self) -
    -
    - Return last index from data - - -

    Parameters:

    - - - - - - -
    -
    - - get_length(self) -
    -
    - Return amount of data - - -

    Parameters:

    - - - - - -
    @@ -474,6 +403,37 @@

    Returns:

    + +
    + + set_use_cache(self, is_use_cache) +
    +
    + Set refresh function for DataList component + + +

    Parameters:

    + + +

    Returns:

    +
      + + druid.data_list + Current DataList instance +
    + + + +

    Fields

    @@ -504,7 +464,7 @@

    Fields

    last_index
    - The current visual last data index + The current last index of visual elements
    - new_hover(self, node, on_hover_callback) + new_hover(self, node, on_hover_callback, on_mouse_hover_callback)
    Create Hover component @@ -679,6 +640,10 @@

    Parameters:

    function or nil Hover callback +
  • on_mouse_hover_callback + function or nil + Mouse hover callback +
  • Returns:

    @@ -772,7 +737,7 @@

    Returns:

    - new_layout(self, node, mode, on_size_changed_callback) + new_layout(self, node, mode)
    Create Layout component @@ -792,10 +757,6 @@

    Parameters:

    string The layout mode -
  • on_size_changed_callback - function or nil - The callback on window resize -
  • Returns:

    @@ -1244,6 +1205,12 @@

    Parameters:

    +

    Returns:

    +
      + + boolean + True if component was removed +
    diff --git a/docs/modules/Helper.html b/docs/modules/Helper.html index 2ede020a..f465d57f 100644 --- a/docs/modules/Helper.html +++ b/docs/modules/Helper.html @@ -151,10 +151,18 @@

    Functions

    Check if device is native mobile (Android or iOS) + helper.is_multitouch_supported() + Check if device is mobile and can support multitouch + + helper.is_web() Check if device is HTML5 + helper.is_web_mobile() + Check if device is HTML5 mobile + + helper.lerp(a, b, t) Lerp between two values @@ -658,6 +666,26 @@

    Returns:

    +
    +
    + + helper.is_multitouch_supported() +
    +
    + Check if device is mobile and can support multitouch + + + +

    Returns:

    +
      + + boolean + Is multitouch supported +
    + + + +
    @@ -678,6 +706,26 @@

    Returns:

    + +
    + + helper.is_web_mobile() +
    +
    + Check if device is HTML5 mobile + + + +

    Returns:

    +
      + + boolean + Is web mobile +
    + + + +
    diff --git a/docs/modules/Hotkey.html b/docs/modules/Hotkey.html index bb8c0daa..68e40151 100644 --- a/docs/modules/Hotkey.html +++ b/docs/modules/Hotkey.html @@ -117,8 +117,12 @@

    Fields

    Visual node - on_change_state - On change state callback(self, state) + on_hotkey_pressed + On hotkey released callback(self, argument) + + + on_hotkey_released + On hotkey released callback(self, argument) @@ -320,15 +324,35 @@

    Fields

    - - on_change_state + + on_hotkey_pressed +
    +
    + On hotkey released callback(self, argument) + + + + + + + + +
    +
    + + on_hotkey_released
    - On change state callback(self, state) + On hotkey released callback(self, argument)
    +
    + + get_text_selected_replaced(self, text) +
    +
    + Replace selected text with new text + + +

    Parameters:

    + + +

    Returns:

    +
      + + string + New input text +
    + + + +
    @@ -250,6 +333,39 @@

    Parameters:

    + +
    + + move_selection(self, delta, is_add_to_selection, is_move_to_end) +
    +
    + Change cursor position by delta + + +

    Parameters:

    + + + + + +
    @@ -267,6 +383,12 @@

    Parameters:

    +

    Returns:

    +
      + + druid.input + Current input instance +
    @@ -292,6 +414,45 @@

    Parameters:

    + +
    + + select_cursor(self, cursor_index, start_index, end_index) +
    +
    + Set cursor position in input field + + +

    Parameters:

    + + +

    Returns:

    +
      + + druid.input + Current input instance +
    + + + +
    @@ -432,10 +593,6 @@

    Fields:

    boolean If true, call unselect on select selected input. Default: false -
  • NO_CONSUME_INPUT_WHILE_SELECTED - boolean - If true, will not consume input while input is selected. It's allow to interact with other components while input is selected (text input still captured). Default: false -
  • on_select function (self, button_node) Callback on input field selecting @@ -502,6 +659,66 @@

    Fields

    + +
    + + current_value +
    +
    + Current input value with marked text + + +
      +
    • current_value + string + +
    • +
    + + + + + +
    +
    + + cursor_index +
    +
    + The cursor index. The index of letter cursor after. Leftmost cursor - 0 + + +
      +
    • cursor_index + number + +
    • +
    + + + + + +
    +
    + + end_index +
    +
    + Theselection end index. The index of letter cursor before. Rightmost selection - #text + + +
      +
    • end_index + number + +
    • +
    + + + + +
    @@ -562,6 +779,46 @@

    Fields

    + +
    + + marked_text_width +
    +
    + Marked text width + + +
      +
    • marked_text_width + number + +
    • +
    + + + + + +
    +
    + + marked_value +
    +
    + Marked text for input field. Info: https://defold.com/manuals/input-key-and-text/#marked-text + + +
      +
    • marked_value + string + +
    • +
    + + + + +
    @@ -628,7 +885,7 @@

    Fields

    on_input_select
    - On input field select callback(self, button_node) + On input field select callback(self, input_instance)
      @@ -668,7 +925,7 @@

      Fields

      on_input_unselect
  • - On input field unselect callback(self, input_text) + On input field unselect callback(self, input_text, input_instance)
    +
    + + text_width +
    +
    + Text width + + + + + + + + +
    +
    + + value +
    +
    + Current input value + + + + + + + +
    diff --git a/docs/modules/Layout.html b/docs/modules/Layout.html index edad2b69..c8f41408 100644 --- a/docs/modules/Layout.html +++ b/docs/modules/Layout.html @@ -32,7 +32,6 @@

    Druid

    Contents

    @@ -79,45 +78,6 @@

    Module Layout

    Example Link

    -

    Functions

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    fit_into_node(self, node)Set node for layout node to fit inside it.
    fit_into_size(self, target_size)Set size for layout node to fit inside it
    fit_into_window(self)Set current size for layout node to fit inside it
    init(self, node, mode, on_size_changed_callback)The Layout constructor
    set_max_gui_upscale(self, max_gui_upscale)Set max gui upscale for FIT adjust mode (or side).
    set_max_size(self, max_size)Set maximum size of layout node
    set_min_size(self, min_size)Set minimal size of layout node
    set_origin_position(self, new_origin_position)Set new origin position of layout node.
    set_origin_size(self, new_origin_size)Set new origin size of layout node.

    Fields

    @@ -128,297 +88,12 @@

    Fields

    - - - -
    node Layout node
    on_size_changedOn window resize callback(self, new_size)


    -

    Functions

    - -
    -
    - - fit_into_node(self, node) -
    -
    - Set node for layout node to fit inside it. Pass nil to reset - - -

    Parameters:

    - - -

    Returns:

    -
      - - Layout - Layout -
    - - - - -
    -
    - - fit_into_size(self, target_size) -
    -
    - Set size for layout node to fit inside it - - -

    Parameters:

    - - -

    Returns:

    -
      - - Layout - Layout -
    - - - - -
    -
    - - fit_into_window(self) -
    -
    - Set current size for layout node to fit inside it - - -

    Parameters:

    - - -

    Returns:

    -
      - - Layout - Layout -
    - - - - -
    -
    - - init(self, node, mode, on_size_changed_callback) -
    -
    - The Layout constructor - - -

    Parameters:

    - - - - - - -
    -
    - - set_max_gui_upscale(self, max_gui_upscale) -
    -
    - Set max gui upscale for FIT adjust mode (or side). It happens on bigger render gui screen - - -

    Parameters:

    - - -

    Returns:

    -
      - - Layout - Layout -
    - - - - -
    -
    - - set_max_size(self, max_size) -
    -
    - Set maximum size of layout node - - -

    Parameters:

    - - -

    Returns:

    -
      - - Layout - Layout -
    - - - - -
    -
    - - set_min_size(self, min_size) -
    -
    - Set minimal size of layout node - - -

    Parameters:

    - - -

    Returns:

    -
      - - Layout - Layout -
    - - - - -
    -
    - - set_origin_position(self, new_origin_position) -
    -
    - Set new origin position of layout node. You should apply this on node movement - - -

    Parameters:

    - - -

    Returns:

    -
      - - Layout - Layout -
    - - - - -
    -
    - - set_origin_size(self, new_origin_size) -
    -
    - Set new origin size of layout node. You should apply this on node manual size change - - -

    Parameters:

    - - -

    Returns:

    -
      - - Layout - Layout -
    - - - - -
    -

    Fields

    @@ -461,26 +136,6 @@

    Fields

    - -
    - - on_size_changed -
    -
    - On window resize callback(self, new_size) - - - - - - - -
    diff --git a/docs/modules/RichInput.html b/docs/modules/RichInput.html index c8d62dca..0c709361 100644 --- a/docs/modules/RichInput.html +++ b/docs/modules/RichInput.html @@ -84,20 +84,32 @@

    Functions

    - + + + + + + + + + + + + +
    get_text(self)GSet input field textSet input field text
    init(self, template, nodes) The RichInput constructor
    select(self)Select input field
    set_allowed_characters(self, characters) Set allowed charaters for input field.
    set_font(self, font)Set input field font
    set_placeholder(self, placeholder_text) Set placeholder text
    set_text(self, text)Set input field text

    Fields

    @@ -135,7 +147,7 @@

    Functions

    get_text(self)
    - GSet input field text + Set input field text

    Parameters:

    @@ -146,12 +158,6 @@

    Parameters:

    -

    Returns:

    -
      - - string - Current input text -
    @@ -185,6 +191,27 @@

    Parameters:

    +
    +
    + + select(self) +
    +
    + Select input field + + +

    Parameters:

    + + + + + +
    @@ -218,6 +245,37 @@

    Returns:

    + +
    + + set_font(self, font) +
    +
    + Set input field font + + +

    Parameters:

    + + +

    Returns:

    +
      + + druid.input + Current input instance +
    + + + +
    @@ -234,16 +292,41 @@

    Parameters:

    RichInput
  • placeholder_text - string or nil + string The placeholder text
  • + + + + + +
    + + set_text(self, text) +
    +
    + Set input field text + + +

    Parameters:

    + +

    Returns:

      - RichInput - Current instance + druid.input + Current input instance
    diff --git a/docs/modules/RichText.html b/docs/modules/RichText.html index 990ce514..c72269b9 100644 --- a/docs/modules/RichText.html +++ b/docs/modules/RichText.html @@ -150,6 +150,10 @@

    Usage:

    Functions

    + + + + @@ -159,11 +163,15 @@

    Functions

    + + + + - + @@ -171,7 +179,7 @@

    Functions

    - +
    characters(self, word)Split a word into it's characters
    clear() Clear all created words. Get current line metrics
    get_text(self)Get current text
    get_words() Get all current words.
    init(self, template, nodes)init(self, text_node, value) The RichText constructor
    Set text for Rich Text
    tagged(tag)tagged(self, tag) Get all words, which has a passed tag.
    @@ -188,6 +196,18 @@

    Fields

    druid The component druid instance + + icon_prefab + The icon prefab node + + + root + The root node of the Rich Text + + + text_prefab + The text prefab node +
    @@ -197,6 +217,37 @@

    Fields

    Functions

    +
    + + characters(self, word) +
    +
    + Split a word into it's characters + + +

    Parameters:

    + + +

    Returns:

    +
      + + druid.rich_text.word[] + characters +
    + + + + +
    clear() @@ -230,6 +281,33 @@

    Returns:

    + +
    + + get_text(self) +
    +
    + Get current text + + +

    Parameters:

    + + +

    Returns:

    +
      + + string + text +
    + + + +
    @@ -253,7 +331,7 @@

    Returns:

    - init(self, template, nodes) + init(self, text_node, value)
    The RichText constructor @@ -265,13 +343,13 @@

    Parameters:

    RichText RichText -
  • template - string - The Rich Text template name +
  • text_node + node or string + The text node to make Rich Text
  • -
  • nodes - table - The node table, if prefab was copied by gui.clone_tree() +
  • value + string or nil + The initial text value. Default will be gui.get_text(text_node)
  • @@ -295,7 +373,7 @@

    Parameters:

    RichText
  • text - string + string or nil The text to set
  • @@ -361,7 +439,7 @@

    Usage:

    - tagged(tag) + tagged(self, tag)
    Get all words, which has a passed tag. @@ -369,6 +447,10 @@

    Usage:

    Parameters:

    +
    + + icon_prefab +
    +
    + The icon prefab node + + + + + + + + +
    +
    + + root +
    +
    + The root node of the Rich Text + + + + + + + + +
    +
    + + text_prefab +
    +
    + The text prefab node + + + + + + + +
    diff --git a/docs/modules/Scroll.html b/docs/modules/Scroll.html index 6eefd066..dcf5180b 100644 --- a/docs/modules/Scroll.html +++ b/docs/modules/Scroll.html @@ -167,6 +167,14 @@

    Functions

    set_vertical_scroll(self, state) Lock or unlock vertical scroll + + set_view_size(self, size) + Set new scroll view size in case the node size was changed. + + + update_view_size(self) + Refresh scroll view size +

    Tables

    @@ -233,6 +241,10 @@

    Fields

    + + + +
    view_node Scroll view node
    view_sizeScroll view size

    @@ -733,6 +745,58 @@

    Returns:

    + +
    + + set_view_size(self, size) +
    +
    + Set new scroll view size in case the node size was changed. + + +

    Parameters:

    + + +

    Returns:

    +
      + + druid.scroll + Current scroll instance +
    + + + + +
    +
    + + update_view_size(self) +
    +
    + Refresh scroll view size + + +

    Parameters:

    + + + + + +

    Tables

    @@ -819,7 +883,7 @@

    Fields

    @@ -1088,6 +1152,26 @@

    Fields

    + +
    + + view_size +
    +
    + Scroll view size + + + + + + + +
    diff --git a/docs/modules/Slider.html b/docs/modules/Slider.html index bc726439..94fc9f08 100644 --- a/docs/modules/Slider.html +++ b/docs/modules/Slider.html @@ -86,10 +86,18 @@

    Functions

    The Slider constructor + is_enabled(self) + Check if Slider component is enabled + + set(self, value, is_silent) Set value for slider + set_enabled(self, is_enabled) + Set Slider input enabled or disabled + + set_input_node(self, input_node) Set input zone for slider. @@ -177,6 +185,33 @@

    Parameters:

    + +
    + + is_enabled(self) +
    +
    + Check if Slider component is enabled + + +

    Parameters:

    + + +

    Returns:

    +
      + + boolean + +
    + + + +
    @@ -206,6 +241,31 @@

    Parameters:

    + +
    + + set_enabled(self, is_enabled) +
    +
    + Set Slider input enabled or disabled + + +

    Parameters:

    + + + + + +
    @@ -291,7 +351,7 @@

    Fields

    diff --git a/docs/modules/StaticGrid.html b/docs/modules/StaticGrid.html index 4f2daeed..076f3bb0 100644 --- a/docs/modules/StaticGrid.html +++ b/docs/modules/StaticGrid.html @@ -154,9 +154,21 @@

    Functions

    Set new in_row elements for grid + set_item_size(self[, width[, height]]) + Set new node size for grid + + + set_items(self, nodes[, is_instant=false]) + Set new items to the grid. + + set_position_function(self, callback) Change set position function for grid nodes. + + sort_nodes(self, comparator) + Sort grid nodes by custom comparator function +

    Tables

    @@ -621,6 +633,73 @@

    Returns:

    + +
    + + set_item_size(self[, width[, height]]) +
    +
    + Set new node size for grid + + +

    Parameters:

    + + +

    Returns:

    +
      + + druid.static_grid + Current grid instance +
    + + + + +
    +
    + + set_items(self, nodes[, is_instant=false]) +
    +
    + Set new items to the grid. All previous items will be removed + + +

    Parameters:

    + + + + + +
    @@ -653,6 +732,37 @@

    Returns:

    + +
    + + sort_nodes(self, comparator) +
    +
    + Sort grid nodes by custom comparator function + + +

    Parameters:

    + + +

    Returns:

    +
      + + druid.static_grid + Current grid instance +
    + + + +

    Tables

    diff --git a/docs/modules/Text.html b/docs/modules/Text.html index eb925ac7..4f7d1967 100644 --- a/docs/modules/Text.html +++ b/docs/modules/Text.html @@ -105,6 +105,10 @@

    Functions

    + + + + @@ -247,6 +251,37 @@

    Returns:

    + +
    + + get_text_index_by_width(self, width) +
    +
    + Get chars count by width + + +

    Parameters:

    + + +

    Returns:

    +
      + + number + Chars count +
    + + + +
    @@ -263,8 +298,8 @@

    Parameters:

    Text
  • text - string or nil - + string + |nil
  • @@ -302,11 +337,11 @@

    Parameters:

  • value string or nil - Initial text. Default value is node text from GUI scene. + Initial text. Default value is node text from GUI scene. Default: nil
  • adjust_type string or nil - Adjust type for text. By default is DOWNSCALE. Look const.TEXT_ADJUST for reference. Default: downscale + Adjust type for text. By default is DOWNSCALE. Look const.TEXT_ADJUST for reference. Default: DOWNSCALE
  • @@ -618,6 +653,14 @@

    Fields:

    string or nil The default adjust type for any text component. Default: DOWNSCALE +
  • ADJUST_STEPS + string or nil + Amount of iterations for text adjust by height. Default: 20 +
  • +
  • ADJUST_SCALE_DELTA + string or nil + Scale step on each height adjust step. Default: 0.02 +
  • diff --git a/docs/modules/Timer.html b/docs/modules/Timer.html index 7c3eb829..9dca7a5f 100644 --- a/docs/modules/Timer.html +++ b/docs/modules/Timer.html @@ -158,7 +158,7 @@

    Parameters:

    Gui text node
  • seconds_from - number + number or nil Start timer value in seconds
  • seconds_to diff --git a/docs/modules/druid.extended.layout.html b/docs/modules/druid.extended.layout.html new file mode 100644 index 00000000..4f01fd5c --- /dev/null +++ b/docs/modules/druid.extended.layout.html @@ -0,0 +1,95 @@ + + + + + Defold Druid UI Framework + + + + +
    + +
    + +
    +
    +
    + + +
    + + + + + + +
    + +

    Module druid.extended.layout

    +

    Druid layout module +

    # Overview # +

    Layout component works like Dynamic Grid before - for aligning elements in a row or column.

    +

    Works like a Figma layout. +

    # Notes

    + + + +
    +
    + + + + +
    +
    +
    +generated by LDoc TESTING +Last updated 2015-01-01 12:00:00 +
    +
    + + diff --git a/docs/modules/druid.system.utf8.html b/docs/modules/druid.system.utf8.html new file mode 100644 index 00000000..05821062 --- /dev/null +++ b/docs/modules/druid.system.utf8.html @@ -0,0 +1,93 @@ + + + + + Defold Druid UI Framework + + + + +
    + +
    + +
    +
    +
    + + +
    + + + + + + +
    + +

    Module druid.system.utf8

    +

    +

    + + + +
    +
    + + + + +
    +
    +
    +generated by LDoc TESTING +Last updated 2015-01-01 12:00:00 +
    +
    + + diff --git a/docs_md/02-creating_custom_components.md b/docs_md/02-creating_custom_components.md index b7aaf993..b763f6f2 100644 --- a/docs_md/02-creating_custom_components.md +++ b/docs_md/02-creating_custom_components.md @@ -14,21 +14,14 @@ A basic custom component template looks like this (you can copy it from `/druid/ ```lua local component = require("druid.component") ----@class component_name : druid.base_component +---@class component_name: druid.base_component local Component = component.create("component_name") -local SCHEME = { - ROOT = "root", - BUTTON = "button", -} - function Component:init(template, nodes) - self:set_template(template) - self:set_nodes(nodes) - self.root = self:get_node(SCHEME.ROOT) - self.druid = self:get_druid() + self.druid = self:get_druid(template, nodes) + self.root = self:get_node("root") - self.button = self.druid:new_button(SCHEME.BUTTON, function() end) + self.button = self.druid:new_button("button", function() end) end function Component:on_remove() end @@ -43,18 +36,12 @@ A full custom component template looks like this (you can copy it from `/druid/t ```lua local component = require("druid.component") ----@class component_name : druid.base_component +---@class component_name: druid.base_component local Component = component.create("component_name") -local SCHEME = { - ROOT = "root", - BUTTON = "button", -} function Component:init(template, nodes) - self:set_template(template) - self:set_nodes(nodes) - self.root = self:get_node(SCHEME.ROOT) - self.druid = self:get_druid() + self.druid = self:get_druid(template, nodes) + self.root = self:get_node("root") end function Component:update(dt) end @@ -92,7 +79,7 @@ local my_component = require("my.amazing.component") function init(self) self.druid = druid.new(self) - self.druid:new(my_component, "template_name", nodes) + self.druid:new(my_component, "template_name") end ``` @@ -107,6 +94,7 @@ local druid = require("druid.druid") local my_component = require("my.amazing.component") function init(self) + -- Register makes a "druid:new_{component_name}" function available druid.register("my_component", my_component) end ``` @@ -149,33 +137,6 @@ Available keywords: uid Timer](01-components.md#timer) component. -## Best Practices for Custom Components - -When working with each component, it's recommended to describe the component scheme in the following 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) - self:set_template(template_name) - self:set_nodes(node_table) - - local root = self:get_node(SCHEME.ROOT) - local druid = self:get_druid() - - -- Create components inside this component using the inner druid instance -end -``` - ## The Power of Using Templates With Druid, you can use a single component but create and customize templates for it. Templates only need to match the component scheme. For example, you can have a component named `player_panel` and two GUI templates named `player_panel` and `enemy_panel` with different layouts. The same component script can be used for both templates. diff --git a/druid/annotations.lua b/druid/annotations.lua index e8395b4a..c8e29dd4 100644 --- a/druid/annotations.lua +++ b/druid/annotations.lua @@ -367,6 +367,12 @@ function druid__data_list.scroll_to_index(self, index) end ---@return druid.data_list Current DataList instance function druid__data_list.set_data(self, data) end +--- Set refresh function for DataList component +---@param self druid.data_list @{DataList} +---@param is_use_cache boolean Use cache version of DataList. Requires make setup of components in on_element_add callback and clean in on_element_remove +---@return druid.data_list Current DataList instance +function druid__data_list.set_use_cache(self, is_use_cache) end + ---@class druid.drag : druid.base_component ---@field can_x boolean Is drag component process vertical dragging. @@ -374,8 +380,8 @@ function druid__data_list.set_data(self, data) end ---@field is_drag boolean Is component now dragging ---@field is_touch boolean Is component now touching ---@field node node Drag node ----@field on_drag druid.event on drag progress callback(self, dx, dy, total_x, total_y) ----@field on_drag_end druid.event Event on drag end callback(self, total_x, total_y) +---@field on_drag druid.event on drag progress callback(self, dx, dy, total_x, total_y, touch) +---@field on_drag_end druid.event Event on drag end callback(self, total_x, total_y, touch) ---@field on_drag_start druid.event Event on drag start callback(self, touch) ---@field on_touch_end druid.event Event on touch end callback(self) ---@field on_touch_start druid.event Event on touch start callback(self) @@ -559,7 +565,8 @@ function druid__event.unsubscribe(self, callback, callback_context) end ---@field button druid.button Button component from click_node ---@field click_node node|nil Button trigger node ---@field node node Visual node ----@field on_change_state druid.event On change state callback(self, state) +---@field on_hotkey_pressed druid.event On hotkey released callback(self, argument) +---@field on_hotkey_released druid.event On hotkey released callback(self, argument) ---@field style druid.hotkey.style Component style params. local druid__hotkey = {} @@ -803,67 +810,8 @@ function druid__lang_text.translate(self, locale_id, a, b, c, d, e, f, g) end ---@class druid.layout : druid.base_component ---@field mode string Current layout mode ---@field node node Layout node ----@field on_size_changed druid.event On window resize callback(self, new_size) local druid__layout = {} ---- Set node for layout node to fit inside it. ---- Pass nil to reset ----@param self druid.layout @{Layout} ----@param node node|nil ----@return druid.layout @{Layout} -function druid__layout.fit_into_node(self, node) end - ---- Set size for layout node to fit inside it ----@param self druid.layout @{Layout} ----@param target_size vector3 ----@return druid.layout @{Layout} -function druid__layout.fit_into_size(self, target_size) end - ---- Set current size for layout node to fit inside it ----@param self druid.layout @{Layout} ----@return druid.layout @{Layout} -function druid__layout.fit_into_window(self) end - ---- The @{Layout} constructor ----@param self druid.layout @{Layout} ----@param node node Gui node ----@param mode string The layout mode (from const.LAYOUT_MODE) ----@param on_size_changed_callback function|nil The callback on window resize -function druid__layout.init(self, node, mode, on_size_changed_callback) end - ---- Set max gui upscale for FIT adjust mode (or side). ---- It happens on bigger render gui screen ----@param self druid.layout @{Layout} ----@param max_gui_upscale number ----@return druid.layout @{Layout} -function druid__layout.set_max_gui_upscale(self, max_gui_upscale) end - ---- Set maximum size of layout node ----@param self druid.layout @{Layout} ----@param max_size vector3 ----@return druid.layout @{Layout} -function druid__layout.set_max_size(self, max_size) end - ---- Set minimal size of layout node ----@param self druid.layout @{Layout} ----@param min_size vector3 ----@return druid.layout @{Layout} -function druid__layout.set_min_size(self, min_size) end - ---- Set new origin position of layout node. ---- You should apply this on node movement ----@param self druid.layout @{Layout} ----@param new_origin_position vector3 ----@return druid.layout @{Layout} -function druid__layout.set_origin_position(self, new_origin_position) end - ---- Set new origin size of layout node. ---- You should apply this on node manual size change ----@param self druid.layout @{Layout} ----@param new_origin_size vector3 ----@return druid.layout @{Layout} -function druid__layout.set_origin_size(self, new_origin_size) end - ---@class druid.pin_knob : druid.base_component ---@field druid druid_instance The component druid instance @@ -1056,9 +1004,9 @@ function druid__rich_text.get_words() end --- The @{RichText} constructor ---@param self druid.rich_text @{RichText} ----@param template string The Rich Text template name ----@param nodes table The node table, if prefab was copied by gui.clone_tree() -function druid__rich_text.init(self, template, nodes) end +---@param text_node node|string The text node to make Rich Text +---@param value string|nil The initial text value. Default will be gui.get_text(text_node) +function druid__rich_text.init(self, text_node, value) end --- Set text for Rich Text ---@param self druid.rich_text @{RichText} @@ -1097,6 +1045,7 @@ local druid__rich_text__style = {} ---@field style druid.scroll.style Component style params. ---@field target_position vector3 Current scroll target position ---@field view_node node Scroll view node +---@field view_size vector3 Scroll view size local druid__scroll = {} --- Bind the grid component (Static or Dynamic) to recalculate scroll size on grid changes @@ -1205,6 +1154,10 @@ function druid__scroll.set_vertical_scroll(self, state) end ---@return druid.scroll Current scroll instance function druid__scroll.set_view_size(self, size) end +--- Refresh scroll view size +---@param self druid.scroll @{Scroll} +function druid__scroll.update_view_size(self) end + ---@class druid.scroll.style ---@field ANIM_SPEED number|nil Scroll gui.animation speed for scroll_to function. Default: 2 @@ -1550,7 +1503,7 @@ local druid__timer = {} --- The @{Timer} constructor ---@param self druid.timer @{Timer} ---@param node node Gui text node ----@param seconds_from number Start timer value in seconds +---@param seconds_from number|nil Start timer value in seconds ---@param seconds_to number|nil End timer value in seconds ---@param callback function|nil Function on timer end function druid__timer.init(self, node, seconds_from, seconds_to, callback) end @@ -1579,14 +1532,6 @@ local druid_instance = {} ---@param self druid_instance function druid_instance.final(self) end ---- Create new component. ----@generic T ----@param self druid_instance ----@param component T Component module ----@param ... any Other component params to pass it to component:init function ----@return T Component instance -function druid_instance.new(self, component, ...) end - --- Create @{BackHandler} component ---@param self druid_instance ---@param callback function|nil @The callback(self, custom_args) to call on back event @@ -1683,9 +1628,8 @@ function druid_instance.new_lang_text(self, node, locale_id, adjust_type) end ---@param self druid_instance ---@param node string|node The_node id or gui.get_node(node_id). ---@param mode string The layout mode ----@param on_size_changed_callback function|nil The callback on window resize ---@return druid.layout @{Layout} component -function druid_instance.new_layout(self, node, mode, on_size_changed_callback) end +function druid_instance.new_layout(self, node, mode) end --- Create @{Progress} component ---@param self druid_instance @@ -1752,7 +1696,7 @@ function druid_instance.new_text(self, node, value, no_adjust) end --- Create @{Timer} component ---@param self druid_instance ---@param node string|node Gui text node ----@param seconds_from number|nil Start timer value in seconds +---@param seconds_from number Start timer value in seconds ---@param seconds_to number|nil End timer value in seconds ---@param callback function|nil Function on timer end ---@return druid.timer @{Timer} component @@ -2021,3 +1965,22 @@ function helper.table_to_string(t) end ---@field height number ---@field max_ascent number ---@field max_descent number + +---@class utf8 +---@field len fun(string: string): number +---@field sub fun(string: string, i: number, j: number): string +---@field gmatch fun(string: string, pattern: string): fun(): string +---@field gsub fun(string: string, pattern: string, repl: string, n: number): string +---@field char fun(...: number): string +---@field byte fun(string: string, i: number, j: number): number + + +---Add generics to some functions. + +---Create new component. +---@generic T: druid.base_component +---@param self druid_instance +---@param component T Component module +---@param ... any Other component params to pass it to component:init function +---@return T Component instance +function druid_instance.new(self, component, ...) end \ No newline at end of file diff --git a/druid/base/button.lua b/druid/base/button.lua index b08d897f..ada2a26f 100755 --- a/druid/base/button.lua +++ b/druid/base/button.lua @@ -344,10 +344,6 @@ function Button.on_input(self, action_id, action) return false end - if not self:is_enabled() then - return false - end - local is_consume = true local is_pick = true local is_key_trigger = (action_id == self.key_trigger) diff --git a/druid/base/drag.lua b/druid/base/drag.lua index 3cae2b9e..fd791058 100644 --- a/druid/base/drag.lua +++ b/druid/base/drag.lua @@ -22,10 +22,10 @@ --- Event on drag start callback(self, touch) -- @tfield DruidEvent on_drag_start @{DruidEvent} ---- on drag progress callback(self, dx, dy, total_x, total_y) +--- on drag progress callback(self, dx, dy, total_x, total_y, touch) -- @tfield DruidEvent on_drag Event @{DruidEvent} ---- Event on drag end callback(self, total_x, total_y) +--- Event on drag end callback(self, total_x, total_y, touch) -- @tfield DruidEvent on_drag_end @{DruidEvent} --- Is component now touching diff --git a/druid/base/scroll.lua b/druid/base/scroll.lua index 58ba1b56..7a806203 100755 --- a/druid/base/scroll.lua +++ b/druid/base/scroll.lua @@ -50,6 +50,9 @@ --- Scroll view node -- @tfield node view_node +--- Scroll view size +-- @tfield vector3 view_size + --- Scroll content node -- @tfield node content_node diff --git a/druid/base/text.lua b/druid/base/text.lua index 3aaa063b..39188f44 100755 --- a/druid/base/text.lua +++ b/druid/base/text.lua @@ -211,6 +211,9 @@ local function update_text_with_trim(self, trim_postfix) text_length = text_length - 1 new_text = utf8.sub(self.last_value, 1, text_length) text_width = self:get_text_size(new_text .. trim_postfix) + if text_length == 0 then + break + end end gui.set_text(self.node, new_text .. trim_postfix) diff --git a/druid/custom/rich_text/module/rt.lua b/druid/custom/rich_text/module/rt.lua index dc27bf5b..74aa6791 100755 --- a/druid/custom/rich_text/module/rt.lua +++ b/druid/custom/rich_text/module/rt.lua @@ -227,7 +227,7 @@ function M._fill_properties(word, metrics, settings) else -- Text properties word.scale = settings.scale * word.relative_scale * settings.adjust_scale - word.pivot = gui.PIVOT_W -- With this pivot adjustments works correctly, but with another some misalignment + word.pivot = gui.PIVOT_SW -- With this pivot adjustments works more correctly than with other pivots word.size = vmath.vector3(metrics.width, metrics.height, 0) word.offset = vmath.vector3(metrics.offset_x, metrics.offset_y, 0) end diff --git a/druid/custom/rich_text/rich_text.gui b/druid/custom/rich_text/rich_text.gui deleted file mode 100644 index 0d6a8fe8..00000000 --- a/druid/custom/rich_text/rich_text.gui +++ /dev/null @@ -1,45 +0,0 @@ -fonts { - name: "game" - font: "/example/assets/fonts/game.font" -} -textures { - name: "items" - texture: "/example/assets/images/kenney.atlas" -} -nodes { - size { - x: 400.0 - y: 100.0 - } - type: TYPE_BOX - id: "root" - inherit_alpha: true - visible: false -} -nodes { - position { - x: -200.0 - } - size { - x: 400.0 - y: 100.0 - } - type: TYPE_TEXT - text: "Rich text" - font: "game" - id: "text_prefab" - pivot: PIVOT_W - parent: "root" - inherit_alpha: true - outline_alpha: 0.0 - shadow_alpha: 0.0 -} -nodes { - type: TYPE_BOX - id: "icon_prefab" - parent: "root" - inherit_alpha: true - size_mode: SIZE_MODE_AUTO -} -material: "/builtins/materials/gui.material" -adjust_reference: ADJUST_REFERENCE_PARENT diff --git a/druid/custom/rich_text/rich_text.lua b/druid/custom/rich_text/rich_text.lua index d4f08f9e..29bb0987 100644 --- a/druid/custom/rich_text/rich_text.lua +++ b/druid/custom/rich_text/rich_text.lua @@ -109,8 +109,8 @@ local RichText = component.create("rich_text") --- The @{RichText} constructor -- @tparam RichText self @{RichText} --- @tparam string template The Rich Text template name --- @tparam table nodes The node table, if prefab was copied by gui.clone_tree() +-- @tparam node|string text_node The text node to make Rich Text +-- @tparam string|nil value The initial text value. Default will be gui.get_text(text_node) function RichText.init(self, text_node, value) self.root = self:get_node(text_node) self.text_prefab = self.root diff --git a/druid/editor_scripts/druid.editor_script b/druid/editor_scripts/druid.editor_script index 6534c1dd..f72488a1 100644 --- a/druid/editor_scripts/druid.editor_script +++ b/druid/editor_scripts/druid.editor_script @@ -25,35 +25,6 @@ end function M.get_commands() return { - { - label = "Print GUI Scheme", - - locations = { "Outline" }, - - query = { - selection = {type = "outline", cardinality = "many"} - }, - - active = function(opts) - return true - end, - - run = function(opts) - print("local SCHEME = {") - - for i = 1, #opts.selection do - local file = opts.selection[i] - if editor.can_get(file, "id") then - local id = editor.get(file, "id") - print("\t" .. string.upper(id) .. " = \"" .. id .. "\",") - end - end - - print("}") - print("") - end - }, - { label = "Assign layers", diff --git a/druid/extended/data_list.lua b/druid/extended/data_list.lua index fa9adcaa..17aba867 100644 --- a/druid/extended/data_list.lua +++ b/druid/extended/data_list.lua @@ -84,6 +84,8 @@ end --- Set refresh function for DataList component -- @tparam DataList self @{DataList} +-- @tparam boolean is_use_cache Use cache version of DataList. Requires make setup of components in on_element_add callback and clean in on_element_remove +-- @treturn druid.data_list Current DataList instance function DataList.set_use_cache(self, is_use_cache) self._is_use_cache = is_use_cache return self @@ -280,7 +282,7 @@ end function DataList._refresh(self) self.scroll:set_size(self.grid:get_size_for(#self._data)) - local start_pos = -self.scroll.position + local start_pos = -self.scroll.position --[[@as vector3]] local start_index = self.grid:get_index(start_pos) start_index = math.max(1, start_index) diff --git a/druid/extended/figma_layout.lua b/druid/extended/figma_layout.lua deleted file mode 100644 index 0748a4e1..00000000 --- a/druid/extended/figma_layout.lua +++ /dev/null @@ -1,392 +0,0 @@ -local helper = require("druid.helper") -local component = require("druid.component") - ----@class druid.figma_layout.row_data ----@field width number ----@field height number ----@field count number - ----@class druid.figma_layout.rows_data ----@field total_width number ----@field total_height number ----@field nodes_width table ----@field nodes_height table ----@field rows druid.figma_layout.row_data[]> - ----@class druid.figma_layout: druid.base_component -local M = component.create("layout") - - ---- The @{Layout} constructor --- @tparam Layout self @{Layout} --- @tparam node node Gui node --- @tparam string layout_type The layout mode (from const.LAYOUT_MODE) --- @tparam function|nil on_size_changed_callback The callback on window resize -function M:init(node, layout_type) - self.node = self:get_node(node) - - print(self.node) - self.is_dirty = true - self.entities = {} - self.margin = { x = 0, y = 0 } - self.padding = gui.get_slice9(self.node) - self.type = layout_type or "horizontal" - self.is_resize_width = false - self.is_resize_height = false - self.is_justify = false -end - - -function M:update() - if not self.is_dirty then - return - end - - self:refresh_layout() -end - - ----@param margin_x number|nil ----@param margin_y number|nil ----@return druid.figma_layout -function M:set_margin(margin_x, margin_y) - self.margin.x = margin_x or self.margin.x - self.margin.y = margin_y or self.margin.y - self.is_dirty = true - - return self -end - - ----@param padding vector4 The vector4 with padding values, where x - left, y - top, z - right, w - bottom -function M:set_padding(padding) - self.padding = padding - self.is_dirty = true - - return self -end - - -function M:set_dirty() - self.is_dirty = true - - return self -end - - ----@param is_justify boolean ----@return druid.figma_layout -function M:set_justify(is_justify) - self.is_justify = is_justify - self.is_dirty = true - - return self -end - - ----@param type string The layout type: "horizontal", "vertical", "horizontal_wrap" -function M:set_type(type) - self.type = type - self.is_dirty = true - - return self -end - - - ----@param is_hug_width boolean ----@param is_hug_height boolean ----@return druid.figma_layout -function M:set_hug_content(is_hug_width, is_hug_height) - self.is_resize_width = is_hug_width or false - self.is_resize_height = is_hug_height or false - self.is_dirty = true - - return self -end - ----@param node_or_node_id string|node ----@return druid.figma_layout -function M:add(node_or_node_id) - -- Acquire node from entity or by id - local node = node_or_node_id - if type(node_or_node_id) == "table" then - assert(node_or_node_id.node, "The entity should have a node") - node = node_or_node_id.node - else - ---@cast node_or_node_id string|node - node = self:get_node(node_or_node_id) - end - - ---@cast node node - table.insert(self.entities, node) - gui.set_parent(node, self.node) - - self.is_dirty = true - - return self -end - - ----@return druid.figma_layout -function M:refresh_layout() - local layout_node = self.node - - local entities = self.entities - local type = self.type -- vertical, horizontal, horizontal_wrap - local margin = self.margin -- {x: horizontal, y: vertical} in pixels, between elements - local padding = self.padding -- {x: left, y: top, z: right, w: bottom} in pixels - local is_justify = self.is_justify - local size = gui.get_size(layout_node) - local max_width = size.x - padding.x - padding.z - local max_height = size.y - padding.y - padding.w - local layout_pivot_offset = helper.get_pivot_offset(gui.get_pivot(layout_node)) -- {x: -0.5, y: -0.5} - is left bot, {x: 0.5, y: 0.5} - is right top - - local rows_data = self:calculate_rows_data() - local rows = rows_data.rows - local row_index = 1 - local row = rows[row_index] - - -- Current x and Current y is a top left corner of the node - local current_x = -row.width * (0.5 + layout_pivot_offset.x) - local current_y = rows_data.total_height * (0.5 - layout_pivot_offset.y) - - if is_justify then - if (type == "horizontal" or type == "horizontal_wrap") and row.count > 1 then - current_x = -max_width * (0.5 + layout_pivot_offset.x) - end - if type == "vertical" then - current_y = max_height * (0.5 - layout_pivot_offset.y) - end - end - - for index = 1, #entities do - local node = entities[index] - local node_width = rows_data.nodes_width[node] - local node_height = rows_data.nodes_height[node] - local pivot_offset = helper.get_pivot_offset(gui.get_pivot(node)) - - if node_width > 0 and node_height > 0 then - -- Calculate position for current node - local position_x, position_y - - if type == "horizontal" then - position_x = current_x + node_width * (0.5 + pivot_offset.x) - position_y = current_y - row.height * (0.5 - pivot_offset.y) - - local node_margin = margin.x - if is_justify and row.count > 1 then - node_margin = (max_width - row.width) / (row.count - 1) + margin.x - end - current_x = current_x + node_width + node_margin - end - - if type == "vertical" then - position_x = current_x + row.width * (0.5 - pivot_offset.x) - position_y = current_y - node_height * (0.5 + pivot_offset.y) - - local node_margin = margin.y - if is_justify then - node_margin = (max_height - rows_data.total_height) / (#rows - 1) + margin.y - end - - current_y = current_y - node_height - node_margin - end - - if type == "horizontal_wrap" then - local width = row.width - if is_justify and row.count > 0 then - width = math.max(row.width, max_width) - end - local new_row_width = width * (0.5 - layout_pivot_offset.x) - - -- Compare with eps due the float loss and element flickering - if current_x + node_width - new_row_width > 0.0001 then - if row_index < #rows then - row_index = row_index + 1 - row = rows[row_index] - end - - current_x = -row.width * (0.5 + layout_pivot_offset.x) - current_y = current_y - row.height - margin.y - if is_justify and row.count > 1 then - current_x = -max_width * (0.5 + layout_pivot_offset.x) - end - end - - position_x = current_x + node_width * (0.5 + pivot_offset.x) - position_y = current_y - row.height * (0.5 - pivot_offset.y) - - local node_margin = margin.x - if is_justify and row.count > 1 then - node_margin = (max_width - row.width) / (row.count - 1) + margin.x - end - current_x = current_x + node_width + node_margin - end - - do -- Padding offset - if layout_pivot_offset.x == -0.5 then - position_x = position_x + padding.x - end - if layout_pivot_offset.y == 0.5 then - position_y = position_y - padding.y - end - if layout_pivot_offset.x == 0.5 then - position_x = position_x - padding.z - end - if layout_pivot_offset.y == -0.5 then - position_y = position_y + padding.w - end - end - - self:set_node_position(node, position_x, position_y) - end - end - - if self.is_resize_width or self.is_resize_height then - if self.is_resize_width then - size.x = rows_data.total_width + padding.x + padding.z - end - if self.is_resize_height then - size.y = rows_data.total_height + padding.y + padding.w - end - gui.set_size(layout_node, size) - end - - self.is_dirty = false - - return self -end - - ----@return druid.figma_layout -function M:clear_layout() - for index = #self.entities, 1, -1 do - self.entities[index] = nil - end - - self.is_dirty = true - - return self -end - - ----@param node node ----@return number, number -function M.get_node_size(node) - if not gui.is_enabled(node, false) then - return 0, 0 - end - - local scale = gui.get_scale(node) - - -- If node has text - get text size instead of node size - if gui.get_text(node) then - local text_metrics = helper.get_text_metrics_from_node(node) - return text_metrics.width * scale.x, text_metrics.height * scale.y - end - - local size = gui.get_size(node) - return size.x * scale.x, size.y * scale.y -end - - ----Calculate rows data for layout. Contains total width, height and rows info (width, height, count of elements in row) ----@private ----@return druid.figma_layout.rows_data -function M:calculate_rows_data() - local entities = self.entities - local margin = self.margin - local type = self.type - local padding = self.padding - - local size = gui.get_size(self.node) - local max_width = size.x - padding.x - padding.z - - -- Collect rows info about width, height and count of elements in row - local current_row = { width = 0, height = 0, count = 0 } - local rows_data = { - total_width = 0, - total_height = 0, - nodes_width = {}, - nodes_height = {}, - rows = { current_row } - } - - for index = 1, #entities do - local node = entities[index] - local node_width = rows_data.nodes_width[node] - local node_height = rows_data.nodes_height[node] - - -- Get node size if it's not calculated yet - if not node_width or not node_height then - node_width, node_height = M.get_node_size(node) - rows_data.nodes_width[node] = node_width - rows_data.nodes_height[node] = node_height - end - - if node_width > 0 and node_height > 0 then - if type == "horizontal" then - current_row.width = current_row.width + node_width + margin.x - current_row.height = math.max(current_row.height, node_height) - current_row.count = current_row.count + 1 - end - - if type == "vertical" then - if current_row.count > 0 then - current_row = { width = 0, height = 0, count = 0 } - table.insert(rows_data.rows, current_row) - end - - current_row.width = math.max(current_row.width, node_width + margin.x) - current_row.height = node_height - current_row.count = current_row.count + 1 - end - - if type == "horizontal_wrap" then - if current_row.width + node_width > max_width and current_row.count > 0 then - current_row = { width = 0, height = 0, count = 0 } - table.insert(rows_data.rows, current_row) - end - - current_row.width = current_row.width + node_width + margin.x - current_row.height = math.max(current_row.height, node_height) - current_row.count = current_row.count + 1 - end - end - end - - -- Remove last margin of each row - -- Calculate total width and height - local rows_count = #rows_data.rows - for index = 1, rows_count do - local row = rows_data.rows[index] - if row.width > 0 then - row.width = row.width - margin.x - end - - rows_data.total_width = math.max(rows_data.total_width, row.width) - rows_data.total_height = rows_data.total_height + row.height - end - - rows_data.total_height = rows_data.total_height + margin.y * (rows_count - 1) - return rows_data -end - - ----@private ----@param node node ----@param x number ----@param y number ----@return node -function M:set_node_position(node, x, y) - local position = gui.get_position(node) - position.x = x - position.y = y - gui.set_position(node, position) - - return node -end - - -return M \ No newline at end of file diff --git a/druid/extended/hotkey.lua b/druid/extended/hotkey.lua index 283839c4..41507bd7 100644 --- a/druid/extended/hotkey.lua +++ b/druid/extended/hotkey.lua @@ -27,7 +27,6 @@ local helper = require("druid.helper") local component = require("druid.component") local Event = require("druid.event") -local const = require("druid.const") local Hotkey = component.create("hotkey") diff --git a/druid/extended/layout.lua b/druid/extended/layout.lua index ff24fefa..d8ac7327 100644 --- a/druid/extended/layout.lua +++ b/druid/extended/layout.lua @@ -1,4 +1,4 @@ --- Copyright (c) 2021 Maksim Tuprikov . This code is licensed under MIT license +-- Copyright (c) 2024 Maksim Tuprikov . This code is licensed under MIT license --- Layout management on node -- @@ -13,205 +13,406 @@ --- Current layout mode -- @tfield string mode ----On window resize callback(self, new_size) --- @tfield DruidEvent on_size_changed @{DruidEvent} - --- - -local const = require("druid.const") local helper = require("druid.helper") local component = require("druid.component") -local Event = require("druid.event") +-- @class druid.layout.row_data +-- @tfield width number +-- @tfield height number +-- @tfield count number -local Layout = component.create("layout") +-- @class druid.layout.rows_data +-- @tfield total_width number +-- @tfield total_height number +-- @tfield nodes_width table +-- @tfield nodes_height table +-- @tfield rows druid.layout.row_data[]> +-- @class druid.layout: druid.base_component +local M = component.create("layout") ---- The @{Layout} constructor +-- The @{Layout} constructor -- @tparam Layout self @{Layout} -- @tparam node node Gui node --- @tparam string mode The layout mode (from const.LAYOUT_MODE) +-- @tparam string layout_type The layout mode (from const.LAYOUT_MODE) -- @tparam function|nil on_size_changed_callback The callback on window resize -function Layout.init(self, node, mode, on_size_changed_callback) +function M.init(self, node, layout_type) self.node = self:get_node(node) - self._min_size = nil - self._max_size = nil - self._current_size = vmath.vector3(0) - self._inited = false - self._max_gui_upscale = nil - self._fit_node = nil - - self._anchors = {} - - self.mode = mode or const.LAYOUT_MODE.FIT - - self.on_size_changed = Event(on_size_changed_callback) -end - - -function Layout.on_late_init(self) - self._inited = true - self.origin_size = self.origin_size or gui.get_size(self.node) - self.fit_size = self.fit_size or vmath.vector3(self.origin_size) - self.pivot = helper.get_pivot_offset(gui.get_pivot(self.node)) - self.origin_position = gui.get_position(self.node) - self.position = vmath.vector3(self.origin_position) - gui.set_size_mode(self.node, gui.SIZE_MODE_MANUAL) - gui.set_adjust_mode(self.node, gui.ADJUST_FIT) - self:on_window_resized() + self.is_dirty = true + self.entities = {} + self.margin = { x = 0, y = 0 } + self.padding = gui.get_slice9(self.node) + self.type = layout_type or "horizontal" + self.is_resize_width = false + self.is_resize_height = false + self.is_justify = false end - -function Layout.on_window_resized(self) - if not self._inited then +function M:update() + if not self.is_dirty then return end - local x_koef, y_koef = helper.get_screen_aspect_koef() - - local revert_scale = 1 - if self._max_gui_upscale then - revert_scale = self._max_gui_upscale / helper.get_gui_scale() - revert_scale = math.min(revert_scale, 1) - end - gui.set_scale(self.node, vmath.vector3(revert_scale)) - - if self._fit_node then - self.fit_size = gui.get_size(self._fit_node) - self.fit_size.x = self.fit_size.x / x_koef - self.fit_size.y = self.fit_size.y / y_koef - end + self:refresh_layout() +end - x_koef = self.fit_size.x / self.origin_size.x * x_koef - y_koef = self.fit_size.y / self.origin_size.y * y_koef - local new_size = vmath.vector3(self.origin_size) +-- @tparam Layout self @{Layout} +-- @tparam number|nil margin_x +-- @tparam number|nil margin_y +-- @treturn druid.layout @{Layout} +function M.set_margin(self, margin_x, margin_y) + self.margin.x = margin_x or self.margin.x + self.margin.y = margin_y or self.margin.y + self.is_dirty = true - if self.mode == const.LAYOUT_MODE.STRETCH then - new_size.x = new_size.x * x_koef / revert_scale - new_size.y = new_size.y * y_koef / revert_scale - end + return self +end - if self.mode == const.LAYOUT_MODE.STRETCH_X then - new_size.x = new_size.x * x_koef / revert_scale - end - if self.mode == const.LAYOUT_MODE.STRETCH_Y then - new_size.y = new_size.y * y_koef / revert_scale - end +-- @tparam Layout self @{Layout} +-- @tparam vector4 padding The vector4 with padding values, where x - left, y - top, z - right, w - bottom +-- @treturn druid.layout @{Layout} +function M.set_padding(self, padding) + self.padding = padding + self.is_dirty = true - -- Fit to the stretched container (node size or other defined) - if self.mode == const.LAYOUT_MODE.ZOOM_MIN then - new_size = new_size * math.min(x_koef, y_koef) - end - if self.mode == const.LAYOUT_MODE.ZOOM_MAX then - new_size = new_size * math.max(x_koef, y_koef) - end + return self +end - if self._min_size then - new_size.x = math.max(new_size.x, self._min_size.x) - new_size.y = math.max(new_size.y, self._min_size.y) - end - if self._max_size then - new_size.x = math.min(new_size.x, self._max_size.x) - new_size.y = math.min(new_size.y, self._max_size.y) - end - self._current_size = new_size - gui.set_size(self.node, new_size) - self.position.x = self.origin_position.x + self.origin_position.x * (x_koef - 1) - self.position.y = self.origin_position.y + self.origin_position.y * (y_koef - 1) - gui.set_position(self.node, self.position) +-- @tparam Layout self @{Layout} +-- @treturn druid.layout @{Layout} +function M.set_dirty(self) + self.is_dirty = true - self.on_size_changed:trigger(self:get_context(), new_size) + return self end ---- Set minimal size of layout node -- @tparam Layout self @{Layout} --- @tparam vector3 min_size --- @treturn Layout @{Layout} -function Layout.set_min_size(self, min_size) - self._min_size = min_size +-- @tparam boolean is_justify +-- @treturn druid.layout @{Layout} +function M.set_justify(self, is_justify) + self.is_justify = is_justify + self.is_dirty = true + return self end ---- Set maximum size of layout node -- @tparam Layout self @{Layout} --- @tparam vector3 max_size --- @treturn Layout @{Layout} -function Layout.set_max_size(self, max_size) - self._max_size = max_size +-- @tparam string type The layout type: "horizontal", "vertical", "horizontal_wrap" +-- @treturn druid.layout @{Layout} +function M.set_type(self, type) + self.type = type + self.is_dirty = true + return self end ---- Set new origin position of layout node. You should apply this on node movement -- @tparam Layout self @{Layout} --- @tparam vector3 new_origin_position --- @treturn Layout @{Layout} -function Layout.set_origin_position(self, new_origin_position) - self.origin_position = new_origin_position or self.origin_position - self:on_window_resized() +-- @tparam boolean is_hug_width +-- @tparam boolean is_hug_height +-- @treturn druid.layout @{Layout} +function M.set_hug_content(self, is_hug_width, is_hug_height) + self.is_resize_width = is_hug_width or false + self.is_resize_height = is_hug_height or false + self.is_dirty = true + return self end ---- Set new origin size of layout node. You should apply this on node manual size change -- @tparam Layout self @{Layout} --- @tparam vector3 new_origin_size --- @treturn Layout @{Layout} -function Layout.set_origin_size(self, new_origin_size) - self.origin_size = new_origin_size or self.origin_size - self:on_window_resized() +-- @tparam string|node node_or_node_id +-- @treturn druid.layout @{Layout} +function M.add(self, node_or_node_id) + -- Acquire node from entity or by id + local node = node_or_node_id + if type(node_or_node_id) == "table" then + assert(node_or_node_id.node, "The entity should have a node") + node = node_or_node_id.node + else + -- @cast node_or_node_id string|node + node = self:get_node(node_or_node_id) + end + + -- @cast node node + table.insert(self.entities, node) + gui.set_parent(node, self.node) + + self.is_dirty = true + return self end ---- Set max gui upscale for FIT adjust mode (or side). It happens on bigger render gui screen -- @tparam Layout self @{Layout} --- @tparam number max_gui_upscale --- @treturn Layout @{Layout} -function Layout.set_max_gui_upscale(self, max_gui_upscale) - self._max_gui_upscale = max_gui_upscale - self:on_window_resized() -end +-- @treturn druid.layout @{Layout} +function M.refresh_layout(self) + local layout_node = self.node + + local entities = self.entities + local type = self.type -- vertical, horizontal, horizontal_wrap + local margin = self.margin -- {x: horizontal, y: vertical} in pixels, between elements + local padding = self.padding -- {x: left, y: top, z: right, w: bottom} in pixels + local is_justify = self.is_justify + local size = gui.get_size(layout_node) + local max_width = size.x - padding.x - padding.z + local max_height = size.y - padding.y - padding.w + local layout_pivot_offset = helper.get_pivot_offset(gui.get_pivot(layout_node)) -- {x: -0.5, y: -0.5} - is left bot, {x: 0.5, y: 0.5} - is right top + + local rows_data = self:calculate_rows_data() + local rows = rows_data.rows + local row_index = 1 + local row = rows[row_index] + + -- Current x and Current y is a top left corner of the node + local current_x = -row.width * (0.5 + layout_pivot_offset.x) + local current_y = rows_data.total_height * (0.5 - layout_pivot_offset.y) + + if is_justify then + if (type == "horizontal" or type == "horizontal_wrap") and row.count > 1 then + current_x = -max_width * (0.5 + layout_pivot_offset.x) + end + if type == "vertical" then + current_y = max_height * (0.5 - layout_pivot_offset.y) + end + end + for index = 1, #entities do + local node = entities[index] + local node_width = rows_data.nodes_width[node] + local node_height = rows_data.nodes_height[node] + local pivot_offset = helper.get_pivot_offset(gui.get_pivot(node)) + + if node_width > 0 and node_height > 0 then + -- Calculate position for current node + local position_x, position_y + + if type == "horizontal" then + position_x = current_x + node_width * (0.5 + pivot_offset.x) + position_y = current_y - row.height * (0.5 - pivot_offset.y) + + local node_margin = margin.x + if is_justify and row.count > 1 then + node_margin = (max_width - row.width) / (row.count - 1) + margin.x + end + current_x = current_x + node_width + node_margin + end + + if type == "vertical" then + position_x = current_x + row.width * (0.5 - pivot_offset.x) + position_y = current_y - node_height * (0.5 + pivot_offset.y) + + local node_margin = margin.y + if is_justify then + node_margin = (max_height - rows_data.total_height) / (#rows - 1) + margin.y + end + + current_y = current_y - node_height - node_margin + end + + if type == "horizontal_wrap" then + local width = row.width + if is_justify and row.count > 0 then + width = math.max(row.width, max_width) + end + local new_row_width = width * (0.5 - layout_pivot_offset.x) + + -- Compare with eps due the float loss and element flickering + if current_x + node_width - new_row_width > 0.0001 then + if row_index < #rows then + row_index = row_index + 1 + row = rows[row_index] + end + + current_x = -row.width * (0.5 + layout_pivot_offset.x) + current_y = current_y - row.height - margin.y + if is_justify and row.count > 1 then + current_x = -max_width * (0.5 + layout_pivot_offset.x) + end + end + + position_x = current_x + node_width * (0.5 + pivot_offset.x) + position_y = current_y - row.height * (0.5 - pivot_offset.y) + + local node_margin = margin.x + if is_justify and row.count > 1 then + node_margin = (max_width - row.width) / (row.count - 1) + margin.x + end + current_x = current_x + node_width + node_margin + end + + do -- Padding offset + if layout_pivot_offset.x == -0.5 then + position_x = position_x + padding.x + end + if layout_pivot_offset.y == 0.5 then + position_y = position_y - padding.y + end + if layout_pivot_offset.x == 0.5 then + position_x = position_x - padding.z + end + if layout_pivot_offset.y == -0.5 then + position_y = position_y + padding.w + end + end + + self:set_node_position(node, position_x, position_y) + end + end + + if self.is_resize_width or self.is_resize_height then + if self.is_resize_width then + size.x = rows_data.total_width + padding.x + padding.z + end + if self.is_resize_height then + size.y = rows_data.total_height + padding.y + padding.w + end + gui.set_size(layout_node, size) + end + + self.is_dirty = false ---- Set size for layout node to fit inside it --- @tparam Layout self @{Layout} --- @tparam vector3 target_size --- @treturn Layout @{Layout} -function Layout.fit_into_size(self, target_size) - self.fit_size = target_size - self:on_window_resized() return self end ---- Set node for layout node to fit inside it. Pass nil to reset -- @tparam Layout self @{Layout} --- @tparam node|nil node --- @treturn Layout @{Layout} -function Layout.fit_into_node(self, node) - self._fit_node = node - self:on_window_resized() +-- @treturn druid.layout @{Layout} +function M.clear_layout(self) + for index = #self.entities, 1, -1 do + self.entities[index] = nil + end + + self.is_dirty = true + return self end ---- Set current size for layout node to fit inside it +-- @private +-- @tparam node node +-- @treturn number, number +function M.get_node_size(node) + if not gui.is_enabled(node, false) then + return 0, 0 + end + + local scale = gui.get_scale(node) + + -- If node has text - get text size instead of node size + if gui.get_text(node) then + local text_metrics = helper.get_text_metrics_from_node(node) + return text_metrics.width * scale.x, text_metrics.height * scale.y + end + + local size = gui.get_size(node) + return size.x * scale.x, size.y * scale.y +end + + +-- @private -- @tparam Layout self @{Layout} --- @treturn Layout @{Layout} -function Layout.fit_into_window(self) - return self:fit_into_size(vmath.vector3( - gui.get_width(), - gui.get_height(), - 0)) +-- Calculate rows data for layout. Contains total width, height and rows info (width, height, count of elements in row) +-- @treturn druid.layout.rows_data +function M.calculate_rows_data(self) + local entities = self.entities + local margin = self.margin + local type = self.type + local padding = self.padding + + local size = gui.get_size(self.node) + local max_width = size.x - padding.x - padding.z + + -- Collect rows info about width, height and count of elements in row + local current_row = { width = 0, height = 0, count = 0 } + local rows_data = { + total_width = 0, + total_height = 0, + nodes_width = {}, + nodes_height = {}, + rows = { current_row } + } + + for index = 1, #entities do + local node = entities[index] + local node_width = rows_data.nodes_width[node] + local node_height = rows_data.nodes_height[node] + + -- Get node size if it's not calculated yet + if not node_width or not node_height then + node_width, node_height = M.get_node_size(node) + rows_data.nodes_width[node] = node_width + rows_data.nodes_height[node] = node_height + end + + if node_width > 0 and node_height > 0 then + if type == "horizontal" then + current_row.width = current_row.width + node_width + margin.x + current_row.height = math.max(current_row.height, node_height) + current_row.count = current_row.count + 1 + end + + if type == "vertical" then + if current_row.count > 0 then + current_row = { width = 0, height = 0, count = 0 } + table.insert(rows_data.rows, current_row) + end + + current_row.width = math.max(current_row.width, node_width + margin.x) + current_row.height = node_height + current_row.count = current_row.count + 1 + end + + if type == "horizontal_wrap" then + if current_row.width + node_width > max_width and current_row.count > 0 then + current_row = { width = 0, height = 0, count = 0 } + table.insert(rows_data.rows, current_row) + end + + current_row.width = current_row.width + node_width + margin.x + current_row.height = math.max(current_row.height, node_height) + current_row.count = current_row.count + 1 + end + end + end + + -- Remove last margin of each row + -- Calculate total width and height + local rows_count = #rows_data.rows + for index = 1, rows_count do + local row = rows_data.rows[index] + if row.width > 0 then + row.width = row.width - margin.x + end + + rows_data.total_width = math.max(rows_data.total_width, row.width) + rows_data.total_height = rows_data.total_height + row.height + end + + rows_data.total_height = rows_data.total_height + margin.y * (rows_count - 1) + return rows_data end +-- @private +-- @tparam node node +-- @tparam number x +-- @tparam number y +-- @treturn node +function M:set_node_position(node, x, y) + local position = gui.get_position(node) + position.x = x + position.y = y + gui.set_position(node, position) + + return node +end -return Layout +return M diff --git a/druid/extended/slider.lua b/druid/extended/slider.lua index 22bd4e49..7af97d35 100644 --- a/druid/extended/slider.lua +++ b/druid/extended/slider.lua @@ -151,6 +151,8 @@ function Slider.on_input(self, action_id, action) self.value = (self.target_pos.y - self.start_pos.y) / self.dist.y end + self.value = math.abs(self.value) + if self.steps then local closest_dist = 1000 local closest = nil diff --git a/druid/styles/default/style.lua b/druid/styles/default/style.lua index 2ce473e7..1a857b99 100644 --- a/druid/styles/default/style.lua +++ b/druid/styles/default/style.lua @@ -42,15 +42,15 @@ M["button"] = { end, on_click_disabled = function(self, node) - settings.play_sound(M.button.BTN_SOUND_DISABLED) + local start_pos = self.start_pos + gui.animate(node, "position.x", start_pos.x - 3, gui.EASING_OUTSINE, 0.05, 0, function() + gui.animate(node, "position.x", start_pos.x + 3, gui.EASING_OUTSINE, 0.1, 0, function() + gui.animate(node, "position.x", start_pos.x, gui.EASING_OUTSINE, 0.05) + end) + end) 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 } diff --git a/druid/system/druid_instance.lua b/druid/system/druid_instance.lua index 52a43c55..4f8f31bb 100755 --- a/druid/system/druid_instance.lua +++ b/druid/system/druid_instance.lua @@ -247,7 +247,7 @@ function DruidInstance.initialize(self, context, style) end ---- Create new component. +-- Create new component. -- @tparam DruidInstance self -- @tparam BaseComponent component Component module -- @tparam any ... Other component params to pass it to component:init function @@ -776,9 +776,8 @@ end -- @tparam DruidInstance self -- @tparam string|node node The_node id or gui.get_node(node_id). -- @tparam string mode The layout mode --- @tparam function|nil on_size_changed_callback The callback on window resize -- @treturn Layout @{Layout} component -function DruidInstance.new_layout(self, node, mode, on_size_changed_callback) +function DruidInstance.new_layout(self, node, mode) return helper.require_component_message("layout") end diff --git a/druid/system/utf8.lua b/druid/system/utf8.lua index 7f139149..6c99fcf7 100644 --- a/druid/system/utf8.lua +++ b/druid/system/utf8.lua @@ -688,7 +688,8 @@ local function matcherGenerator(regex, plain) local sum = 0 local bc, ec = utf8sub(str, 1, 1), utf8sub(str, 2, 2) local skip = len(bc) + len(ec) - bc, ec = utf8unicode(bc), utf8unicode(ec) + bc = utf8unicode(bc) --[[@as string]] + ec = utf8unicode(ec) --[[@as string]] return function(cC) if cC == ec and sum > 0 then sum = sum - 1 diff --git a/druid/templates/component.template.lua b/druid/templates/component.template.lua index df482777..d67bf32f 100644 --- a/druid/templates/component.template.lua +++ b/druid/templates/component.template.lua @@ -3,18 +3,13 @@ local component = require("druid.component") ---@class component_name : druid.base_component local Component = component.create("component_name") -local SCHEME = { - ROOT = "root", - BUTTON = "button", -} - -- Component constructor. Template name and nodes are optional. Pass it if you use it in your component function Component:init(template, nodes) self.druid = self:get_druid(template, nodes) - self.root = self:get_node(SCHEME.ROOT) + self.root = self:get_node("root") - self.button = self.druid:new_button(SCHEME.BUTTON, function() end) + self.button = self.druid:new_button("button", function() end) end diff --git a/druid/templates/component_full.template.lua b/druid/templates/component_full.template.lua index e4a2f7a9..1978cf2f 100644 --- a/druid/templates/component_full.template.lua +++ b/druid/templates/component_full.template.lua @@ -3,13 +3,6 @@ local component = require("druid.component") ---@class component_name : druid.base_component local Component = component.create("component_name") --- Scheme of component gui nodes -local SCHEME = { - ROOT = "root", - BUTTON = "button", -} - - -- Component constructor. Template name and nodes are optional. Pass it if you use it in your component function Component:init(template, nodes) -- If your component is gui template, pass the template name and set it @@ -18,7 +11,7 @@ function Component:init(template, nodes) self.druid = self:get_druid(template, nodes) -- self:get_node will auto process component template and nodes - self.root = self:get_node(SCHEME.ROOT) + self.root = self:get_node("root") end diff --git a/example/examples/data_list/with_component/button_component/button_component.lua b/example/examples/data_list/with_component/button_component/button_component.lua index 39c6c234..2609c8ef 100644 --- a/example/examples/data_list/with_component/button_component/button_component.lua +++ b/example/examples/data_list/with_component/button_component/button_component.lua @@ -14,24 +14,15 @@ local component = require("druid.component") ---@field druid druid_instance local ButtonComponent = component.create("button_component") -local SCHEME = { - ROOT = "root", - TEXT = "text", - ICON = "icon", - CHECKBOX = "checkbox" -} - ---@param template string ---@param nodes table function ButtonComponent:init(template, nodes) - self:set_template(template) - self:set_nodes(nodes) - self.druid = self:get_druid() + self.druid = self:get_druid(template, nodes) - self.root = self:get_node(SCHEME.ROOT) - self.text = self.druid:new_text(SCHEME.TEXT) - self.checkbox = self:get_node(SCHEME.CHECKBOX) + self.root = self:get_node("root") + self.text = self.druid:new_text("text") + self.checkbox = self:get_node("checkbox") self.button = self.druid:new_button(self.root, self._on_click) diff --git a/utils/annotations_manual.lua b/utils/annotations_manual.lua index cc258b29..a72a32e8 100644 --- a/utils/annotations_manual.lua +++ b/utils/annotations_manual.lua @@ -7,6 +7,8 @@ ---@field height number ---@field offset_x number|nil ---@field offset_y number|nil +---@field max_ascent number +---@field max_descent number ---@field node_size vector3|nil @For images only ---@class druid.rich_text.lines_metrics @@ -47,6 +49,7 @@ ---@field parent node ---@field size number ---@field fonts table +---@field scale vector3 ---@field color vector4 ---@field shadow vector4 ---@field outline vector4 @@ -54,12 +57,9 @@ ---@field image_pixel_grid_snap boolean ---@field combine_words boolean ---@field default_animation string ----@field node_prefab node ---@field text_prefab node ----@field text_scale vector3 ---@field adjust_scale number ---@field default_texture string ----@field node_scale vector3 ---@field is_multiline boolean ---@field text_leading number ---@field font hash @@ -71,3 +71,22 @@ ---@field height number ---@field max_ascent number ---@field max_descent number + +---@class utf8 +---@field len fun(string: string): number +---@field sub fun(string: string, i: number, j: number): string +---@field gmatch fun(string: string, pattern: string): fun(): string +---@field gsub fun(string: string, pattern: string, repl: string, n: number): string +---@field char fun(...: number): string +---@field byte fun(string: string, i: number, j: number): number + + +---Add generics to some functions. + +---Create new component. +---@generic T: druid.base_component +---@param self druid_instance +---@param component T Component module +---@param ... any Other component params to pass it to component:init function +---@return T Component instance +function druid_instance.new(self, component, ...) end \ No newline at end of file
  • Return current text adjust type
    get_text_index_by_width(self, width)Get chars count by width
    get_text_size(self, text) Calculate text width with font with respect to trailing space