using GlitchyEditor.EditWindows; using ImGui; using GlitchyEngine; using System; using System.Collections; using System.Reflection; namespace GlitchyEditor { class SettingsWindow : EditorWindow { class Binding { public Object SettingsObject; public String Name ~ delete _; public String FieldName ~ delete _; public StringView Tooltip; public this(StringView name, StringView fieldName, Object settingsObject, StringView tooltip) { Name = new String(name); FieldName = new String(fieldName); SettingsObject = settingsObject; Tooltip = tooltip; } } class Category { public List _bindings = new .() ~ DeleteContainerAndItems!(_); public String Header ~ delete _; public Object SettingsObject; public this(String header) { Header = new String(header); } public void AddSetting(StringView name, StringView fieldName, Object settingsObject = null, StringView tooltip = "") { Binding binding = new .(name, fieldName, settingsObject ?? SettingsObject, tooltip); _bindings.Add(binding); } } Settings _settings; bool _settingsChanged = false; private Dictionary _categories = new .() ~ DeleteDictionaryAndValues!(_); public this() { _open = false; _settings = Application.Get().Settings; Create(); } void Create() { ScanForSettings(_settings); /*for (ISettings settings in _settings.[Friend]_userSettings) { ScanForSettings(settings); }*/ } Category AddCategory(String header) { if (_categories.TryGetValue(header, let category)) return category; Category newCat = new Category(header); _categories.Add(newCat.Header, newCat); return newCat; } void ScanForSettings(Object container) { Type type = container.GetType(); for (var field in type.GetFields()) { Result settingResult = field.GetCustomAttribute(); if (settingResult case .Ok(let settingInfo)) { AddSetting(settingInfo.Category, settingInfo.Name, field.Name, container, settingInfo.Tooltip); } Result containerResult = field.GetCustomAttribute(); if (containerResult case .Ok(let containerInfo)) { var res = field.GetValue(container, let childContainer); if (res case .Err(let error)) { Log.EngineLogger.Error($"Failed to get settings container. Error: {error}"); continue; } ScanForSettings(childContainer); } } } void AddSetting(String categoryName, String name, StringView fieldName, Object container, StringView tooltip) { Category category = AddCategory(categoryName); category.AddSetting(name, fieldName, container, tooltip); } protected override void InternalShow() { ImGui.Begin("Settings", &_open, .NoDocking); defer ImGui.End(); // Leave room for 1 line below us ImGui.BeginChild("item view", ImGui.Vec2(0, -ImGui.GetFrameHeightWithSpacing())); ImGui.BeginTabBar("##Tabs"); for (Category category in _categories.Values) { if (ImGui.BeginTabItem(category.Header)) { ShowCategory(category); ImGui.EndTabItem(); } } ImGui.EndTabBar(); ImGui.EndChild(); ImGui.BeginDisabled(!_settingsChanged); if (ImGui.Button("Save")) { _settings.Save(); _settings.Apply(); _open = false; } ImGui.SameLine(); if (ImGui.Button("Apply")) { _settings.Apply(); } ImGui.EndDisabled(); ImGui.SameLine(); if (ImGui.Button("Cancel")) { Settings.Load(); _open = false; } } private void ShowCategory(Category category) { if (ImGui.BeginTable("SettingsTable", 2, .SizingFixedFit | .RowBg)) { ImGui.TableSetupColumn("Settings"); ImGui.TableSetupColumn("Values", .WidthStretch); ImGui.TableNextRow(); ImGui.TableSetColumnIndex(1); ImGui.PushItemWidth(ImGui.GetContentRegionAvail().x); for (Binding setting in category._bindings) { ImGui.TableNextRow(); ImGui.TableNextColumn(); // Name of setting ImGui.TextUnformatted(setting.Name); if (!setting.Tooltip.IsWhiteSpace) ImGui.AttachTooltip(setting.Tooltip); ImGui.TableNextColumn(); Type settingsObjectType = setting.SettingsObject.GetType(); Result result = settingsObjectType.GetField(setting.FieldName); if (result case .Err) { ImGui.PushStyleColor(.Text, ImGui.Vec4(1f, 0f, 0f, 1f)); ImGui.Text($"Field {setting.FieldName} not found."); ImGui.PopStyleColor(); continue; } FieldInfo fieldInfo = result.Get(); Type fieldType = fieldInfo.FieldType; mixin GetSettingValue() { var error = fieldInfo.GetValue(setting.SettingsObject, var value); if (error case .Err(let err)) { Log.EngineLogger.Error($"Could not get value of setting {setting.FieldName}. Error: {err}"); ImGui.PushStyleColor(.Text, ImGui.Vec4(1f, 0f, 0f, 1f)); ImGui.Text($"Could not get value of setting {setting.FieldName}. Error: {err}"); ImGui.PopStyleColor(); break; } value } mixin SetSettingValue(T value) { var error = fieldInfo.SetValue(setting.SettingsObject, value); if (error case .Err(let err)) { Log.EngineLogger.Error($"Could not set value of setting {setting.FieldName}. Error: {err}"); } } switch (fieldType) { case typeof(bool): let value = GetSettingValue!(); if (!ImGui.Checkbox(scope $"##{setting.Name}", &value)) break; SetSettingValue!(value); _settingsChanged = true; case typeof(int32): int32 value = GetSettingValue!(); if (!ImGui.InputInt(scope $"##{setting.Name}", &value)) break; SetSettingValue!(value); _settingsChanged = true; case typeof(String): String value = GetSettingValue!(); char8[256] buffer = .(); value.CopyTo(buffer); if (ImGui.InputText(scope $"##{setting.Name}", &buffer, buffer.Count)) { value..Clear().Append(&buffer); _settingsChanged = true; } } } ImGui.EndTable(); } } } }