Druid UI Framework for Defold

repository·master·Indexed 20 days ago

https://github.com/insality/druid

A powerful and flexible UI framework for Defold that provides a rich set of components and a widget system for creating responsive, customizable, and event-driven GUIs. It includes base components like Button, Text, Scroll, and Blocker, as well as extended components for swipes, data lists, and localization. Druid integrates with Defold Event for callbacks and manages UI logic over Defold GUI nodes.

Tokens
39.9K
Snippets
155
Records
206
Agent score
69%

What's inside Druid

  1. How druid.scroll works: View nodes, Content nodes, and Inertia

    master

    The druid.scroll component manages the relationship between a static viewport and dynamic content.

    Core Concepts

    • View Node vs. Content Node: The view_node is the static part that captures input. The content_node is the dynamic part that moves.
    • Inertia: By default, the scroll style includes inertia and an extra size for a stretching effect.
    • Points of Interest: You can set specific points that the scroll system will attempt to center on when scrolling.
    • Multitouch: The component requires multitouch and correctly handles touch_id swaps during dragging.

    Scroll Events

    You can assign callbacks to these fields to react to scroll actions:

    • on_scroll(self, position): Triggered during a scroll move.
    • on_scroll_to(self, position, is_instant): Triggered when a scroll_to function is called.
    • on_point_scroll(self, item_index, position): Triggered when scroll_to_index is called.
  2. Implement Druid component lifecycle methods

    master

    When creating a component, you can override several lifecycle methods to handle different stages of the component's existence and environmental changes:

    Initialization & Destruction

    • init(): Called when the component is created.
    • on_late_init(): Called once after GUI initialization, before the first update().
    • on_remove(): Called when the component is removed.

    Frame & Event Handling

    • update(): Called every frame.
    • on_input(event): Called when an input event is triggered.
    • on_input_interrupt(event): Called when an input event is consumed by something else before reaching this component.
    • on_message(message): Called when a message is received.

    Environmental Changes

    • on_focus_lost() / on_focus_gained(): Called when the application gains or loses focus.
    • on_style_change(): Called when the component style is changed.
    • on_layout_change(): Called when the GUI layout changes.
    • on_window_resized(): Called when the window is resized.
    • on_language_change(): Called when the language is changed.
  3. How the blocker component works

    master

    The druid.blocker component acts as an input interceptor. When enabled, it captures all input events that hit its target node, preventing those events from propagating to other components.

    Key behaviors:

    • Input Capture: It prevents input from reaching components located 'behind' it in the input stack.
    • Stacking: The blocker behaves like a standard component in the stack. You can place other components on top of the blocker, and those top-level components will function normally and receive input.
  4. What are Widgets and how do they differ from Custom Components

    master
    Widgets are reusable UI components in Druid that replace the older "custom component" pattern. While custom components require significant boilerplate (using druid.component and manual initialization of templates and nodes), Widgets are simple Lua tables. When using a Widget, the Druid instance, templates, and nodes are automatically initialized and made available to the widget module.
  5. Use druid.lang_text for localized text

    master

    The druid.lang_text component manages text nodes that need to respond to locale changes. It supports translation via IDs, string formatting with parameters, and automatic updates.

    Key Capabilities

    • Automatic Updates: Text updates automatically when the global locale changes.
    • Localization: Uses get_text_function to fetch strings by ID.
    • Formatting: Supports string formatting (e.g., injecting variables into localized strings).
    • Raw Text Override: You can set raw text directly, which clears any existing locale settings.
  6. Configure text adjustment types

    master

    Druid text components support several adjustment modes to handle text that is larger than its assigned GUI node size. The text will never be larger than the node size defined in your GUI scene.

    Available adjust_type options:

    • "downscale": (Default) Changes text scale to fit the node size.
    • "trim": Trims text with a postfix (default is "...") to fit the node size.
    • "no_adjust": No adjustment (behaves like a standard Defold text node).
    • "downscale_limited": Changes scale to fit, but respects a minimum scale limit.
    • "scroll": Changes text pivot to imitate scrolling. For best results, use this with a stencil node.
    • "scale_then_scroll": First performs a limited downscale, then scrolls if still too large.
    • "trim_left": Trims text with a postfix (default "...") from the left side.
    • "scale_then_trim": First performs a limited downscale, then trims.
    • "scale_then_trim_left": First performs a limited downscale, then trims left.
  7. How Druid styles work

    master

    A Style is a table of functions and parameters used by components to customize their behavior.

    Structure

    The style object is organized by component name. The top-level keys are the names of the components, and the values are tables containing the specific style parameters for that component.

    Creating a custom style

    To create a custom style, create a Lua module that returns a table mapping component names to their respective style parameter tables. You can discover available parameters by:

    1. Checking the Druid API documentation for the specific component's style section.
    2. Inspecting the on_style_change function within the component's source code to see which fields it consumes.

    Application Methods

    You can apply your custom style in three ways:

    • Globally: druid.set_default_style(style_table)
    • Per Instance: druid.new(self, style_table)
    • Per Component: component:set_style(style_table)
  8. Use the druid.instance API to manage components

    master

    The druid.instance module (located at /druid/system/druid_instance.lua) serves as the Druid Factory. It is the central authority used to create, manage, and lifecycle-control Druid components within your application.

    -- The druid.instance module is the factory for all components
    local druid_instance = require("/druid/system/druid_instance.lua")
  9. How widgets work in Druid

    master

    A Widget is a reusable UI component that encapsulates multiple Druid components.

    Creating a Widget

    1. Create a Lua module (e.g., my_widget.lua).
    2. Define an init function. Inside init, use self:get_node("node_id") to access nodes and self.druid:new_component(...) to initialize internal components.
    3. The self passed to widget callbacks is correctly scoped to the widget instance.

    Using a Widget

    Use druid:new_widget(widget_module, template_id) to instantiate a widget. You can then call custom functions defined in the widget module or access its internal components directly.

    ---@class my_widget: druid.widget
    local M = {}
    
    function M:init()
        self.root = self:get_node("root")
        -- Components are accessible via self outside the widget
        self.button = self.druid:new_button("button_node_id", self.on_click)
        self.text = self.druid:new_text("text_node_id", "Hello!")
    end
    
    function M:on_click()
        self.text:set_text("Clicked!")
    end
    
    function M:say_hello()
        self.text:set_text("Hello, Druid!")
    end
    
    return M
  10. How druid.container works for adaptable layouts

    master

    The druid.container component manages the size and position of GUI nodes and their relationships to other containers to create responsive layouts.

    Key Capabilities:

    • Nesting: Containers can be nested inside other containers to create complex hierarchies.
    • Layout Modes: Supports FIT, STRETCH, STRETCH_X, and STRETCH_Y to define how children occupy space.
    • Sizing Constraints: Supports fixed margins, percentage-based sizing, and minimum/maximum size constraints.
    • Positioning: Uses pivot points for precise positioning.
    • Responsiveness: Can be configured to respond to window size changes or fit into specific nodes/windows.
  11. Setup the druid.scroll component

    master

    To create a scroll component, use the druid:new_scroll(view_node, content_node) method.

    • view_node (string|node): The static GUI node that captures user input and recognizes scrolling touches.
    • content_node (string|node): The dynamic GUI node that changes position according to the scroll system.

    Mental Model:

    • The view_node acts as the window/viewport.
    • The content_node acts as the actual content being moved.
    • Initial scroll size is equal to the content_node size, and the initial view box is equal to the view_node size.
    scroll = druid:new_scroll(view_node, content_node)
  12. Create a full custom component with lifecycle methods

    master

    If your component requires handling input, messages, or layout changes, use the full component template. This template provides hooks for the entire lifecycle of the component.

    Available lifecycle methods include:

    • update(dt): Called every frame.
    • on_input(action_id, action): Handles input actions (return false to propagate).
    • on_style_change(style): Triggered when styles change.
    • on_message(message_id, message, sender): Handles incoming messages.
    • on_language_change(): Triggered on language changes.
    • on_layout_change(): Triggered when layout changes.
    • on_window_resized(): Triggered when the window is resized.
    • on_input_interrupt(): Triggered on input interruption.
    • on_focus_lost() / on_focus_gained(): Focus lifecycle.
    • on_remove(): Called when the component is removed.
    local component =  require("druid.component")
    
    ---@class component_name: druid.base_component
    local M = component.create("component_name")
    
    function M:init(template, nodes)
        self.druid = self:get_druid(template, nodes)
        self.root = self:get_node("root")
    end
    
    function M:update(dt) end
    
    function M:on_input(action_id, action) return false end
    
    function M:on_style_change(style) end
    
    function M:on_message(message_id, message, sender) end
    
    function M:on_language_change() end
    
    function M:on_layout_change() end
    
    function M:on_window_resized() end
    
    function M:on_input_interrupt() end
    
    function M:on_focus_lost() end
    
    function M:on_focus_gained() end
    
    function M:on_remove() end
    
    return M