Entity GUI Library

by Lukah

A library for creating custom entity GUIs that replace vanilla ones. Provides a barebones frame with title, close button, entity preview, and status.

Internal
11 hours ago
2.0
3

g [Guide] How do I use this library?

a day ago
(updated a day ago)

Entity GUI Library - Modder's Guide

This guide explains how to use Entity GUI Library to create custom entity GUIs for your Factorio mod.

Why Use This Library?

Creating custom entity GUIs requires handling:
- Frame creation and styling
- Titlebar with drag handle and close button
- Entity preview widget
- Status display
- GUI lifecycle (open/close events)
- E key to close

This library handles all of that, letting you focus on your custom content.

Quick Start

1. Add Dependency

In your info.json:

{
    "dependencies": ["entity-gui-lib >= 0.1.1"]
}

2. Create Your Remote Interface

In your control.lua, define callbacks for building your GUI:

remote.add_interface("my_mod", {
    build_inserter_gui = function(container, entity, player)
        -- Add your custom GUI elements here
        container.add{
            type = "label",
            caption = "Custom inserter interface",
            style = "caption_label",
        }

        container.add{
            type = "button",
            name = "my_mod_rotate_button",
            caption = "Rotate",
        }
    end,

    close_inserter_gui = function(entity, player)
        -- Optional: called when GUI closes
    end,
})

3. Register Your Entity

local function register_guis()
    remote.call("entity_gui_lib", "register", {
        mod_name = "my_mod",              -- Your remote interface name
        entity_type = "inserter",          -- Or entity_name for specific entities
        title = "Custom Inserter",         -- Optional custom title
        on_build = "build_inserter_gui",   -- Your callback name
        on_close = "close_inserter_gui",   -- Optional
    })
end

script.on_init(register_guis)
script.on_load(register_guis)

4. Handle GUI Events

script.on_event(defines.events.on_gui_click, function(event)
    if event.element.name == "my_mod_rotate_button" then
        local entity = remote.call("entity_gui_lib", "get_entity", event.player_index)
        if entity and entity.valid then
            entity.rotate()
        end
    end
end)

That's it! The library handles the frame, titlebar, preview, status, and E key closing.


Advanced Features

Auto-Refresh for Live Data

For progress bars, energy levels, or other dynamic content:

remote.add_interface("my_mod", {
    build_drill_gui = function(container, entity, player)
        container.add{
            type = "progressbar",
            name = "my_progress",
            value = entity.mining_progress or 0,
        }
    end,

    update_drill_gui = function(content, entity, player)
        -- Called every update_interval ticks
        for _, child in pairs(content.children) do
            if child.name == "my_progress" then
                child.value = entity.mining_progress or 0
            end
        end
    end,
})

remote.call("entity_gui_lib", "register", {
    mod_name = "my_mod",
    entity_type = "mining-drill",
    on_build = "build_drill_gui",
    on_update = "update_drill_gui",
    update_interval = 10,  -- Every 10 ticks (~6 times/sec)
})

Tabbed Interfaces

build_my_gui = function(container, entity, player)
    local _, tabs = remote.call("entity_gui_lib", "create_tabs", container, {
        {name = "main", caption = "Main"},
        {name = "settings", caption = "Settings"},
    })

    -- Add content to tabs
    tabs.main.add{type = "label", caption = "Main content"}
    tabs.settings.add{type = "label", caption = "Settings content"}
end,

Confirmation Dialogs

-- In your remote interface
show_confirm = function(player_index, entity)
    remote.call("entity_gui_lib", "show_confirmation", player_index, {
        mod_name = "my_mod",
        title = "Confirm Action",
        message = "Are you sure?",
        on_confirm = "do_action",
        on_cancel = "cancel_action",
        data = {entity = entity},
    })
end,

do_action = function(player, data)
    -- User clicked confirm
end,

cancel_action = function(player, data)
    -- User clicked cancel
end,

Custom Preview Size

remote.call("entity_gui_lib", "register", {
    mod_name = "my_mod",
    entity_type = "accumulator",
    on_build = "build_gui",
    preview_size = 200,  -- Default is 148
})

Priority System

If multiple mods register for the same entity, highest priority wins:

remote.call("entity_gui_lib", "register", {
    mod_name = "my_mod",
    entity_type = "inserter",
    on_build = "build_gui",
    priority = 100,  -- Higher than default 0
})

-- Check existing registrations
local existing = remote.call("entity_gui_lib", "get_registrations", "inserter")
for _, reg in ipairs(existing) do
    log(reg.mod_name .. " has priority " .. reg.priority)
end

API Reference

Registration Config

Field Type Required Description
mod_name string Yes Your remote interface name
entity_name string One required Specific entity name
entity_type string Entity type (e.g., "inserter")
title LocalisedString No Custom title
on_build string Yes Build callback name
on_close string No Close callback name
on_update string No Update callback name
update_interval number No Ticks between updates (default: 10)
priority number No Conflict resolution (default: 0)
preview_size number No Preview size in pixels (default: 148)

Remote Functions

-- Register/unregister
remote.call("entity_gui_lib", "register", config)
remote.call("entity_gui_lib", "unregister", "entity-type", "mod_name")

-- Get GUI info
remote.call("entity_gui_lib", "get_content", player_index)
remote.call("entity_gui_lib", "get_entity", player_index)
remote.call("entity_gui_lib", "get_registrations", "entity-type")

-- Actions
remote.call("entity_gui_lib", "refresh", player_index)
remote.call("entity_gui_lib", "close", player_index)

-- Helpers
remote.call("entity_gui_lib", "create_tabs", container, tabs)
remote.call("entity_gui_lib", "show_confirmation", player_index, config)
remote.call("entity_gui_lib", "create_slider", container, config)
remote.call("entity_gui_lib", "create_number_input", container, config)
remote.call("entity_gui_lib", "create_dropdown", container, config)
remote.call("entity_gui_lib", "create_toggle_group", container, config)
remote.call("entity_gui_lib", "create_inventory_display", container, config)
remote.call("entity_gui_lib", "create_item_selector", container, config)
remote.call("entity_gui_lib", "create_recipe_selector", container, config)
remote.call("entity_gui_lib", "create_elem_button", container, config)
remote.call("entity_gui_lib", "create_signal_selector", container, config)
remote.call("entity_gui_lib", "create_color_picker", container, config)

-- Debug
remote.call("entity_gui_lib", "set_debug_mode", true)

Tips

  1. Name your elements uniquely - Prefix with your mod name to avoid conflicts
  2. Use debug mode - Enable to see registrations in the log file
  3. Check the example mod - Located in examples/entity-gui-lib-example
  4. Entity must have vanilla GUI - Library can only replace existing GUIs
  5. Avoid reserved tab names - Don't use names like "items", "style", "parent" etc. as tab names since they conflict with LuaGuiElement properties

Questions?

Post here. I'll do my best to answer.

New response