Skip to content

Creating Custom Modules

Debby's modular architecture allows you to create custom debugging modules that integrate seamlessly with the built-in modules. Custom modules can display any information or provide any functionality you need during development and testing.

Overview

A custom module is a class that extends DebbyModule and provides its own UI and functionality. Custom modules:

  • Appear in the Debby sidebar alongside built-in modules
  • Can use all Debby UI controls for consistent styling (or build your own!)
  • Can be registered any time after Debby initialization
  • Support both Desktop and Mobile layout modes
    • Note: It's up to you to make sure your layout is responsive and works across all screen sizes you need to support.

Creating a Custom Module

Basic Structure

To create a custom module, extend the DebbyModule class and implement the required abstract members:

csharp
using Debology.Debby.Modules;
using Debology.Debby.Elements;
using UnityEngine;
using UnityEngine.UIElements;

public class MyCustomModule : DebbyModule
{
    // Required: The name displayed in the sidebar
    public override string Name => "My Module";

    // Required: USS class for the button icon (use "module-button--custom" for custom modules)
    public override string ButtonClass => "module-button--custom";

    // Required: Create and return the module's UI
    protected override VisualElement OnModuleCreate()
    {
        var root = new ScrollView();

        // Add your UI elements here
        var category = new DebbyCategory("MY CATEGORY");
        root.Add(category);

        var button = new DebbyButton("Click Me", DebbyIcon.Play);
        button.clicked += () => Debug.Log("Button clicked!");
        category.Add(button);

        return root;
    }
}

Registering Your Module

Register your custom module after Debby has been initialized:

csharp
using UnityEngine;
using Debology.Debby;

public class GameManager : MonoBehaviour
{
    void Start()
    {
        // Ensure Debby is initialized first
        if (!Debby.IsInitialized)
        {
            Debby.Initialize();
        }

        // Register your custom module
        Debby.RegisterCustomModule<MyCustomModule>();

        // Or register with an instance
        var myModule = new MyCustomModule();
        Debby.RegisterCustomModule(myModule);
    }
}

Module Lifecycle

The DebbyModule base class provides several lifecycle methods you can override:

OnModuleCreate()

csharp
protected abstract VisualElement OnModuleCreate();

Called: Once, the first time the module window is opened.

Purpose: Create and return the root visual element for your module's UI. This is where you build your module's interface.

Important: This method is only called once. The returned element is cached and reused.

csharp
protected override VisualElement OnModuleCreate()
{
    var root = new VisualElement();
    root.AddToClassList("my-module-root");

    // Build your UI
    _scrollView = new ScrollView();
    root.Add(_scrollView);

    return root;
}

OnModuleOpen()

csharp
protected virtual void OnModuleOpen();

Called: Every time the module window is opened.

Purpose: Refresh data, update UI, or perform any actions needed when the module becomes visible.

csharp
protected override void OnModuleOpen()
{
    // Refresh data display
    UpdateStatistics();

    // Log analytics
    Debug.Log("My module was opened");
}

OnModuleClose()

csharp
protected virtual void OnModuleClose();

Called: Every time the module window is closed.

Purpose: Clean up temporary resources, pause expensive operations, or save state.

csharp
protected override void OnModuleClose()
{
    // Stop expensive operations
    _isMonitoring = false;

    // Save state
    SaveModulePreferences();
}

OnModuleDestroy()

csharp
protected virtual void OnModuleDestroy();

Called: When Debby is destroyed via Debby.Destroy().

Purpose: Clean up resources, unregister callbacks, or perform final cleanup.

Note: Only needed if you call Debby.Destroy() explicitly.

csharp
protected override void OnModuleDestroy()
{
    // Unregister callbacks
    Application.logMessageReceived -= HandleLog;

    // Clean up resources
    _customResource?.Dispose();
}

OnLiveReload()

csharp
protected internal virtual void OnLiveReload();

Called: When USS/UXML files are modified during runtime (development only).

Purpose: Reload your custom UI if you're working with custom USS/UXML files.

Note: Only needed if you have custom USS/UXML assets and want hot-reloading during development.

csharp
protected internal override void OnLiveReload()
{
    // Rebuild UI with updated styles
    if (IsCreated)
    {
        Root.Clear();
        BuildUI();
    }
}

Module Properties

Name (Required)

csharp
public override string Name => "My Module";

The display name shown in the sidebar and module title.

ButtonClass (Required)

csharp
public override DebbyIcon Icon => DebbyIcon.Fire;

The icon displayed in the sidebar button.

DesktopDefaultSize

csharp
protected internal override Vector2Int DesktopDefaultSize => new(1200, 800);

The starting size when the module is opened in Desktop layout mode.

Default: (1920, 1080)

DesktopMinSize

csharp
protected internal override Vector2Int DesktopMinSize => new(600, 400);

The minimum size the module window can be resized to in Desktop layout mode.

Default: (960, 540)

IsOpened

csharp
public bool IsOpened { get; }

Returns true if the module is currently opened and showing. Read-only.

IsCreated

csharp
public bool IsCreated { get; }

Returns true if OnModuleCreate() has been called. Read-only.

Root

csharp
internal VisualElement Root { get; }

The root visual element returned by OnModuleCreate(). Available after the module is created.

Building the UI

Using Debby Elements

Debby provides a comprehensive set of UI elements designed for debugging interfaces. These elements maintain visual consistency and follow Debby's design language.

See Elements for complete documentation of all available elements.

Organizational Elements

csharp
// Category - top-level horizontal container with title
var category = new DebbyCategory("PLAYER STATS");
root.Add(category);

// Group - vertical container within a category
var group = new DebbyGroup("Health");
category.Add(group);

Control Elements

csharp
// Button with icon
var button = new DebbyButton("Respawn Player", DebbyIcon.Refresh);
button.clicked += () => RespawnPlayer();
category.Add(button);

// Toggle switch
// You can optionally use an AdjustableValue<T> to avoid manually registering callbacks.
var godMode = new AdjustableValue<bool>();

var toggle = new DebbyToggle("God Mode");
godMode.BindTwoWay(toggle);
category.Add(toggle);

// Text field
var textField = new DebbyTextField("Player Name");
textField.RegisterValueChangedCallback(evt => SetPlayerName(evt.newValue));
category.Add(textField);

// Int slider
var healthSlider = new DebbyIntSlider("Health", 0, 100);
healthSlider.RegisterValueChangedCallback(evt => SetHealth(evt.newValue));
category.Add(healthSlider);

// Float slider
var speedSlider = new DebbyFloatSlider("Speed", 0f, 10f, 0.1f);
speedSlider.RegisterValueChangedCallback(evt => SetSpeed(evt.newValue));
category.Add(speedSlider);

Using UXML

For complex layouts, you can use UXML files:

csharp
protected override VisualElement OnModuleCreate()
{
    var asset = Resources.Load<VisualTreeAsset>("MyModule/MyModuleLayout");
    var root = asset.CloneTree();

    // Query elements
    _container = root.Q<ScrollView>("Container");
    _statusLabel = root.Q<Label>("StatusLabel");

    return root;
}

Accessing Other Modules

You can access other Debby modules from your custom module:

csharp
public class MyCustomModule : DebbyModule
{
    private IAdjustModule _adjustModule;
    private IConsoleModule _consoleModule;

    protected override VisualElement OnModuleCreate()
    {
        // Get references to other modules
        _adjustModule = Debby.Adjust;
        _consoleModule = Debby.Console;

        var root = new ScrollView();

        var button = new DebbyButton("Log Message", DebbyIcon.Console);
        button.clicked += () =>
        {
            _consoleModule.PushLogMessage("Custom module message", LogType.Log);
        };

        root.Add(button);
        return root;
    }
}

Example

See the Notepad Example for a working example of a custom module.