Release 0.4 Results

Published: (December 12, 2025 at 06:00 PM EST)
3 min read
Source: Dev.to

Source: Dev.to

What I Did

The goal was to add a setting to turn the default, tree‑style look:

Tree view

into one with tabs. The logic for creating new categories and entities already existed, so this was mostly UI work.

Building the Tab system

The core logic lives in the PandaEditor scene. Within the Godot editor you can add nodes to each scene, including containers and UI elements.

Godot editor view

I started by adding a TabContainer to the scene and setting it to hidden. This will eventually be populated with tabs when the setting is chosen and become visible.

Adding the setting

pandora-settings holds the code for adding settings, so I added a boolean toggle to swap to tab view:

const USE_CATEGORY_TABS = "pandora/use_category_tabs"

static func get_use_category_tabs() -> bool:
    return ProjectSettings.get_setting(USE_CATEGORY_TABS, false)

Modifying data population

Next, I added tab populating to pandora-editor.gd. There already existed a function called _populate_data() that handled data by default, but I needed to modify it to check if the tabs setting was enabled:

var use_tabs = PandoraSettings.get_use_category_tabs()

if use_tabs:
    await _populate_tabs(data)
else:
    tree.set_data(data)

Creating tabs for root categories

I created the _populate_tabs() function to take the data and, instead of mapping it to a tree, create a tab for each root category:

# Create tabs for root categories
for i in range(root_categories.size()):
    var tab_content = Control.new()
    category_tab_container.add_child(tab_content)
    category_tab_container.set_tab_title(i, root_categories[i].get_entity_name())

Handling tab changes

To filter the tree to show only the children of the selected category when a tab is selected, I added _on_tab_changed():

func _populate_tabs(root_categories: Array[PandoraEntity]) -> void:
    # ... create tabs ...

    # Store root categories
    category_tab_container.set_meta("root_categories", root_categories)

    # Connect tab change signal
    category_tab_container.tab_changed.connect(_on_tab_changed)

func _on_tab_changed(tab_index: int) -> void:
    var root_categories = category_tab_container.get_meta("root_categories", []) as Array
    var selected_category = root_categories[tab_index]

    # Filter tree to show only entities in the selected category
    var filtered_data: Array[PandoraEntity] = []
    filtered_data.assign(selected_category._children)
    tree.set_data(filtered_data)

Adding a “+” tab for new categories

The original view lacked a way to create categories from the tab bar, so I added a “+” tab after the root categories:

func _populate_tabs(root_categories: Array[PandoraEntity]) -> void:
    # ... create category tabs ...

    # Add '+' tab for creating new root categories
    var add_tab_content = Control.new()
    add_tab_content.name = "+"
    category_tab_container.add_child(add_tab_content)

Then I updated _on_tab_changed() to detect when the “+” tab is clicked and create a new root category:

func _on_tab_changed(tab_index: int) -> void:
    var root_categories = category_tab_container.get_meta("root_categories", []) as Array

    # Create new root category if '+' tab is selected
    if tab_index >= root_categories.size():
        _create_root_category()
        return

    # Existing handling for regular tabs...

Context menu for rename / delete

To allow renaming and deleting categories, I added a right‑click context menu on the tab bar:

func _on_tab_bar_input(event: InputEvent) -> void:
    if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_RIGHT and event.pressed:
        var tab_bar = category_tab_container.get_tab_bar()
        var clicked_tab = tab_bar.get_tab_idx_at_point(event.position)

        if clicked_tab >= 0:
            var root_categories = category_tab_container.get_meta("root_categories", []) as Array

            # Don't show menu for '+' tab
            if clicked_tab >= root_categories.size():
                return

            # Store which tab was clicked
            category_tab_container.set_meta("context_menu_tab", clicked_tab)

            # Show context menu
            tab_context_menu.position = tab_bar.get_screen_position() + event.position
            tab_context_menu.popup()

func _on_tab_context_menu_pressed(id: int) -> void:
    var clicked_tab = category_tab_container.get_meta("context_menu_tab", -1)
    if clicked_tab = root_categories.size():
        return

    var category = root_categories[clicked_tab]

    match id:
        0:  # Rename
            _show_rename_dialog(category, clicked_tab)
        1:  # Delete
            _show_delete_confirmation(category, clicked_tab)

Final view

With the tabs, “+” button, and context menu in place, the editor now looks like this:

Tab view screenshot

and the setting toggle appears in the project settings:

Project settings toggle

The only remaining issue was that the setting didn’t take effect until the project was reloaded. I solved this by adding a simple reload routine when the setting changes.

Back to Blog

Related posts

Read more »

Contributing to streamplace

How I found the Project Nowadays I've been reading and writing Go code regularly, and my Go journey started with A Tour of Gohttps://go.dev/tour/welcome/1. Whi...