16 Commits
1.1.1 ... 1.1.6

Author SHA1 Message Date
Insality
b2d73bafa4 Release 1.1.6 2025-09-27 14:02:11 +03:00
Insality
872af8fb8e Update editor script linking messages 2025-09-27 14:02:11 +03:00
Insality
46b7d828f0 Use editor script to set gui script path 2025-09-27 14:02:11 +03:00
Maksim Tuprikov
044eec50b2 Merge pull request #322 from vlaaad/patch-1 2025-08-22 14:43:05 +02:00
vlaaad
066a81f5f3 Create ext.properties 2025-08-22 14:36:54 +02:00
Insality
ba7ee40510 Release 1.1.5 2025-07-02 21:51:43 +03:00
Insality
d019247ae4 Update for event v12 2025-07-02 21:50:48 +03:00
Insality
616c513fbd Use tagged releases of dependencies 2025-06-19 20:28:45 +03:00
Insality
61111536a8 Release 1.1.4 2025-05-20 00:51:34 +03:00
Maksim Tuprikov
b5d2f313cc Merge pull request #312 from polivanni/master
Reset width and leading of the reusable table TEXT_METRICS_OPTIONS by default
2025-05-20 00:50:09 +03:00
Ivan Polovyi
d0067e5496 Reset width and leading of the reusable table TEXT_METRICS_OPTIONS by default 2025-05-18 23:33:40 +03:00
Insality
58f14d0a64 Return hack to keep cloned node_ids consistent 2025-05-13 21:43:05 +03:00
Insality
6572da3b14 Release 1.1.2 2025-05-07 23:54:30 +03:00
Insality
d7c26358a0 Fix for data list element amounts issue 2025-05-07 21:43:48 +03:00
Maksim Tuprikov
f007a28ab7 Merge pull request #310 from rigo128/example_data_list_matrix_basic
Add data_list_matrix_basic example
2025-05-07 18:50:53 +03:00
Yury Grigoryev
d98c3c2ef1 Add data_list_matrix_basic example 2025-05-07 17:07:58 +03:00
18 changed files with 368 additions and 107 deletions

View File

@@ -33,7 +33,8 @@
"Lua.runtime.version": "Lua 5.1", "Lua.runtime.version": "Lua 5.1",
"Lua.workspace.library": [ "Lua.workspace.library": [
"~/Library/Application Support/Cursor/User/globalStorage/astronachos.defold", "~/Library/Application Support/Cursor/User/globalStorage/astronachos.defold",
"~/Library/Application Support/Cursor/User/workspaceStorage/1446075a23c89451a63f0e82b2291def/astronachos.defold" "~/Library/Application Support/Cursor/User/workspaceStorage/1446075a23c89451a63f0e82b2291def/astronachos.defold",
"~/Library/Application Support/Cursor/User/workspaceStorage/7975bec62a9fa9724d190779fa01ec63/astronachos.defold"
], ],
"files.exclude": { "files.exclude": {
"**/*.gui": true "**/*.gui": true

View File

@@ -39,13 +39,13 @@ Open your `game.project` file and add the following lines to the dependencies fi
**[Druid](https://github.com/Insality/druid/)** **[Druid](https://github.com/Insality/druid/)**
``` ```
https://github.com/Insality/druid/archive/refs/tags/1.1.1.zip https://github.com/Insality/druid/archive/refs/tags/1.1.6.zip
``` ```
**[Defold Event](https://github.com/Insality/defold-event)** **[Defold Event](https://github.com/Insality/defold-event)**
``` ```
https://github.com/Insality/defold-event/archive/refs/tags/11.zip https://github.com/Insality/defold-event/archive/refs/tags/12.zip
``` ```
After that, select `Project ▸ Fetch Libraries` to update [library dependencies]((https://defold.com/manuals/libraries/#setting-up-library-dependencies)). This happens automatically whenever you open a project so you will only need to do this if the dependencies change without re-opening the project. After that, select `Project ▸ Fetch Libraries` to update [library dependencies]((https://defold.com/manuals/libraries/#setting-up-library-dependencies)). This happens automatically whenever you open a project so you will only need to do this if the dependencies change without re-opening the project.

View File

@@ -117,7 +117,17 @@ function M:set_nodes(nodes)
nodes = gui.clone_tree(nodes) --[[@as table<hash, node>]] nodes = gui.clone_tree(nodes) --[[@as table<hash, node>]]
end end
-- When we use gui.clone_tree in inner template (template inside other template)
-- this nodes have no id. We have table: hash(correct_id) : hash("") or hash("_nodeX"
-- It's wrong and we use this hack to fix this
if nodes then
for id, node in pairs(nodes) do
gui.set_id(node, id)
end
end
self._meta.nodes = nodes self._meta.nodes = nodes
return self return self
end end

View File

@@ -1,6 +1,6 @@
local component = require("druid.component") local component = require("druid.component")
local helper = require("druid.helper") local helper = require("druid.helper")
local defer = require("event.defer") local queues = require("event.queues")
---@class druid.tiling_node: druid.component ---@class druid.tiling_node: druid.component
---@field animation table ---@field animation table
@@ -28,7 +28,7 @@ function M:init(node)
print("The druid.script is not found, please add it nearby to the GUI collection", msg.url()) print("The druid.script is not found, please add it nearby to the GUI collection", msg.url())
end) end)
defer.push("druid.get_atlas_path", { queues.push("druid.get_atlas_path", {
texture_name = gui.get_texture(self.node), texture_name = gui.get_texture(self.node),
sender = msg.url(), sender = msg.url(),
}, self.on_get_atlas_path, self) }, self.on_get_atlas_path, self)

View File

@@ -3,9 +3,9 @@
-- This one is a required to make a unified "Shaders" pipeline in the GUI scripts -- This one is a required to make a unified "Shaders" pipeline in the GUI scripts
-- This required to grab a texture data with `go.get` function -- This required to grab a texture data with `go.get` function
local defer = require("event.defer") local queues = require("event.queues")
---Usage: defer.push("druid.get_atlas_path", { ---Usage: queues.push("druid.get_atlas_path", {
--- texture_name = gui.get_texture(self.node), --- texture_name = gui.get_texture(self.node),
--- sender = msg.url(), --- sender = msg.url(),
---}, callback, [context]) ---}, callback, [context])
@@ -35,10 +35,10 @@ end
function init(self) function init(self)
defer.subscribe(MESSAGE_GET_ATLAS_PATH, get_atlas_path, self) queues.subscribe(MESSAGE_GET_ATLAS_PATH, get_atlas_path, self)
end end
function final(self) function final(self)
defer.unsubscribe(MESSAGE_GET_ATLAS_PATH, get_atlas_path, self) queues.unsubscribe(MESSAGE_GET_ATLAS_PATH, get_atlas_path, self)
end end

View File

@@ -12,7 +12,7 @@ end
function M.create_druid_gui_script(selection) function M.create_druid_gui_script(selection)
local gui_filepath = editor.get(selection, "path") local gui_filepath = editor.get(selection, "path")
local filename = gui_filepath:match("([^/]+)%.gui$") local filename = gui_filepath:match("([^/]+)%.gui$")
print("Create Druid GUI Script for", gui_filepath) print("Create GUI script for", gui_filepath)
local absolute_project_path = editor.external_file_attributes(".").path local absolute_project_path = editor.external_file_attributes(".").path
local widget_resource_path = gui_filepath:gsub("%.gui$", ".gui_script") local widget_resource_path = gui_filepath:gsub("%.gui$", ".gui_script")
@@ -25,8 +25,8 @@ function M.create_druid_gui_script(selection)
local f = io.open(new_widget_absolute_path, "r") local f = io.open(new_widget_absolute_path, "r")
if f then if f then
f:close() f:close()
print("Widget file already exists at " .. new_widget_absolute_path) print("GUI script file already exists at " .. new_widget_absolute_path)
print("Creation aborted to prevent overwriting") error("Creation aborted to prevent overwriting")
return return
end end
@@ -37,7 +37,7 @@ function M.create_druid_gui_script(selection)
local template_content = editor.get(template_path, "text") local template_content = editor.get(template_path, "text")
if not template_content then if not template_content then
print("Error: Could not load template from", template_path) print("Error: Could not load template from", template_path)
print("Check the template path in [Druid] Settings") error("Check the template path in [Druid] Settings")
return return
end end
@@ -54,90 +54,12 @@ function M.create_druid_gui_script(selection)
file:write(template_content) file:write(template_content)
file:close() file:close()
print("Widget created at " .. widget_resource_path) print("Widget created: " .. widget_resource_path)
editor.transact({
editor.tx.set(selection, "script", widget_resource_path)
})
editor.save()
M.link_gui_script(selection, widget_resource_path)
end
---Links a GUI script to a GUI file by updating the script property
---@param selection string The GUI resource to modify
---@param widget_resource_path string The path to the GUI script to link
function M.link_gui_script(selection, widget_resource_path)
local defold_parser = require("druid.editor_scripts.defold_parser.defold_parser")
local system = require("druid.editor_scripts.defold_parser.system.system")
local gui_filepath = editor.get(selection, "path")
print("Linking GUI script to", gui_filepath)
-- Get the absolute path to the file
local absolute_project_path = editor.external_file_attributes(".").path
if not absolute_project_path:match("[\\/]$") then
absolute_project_path = absolute_project_path .. "/"
end
local clean_gui_path = gui_filepath
if clean_gui_path:sub(1, 1) == "/" then
clean_gui_path = clean_gui_path:sub(2)
end
local gui_absolute_path = absolute_project_path .. clean_gui_path
-- Create a backup
local backup_path = gui_absolute_path .. ".backup"
print("Creating backup at:", backup_path)
-- Read and write backup
local content, err_read = system.read_file(gui_absolute_path)
if not content then
print("Error reading original file for backup:", err_read)
return
end
local success, err_write = system.write_file(backup_path, content)
if not success then
print("Error creating backup file:", err_write)
return
end
-- Parse the GUI file
print("Parsing GUI file...")
local gui_data = defold_parser.load_from_file(gui_absolute_path)
if not gui_data then
print("Error: Failed to parse GUI file")
return
end
-- Update the script property
print("Setting script property to:", widget_resource_path)
gui_data.script = widget_resource_path
-- Write the updated GUI file
print("Writing updated GUI file...")
local save_success = defold_parser.save_to_file(gui_absolute_path, gui_data)
if not save_success then
print("Error: Failed to save GUI file")
print("Attempting to restore from backup...")
-- Restore from backup on failure
local backup_content, backup_err_read = system.read_file(backup_path)
if not backup_content then
print("Error reading backup file:", backup_err_read)
return
end
local restore_success, restore_err_write = system.write_file(gui_absolute_path, backup_content)
if not restore_success then
print("Critical: Failed to restore from backup:", restore_err_write)
return
end
print("Restored successfully from backup")
return
end
-- Remove backup on success
os.remove(backup_path)
print("Successfully linked GUI script to:", gui_filepath)
end end

View File

@@ -26,7 +26,7 @@ function M.create_druid_widget(selection)
if f then if f then
f:close() f:close()
print("Widget file already exists at " .. new_widget_absolute_path) print("Widget file already exists at " .. new_widget_absolute_path)
print("Creation aborted to prevent overwriting") error("Creation aborted to prevent overwriting")
return return
end end
@@ -37,7 +37,7 @@ function M.create_druid_widget(selection)
local template_content = editor.get(template_path, "text") local template_content = editor.get(template_path, "text")
if not template_content then if not template_content then
print("Error: Could not load template from", template_path) print("Error: Could not load template from", template_path)
print("Check the template path in [Druid] Settings") error("Check the template path in [Druid] Settings")
return return
end end

35
druid/ext.properties Normal file
View File

@@ -0,0 +1,35 @@
[druid]
help = Settings for Druid extension
group = Runtime
input_text.default = text
input_touch.default = touch
input_marked_text.default = marked_text
input_key_esc.default = key_esc
input_key_back.default = key_back
input_key_enter.default = key_enter
input_key_backspace.default = key_backspace
input_multitouch.default = touch_multi
input_scroll_up.default = mouse_wheel_up
input_scroll_down.default = mouse_wheel_down
input_key_left.default = key_left
input_key_right.default = key_right
input_key_lshift.default = key_lshift
input_key_lctrl.default = key_lctrl
input_key_lsuper.default = key_lsuper
no_auto_input.type = bool

View File

@@ -267,10 +267,17 @@ function M:_refresh()
local start_index = self.grid:get_index(start_pos) local start_index = self.grid:get_index(start_pos)
start_index = math.max(1, start_index) start_index = math.max(1, start_index)
local pivot = helper.get_pivot_offset(gui.get_pivot(self.scroll.view_node)) local offset_x = self.scroll.view_size.x
local offset_x = self.scroll.view_size.x * (0.5 - pivot.x) local offset_y = self.scroll.view_size.y
local offset_y = self.scroll.view_size.y * (0.5 + pivot.y)
local end_pos = vmath.vector3(start_pos.x + offset_x, start_pos.y - offset_y, 0) local end_pos = vmath.vector3(start_pos.x + offset_x, start_pos.y - offset_y, 0)
local max_offset_x = (self.grid.in_row - 1) * self.grid.node_size.x
end_pos.x = math.min(end_pos.x, start_pos.x + max_offset_x)
if #self._data <= self.grid.in_row then
end_pos.y = start_pos.y
end
local end_index = self.grid:get_index(end_pos) local end_index = self.grid:get_index(end_pos)
end_index = math.min(#self._data, end_index) end_index = math.min(#self._data, end_index)

View File

@@ -473,6 +473,9 @@ function M.get_text_metrics_from_node(text_node)
options.tracking = gui.get_tracking(text_node) options.tracking = gui.get_tracking(text_node)
options.line_break = gui.get_line_break(text_node) options.line_break = gui.get_line_break(text_node)
options.width = 0
options.leading = 0
-- Gather other options only if it used in node -- Gather other options only if it used in node
if options.line_break then if options.line_break then
options.width = gui.get_size(text_node).x options.width = gui.get_size(text_node).x

View File

@@ -2863,6 +2863,49 @@ nodes {
parent: "data_list_cache_with_component/button_component/root" parent: "data_list_cache_with_component/button_component/root"
template_node_child: true template_node_child: true
} }
nodes {
type: TYPE_TEMPLATE
id: "data_list_matrix_basic"
parent: "data_list"
inherit_alpha: true
template: "/example/examples/data_list/basic/data_list_matrix_basic.gui"
}
nodes {
type: TYPE_BOX
id: "data_list_matrix_basic/root"
parent: "data_list_matrix_basic"
template_node_child: true
}
nodes {
type: TYPE_BOX
id: "data_list_matrix_basic/view"
parent: "data_list_matrix_basic/root"
template_node_child: true
}
nodes {
type: TYPE_BOX
id: "data_list_matrix_basic/content"
parent: "data_list_matrix_basic/view"
template_node_child: true
}
nodes {
type: TYPE_BOX
id: "data_list_matrix_basic/prefab"
parent: "data_list_matrix_basic/content"
template_node_child: true
}
nodes {
type: TYPE_BOX
id: "data_list_matrix_basic/panel"
parent: "data_list_matrix_basic/prefab"
template_node_child: true
}
nodes {
type: TYPE_TEXT
id: "data_list_matrix_basic/text"
parent: "data_list_matrix_basic/prefab"
template_node_child: true
}
nodes { nodes {
type: TYPE_BOX type: TYPE_BOX
texture: "druid_example/empty" texture: "druid_example/empty"

View File

@@ -1,4 +1,4 @@
---@class widget.container_anchors: druid.widget ---@class widget.container_resize: druid.widget
local M = {} local M = {}

View File

@@ -0,0 +1,117 @@
fonts {
name: "text_bold"
font: "/example/assets/fonts/text_bold.font"
}
textures {
name: "druid_example"
texture: "/example/assets/druid_example.atlas"
}
nodes {
type: TYPE_BOX
texture: "druid_example/empty"
id: "root"
inherit_alpha: true
size_mode: SIZE_MODE_AUTO
visible: false
}
nodes {
position {
y: 350.0
}
size {
x: 400.0
y: 700.0
}
color {
x: 0.173
y: 0.184
z: 0.204
}
type: TYPE_BOX
texture: "druid_example/pixel"
id: "view"
pivot: PIVOT_N
parent: "root"
inherit_alpha: true
clipping_mode: CLIPPING_MODE_STENCIL
}
nodes {
size {
x: 400.0
y: 700.0
}
type: TYPE_BOX
texture: "druid_example/empty"
id: "content"
pivot: PIVOT_N
parent: "view"
inherit_alpha: true
visible: false
}
nodes {
position {
y: -350.0
}
size {
x: 100.0
y: 100.0
}
type: TYPE_BOX
id: "prefab"
parent: "content"
inherit_alpha: true
visible: false
}
nodes {
size {
x: 90.0
y: 90.0
}
color {
x: 0.631
y: 0.843
z: 0.961
}
type: TYPE_BOX
texture: "druid_example/ui_circle_32"
id: "panel"
parent: "prefab"
inherit_alpha: true
slice9 {
x: 16.0
y: 16.0
z: 16.0
w: 16.0
}
}
nodes {
size {
x: 50.0
y: 50.0
}
color {
x: 0.31
y: 0.318
z: 0.322
}
type: TYPE_TEXT
text: "1"
font: "text_bold"
id: "text"
outline {
x: 1.0
y: 1.0
z: 1.0
}
shadow {
x: 1.0
y: 1.0
z: 1.0
}
parent: "prefab"
inherit_alpha: true
outline_alpha: 0.0
shadow_alpha: 0.0
}
material: "/builtins/materials/gui.material"
adjust_reference: ADJUST_REFERENCE_PARENT

View File

@@ -0,0 +1,95 @@
local event = require("event.event")
---@class examples.data_list_matrix_basic: druid.widget
---@field prefab node
---@field scroll druid.scroll
---@field grid druid.grid
---@field data_list druid.data_list
---@field on_item_click event
local M = {}
function M:init()
self.prefab = self:get_node("prefab")
gui.set_enabled(self.prefab, false)
self.scroll = self.druid:new_scroll("view", "content")
self.grid = self.druid:new_grid("content", self.prefab, 4)
self.data_list = self.druid:new_data_list(self.scroll, self.grid, self.create_item_callback) --[[@as druid.data_list]]
local data = {}
for index = 1, 1000 do
table.insert(data, {})
end
self.data_list:set_data(data)
self.on_item_click = event.create()
end
---@param item_data table
---@param index number
---@return node, druid.component
function M:create_item_callback(item_data, index)
local nodes = gui.clone_tree(self.prefab)
local root = nodes[self:get_template() .. "/prefab"]
local text = nodes[self:get_template() .. "/text"]
gui.set_enabled(root, true)
gui.set_text(text, tostring(index))
local button = self.druid:new_button(root, self.on_button_click, index)
return root, button
end
function M:on_button_click(index)
self.on_item_click:trigger(index)
end
---@param output_list output_list
function M:on_example_created(output_list)
self.on_item_click:subscribe(function(index)
output_list:add_log_text("Item clicked: " .. index)
end)
end
---@param properties_panel properties_panel
function M:properties_control(properties_panel)
local view_node = self.scroll.view_node
local is_stencil = gui.get_clipping_mode(view_node) == gui.CLIPPING_MODE_STENCIL
properties_panel:add_checkbox("ui_clipping", is_stencil, function(value)
gui.set_clipping_mode(view_node, value and gui.CLIPPING_MODE_STENCIL or gui.CLIPPING_MODE_NONE)
end)
properties_panel:add_slider("ui_scroll", 0, function(value)
self.scroll:scroll_to_percent(vmath.vector3(0, 1 - value, 0), true)
end)
end
---@return string
function M:get_debug_info()
local data_list = self.data_list
local data = data_list:get_data()
local info = ""
info = info .. "Data length: " .. #data .. "\n"
info = info .. "First Visual Index: " .. data_list.top_index .. "\n"
info = info .. "Last Visual Index: " .. data_list.last_index .. "\n"
local s = self.scroll
info = info .. "\n"
info = info .. "View Size Y: " .. gui.get(s.view_node, "size.y") .. "\n"
info = info .. "Content Size Y: " .. gui.get(s.content_node, "size.y") .. "\n"
info = info .. "Content position Y: " .. math.ceil(s.position.y) .. "\n"
info = info .. "Content Range Y: " .. s.available_pos.y .. " - " .. s.available_pos.w .. "\n"
return info
end
return M

View File

@@ -21,6 +21,15 @@ function M.get_examples()
widget_class = require("example.examples.data_list.basic.data_list_horizontal_basic"), widget_class = require("example.examples.data_list.basic.data_list_horizontal_basic"),
}, },
{
name_id = "ui_example_data_list_matrix_basic",
information_text_id = "ui_example_data_list_matrix_basic_description",
template = "data_list_matrix_basic",
root = "data_list_matrix_basic/root",
code_url = "example/examples/data_list/basic/data_list_matrix_basic.lua",
widget_class = require("example.examples.data_list.basic.data_list_matrix_basic"),
},
{ {
name_id = "ui_example_data_list_add_remove_clear", name_id = "ui_example_data_list_add_remove_clear",
information_text_id = "ui_example_data_list_add_remove_clear_description", information_text_id = "ui_example_data_list_add_remove_clear_description",

View File

@@ -147,6 +147,9 @@
"ui_example_data_list_horizontal_basic": "Data List Horizontal Basic", "ui_example_data_list_horizontal_basic": "Data List Horizontal Basic",
"ui_example_data_list_horizontal_basic_description": "How to make a horizontal data list", "ui_example_data_list_horizontal_basic_description": "How to make a horizontal data list",
"ui_example_data_list_matrix_basic": "Data List Matrix Basic",
"ui_example_data_list_matrix_basic_description": "How to make a matrix data list",
"ui_example_data_list_add_remove_clear": "Data List Add Remove Clear", "ui_example_data_list_add_remove_clear": "Data List Add Remove Clear",
"ui_example_data_list_add_remove_clear_description": "How the add, remove and clear functions work in the data list", "ui_example_data_list_add_remove_clear_description": "How the add, remove and clear functions work in the data list",

View File

@@ -14,16 +14,16 @@ update_frequency = 60
[project] [project]
title = Druid title = Druid
version = 1.1.1 version = 1.1.5
publisher = Insality publisher = Insality
developer = Maksim Tuprikov developer = Maksim Tuprikov
custom_resources = /example/locales custom_resources = /example/locales
dependencies#0 = https://github.com/britzl/deftest/archive/refs/tags/2.8.0.zip dependencies#0 = https://github.com/britzl/deftest/archive/refs/tags/2.8.0.zip
dependencies#1 = https://github.com/Insality/defold-saver/archive/refs/heads/develop.zip dependencies#1 = https://github.com/Insality/defold-saver/archive/refs/tags/5.zip
dependencies#2 = https://github.com/Insality/defold-tweener/archive/refs/tags/3.zip dependencies#2 = https://github.com/Insality/defold-tweener/archive/refs/tags/3.zip
dependencies#3 = https://github.com/Insality/panthera/archive/refs/heads/develop.zip dependencies#3 = https://github.com/Insality/panthera/archive/refs/tags/runtime.4.zip
dependencies#4 = https://github.com/Insality/defold-lang/archive/refs/tags/3.zip dependencies#4 = https://github.com/Insality/defold-lang/archive/refs/tags/3.zip
dependencies#5 = https://github.com/Insality/defold-event/archive/refs/heads/develop.zip dependencies#5 = https://github.com/Insality/defold-event/archive/refs/tags/12.zip
dependencies#6 = https://github.com/subsoap/defos/archive/refs/tags/v2.8.0.zip dependencies#6 = https://github.com/subsoap/defos/archive/refs/tags/v2.8.0.zip
[library] [library]

View File

@@ -705,3 +705,19 @@ Please support me if you like this project! It will help me keep engaged to upda
#### Druid 1.1.1 #### Druid 1.1.1
- [#309] Added max_size_x and max_size_y to container (by [astrochili](https://github.com/astrochili)) - [#309] Added max_size_x and max_size_y to container (by [astrochili](https://github.com/astrochili))
#### Druid 1.1.2
- [#310] Add data list matrix example (Grid 4 in row)
- [Data List] Fix for data list element amounts issue
#### Druid 1.1.3
- Fix for node_id of cloned nodes with `gui.clone_tree`
#### Druid 1.1.4
- [#312] Fix for text metrics issue if returned height is 0 sometimes
#### Druid 1.1.5
- Update for using `defold-event` library v12
#### Druid 1.1.6
- [#326] Fix for Editor Scripts corrupt file issue