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:
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:
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()
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.
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()
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.
protected override void OnModuleOpen()
{
// Refresh data display
UpdateStatistics();
// Log analytics
Debug.Log("My module was opened");
}OnModuleClose()
protected virtual void OnModuleClose();Called: Every time the module window is closed.
Purpose: Clean up temporary resources, pause expensive operations, or save state.
protected override void OnModuleClose()
{
// Stop expensive operations
_isMonitoring = false;
// Save state
SaveModulePreferences();
}OnModuleDestroy()
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.
protected override void OnModuleDestroy()
{
// Unregister callbacks
Application.logMessageReceived -= HandleLog;
// Clean up resources
_customResource?.Dispose();
}OnLiveReload()
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.
protected internal override void OnLiveReload()
{
// Rebuild UI with updated styles
if (IsCreated)
{
Root.Clear();
BuildUI();
}
}Module Properties
Name (Required)
public override string Name => "My Module";The display name shown in the sidebar and module title.
ButtonClass (Required)
public override DebbyIcon Icon => DebbyIcon.Fire;The icon displayed in the sidebar button.
DesktopDefaultSize
protected internal override Vector2Int DesktopDefaultSize => new(1200, 800);The starting size when the module is opened in Desktop layout mode.
Default: (1920, 1080)
DesktopMinSize
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
public bool IsOpened { get; }Returns true if the module is currently opened and showing. Read-only.
IsCreated
public bool IsCreated { get; }Returns true if OnModuleCreate() has been called. Read-only.
Root
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
// 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
// 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:
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:
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.