How to extend Druid styles?

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:

  1. is there a better way to include a label/child in a Druid style?
  2. why are my custom styles disappearing after I set them?
1 Like

Could you please attach some code snippets too? :blush:

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
2 Likes