diff --git a/SessionZero.sln.DotSettings.user b/SessionZero.sln.DotSettings.user
index 80cfb75..903dfe1 100644
--- a/SessionZero.sln.DotSettings.user
+++ b/SessionZero.sln.DotSettings.user
@@ -1,2 +1,3 @@
- ForceIncluded
\ No newline at end of file
+ ForceIncluded
+ ForceIncluded
\ No newline at end of file
diff --git a/SzCore/DataObjects/SzCharacterTemplate.cs b/SzCore/DataObjects/SzCharacterTemplate.cs
new file mode 100644
index 0000000..d2e68c1
--- /dev/null
+++ b/SzCore/DataObjects/SzCharacterTemplate.cs
@@ -0,0 +1,9 @@
+namespace SzCore.DataObjects;
+
+public class SzCharacterTemplate : ISzTemplate
+{
+ public string Name { get; set; } = "";
+ public string Id { get; set; } = "";
+ public string Description { get; set; } = "";
+ public Dictionary TemplateFields { get; set; } = [];
+}
\ No newline at end of file
diff --git a/SzCore/SzDataHandler.cs b/SzCore/SzDataHandler.cs
index 9a3f962..fce4f10 100644
--- a/SzCore/SzDataHandler.cs
+++ b/SzCore/SzDataHandler.cs
@@ -7,6 +7,9 @@ public class SzDataHandler
private readonly Dictionary _loadedDatasets = [];
private readonly Dictionary _loadedTemplates = [];
+ public Dictionary LoadedDatasets => _loadedDatasets;
+ public Dictionary LoadedTemplates => _loadedTemplates;
+
public SzDataHandler() { }
public void ClearCache()
@@ -247,5 +250,18 @@ public class SzDataHandler
await LoadDatasetAsync(id, ct);
}
}
+
+ public async Task LoadAllDataObjectTemplatesAsync(CancellationToken ct = default)
+ {
+ _loadedTemplates.Clear();
+ if (!Directory.Exists(SZ.LocalFileManager.TemplatesPath)) return;
+
+ var dirs = Directory.GetDirectories(SZ.LocalFileManager.TemplatesPath);
+ foreach (var dir in dirs)
+ {
+ var id = Path.GetFileName(dir);
+ await LoadTemplateAsync(id, ct);
+ }
+ }
#endregion
}
diff --git a/SzGui/ApplicationSettings.cs b/SzGui/ApplicationSettings.cs
new file mode 100644
index 0000000..6cb2ca3
--- /dev/null
+++ b/SzGui/ApplicationSettings.cs
@@ -0,0 +1,81 @@
+using System.IO;
+using Raylib_cs;
+using Tomlyn;
+
+namespace SzGui;
+
+public static class ApplicationSettings
+{
+ private static Dictionary _settings = new();
+ private static readonly string _filePath = "settings.toml";
+
+ public static Dictionary GetAllSettings() => _settings;
+
+ public static void CreateInitialSettings()
+ {
+ CreateSetting("ShowStartup", false);
+ CreateSetting("BackgroundColor", Color.DarkGray);
+ SaveSettings();
+ }
+
+ public static T GetSettingValue(string settingName, T defaultValue = default!)
+ {
+ if (_settings.TryGetValue(settingName, out var value))
+ {
+ try
+ {
+ if (typeof(T) == typeof(float) && value is double d)
+ return (T)(object)(float)d;
+
+ return (T)Convert.ChangeType(value, typeof(T));
+ }
+ catch { return defaultValue; }
+ }
+ return defaultValue;
+ }
+
+ public static bool SetSettingValue(string settingName, object value)
+ {
+ if (!_settings.ContainsKey(settingName)) return false;
+ _settings[settingName] = value;
+ return true;
+ }
+
+ public static bool CreateSetting(string name, object value)
+ {
+ return _settings.TryAdd(name, value);
+ }
+
+ public static void SaveSettings()
+ {
+ try
+ {
+ var saveDict = new Dictionary();
+
+ foreach(var kvp in _settings)
+ {
+ if (kvp.Value is Color c)
+ saveDict[kvp.Key] = new int[] { c.R, c.G, c.B };
+ else
+ saveDict[kvp.Key] = kvp.Value;
+ }
+
+ var tomlString = Toml.FromModel(saveDict);
+ File.WriteAllText(_filePath, tomlString);
+ }
+ catch (Exception ex) { Console.WriteLine($"Save Error: {ex.Message}"); }
+ }
+
+ public static void LoadSettings()
+ {
+ if (!File.Exists(_filePath)) { SaveSettings(); return; }
+
+ try
+ {
+ var tomlString = File.ReadAllText(_filePath);
+ var loaded = Toml.ToModel>(tomlString);
+ if (loaded != null) _settings = loaded;
+ }
+ catch (Exception ex) { Console.WriteLine($"Load Error: {ex.Message}"); }
+ }
+}
\ No newline at end of file
diff --git a/SzGui/Program.cs b/SzGui/Program.cs
index 3b76925..bcf6be1 100644
--- a/SzGui/Program.cs
+++ b/SzGui/Program.cs
@@ -1,6 +1,12 @@
// See https://aka.ms/new-console-template for more information
+using SzCore;
+using SzCore.Defaults;
using SzGui;
-var app = new SzApp();
-app.Init();
\ No newline at end of file
+DefaultLocalFileManager _fileManager = new();
+ISzLogger _logger = new SzLogger();
+ISzDatabaseHandler _databaseHandler = new SzDatabaseHandler();
+
+SZ.Init(_fileManager, _logger, _databaseHandler);
+SzApp.AppInstance.Init();
\ No newline at end of file
diff --git a/SzGui/SzApp.cs b/SzGui/SzApp.cs
index d1bfc47..e5bca2d 100644
--- a/SzGui/SzApp.cs
+++ b/SzGui/SzApp.cs
@@ -2,29 +2,51 @@ using System.Numerics;
using ImGuiNET;
using Raylib_cs;
using rlImGui_cs;
+using SzGui.Windows;
namespace SzGui;
public class SzApp
{
+ public static readonly SzApp AppInstance = new();
+ private SzApp() { }
+
+ private List _activeWindows = new();
+
+ public void ActivateWindow(SzGuiWindowBase window)
+ {
+ if (_activeWindows.Any(w => w.GetType() == window.GetType()))
+ {
+ ImGui.SetWindowFocus(window.WindowName);
+ return;
+ }
+
+ _activeWindows.Add(window);
+ }
+
public void Init()
{
+ ApplicationSettings.LoadSettings();
+
Raylib.SetConfigFlags(ConfigFlags.Msaa4xHint | ConfigFlags.VSyncHint | ConfigFlags.ResizableWindow);
Raylib.InitWindow(1280, 800, "SessionZero");
rlImGui.Setup(true, true);
ImGui.GetIO().ConfigWindowsMoveFromTitleBarOnly = true;
-
+
+ if (ApplicationSettings.GetSettingValue("ShowStartup")) ActivateWindow(new StartupWindow());
+
while (!Raylib.WindowShouldClose())
{
Raylib.BeginDrawing();
- Raylib.ClearBackground(new Color(0, 20, 50));
+ Raylib.ClearBackground(Color.Gray);
rlImGui.Begin();
+ ImGui.PushStyleColor(ImGuiCol.WindowBg, new Vector4(0, 0, 0, 0));
ImGui.DockSpaceOverViewport(0);
-
- // ImGui.ShowDemoWindow();
- ImGui.ShowStyleEditor();
+ ImGui.PopStyleColor();
+
+ HandleShortcuts();
DrawImGui();
rlImGui.End();
@@ -34,13 +56,72 @@ public class SzApp
rlImGui.Shutdown();
Raylib.CloseWindow();
}
+
+ private void HandleShortcuts()
+ {
+ var io = ImGui.GetIO();
+
+ if (io is { KeyCtrl: true, KeyAlt: true } && ImGui.IsKeyPressed(ImGuiKey.S))
+ {
+ ActivateWindow(new SettingsWindow());
+ }
+
+ if (io is { KeyCtrl: true, KeyAlt: true } && ImGui.IsKeyPressed(ImGuiKey.Q))
+ {
+ Raylib.CloseWindow();
+ }
+ }
+
+ private void UpdateImGuiTheme()
+ {
+ var c = ApplicationSettings.GetSettingValue("BackgroundColor");
+ Vector4 styleColor = new Vector4(c.R / 255f, c.G / 255f, c.B / 255f, 1.0f);
+ ImGui.GetStyle().Colors[(int)ImGuiCol.WindowBg] = styleColor;
+ }
private void DrawImGui()
{
- if(ImGui.Begin("SessionZero"))
+ DrawGlobalMenuBar();
+
+ for (int i = _activeWindows.Count - 1; i >= 0; i--)
{
- ImGui.TextUnformatted("Test: " + IconFonts.FontAwesome6.House);
+ _activeWindows[i].Draw();
+ if (!_activeWindows[i].IsOpen) _activeWindows.RemoveAt(i);
}
- ImGui.End();
}
+
+ private void DrawGlobalMenuBar()
+ {
+ if (ImGui.BeginMainMenuBar())
+ {
+ if (ImGui.BeginMenu("File"))
+ {
+ if (ImGui.MenuItem("Exit", "Ctrl+Alt+Q")) Raylib.CloseWindow();
+ ImGui.EndMenu();
+ }
+
+ if (ImGui.BeginMenu("Edit"))
+ {
+ if (ImGui.MenuItem("Settings", "Ctrl+Alt+S"))
+ {
+ ActivateWindow(new SettingsWindow());
+ }
+ ImGui.EndMenu();
+ }
+
+ if (ImGui.BeginMenu("View"))
+ {
+ if (ImGui.MenuItem("Start window"))
+ {
+ ActivateWindow(new StartupWindow());
+ }
+ ImGui.EndMenu();
+ }
+
+ if (ImGui.MenuItem("Library")) ActivateWindow(new SzLibraryWindow());
+
+ ImGui.EndMainMenuBar();
+ }
+ }
+
}
diff --git a/SzGui/SzDatabaseHandler.cs b/SzGui/SzDatabaseHandler.cs
new file mode 100644
index 0000000..1aece7d
--- /dev/null
+++ b/SzGui/SzDatabaseHandler.cs
@@ -0,0 +1,19 @@
+using SzCore;
+using SzCore.DataObjects;
+
+namespace SzGui;
+
+public class SzDatabaseHandler : ISzDatabaseHandler
+{
+ public bool InternetAllowed { get; set; }
+ public Dictionary SzDbUrls { get; set; }
+ public Task TryRetrieveDatasetAsync(Guid uuid, CancellationToken ct = default)
+ {
+ return null;
+ }
+
+ public Task TryRetrieveTemplateAsync(Guid uuid, CancellationToken ct = default)
+ {
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/SzGui/SzGui.csproj b/SzGui/SzGui.csproj
index 16c9b9c..5df68a5 100644
--- a/SzGui/SzGui.csproj
+++ b/SzGui/SzGui.csproj
@@ -11,6 +11,7 @@
+
diff --git a/SzGui/SzLogger.cs b/SzGui/SzLogger.cs
new file mode 100644
index 0000000..c7c79eb
--- /dev/null
+++ b/SzGui/SzLogger.cs
@@ -0,0 +1,23 @@
+using SzCore;
+
+namespace SzGui;
+
+public class SzLogger : ISzLogger
+{
+ public bool LogToFile { get; set; }
+ public string LogFilePath { get; set; }
+ public int LogFileMaxLines { get; set; }
+ public void Log(string text)
+ {
+
+ }
+
+ public void LogError(string text)
+ {
+
+ }
+
+ public void LogWarning(string text)
+ {
+ }
+}
\ No newline at end of file
diff --git a/SzGui/Windows/DatasetCreatorWindow.cs b/SzGui/Windows/DatasetCreatorWindow.cs
new file mode 100644
index 0000000..3ca54aa
--- /dev/null
+++ b/SzGui/Windows/DatasetCreatorWindow.cs
@@ -0,0 +1,68 @@
+using ImGuiNET;
+using SzCore;
+using SzCore.DataObjects;
+
+namespace SzGui.Windows;
+
+public class DatasetCreatorWindow : SzGuiWindowBase
+{
+ private string _datasetName = "";
+ private int _selectedTemplateIndex = 0;
+ private List _templateIds = new();
+
+ public DatasetCreatorWindow()
+ {
+ WindowName = "Create New Dataset";
+ RefreshTemplates();
+ }
+
+ private void RefreshTemplates()
+ {
+ _ = SZ.DataHandler.LoadAllDataObjectTemplatesAsync();
+ _templateIds = SZ.DataHandler.LoadedTemplates.Keys.ToList();
+ }
+
+ protected override void RenderContent()
+ {
+ ImGui.InputText("Dataset Name", ref _datasetName, 128);
+
+ if (_templateIds.Count == 0)
+ {
+ ImGui.TextColored(new System.Numerics.Vector4(1, 0, 0, 1), "No templates loaded! Create a template first.");
+ if (ImGui.Button("Refresh Template List")) RefreshTemplates();
+ }
+ else
+ {
+ string[] items = _templateIds.ToArray();
+ ImGui.Combo("Template", ref _selectedTemplateIndex, items, items.Length);
+
+ ImGui.Separator();
+
+ if (ImGui.Button("Create Dataset", new System.Numerics.Vector2(0, 30)))
+ {
+ if (!string.IsNullOrWhiteSpace(_datasetName))
+ {
+ ExecuteCreation();
+ }
+ }
+ }
+ }
+
+ private async void ExecuteCreation()
+ {
+ string selectedId = _templateIds[_selectedTemplateIndex];
+
+ var result = await SZ.DataHandler.CreateDatasetAsync(_datasetName, selectedId);
+
+ if (result.IsSuccess && result.Value != null)
+ {
+ await SZ.DataHandler.SaveDatasetAsync(result.Value);
+
+ IsOpen = false;
+ }
+ else
+ {
+ Console.WriteLine($"Error creating dataset: {result.Error}");
+ }
+ }
+}
\ No newline at end of file
diff --git a/SzGui/Windows/NewObjectModal.cs b/SzGui/Windows/NewObjectModal.cs
new file mode 100644
index 0000000..2949a56
--- /dev/null
+++ b/SzGui/Windows/NewObjectModal.cs
@@ -0,0 +1,44 @@
+using ImGuiNET;
+using SzCore;
+using SzCore.DataObjects;
+using SzGui.Windows;
+
+public class NewObjectModal : SzGuiWindowBase
+{
+ private readonly SzDataset _dataset;
+ private string _objName = "";
+
+ public NewObjectModal(SzDataset dataset)
+ {
+ _dataset = dataset;
+ WindowName = $"Add Object to {dataset.Name}";
+ }
+
+ protected override void RenderContent()
+ {
+ ImGui.Text($"Creating object for template: {_dataset.DataObjectTemplateId}");
+ ImGui.InputText("Object Name", ref _objName, 64);
+
+ if (ImGui.Button("Create", new System.Numerics.Vector2(100, 0)))
+ {
+ Create();
+ }
+ }
+
+ private async void Create()
+ {
+ var templateResult = await SZ.DataHandler.LoadTemplateAsync(_dataset.DataObjectTemplateId);
+
+ if (templateResult.IsSuccess)
+ {
+ var objResult = SZ.DataHandler.CreateDataObject(templateResult.Value!, _objName);
+
+ if (objResult.IsSuccess)
+ {
+ _dataset.DataObjects.Add(objResult.Value!.Id, objResult.Value);
+ await SZ.DataHandler.SaveDatasetAsync(_dataset);
+ IsOpen = false;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/SzGui/Windows/SettingsWindow.cs b/SzGui/Windows/SettingsWindow.cs
new file mode 100644
index 0000000..4355acc
--- /dev/null
+++ b/SzGui/Windows/SettingsWindow.cs
@@ -0,0 +1,76 @@
+using System.Numerics;
+using ImGuiNET;
+
+namespace SzGui.Windows;
+
+public class SettingsWindow : SzGuiWindowBase
+{
+ private bool _settingsDirty = false;
+
+ public SettingsWindow() => WindowName = "App Settings";
+
+ protected override void RenderContent()
+ {
+ ImGui.SeparatorText("Application Settings");
+
+ foreach (var setting in ApplicationSettings.GetAllSettings().ToList())
+ {
+ string key = setting.Key;
+ object val = setting.Value;
+ bool changed = false;
+
+ if (val is bool boolVal)
+ {
+ if (ImGui.Checkbox(key, ref boolVal))
+ {
+ ApplicationSettings.SetSettingValue(key, boolVal);
+ changed = true;
+ }
+ }
+ else if (val is float floatVal || val is double)
+ {
+ float f = Convert.ToSingle(val);
+ if (ImGui.DragFloat(key, ref f, 0.01f))
+ {
+ ApplicationSettings.SetSettingValue(key, f);
+ changed = true;
+ }
+ }
+ else if (val is string strVal)
+ {
+ if (ImGui.InputText(key, ref strVal, 256))
+ {
+ ApplicationSettings.SetSettingValue(key, strVal);
+ changed = true;
+ }
+ }
+
+ if (ImGui.IsItemHovered())
+ {
+ ImGui.SetTooltip($"Internal Key: {key}");
+ }
+
+ if (changed) _settingsDirty = true;
+ }
+
+ ImGui.Separator();
+
+ if (ImGui.Button("Save Changes"))
+ {
+ ApplicationSettings.SaveSettings();
+ _settingsDirty = false;
+ }
+
+ ImGui.SameLine();
+
+ if (ImGui.Button("Reload from Disk"))
+ {
+ ApplicationSettings.LoadSettings();
+ }
+
+ if (_settingsDirty)
+ {
+ ImGui.TextColored(new Vector4(1, 0.5f, 0, 1), "You have unsaved changes!");
+ }
+ }
+}
diff --git a/SzGui/Windows/StartupWindow.cs b/SzGui/Windows/StartupWindow.cs
new file mode 100644
index 0000000..f3bd1e2
--- /dev/null
+++ b/SzGui/Windows/StartupWindow.cs
@@ -0,0 +1,15 @@
+using ImGuiNET;
+
+namespace SzGui.Windows;
+
+public class StartupWindow : SzGuiWindowBase
+{
+ public StartupWindow() => WindowName = "Startup";
+
+ protected override void RenderContent()
+ {
+ ImGui.TextUnformatted("Welcome to SessionZero!");
+ var testBttn = ImGui.Button(IconFonts.FontAwesome6.Gear + " Settings");
+ if (testBttn) SzApp.AppInstance.ActivateWindow(new SettingsWindow());
+ }
+}
\ No newline at end of file
diff --git a/SzGui/Windows/SzDataObjectEditorWindow.cs b/SzGui/Windows/SzDataObjectEditorWindow.cs
new file mode 100644
index 0000000..4b024c1
--- /dev/null
+++ b/SzGui/Windows/SzDataObjectEditorWindow.cs
@@ -0,0 +1,93 @@
+using ImGuiNET;
+using System.Numerics;
+using SzCore;
+using SzCore.DataObjects;
+
+namespace SzGui.Windows;
+
+public class SzDataObjectEditorWindow : SzGuiWindowBase
+{
+ private readonly SzDataset _dataset;
+ private readonly SzDataObject _dataObject;
+ private bool _isDirty = false;
+
+ public SzDataObjectEditorWindow(SzDataset dataset, SzDataObject dataObject)
+ {
+ _dataset = dataset;
+ _dataObject = dataObject;
+ WindowName = $"Editing: {dataObject.Name} ({dataset.Name})";
+ }
+
+ protected override void RenderContent()
+ {
+ ImGui.TextDisabled($"ID: {_dataObject.Id}");
+ ImGui.SeparatorText("Fields");
+
+ foreach (var field in _dataObject.Fields.Values)
+ {
+ DrawFieldInput(field);
+ }
+
+ ImGui.Separator();
+
+ if (ImGui.Button("Save Object", new Vector2(120, 30)))
+ {
+ Save();
+ }
+
+ if (_isDirty)
+ {
+ ImGui.SameLine();
+ ImGui.TextColored(new Vector4(1, 0.5f, 0, 1), "Unsaved changes...");
+ }
+ }
+
+ private void DrawFieldInput(SzField field)
+ {
+ string label = $"{field.Id}##{field.Id}";
+ string val = field.Value ?? "";
+
+ switch (field.FieldType)
+ {
+ case SzFieldType.Text:
+ if (ImGui.InputText(label, ref val, 1024))
+ {
+ field.Value = val;
+ _isDirty = true;
+ }
+ break;
+
+ case SzFieldType.Bool:
+ bool bVal = val.ToLower() == "true";
+ if (ImGui.Checkbox(label, ref bVal))
+ {
+ field.Value = bVal.ToString().ToLower();
+ _isDirty = true;
+ }
+ break;
+
+ case SzFieldType.Number:
+ // Handling numbers as strings in the DataObject, but using DragFloat for UX
+ float.TryParse(val, out float fVal);
+ if (ImGui.DragFloat(label, ref fVal))
+ {
+ field.Value = fVal.ToString();
+ _isDirty = true;
+ }
+ break;
+
+ default:
+ ImGui.Text($"{field.Id}: {val} (Unsupported Type: {field.FieldType})");
+ break;
+ }
+ }
+
+ private async void Save()
+ {
+ var result = await SZ.DataHandler.SaveDatasetAsync(_dataset);
+ if (result.IsSuccess)
+ {
+ _isDirty = false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/SzGui/Windows/SzDataObjectTemplateEditorWindow.cs b/SzGui/Windows/SzDataObjectTemplateEditorWindow.cs
new file mode 100644
index 0000000..ca179c0
--- /dev/null
+++ b/SzGui/Windows/SzDataObjectTemplateEditorWindow.cs
@@ -0,0 +1,137 @@
+using ImGuiNET;
+using System.Numerics;
+using SzCore;
+using SzCore.DataObjects;
+
+namespace SzGui.Windows;
+
+public class SzDataObjectTemplateEditorWindow : SzGuiWindowBase
+{
+ private readonly ISzTemplate _template;
+ private string _name;
+ private string _type;
+ private string _description = "";
+ private List _fields;
+
+ // Split ID components
+ private string _baseId = "";
+ private string _version = "1_0";
+
+ private string _newFieldId = "";
+ private int _selectedTypeIndex = 0;
+
+ public SzDataObjectTemplateEditorWindow(SzDataObjectTemplate template)
+ {
+ _template = template;
+ _name = template.Name;
+ _type = template.DataObjectType;
+ _fields = template.TemplateFields.Values.ToList();
+
+ if (template.Id.Contains('@'))
+ {
+ var parts = template.Id.Split('@');
+ _baseId = parts[0];
+ _version = parts[1];
+ }
+ else
+ {
+ _baseId = template.Id;
+ }
+
+ WindowName = $"Edit Template: {template.Id}";
+ }
+
+ protected override void RenderContent()
+ {
+ ImGui.SeparatorText("Metadata");
+
+ ImGui.InputText("Display Name", ref _name, 128);
+ ImGui.InputText("Object Type", ref _type, 128);
+
+ ImGui.Spacing();
+
+ ImGui.Text("Internal Identity");
+ ImGui.InputText("Base ID", ref _baseId, 64);
+ ImGui.SameLine();
+ ImGui.Text("@");
+ ImGui.SameLine();
+ ImGui.SetNextItemWidth(100);
+ ImGui.InputText("Version", ref _version, 16);
+
+ string fullIdPreview = $"{_baseId}@{_version}";
+ ImGui.TextDisabled($"Final ID Preview: {fullIdPreview}");
+
+ ImGui.SeparatorText("Fields Configuration");
+
+ if (ImGui.BeginTable("TemplateFieldsTable", 3, ImGuiTableFlags.Borders | ImGuiTableFlags.RowBg))
+ {
+ ImGui.TableSetupColumn("Field ID", ImGuiTableColumnFlags.WidthStretch);
+ ImGui.TableSetupColumn("Type", ImGuiTableColumnFlags.WidthFixed, 100);
+ ImGui.TableSetupColumn("Action", ImGuiTableColumnFlags.WidthFixed, 70);
+ ImGui.TableHeadersRow();
+
+ for (int i = 0; i < _fields.Count; i++)
+ {
+ ImGui.TableNextRow();
+ ImGui.TableNextColumn();
+ ImGui.TextUnformatted(_fields[i].Id);
+
+ ImGui.TableNextColumn();
+ ImGui.TextUnformatted(_fields[i].FieldType.ToString());
+
+ ImGui.TableNextColumn();
+ if (ImGui.SmallButton($"Remove##{i}")) _fields.RemoveAt(i);
+ }
+ ImGui.EndTable();
+ }
+
+ ImGui.Spacing();
+ ImGui.SeparatorText("Add New Field");
+
+ ImGui.InputText("Field ID", ref _newFieldId, 64);
+ string[] typeNames = Enum.GetNames();
+ ImGui.Combo("Type", ref _selectedTypeIndex, typeNames, typeNames.Length);
+
+ if (ImGui.Button("Add Field") && !string.IsNullOrWhiteSpace(_newFieldId))
+ {
+ var result = SZ.DataHandler.CreateTemplateField(_newFieldId, (SzFieldType)_selectedTypeIndex);
+ if (result.IsSuccess)
+ {
+ _fields.Add(result.Value!);
+ _newFieldId = "";
+ }
+ }
+
+ ImGui.Separator();
+
+ bool isNewVersion = fullIdPreview != _template.Id;
+ if (isNewVersion)
+ {
+ ImGui.PushStyleColor(ImGuiCol.Button, new Vector4(0.2f, 0.6f, 0.2f, 1.0f));
+ if (ImGui.Button("Save as New Version", new Vector2(0, 30))) Save(fullIdPreview);
+ ImGui.PopStyleColor();
+ }
+ else
+ {
+ if (ImGui.Button("Overwrite Template", new Vector2(0, 30))) Save(fullIdPreview);
+ }
+ }
+
+ private async void Save(string finalId)
+ {
+ var result = SZ.DataHandler.CreateDataObjectTemplate(
+ _name,
+ _type,
+ _version,
+ finalId,
+ null,
+ _fields
+ );
+
+ if (result.IsSuccess && result.Value != null)
+ {
+ await SZ.DataHandler.SaveTemplateAsync(result.Value);
+ IsOpen = false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/SzGui/Windows/SzDatasetViewerWindow.cs b/SzGui/Windows/SzDatasetViewerWindow.cs
new file mode 100644
index 0000000..70fa038
--- /dev/null
+++ b/SzGui/Windows/SzDatasetViewerWindow.cs
@@ -0,0 +1,57 @@
+using ImGuiNET;
+using System.Numerics;
+using SzCore;
+using SzCore.DataObjects;
+
+namespace SzGui.Windows;
+
+public class SzDatasetViewerWindow : SzGuiWindowBase
+{
+ private readonly SzDataset _dataset;
+
+ public SzDatasetViewerWindow(SzDataset dataset)
+ {
+ _dataset = dataset;
+ WindowName = $"Dataset: {dataset.Name} ({dataset.DataObjectTemplateId})";
+ }
+
+ protected override void RenderContent()
+ {
+ if (ImGui.Button("Add New Object"))
+ {
+ SzApp.AppInstance.ActivateWindow(new NewObjectModal(_dataset));
+ }
+
+ ImGui.Separator();
+
+ if (ImGui.BeginTable("ObjectTable", 2, ImGuiTableFlags.Borders | ImGuiTableFlags.RowBg | ImGuiTableFlags.ScrollY))
+ {
+ ImGui.TableSetupColumn("Object Name", ImGuiTableColumnFlags.WidthStretch);
+ ImGui.TableSetupColumn("Actions", ImGuiTableColumnFlags.WidthFixed, 110);
+ ImGui.TableHeadersRow();
+
+ foreach (var dataObj in _dataset.DataObjects.Values.ToList())
+ {
+ ImGui.TableNextRow();
+
+ ImGui.TableNextColumn();
+ ImGui.TextUnformatted(dataObj.Name);
+
+ ImGui.TableNextColumn();
+ if (ImGui.SmallButton($"Edit##{dataObj.Id}"))
+ {
+ SzApp.AppInstance.ActivateWindow(new SzDataObjectEditorWindow(_dataset, dataObj));
+ }
+
+ ImGui.SameLine();
+
+ if (ImGui.SmallButton($"Delete##{dataObj.Id}"))
+ {
+ _dataset.DataObjects.Remove(dataObj.Id);
+ _ = SZ.DataHandler.SaveDatasetAsync(_dataset);
+ }
+ }
+ ImGui.EndTable();
+ }
+ }
+}
\ No newline at end of file
diff --git a/SzGui/Windows/SzGuiWindowBase.cs b/SzGui/Windows/SzGuiWindowBase.cs
new file mode 100644
index 0000000..f9c0717
--- /dev/null
+++ b/SzGui/Windows/SzGuiWindowBase.cs
@@ -0,0 +1,22 @@
+using ImGuiNET;
+
+namespace SzGui.Windows;
+
+public abstract class SzGuiWindowBase
+{
+ public bool IsOpen = true;
+ public string WindowName { get; protected set; } = "Window";
+
+ public virtual void Draw()
+ {
+ if (!IsOpen) return;
+
+ if (ImGui.Begin(WindowName, ref IsOpen, ImGuiWindowFlags.MenuBar))
+ {
+ RenderContent();
+ }
+ ImGui.End();
+ }
+
+ protected abstract void RenderContent();
+}
\ No newline at end of file
diff --git a/SzGui/Windows/SzLibraryWindow.cs b/SzGui/Windows/SzLibraryWindow.cs
new file mode 100644
index 0000000..6d27f66
--- /dev/null
+++ b/SzGui/Windows/SzLibraryWindow.cs
@@ -0,0 +1,127 @@
+using ImGuiNET;
+using SzCore;
+using SzCore.DataObjects;
+
+namespace SzGui.Windows;
+
+public class SzLibraryWindow : SzGuiWindowBase
+{
+ public SzLibraryWindow()
+ {
+ WindowName = "SZ Library";
+ RefreshData();
+ }
+
+ private Dictionary _loadedDatasets = [];
+ private Dictionary _loadedDataObjectTemplates = [];
+
+ private async void RefreshData()
+ {
+ await SZ.DataHandler.LoadAllDatasetsAsync();
+ await SZ.DataHandler.LoadAllDataObjectTemplatesAsync();
+
+ _loadedDatasets = SZ.DataHandler.LoadedDatasets;
+ _loadedDataObjectTemplates = SZ.DataHandler.LoadedTemplates.Values
+ .OfType()
+ .ToDictionary(t => t.Id, t => t);
+ }
+
+ protected override void RenderContent()
+ {
+ if (ImGui.BeginMenuBar())
+ {
+ if (ImGui.BeginMenu("Create"))
+ {
+ if (ImGui.MenuItem("New Template"))
+ {
+ SzApp.AppInstance.ActivateWindow(new SzTemplateCreatorWindow());
+ }
+
+ if (ImGui.MenuItem("New Dataset"))
+ {
+ SzApp.AppInstance.ActivateWindow(new DatasetCreatorWindow());
+ }
+ ImGui.EndMenu();
+ }
+
+ // Added Refresh to the menu bar
+ if (ImGui.MenuItem("Refresh"))
+ {
+ RefreshData();
+ }
+
+ ImGui.EndMenuBar();
+ }
+
+ if (ImGui.CollapsingHeader("Data Object Templates", ImGuiTreeNodeFlags.DefaultOpen))
+ {
+ if (ImGui.BeginTable("DataObjectTemplatesTable", 2, ImGuiTableFlags.Borders | ImGuiTableFlags.RowBg))
+ {
+ ImGui.TableSetupColumn("Template ID", ImGuiTableColumnFlags.WidthStretch);
+ ImGui.TableSetupColumn("Actions", ImGuiTableColumnFlags.WidthFixed, 120);
+ ImGui.TableHeadersRow();
+
+ foreach (var template in _loadedDataObjectTemplates)
+ {
+ ImGui.TableNextRow();
+ ImGui.TableNextColumn();
+ ImGui.TextUnformatted(template.Key);
+
+ ImGui.TableNextColumn();
+
+ if (ImGui.SmallButton($"Edit##T_{template.Key}"))
+ {
+ SzApp.AppInstance.ActivateWindow(new SzDataObjectTemplateEditorWindow(template.Value));
+ }
+
+ ImGui.SameLine();
+
+ if (ImGui.SmallButton($"Delete##T_{template.Key}"))
+ {
+ _ = Task.Run(async () => {
+ await SZ.DataHandler.DeleteTemplateAsync(template.Key);
+ RefreshData();
+ });
+ }
+ }
+ ImGui.EndTable();
+ }
+ }
+
+ ImGui.Spacing();
+
+ if (ImGui.CollapsingHeader("Datasets", ImGuiTreeNodeFlags.DefaultOpen))
+ {
+ if (ImGui.BeginTable("DatasetsTable", 2, ImGuiTableFlags.Borders | ImGuiTableFlags.RowBg))
+ {
+ ImGui.TableSetupColumn("Dataset Name", ImGuiTableColumnFlags.WidthStretch);
+ ImGui.TableSetupColumn("Actions", ImGuiTableColumnFlags.WidthFixed, 120);
+ ImGui.TableHeadersRow();
+
+ foreach (var datasetEntry in _loadedDatasets)
+ {
+ var dataset = datasetEntry.Value;
+ ImGui.TableNextRow();
+
+ ImGui.TableNextColumn();
+ ImGui.TextUnformatted(dataset.Name);
+
+ ImGui.TableNextColumn();
+ if (ImGui.SmallButton($"Open##{dataset.Id}"))
+ {
+ SzApp.AppInstance.ActivateWindow(new SzDatasetViewerWindow(dataset));
+ }
+ ImGui.SameLine();
+ if (ImGui.SmallButton($"Delete##{dataset.Id}"))
+ {
+ _ = Task.Run(async () => {
+ await SZ.DataHandler.DeleteDatasetAsync(dataset.Id);
+ RefreshData();
+ });
+ }
+ }
+ ImGui.EndTable();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/SzGui/Windows/SzTemplateCreatorWindow.cs b/SzGui/Windows/SzTemplateCreatorWindow.cs
new file mode 100644
index 0000000..846d578
--- /dev/null
+++ b/SzGui/Windows/SzTemplateCreatorWindow.cs
@@ -0,0 +1,66 @@
+using ImGuiNET;
+using SzCore;
+using SzCore.DataObjects;
+
+namespace SzGui.Windows;
+
+public class SzTemplateCreatorWindow : SzGuiWindowBase
+{
+ private string _name = "";
+ private string _type = "";
+ private List _fields = new();
+
+ private string _newFieldId = "";
+ private int _selectedTypeIndex = 0;
+
+ public SzTemplateCreatorWindow() => WindowName = "Template Creator";
+
+ protected override void RenderContent()
+ {
+ ImGui.InputText("Template Name", ref _name, 64);
+ ImGui.InputText("Object Type", ref _type, 64);
+
+ ImGui.SeparatorText("Fields");
+
+ for (int i = 0; i < _fields.Count; i++)
+ {
+ ImGui.BulletText($"{_fields[i].Id} ({_fields[i].FieldType})");
+ ImGui.SameLine();
+ if (ImGui.Button($"Remove##{i}")) _fields.RemoveAt(i);
+ }
+
+ ImGui.Separator();
+
+ ImGui.InputText("Field ID", ref _newFieldId, 64);
+
+ string[] typeNames = Enum.GetNames();
+ ImGui.Combo("Type", ref _selectedTypeIndex, typeNames, typeNames.Length);
+
+ if (ImGui.Button("Add Field"))
+ {
+ var result = SZ.DataHandler.CreateTemplateField(_newFieldId, (SzFieldType)_selectedTypeIndex);
+ if (result.IsSuccess)
+ {
+ _fields.Add(result.Value!);
+ _newFieldId = ""; // Reset
+ }
+ }
+
+ ImGui.Separator();
+
+ if (ImGui.Button("Save Template", new System.Numerics.Vector2(0, 30)))
+ {
+ Save();
+ }
+ }
+
+ private async void Save()
+ {
+ var result = SZ.DataHandler.CreateDataObjectTemplate(_name, _type, fields: _fields);
+ if (result.IsSuccess)
+ {
+ await SZ.DataHandler.SaveTemplateAsync(result.Value!);
+ IsOpen = false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/SzGui/imgui.ini b/SzGui/imgui.ini
index 09d83fd..0c19bdd 100644
--- a/SzGui/imgui.ini
+++ b/SzGui/imgui.ini
@@ -1,23 +1,25 @@
[Window][Debug##Default]
-Pos=1006,402
-Size=762,814
+Pos=637,781
+Size=776,368
Collapsed=0
[Window][SessionZero]
Pos=0,0
-Size=3792,2067
+Size=1692,1377
Collapsed=0
-DockId=0x08BD597D,0
+DockId=0x00000001,1
[Window][Dear ImGui Demo]
-Size=1894,2067
+Pos=0,0
+Size=1692,1377
Collapsed=0
-DockId=0x08BD597D,0
+DockId=0x00000001,0
[Window][Dear ImGui Style Editor]
-Pos=96,60
-Size=692,1365
+Pos=1694,0
+Size=834,1377
Collapsed=0
+DockId=0x00000002,0
[Window][Example: Assets Browser]
Pos=60,60
@@ -61,7 +63,7 @@ Size=300,409
[Window][WindowOverViewport_11111111]
Pos=0,0
-Size=3792,2067
+Size=2528,1377
Collapsed=0
[Table][0xB6880529,2]
@@ -74,6 +76,8 @@ Column 0 Width=4
Column 1 Weight=2.0000
[Docking][Data]
-DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=0,0 Size=3792,2067 CentralNode=1 Selected=0x3C0097EB
-DockSpace ID=0xC0DFADC4 Pos=0,38 Size=3840,2122 CentralNode=1
+DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=0,0 Size=2528,1377 Split=X Selected=0x5E5F7166
+ DockNode ID=0x00000001 Parent=0x08BD597D SizeRef=1692,1377 CentralNode=1 Selected=0x5E5F7166
+ DockNode ID=0x00000002 Parent=0x08BD597D SizeRef=834,1377 Selected=0x3EEA4247
+DockSpace ID=0xC0DFADC4 Pos=0,38 Size=3840,2122 CentralNode=1