I’m trying to extend Druid’s styles (specifically for buttons) to make my buttons a little juicier.
First thing I did was add an on_pressed style; I had to tweak the default button a bit, but it worked, so I can have separate animations play for press & release. (and the button will animate when you press it, instead of only after!)
But the big thing I want to animate the button labels along with the button itself. I created on_pressed_label and on_click_label styles, and added all the necessary hooks for them to get called, but… after setting the button’s style, and making sure these get set, when I go to actually call them, they’re not there? I’m assuming this is some metatable shenanigans, but I can’t figure out what I’m missing.
So I guess I have 2 questions:
is there a better way to include a label/child in a Druid style?
why are my custom styles disappearing after I set them?
When I need a druid button that requires a bit more functionality I use my gui helper module to create it. This is more or less what it looks like.
local Button = require("druid.base.button")
--- Set text node that is inside this button
--- @param self unknown
--- @param text_node any node name or the actual node
---@return druid.button @{Button} component
function Button.set_text(self, text_node)
if type(text_node) == "string" then
self.text_node = gui.get_node(text_node)
else
self.text_node = text_node
end
return self
end
local M = {}
function M.new_button(druid, node, callback, text_node)
--
local btn = druid:new_button(node, function (_self, args, button)
-- Animation reasons
timer.delay(0.1, false, function ()
callback(_self, args, button)
end)
end)
if text_node ~= nil then
-- This is the `function Button.set_text(self, text_node)` above.
btn:set_text(text_node)
end
--
return btn
end
return M
Then in the style functions:
if self.text_node then
-- Animate the text node or whatever
end
I do not know if this is the intented or the best way to do this but It works for me.
Styles currently in Druid are general settings for all components. They can have only access to elements to itself. So the idea was to adjust general animations and later to adjust them if required
In case the “button + text” component I suggest the best way is to create a custom component for it.
Example for your case:
-- The button_text.lua file
local component = require("druid.component")
---@class button_text: druid.base_component
---@field druid druid_instance
---@field button druid.button
---@field text druid.text
local M = component.create("button_text")
---@param template string
---@param nodes table<hash, node>
---@param callback function
function M:init(template, nodes, callback)
self:set_template(template)
self:set_nodes(nodes)
self.druid = self:get_druid()
self.callback = callback
self.button = self.druid:new_button("button", self.on_button)
self.button:set_style(nil)
self.button.on_pressed:subscribe(self.on_button_pressed)
-- We need to pass "self" here, the context of inner hover component is a button
self.button.hover.on_mouse_hover:subscribe(self.on_button_hover, self)
self.text = self.druid:new_text("text")
end
-- The _ arg here is a button instance from hover event, we skip it
function M:on_button_hover(_, is_hover)
local scale = is_hover and 1.2 or 1
gui.animate(self.button.node, gui.PROP_SCALE, vmath.vector3(scale, scale, 1), gui.EASING_OUTSINE, 0.2)
local font = is_hover and "text_bold" or "text_regular"
gui.set_font(self.text.node, font)
end
function M:on_button_pressed()
gui.animate(self.button.node, gui.PROP_SCALE, vmath.vector3(1.4), gui.EASING_OUTBACK, 0.15)
end
function M:on_button()
if self.callback then
self.callback()
end
gui.animate(self.button.node, gui.PROP_SCALE, vmath.vector3(1), gui.EASING_OUTBACK, 0.15)
end
function M:my_function()
print("My function")
end
return M
In the place of gui_script instead of druid:new_button now you can create this component:
-- GUI file
local druid = require("druid.druid")
local button_text = require("components.button_text")
function init(self)
self.druid = druid.new(self)
-- The "button_text" here is a template name on this GUI scene with nodes "button" and "text"
-- If you are not using templates, just remove it
-- Nodes is nil, since we don't create it with `gui.clone_tree`, picking nodes from the GUI scene
self.button_text = self.druid:new(button_text, "button_text", nil, callback)
-- Now we can access to the button instance
self.button_text:my_function() -- Prints "My function"
end