Notepad Custom Module Example
This demonstrates how to create a custom module using Debby's element library.
To use this example:
- Copy this file to your project's Scripts folder
- After Debby initialization, register the module:
Debby.RegisterCustomModule<NotepadModule>();
This module provides a simple notepad for taking notes during testing/debugging. Notes are persisted between sessions using PlayerPrefs.
csharp
using Debology.Debby;
using Debology.Debby.Elements;
using Debology.Debby.Modules;
using Debology.Debby.Modules.Adjust;
using UnityEngine;
using UnityEngine.UIElements;
/// <summary>
/// A simple notepad module for taking notes during testing and debugging.
/// Demonstrates the use of Debby elements and module lifecycle methods.
/// </summary>
public class NotepadModule : DebbyModule
{
// Module configuration
public override string Name => "Notepad";
public override DebbyIcon Icon => DebbyIcon.FloppyDisk;
protected internal override Vector2Int DesktopDefaultSize => new(800, 600);
protected internal override Vector2Int DesktopMinSize => new(400, 300);
// PlayerPrefs keys
private const string NOTES_KEY = "DebbyNotepad_Notes";
private const string AUTOSAVE_KEY = "DebbyNotepad_AutoSave";
private const string TIMESTAMP_KEY = "DebbyNotepad_Timestamp";
// UI elements
private Label _statusLabel;
private DebbyButton _saveButton;
private DebbyButton _clearButton;
// State
private readonly AdjustableValue<string> _notes = new(string.Empty);
private readonly AdjustableValue<bool> _autoSave = new(true);
private bool _hasUnsavedChanges;
/// <summary>
/// Called once when the module is first opened.
/// This is where we build the module's UI.
/// </summary>
protected override VisualElement OnModuleCreate()
{
// Create root container
var root = new ScrollView(ScrollViewMode.Vertical);
// Settings category
var settingsCategory = new DebbyCategory("SETTINGS");
root.Add(settingsCategory);
// NOTE: When creating UI Toolkit layouts from code, it can be convenient
// to use empty statement blocks {} to show the hierarchy / tree view clearer.
{
// Auto-save toggle
_autoSave.value = PlayerPrefs.GetInt(AUTOSAVE_KEY, 1) == 1;
var autoSaveToggle = new DebbyToggle("Auto-save notes");
_autoSave.BindTwoWay(autoSaveToggle);
_autoSave.OnValueChanged += OnAutoSaveChanged;
settingsCategory.Add(autoSaveToggle);
}
// Notes category
var notesCategory = new DebbyCategory("NOTES");
root.Add(notesCategory);
{
// Notes text field
// You can also bind an AdjustableValue to any Debby control, instead of
// using a value change callback.
var notesField = new DebbyTextField("Note Content");
notesField.style.height = 300;
_notes.BindTwoWay(notesField);
_notes.OnValueChanged += OnNotesChanged;
notesCategory.Add(notesField);
// Actions group
var actionsGroup = new DebbyGroup("Actions");
notesCategory.Add(actionsGroup);
{
// Save button
_saveButton = new DebbyButton("Save Notes", DebbyIcon.FloppyDisk);
_saveButton.Clicked += SaveNotes;
actionsGroup.Add(_saveButton);
// Clear button
_clearButton = new DebbyButton("Clear Notes", DebbyIcon.Trash);
_clearButton.Clicked += ClearNotes;
actionsGroup.Add(_clearButton);
// Export button
var exportButton = new DebbyButton("Copy to Clipboard", DebbyIcon.Share);
exportButton.Clicked += CopyToClipboard;
actionsGroup.Add(exportButton);
}
}
// Info category
var infoCategory = new DebbyCategory("INFO");
root.Add(infoCategory);
{
// Status label
_statusLabel = new Label("No notes saved yet")
{
style =
{
color = new Color(0.7f, 0.7f, 0.7f),
unityFontStyleAndWeight = FontStyle.Italic,
paddingLeft = 8,
paddingTop = 4,
},
};
infoCategory.Add(_statusLabel);
}
return root;
}
/// <summary>
/// Called every time the module is opened.
/// We use this to load the saved notes.
/// </summary>
protected override void OnModuleOpen()
{
// Load notes from PlayerPrefs
LoadNotes();
// Reset unsaved changes flag
_hasUnsavedChanges = false;
UpdateSaveButtonState();
}
/// <summary>
/// Called every time the module is closed.
/// We use this to auto-save if enabled.
/// </summary>
protected override void OnModuleClose()
{
// Auto-save if enabled and there are unsaved changes
if (_autoSave && _hasUnsavedChanges)
{
SaveNotes();
}
}
/// <summary>
/// Called when Debby is destroyed.
/// Clean up any resources here if needed.
/// </summary>
protected override void OnModuleDestroy()
{
// Nothing to clean up in this example
// But you would unregister event handlers, dispose resources, etc. here
}
#region Event Handlers
private void OnAutoSaveChanged(bool value)
{
PlayerPrefs.SetInt(AUTOSAVE_KEY, value ? 1 : 0);
PlayerPrefs.Save();
UpdateSaveButtonState();
Debug.Log($"Notepad auto-save {(value ? "enabled" : "disabled")}");
}
private void OnNotesChanged(string str)
{
_hasUnsavedChanges = true;
UpdateSaveButtonState();
// Auto-save if enabled
if (_autoSave)
{
SaveNotes();
}
}
#endregion
#region Actions
private void LoadNotes()
{
var notes = PlayerPrefs.GetString(NOTES_KEY, string.Empty);
_notes.SetValueWithoutNotify(notes);
if (PlayerPrefs.HasKey(TIMESTAMP_KEY))
{
var timestamp = PlayerPrefs.GetString(TIMESTAMP_KEY);
_statusLabel.text = $"Last saved: {timestamp}";
}
else
{
_statusLabel.text = "No notes saved yet";
}
}
private void SaveNotes()
{
PlayerPrefs.SetString(NOTES_KEY, _notes);
PlayerPrefs.SetString(TIMESTAMP_KEY, System.DateTime.Now.ToString("g"));
PlayerPrefs.Save();
_hasUnsavedChanges = false;
UpdateSaveButtonState();
var timestamp = PlayerPrefs.GetString(TIMESTAMP_KEY);
_statusLabel.text = $"Last saved: {timestamp}";
Debug.Log("Notepad saved successfully");
}
private void ClearNotes()
{
// Ask for confirmation by logging
if (_notes.IsNullOrEmpty())
{
Debug.LogWarning("Clearing notepad notes...");
}
_notes.value = string.Empty;
_hasUnsavedChanges = true;
UpdateSaveButtonState();
if (_autoSave)
{
SaveNotes();
}
}
private void CopyToClipboard()
{
if (_notes.IsNullOrEmpty())
{
Debby.ShowToast(DebbyToastType.Error, "No notes to copy");
return;
}
GUIUtility.systemCopyBuffer = _notes;
Debby.ShowToast(DebbyToastType.Success, "Notes copied to clipboard");
}
private void UpdateSaveButtonState()
{
// Disable save button if auto-save is on or no unsaved changes
var shouldEnableSave = !_autoSave && _hasUnsavedChanges;
_saveButton.SetEnabled(shouldEnableSave);
}
#endregion
}