Entity GUI Library

by Lukah

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

Internal
7 days ago
2.0
859

g [Guide] How do I use this library?

22 days ago
(updated 21 days 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.

16 days ago

I'd like to use this to extend existing GUIs instead of replacing the vanilla GUI. Could this be modified to have an option to disable the automatic destruction of the old GUI?

16 days ago
(updated 16 days ago)

Hi em-t-tree! Cool idea, I never thought about that approach.

You can now keep the vanilla GUI open instead of replacing it in v.0.1.8! This lets you add supplementary panels alongside vanilla entity GUIs.

Usage

remote.call("entity_gui_lib", "register", {
mod_name = "my_mod",
entity_type = "assembling-machine",
title = "Extra Info",
keep_vanilla_gui = true, -- NEW: Vanilla GUI stays open!
on_build = "build_info_panel",
})

Use Cases

  • Add stats/analytics panels to vanilla machines
  • Display extra entity info without losing vanilla controls
  • Build overlay mods that enhance rather than replace
  • Create debugging tools that show internal state

When enabled, both GUIs appear simultaneously - vanilla remains fully functional while your custom panel provides additional features.

There might be bugs with this as it's still being tested, but feel free to give it a try!

New response