mirror of
https://github.com/aharabada/glitchy-engine-beef.git
synced 2026-09-05 13:01:52 +00:00
Auto detect all Visual Studio and Rider installs
This commit is contained in:
@@ -3,9 +3,7 @@ namespace GlitchyEditor.CodeEditors;
|
||||
|
||||
interface IIdeAdapter
|
||||
{
|
||||
static void OpenScript(StringView fileName);
|
||||
void OpenScript(StringView fileName, int lineNumber = 0);
|
||||
|
||||
static void OpenScript(StringView fileName, int lineNumber);
|
||||
|
||||
static void OpenScriptProject();
|
||||
void OpenScriptProject();
|
||||
}
|
||||
|
||||
@@ -6,29 +6,34 @@ using System.IO;
|
||||
using System.IO;
|
||||
using GlitchyEditor.EditWindows;
|
||||
using ImGui;
|
||||
using GlitchyEditor.Settings;
|
||||
|
||||
namespace GlitchyEditor.CodeEditors;
|
||||
|
||||
class RiderIdeAdapter : IIdeAdapter
|
||||
{
|
||||
public static void OpenScript(StringView fileName)
|
||||
private IdeInstallation _ideInstallation;
|
||||
|
||||
public this(IdeInstallation ideInstallation)
|
||||
{
|
||||
OpenScript(fileName, 0);
|
||||
Debug.Assert(ideInstallation.Ide == .Rider);
|
||||
|
||||
_ideInstallation = ideInstallation;
|
||||
}
|
||||
|
||||
public static void OpenScript(StringView fileName, int lineNumber)
|
||||
public void OpenScript(StringView fileName, int lineNumber)
|
||||
{
|
||||
String solutionPath = scope .();
|
||||
Editor.Instance.CurrentProject.GetPathToScriptSolutionFile(solutionPath);
|
||||
|
||||
ProcessStartInfo startInfo = scope .();
|
||||
startInfo.SetFileName(EditorApp.Instance.Settings.ScriptSettings.RiderPath);
|
||||
startInfo.SetFileName(_ideInstallation.Path);
|
||||
startInfo.SetArguments(scope $"{solutionPath} --line {lineNumber} {fileName}");
|
||||
|
||||
scope SpawnedProcess().Start(startInfo);
|
||||
}
|
||||
|
||||
public static void OpenScriptProject()
|
||||
public void OpenScriptProject()
|
||||
{
|
||||
String solutionPath = scope .();
|
||||
Editor.Instance.CurrentProject.GetPathToScriptSolutionFile(solutionPath);
|
||||
@@ -39,7 +44,7 @@ class RiderIdeAdapter : IIdeAdapter
|
||||
return;
|
||||
}
|
||||
|
||||
if (!File.Exists(EditorApp.Instance.Settings.ScriptSettings.RiderPath))
|
||||
if (!File.Exists(_ideInstallation.Path))
|
||||
{
|
||||
Editor.Instance.ShowSettings();
|
||||
Editor.Instance.SettingsWindow.HighlightSetting("Tools", "Rider path");
|
||||
@@ -51,7 +56,7 @@ class RiderIdeAdapter : IIdeAdapter
|
||||
}
|
||||
|
||||
ProcessStartInfo startInfo = scope .();
|
||||
startInfo.SetFileName(EditorApp.Instance.Settings.ScriptSettings.RiderPath);
|
||||
startInfo.SetFileName(_ideInstallation.Path);
|
||||
startInfo.SetArguments(solutionPath);
|
||||
|
||||
scope SpawnedProcess().Start(startInfo);
|
||||
|
||||
@@ -2,12 +2,22 @@ using System;
|
||||
using System.Diagnostics;
|
||||
using GlitchyEngine;
|
||||
using System.Collections;
|
||||
using GlitchyEditor.Settings;
|
||||
|
||||
namespace GlitchyEditor.CodeEditors;
|
||||
|
||||
class VisualStudioIdeAdapter : IIdeAdapter
|
||||
{
|
||||
public static bool IsRunning()
|
||||
private IdeInstallation _ideInstallation;
|
||||
|
||||
public this(IdeInstallation ideInstallation)
|
||||
{
|
||||
Debug.Assert(ideInstallation.Ide == .VisualStudio);
|
||||
|
||||
_ideInstallation = ideInstallation;
|
||||
}
|
||||
|
||||
public bool IsRunning()
|
||||
{
|
||||
List<Process> processes = scope .();
|
||||
|
||||
@@ -19,6 +29,7 @@ class VisualStudioIdeAdapter : IIdeAdapter
|
||||
|
||||
for (Process process in processes)
|
||||
{
|
||||
// TODO?
|
||||
}
|
||||
|
||||
ClearAndDeleteItems!(processes);
|
||||
@@ -26,12 +37,12 @@ class VisualStudioIdeAdapter : IIdeAdapter
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void OpenScript(StringView fileName)
|
||||
public void OpenScript(StringView fileName, int lineNumber)
|
||||
{
|
||||
if (IsRunning())
|
||||
{
|
||||
ProcessStartInfo startInfo = scope .();
|
||||
startInfo.SetFileName(EditorApp.Instance.Settings.ScriptSettings.VisualStudioPath);
|
||||
startInfo.SetFileName(_ideInstallation.Path);
|
||||
startInfo.SetArguments(scope $"/Edit {fileName}");
|
||||
|
||||
scope SpawnedProcess().Start(startInfo);
|
||||
@@ -39,25 +50,20 @@ class VisualStudioIdeAdapter : IIdeAdapter
|
||||
else
|
||||
{
|
||||
ProcessStartInfo startInfo = scope .();
|
||||
startInfo.SetFileName(EditorApp.Instance.Settings.ScriptSettings.VisualStudioPath);
|
||||
startInfo.SetFileName(_ideInstallation.Path);
|
||||
startInfo.SetArguments(scope $"/Edit {fileName}");
|
||||
|
||||
scope SpawnedProcess().Start(startInfo);
|
||||
}
|
||||
}
|
||||
|
||||
public static void OpenScript(StringView fileName, int lineNumber)
|
||||
{
|
||||
OpenScript(fileName);
|
||||
}
|
||||
|
||||
public static void OpenScriptProject()
|
||||
public void OpenScriptProject()
|
||||
{
|
||||
String solutionPath = scope .();
|
||||
Editor.Instance.CurrentProject.GetPathToScriptSolutionFile(solutionPath);
|
||||
|
||||
ProcessStartInfo psi = scope .();
|
||||
psi.SetFileName("devenv");
|
||||
psi.SetFileName(_ideInstallation.Path);
|
||||
psi.SetArguments(solutionPath);
|
||||
|
||||
scope SpawnedProcess().Start(psi);
|
||||
|
||||
@@ -604,13 +604,7 @@ namespace GlitchyEditor.EditWindows
|
||||
|
||||
if (ImGui.MenuItem("Open C# Project..."))
|
||||
{
|
||||
switch (EditorApp.Instance.Settings.ScriptSettings.SelectedIde)
|
||||
{
|
||||
case .VisualStudio:
|
||||
VisualStudioIdeAdapter.OpenScriptProject();
|
||||
case .Rider:
|
||||
RiderIdeAdapter.OpenScriptProject();
|
||||
}
|
||||
Editor.Instance.IdeAdapter.OpenScriptProject();
|
||||
}
|
||||
|
||||
ImGui.AttachTooltip("Opens the C# Solution of this project.");
|
||||
@@ -1440,8 +1434,7 @@ namespace GlitchyEditor.EditWindows
|
||||
// Special treatment for scripts, open them in Visual Studio.
|
||||
if (entry->Path.EndsWith(".cs"))
|
||||
{
|
||||
// Obviously windows only
|
||||
RiderIdeAdapter.OpenScript(entry->Path);
|
||||
Editor.Instance.IdeAdapter.OpenScript(entry->Path);
|
||||
}
|
||||
else if (Path.OpenFolder(entry->Path) case .Err)
|
||||
Log.EngineLogger.Error("Failed to open file.");
|
||||
|
||||
@@ -317,7 +317,7 @@ class LogWindow : EditorWindow
|
||||
{
|
||||
if (message.Source.MessageOrigin != null)
|
||||
{
|
||||
RiderIdeAdapter.OpenScript(message.Source.MessageOrigin.FileName, message.Source.MessageOrigin.LineNumber);
|
||||
Editor.Instance.IdeAdapter.OpenScript(message.Source.MessageOrigin.FileName, message.Source.MessageOrigin.LineNumber);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ using GlitchyEngine.Collections;
|
||||
using GlitchyEditor.EditWindows;
|
||||
using GlitchyEngine;
|
||||
using GlitchyEditor.Assets;
|
||||
using GlitchyEditor.CodeEditors;
|
||||
|
||||
namespace GlitchyEditor
|
||||
{
|
||||
@@ -79,6 +80,18 @@ namespace GlitchyEditor
|
||||
|
||||
private static Editor s_Instance;
|
||||
|
||||
private IIdeAdapter _ideAdapter = null ~ delete _;
|
||||
|
||||
public IIdeAdapter IdeAdapter
|
||||
{
|
||||
get => _ideAdapter;
|
||||
private set
|
||||
{
|
||||
delete _ideAdapter;
|
||||
_ideAdapter = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static Editor Instance => s_Instance;
|
||||
|
||||
/// Creates a new editor for the given world
|
||||
@@ -87,6 +100,24 @@ namespace GlitchyEditor
|
||||
Log.EngineLogger.AssertDebug(s_Instance == null, "Cannot create a second instance of a singleton.");
|
||||
s_Instance = this;
|
||||
|
||||
EditorApp.Instance.Settings.OnApplySettings.Add(new (s, e) => {
|
||||
let activeIde = EditorApp.Instance.Settings.ScriptSettings.ActiveIde;
|
||||
if (activeIde != null)
|
||||
{
|
||||
switch (activeIde.Ide)
|
||||
{
|
||||
case .VisualStudio:
|
||||
IdeAdapter = new VisualStudioIdeAdapter(activeIde);
|
||||
case .Rider:
|
||||
IdeAdapter = new RiderIdeAdapter(activeIde);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
IdeAdapter = null;
|
||||
}
|
||||
});
|
||||
|
||||
_activeScene = activeScene;
|
||||
_editorScene = editorScene;
|
||||
_contentManager = contentManager;
|
||||
|
||||
@@ -13,6 +13,7 @@ using GlitchyEditor.Platform;
|
||||
using System.Diagnostics;
|
||||
using GlitchyEditor.ImGui;
|
||||
using GlitchyEngine.Events;
|
||||
using GlitchyEditor.Settings;
|
||||
|
||||
namespace GlitchyEditor
|
||||
{
|
||||
@@ -48,6 +49,8 @@ namespace GlitchyEditor
|
||||
|
||||
DragDropManager.Init();
|
||||
|
||||
GlitchyEditor.Settings.Settings.Load();
|
||||
|
||||
#if IMGUI
|
||||
_imGuiLayer = new ImGuiLayer();
|
||||
PushOverlay(_imGuiLayer);
|
||||
@@ -56,7 +59,6 @@ namespace GlitchyEditor
|
||||
_editorLayer = new EditorLayer(args, _contentManager);
|
||||
PushLayer(_editorLayer);
|
||||
|
||||
GlitchyEditor.Settings.Load();
|
||||
Settings.OnApplySettings.Add(new (s, e) => {
|
||||
OnEvent(scope SettingsAppliedEvent());
|
||||
});
|
||||
|
||||
@@ -1,292 +0,0 @@
|
||||
using System;
|
||||
using GlitchyEngine;
|
||||
using Bon;
|
||||
using GlitchyEditor;
|
||||
using GlitchyEditor.CodeEditors;
|
||||
using System.Collections;
|
||||
using System.IO;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
namespace GlitchyEditor
|
||||
{
|
||||
extension Settings
|
||||
{
|
||||
#if DEBUG
|
||||
[SettingContainer, BonInclude]
|
||||
public readonly DevSettings DevSettings = new .() ~ delete _;
|
||||
#endif
|
||||
|
||||
[SettingContainer, BonInclude]
|
||||
public readonly EditorSettings EditorSettings = new .() ~ delete _;
|
||||
|
||||
[SettingContainer, BonInclude]
|
||||
public readonly ScriptSettings ScriptSettings = new .() ~ delete _;
|
||||
|
||||
protected override void RegisterEventListeners()
|
||||
{
|
||||
#if DEBUG
|
||||
OnApplySettings.Add(new (s, e) => DevSettings.Apply());
|
||||
#endif
|
||||
OnApplySettings.Add(new (s, e) => EditorSettings.Apply());
|
||||
OnApplySettings.Add(new (s, e) => ScriptSettings.Apply());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace GlitchyEditor;
|
||||
|
||||
#if DEBUG
|
||||
[Reflect]
|
||||
class DevSettings
|
||||
{
|
||||
[Setting("Dev", "Use ScriptCore csproj", "If enabled the Editor will include the csproj of the ScriptCore instead of the compiled dll."), BonInclude]
|
||||
public bool UseScriptCoreDll = true;
|
||||
|
||||
public void Apply()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
[Reflect]
|
||||
enum ScriptIde
|
||||
{
|
||||
Rider,
|
||||
VisualStudio
|
||||
}
|
||||
|
||||
[Reflect]
|
||||
class ScriptSettings
|
||||
{
|
||||
[Setting("Tools", "Visual Studio path", "The path of Visual Studio's \"devenv.exe\""), BonInclude]
|
||||
public String VisualStudioPath ~ delete _;
|
||||
|
||||
[Setting("Tools", "Rider path", "The path to JetBrains Rider IDE (rider64.exe)"), BonInclude]
|
||||
public String RiderPath ~ delete _;
|
||||
|
||||
[Setting("Tools", "IDE", "The IDE that will be used to open scripts for editing."), BonInclude]
|
||||
public ScriptIde SelectedIde;
|
||||
|
||||
private bool _lookedForVs = false;
|
||||
private bool _lookedForRider = false;
|
||||
|
||||
public void Apply()
|
||||
{
|
||||
#if BF_PLATFORM_WINDOWS
|
||||
if (!_lookedForVs && String.IsNullOrWhiteSpace(VisualStudioPath))
|
||||
{
|
||||
FindVisualStudio();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!_lookedForRider && String.IsNullOrWhiteSpace(RiderPath))
|
||||
{
|
||||
FindRider();
|
||||
}
|
||||
}
|
||||
|
||||
void FindVisualStudio()
|
||||
{
|
||||
_lookedForVs = true;
|
||||
|
||||
Thread thread = new Thread(new => FindVisualStudioImpl);
|
||||
thread.AutoDelete = true;
|
||||
thread.IsBackground = true;
|
||||
thread.Start();
|
||||
}
|
||||
|
||||
void FindVisualStudioImpl()
|
||||
{
|
||||
Log.EngineLogger.Info("Using vswhere.exe to search for Visual Studio...");
|
||||
|
||||
String exeFile = scope .();
|
||||
Environment.GetExecutableFilePath(exeFile);
|
||||
|
||||
String exeDirectory = scope .();
|
||||
Path.GetDirectoryPath(exeFile, exeDirectory);
|
||||
|
||||
String vsWherePath = scope .();
|
||||
Path.Combine(vsWherePath, exeDirectory, "vswhere.exe");
|
||||
|
||||
ProcessStartInfo processInfo = scope .();
|
||||
processInfo.UseShellExecute = false;
|
||||
processInfo.RedirectStandardOutput = true;
|
||||
processInfo.CreateNoWindow = true;
|
||||
|
||||
processInfo.SetFileName(vsWherePath);
|
||||
// Find the installation path of the latest visual studio with an installed IDE.
|
||||
processInfo.SetArguments("-latest -requires Microsoft.VisualStudio.Workload.NativeDesktop -property productPath");
|
||||
|
||||
let process = scope SpawnedProcess();
|
||||
if (process.Start(processInfo) case .Ok)
|
||||
{
|
||||
FileStream outputStream = scope FileStream();
|
||||
process.AttachStandardOutput(outputStream);
|
||||
|
||||
process.WaitFor(1000);
|
||||
|
||||
if (VisualStudioPath == null)
|
||||
{
|
||||
VisualStudioPath = new String();
|
||||
}
|
||||
else
|
||||
{
|
||||
VisualStudioPath.Clear();
|
||||
}
|
||||
|
||||
if (outputStream.Length > 0)
|
||||
{
|
||||
StreamReader streamReaderOut = scope StreamReader(outputStream, null, false, 4096);
|
||||
streamReaderOut.ReadToEnd(VisualStudioPath).IgnoreError();
|
||||
|
||||
VisualStudioPath.Trim();
|
||||
|
||||
if (File.Exists(VisualStudioPath))
|
||||
{
|
||||
Log.EngineLogger.Info($"Located Visual Studio: {VisualStudioPath}");
|
||||
|
||||
Apply();
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.EngineLogger.Error($"Failed to find Visual Studio. vswhere output: \n{VisualStudioPath}\n\n");
|
||||
VisualStudioPath.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.EngineLogger.Error("Couldn't automatically determine location of Visual Studio: Failed to launch vswhere.exe");
|
||||
}
|
||||
}
|
||||
|
||||
void FindRider()
|
||||
{
|
||||
_lookedForRider = true;
|
||||
|
||||
Thread thread = new Thread(new => FindRiderImpl);
|
||||
thread.AutoDelete = true;
|
||||
thread.IsBackground = true;
|
||||
thread.Start();
|
||||
}
|
||||
|
||||
void FindRiderImpl()
|
||||
{
|
||||
List<RiderInstallInfo> installs = new .();
|
||||
|
||||
RiderPathLocator.CollectAllPaths(installs);
|
||||
|
||||
// Don't touch the settings or the log from this thread: the editor's log window appends to
|
||||
// a plain List and isn't synchronized, so logging from here corrupts it.
|
||||
Application.Instance.InvokeOnMainThread(new () => ReportRiderInstalls(installs));
|
||||
}
|
||||
|
||||
/// Logs the located Rider installations and stores the newest one in RiderPath.
|
||||
/// @param installs The located installations, newest first. Takes ownership.
|
||||
bool ReportRiderInstalls(List<RiderInstallInfo> installs)
|
||||
{
|
||||
defer { DeleteContainerAndItems!(installs); }
|
||||
|
||||
for (RiderInstallInfo install in installs)
|
||||
{
|
||||
Log.EngineLogger.Info($"Found Rider {install.Version} at \"{install.Path}\" ({install.InstallType})");
|
||||
}
|
||||
|
||||
if (installs.IsEmpty)
|
||||
{
|
||||
Log.EngineLogger.Warning("Couldn't automatically determine location of Rider: No installation found.");
|
||||
return true;
|
||||
}
|
||||
|
||||
// CollectAllPaths sorts the installations, newest first.
|
||||
RiderInstallInfo newestInstall = installs.Front;
|
||||
|
||||
if (RiderPath == null)
|
||||
{
|
||||
RiderPath = new String(newestInstall.Path);
|
||||
}
|
||||
else
|
||||
{
|
||||
RiderPath..Clear().Append(newestInstall.Path);
|
||||
}
|
||||
|
||||
Log.EngineLogger.Info($"Located Rider: {RiderPath}");
|
||||
|
||||
Apply();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
[Reflect]
|
||||
class EditorSettings
|
||||
{
|
||||
[Setting("Editor", "Switch to Player on play", "If checked the editor will automatically switch to the \"Play\" window after starting the game."), BonInclude]
|
||||
public bool SwitchToPlayerOnPlay = true;
|
||||
|
||||
[Setting("Editor", "Switch to Player on simulate", "If checked the editor will automatically switch to the \"Play\" window after starting the simulation."), BonInclude]
|
||||
public bool SwitchToPlayerOnSimulate = true;
|
||||
|
||||
[Setting("Editor", "Switch to Player on continue", "If checked the editor will automatically switch to the \"Play\" window when the game is continued after pausing."), BonInclude]
|
||||
public bool SwitchToPlayerOnResume = false;
|
||||
|
||||
[Setting("Editor", "Switch to Editor on stop", "If checked the editor will automatically switch to the \"Editor\" window after stopping the game."), BonInclude]
|
||||
public bool SwitchToEditorOnStop = true;
|
||||
|
||||
[Setting("Editor", "Switch to Editor on pause", "If checked the editor will automatically switch to the \"Editor\" window when the game is being paused."), BonInclude]
|
||||
public bool SwitchToEditorOnPause = false;
|
||||
|
||||
[Setting("Editor", "Clear log on play", "If checked the message log will be cleared when the play-mode is entered."), BonInclude]
|
||||
public bool ClearLogOnPlay = true;
|
||||
|
||||
[BonInclude]
|
||||
private List<String> _recentProjects ~ DeleteContainerAndItems!(_);
|
||||
|
||||
/// Gets or sets the path of the Project that was last open.
|
||||
public StringView LastOpenedProject
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_recentProjects == null || _recentProjects.Count == 0)
|
||||
return "";
|
||||
|
||||
return _recentProjects[0];
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_recentProjects == null)
|
||||
_recentProjects = new List<String>();
|
||||
|
||||
// Remove duplicates
|
||||
for (var entry in _recentProjects)
|
||||
{
|
||||
if (entry == value)
|
||||
{
|
||||
delete entry;
|
||||
@entry.Remove();
|
||||
}
|
||||
}
|
||||
|
||||
_recentProjects.Insert(0, new String(value));
|
||||
|
||||
if (_recentProjects.Count > 10)
|
||||
{
|
||||
// We only store 10 entries, delete the rest
|
||||
for (int i = 10; i < _recentProjects.Count; i++)
|
||||
{
|
||||
delete _recentProjects[i];
|
||||
}
|
||||
|
||||
_recentProjects.Count = 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> RecentProjects => _recentProjects;
|
||||
|
||||
public void Apply()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ using Bon;
|
||||
using GlitchyEngine;
|
||||
using GlitchyEngine.Scripting;
|
||||
using Xml_Beef;
|
||||
using GlitchyEditor.Settings;
|
||||
|
||||
namespace GlitchyEditor;
|
||||
|
||||
|
||||
@@ -1,146 +0,0 @@
|
||||
using System;
|
||||
using ImGui;
|
||||
using System.Collections;
|
||||
using System.IO;
|
||||
using Bon;
|
||||
using GlitchyEngine;
|
||||
|
||||
namespace GlitchyEditor
|
||||
{
|
||||
interface ISettings
|
||||
{
|
||||
void Apply();
|
||||
}
|
||||
|
||||
/// Fields with this Attribute will be scanned for Settings.
|
||||
[AttributeUsage(.Field, .ReflectAttribute)]
|
||||
struct SettingContainerAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
enum SettingEditor
|
||||
{
|
||||
case Default;
|
||||
case Path;//(bool MultiSelect, bool OpenFolderDialog, StringView Filter);
|
||||
}
|
||||
|
||||
/// Fields with this Attribute will be exposed as settings.
|
||||
[AttributeUsage(.Field, .ReflectAttribute)]
|
||||
struct SettingAttribute : Attribute
|
||||
{
|
||||
public String Category;
|
||||
public String Name;
|
||||
public String Tooltip;
|
||||
public SettingEditor EditorMode;
|
||||
|
||||
public this(String category, String name, String tooltip = "", SettingEditor editorMode = .Default)
|
||||
{
|
||||
Category = category;
|
||||
Name = name;
|
||||
Tooltip = tooltip;
|
||||
EditorMode = editorMode;
|
||||
}
|
||||
}
|
||||
|
||||
[Reflect, BonTarget]
|
||||
class Settings
|
||||
{
|
||||
#if IMGUI
|
||||
[SettingContainer, BonInclude]
|
||||
public readonly ImGuiSettings ImGuiSettings = new .() ~ delete _;
|
||||
#endif
|
||||
[BonIgnore]
|
||||
public Event<EventHandler> OnApplySettings = .() ~ _.Dispose();
|
||||
|
||||
/*
|
||||
[BonInclude]
|
||||
private List<ISettings> _userSettings ~ ClearAndDeleteItems!(_);
|
||||
*/
|
||||
|
||||
public this()
|
||||
{
|
||||
/*List<ISettings> userSettings = append .();
|
||||
_userSettings = userSettings;*/
|
||||
RegisterEventListeners();
|
||||
}
|
||||
|
||||
protected virtual void RegisterEventListeners()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Apply()
|
||||
{
|
||||
#if IMGUI
|
||||
ImGuiSettings.Apply();
|
||||
#endif
|
||||
|
||||
OnApplySettings.Invoke(this, .Empty);
|
||||
|
||||
/*for (let settings in _userSettings)
|
||||
{
|
||||
settings.Apply();
|
||||
}*/
|
||||
}
|
||||
|
||||
public static void Load()
|
||||
{
|
||||
Settings settings = EditorApp.Instance.Settings;
|
||||
|
||||
if (!File.Exists("./settings.bon"))
|
||||
{
|
||||
settings.Save();
|
||||
}
|
||||
|
||||
var result = Bon.DeserializeFromFile(ref settings, "./settings.bon");
|
||||
|
||||
if (result case .Err)
|
||||
Log.EngineLogger.Error("Failed to deserialze settings.");
|
||||
|
||||
settings.Apply();
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
gBonEnv.serializeFlags |= .Verbose;
|
||||
Bon.SerializeIntoFile(this, "./settings.bon");
|
||||
}
|
||||
|
||||
/*
|
||||
/// Registers a instance of a settings interface. Note: Takes ownership of the instance.
|
||||
public void RegisterUserSettings(ISettings settings)
|
||||
{
|
||||
_userSettings.Add(settings);
|
||||
}
|
||||
|
||||
public T GetUserSettings<T>() where T : ISettings, class
|
||||
{
|
||||
for (let v in _userSettings)
|
||||
{
|
||||
if (v is T)
|
||||
{
|
||||
return (T)v;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}*/
|
||||
}
|
||||
|
||||
#if IMGUI
|
||||
[Reflect]
|
||||
class ImGuiSettings
|
||||
{
|
||||
[Setting("UI", "Font Size"), BonInclude]
|
||||
public int32 FontSize = 14;
|
||||
|
||||
[Setting("UI", "Font name"), BonInclude]
|
||||
public readonly String FontName = new .("Fonts/CascadiaCode.ttf") ~ delete _;
|
||||
|
||||
public void Apply()
|
||||
{
|
||||
EditorApp.Instance.[Friend]_imGuiLayer.SettingsInvalid = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
using GlitchyEngine;
|
||||
using System.Collections;
|
||||
using GlitchyEditor.CodeEditors;
|
||||
using System.IO;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using ImGui;
|
||||
using GlitchyEditor.Multithreading;
|
||||
|
||||
namespace GlitchyEditor.Settings;
|
||||
|
||||
|
||||
class DetectIDEsBackgroundTask : BackgroundTask
|
||||
{
|
||||
enum Stage
|
||||
{
|
||||
None,
|
||||
SearchVisualStudio,
|
||||
SearchRider
|
||||
}
|
||||
|
||||
private Stage _stage = .None;
|
||||
|
||||
private ScriptSettings _settings;
|
||||
|
||||
public this(ScriptSettings settings)
|
||||
{
|
||||
_settings = settings;
|
||||
}
|
||||
|
||||
public override RunResult Run()
|
||||
{
|
||||
// Remove all auto detected IDEs, keep manual ones
|
||||
using (_settings.EnterAndGetIdeInstallations(let installations))
|
||||
{
|
||||
for (let ide in installations)
|
||||
{
|
||||
if (ide.IsAutoDetected)
|
||||
{
|
||||
@ide.Remove();
|
||||
delete ide;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if BF_PLATFORM_WINDOWS
|
||||
_stage = .SearchVisualStudio;
|
||||
FindVisualStudio();
|
||||
#endif
|
||||
|
||||
_stage = .SearchRider;
|
||||
FindRider();
|
||||
|
||||
return .Finished;
|
||||
}
|
||||
|
||||
public override void OnRenderPopup()
|
||||
{
|
||||
if (ImGui.Begin("Detecting IDEs...", null, .NoDocking | .NoCollapse | .Modal | .NoResize | .AlwaysAutoResize))
|
||||
{
|
||||
if (_stage == .SearchVisualStudio)
|
||||
{
|
||||
ImGui.Text("Searching Visual Studio installations...");
|
||||
}
|
||||
else if (_stage == .SearchRider)
|
||||
{
|
||||
ImGui.Text("Searching Rider installations...");
|
||||
}
|
||||
|
||||
ImGui.End();
|
||||
}
|
||||
}
|
||||
|
||||
#if BF_PLATFORM_WINDOWS
|
||||
void FindVisualStudio()
|
||||
{
|
||||
Log.EngineLogger.Info("Using vswhere.exe to search for Visual Studio...");
|
||||
|
||||
String exeFile = scope .();
|
||||
Environment.GetExecutableFilePath(exeFile);
|
||||
|
||||
String exeDirectory = scope .();
|
||||
Path.GetDirectoryPath(exeFile, exeDirectory);
|
||||
|
||||
String vsWherePath = scope .();
|
||||
Path.Combine(vsWherePath, exeDirectory, "vswhere.exe");
|
||||
|
||||
ProcessStartInfo processInfo = scope .();
|
||||
processInfo.UseShellExecute = false;
|
||||
processInfo.RedirectStandardOutput = true;
|
||||
processInfo.CreateNoWindow = true;
|
||||
|
||||
processInfo.SetFileName(vsWherePath);
|
||||
// Find the installation path of the latest visual studio with an installed IDE.
|
||||
processInfo.SetArguments("-sort -requires Microsoft.VisualStudio.Component.Roslyn.LanguageServices -format text");
|
||||
|
||||
let process = scope SpawnedProcess();
|
||||
if (process.Start(processInfo) case .Ok)
|
||||
{
|
||||
FileStream outputStream = scope FileStream();
|
||||
process.AttachStandardOutput(outputStream);
|
||||
|
||||
process.WaitFor(1000);
|
||||
|
||||
StreamReader sr = scope .(outputStream);
|
||||
|
||||
IdeInstallation installation = null;
|
||||
|
||||
String line = scope .(256);
|
||||
while (sr.ReadLine(line..Clear()) case .Ok)
|
||||
{
|
||||
const String InstanceIdPropertyName = "instanceId: ";
|
||||
const String ProductPathPropertyName = "productPath: ";
|
||||
const String DisplayNamePropertyName = "displayName: ";
|
||||
|
||||
if (line.StartsWith(InstanceIdPropertyName))
|
||||
{
|
||||
if (installation != null)
|
||||
{
|
||||
using (_settings.EnterAndGetIdeInstallations(let installations))
|
||||
{
|
||||
installations.Add(installation);
|
||||
}
|
||||
}
|
||||
|
||||
installation = new IdeInstallation();
|
||||
installation.Ide = .VisualStudio;
|
||||
installation.IsAutoDetected = true;
|
||||
}
|
||||
else if (line.StartsWith(ProductPathPropertyName))
|
||||
{
|
||||
installation.Path = line.Substring(ProductPathPropertyName.Length);
|
||||
}
|
||||
else if (line.StartsWith(DisplayNamePropertyName))
|
||||
{
|
||||
installation.Name = line.Substring(DisplayNamePropertyName.Length);
|
||||
}
|
||||
}
|
||||
|
||||
if (!installation.Name.IsEmpty && !installation.Path.IsEmpty)
|
||||
{
|
||||
using (_settings.EnterAndGetIdeInstallations(let installations))
|
||||
{
|
||||
installations.Add(installation);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
delete installation;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.EngineLogger.Error("Couldn't automatically determine location of Visual Studio: Failed to launch vswhere.exe");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private void FindRider()
|
||||
{
|
||||
List<RiderInstallInfo> installs = scope .();
|
||||
|
||||
RiderPathLocator.CollectAllPaths(installs);
|
||||
|
||||
ReportRiderInstalls(installs);
|
||||
|
||||
ClearAndDeleteItems!(installs);
|
||||
}
|
||||
|
||||
private void ReportRiderInstalls(List<RiderInstallInfo> installs)
|
||||
{
|
||||
if (installs.IsEmpty)
|
||||
{
|
||||
Log.EngineLogger.Warning("Couldn't automatically determine location of Rider: No installation found.");
|
||||
}
|
||||
|
||||
using (_settings.EnterAndGetIdeInstallations(let installations))
|
||||
{
|
||||
for (RiderInstallInfo install in installs)
|
||||
{
|
||||
Log.EngineLogger.Info($"Found Rider {install.Version} at \"{install.Path}\" ({install.InstallType})");
|
||||
IdeInstallation ide = new IdeInstallation();
|
||||
ide.IsAutoDetected = true;
|
||||
ide.Ide = .Rider;
|
||||
ide.Path = install.Path;
|
||||
ide.Name = scope $"Rider {install.Version}";
|
||||
|
||||
installations.Add(ide);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,386 @@
|
||||
using System;
|
||||
using ImGui;
|
||||
using System.Collections;
|
||||
using System.IO;
|
||||
using Bon;
|
||||
using GlitchyEngine;
|
||||
using System.Threading;
|
||||
using System.Linq;
|
||||
|
||||
using static GlitchyEditor.SettingsWindow;
|
||||
|
||||
namespace GlitchyEditor.Settings;
|
||||
|
||||
interface ISettings
|
||||
{
|
||||
void Apply();
|
||||
}
|
||||
|
||||
function void RenderMethod(SettingsWindow.Category category);
|
||||
function void FieldRenderMethod(SettingsWindow.Binding setting);
|
||||
|
||||
/// Fields with this Attribute will be scanned for Settings.
|
||||
[AttributeUsage(.Field, .ReflectAttribute)]
|
||||
struct SettingContainerAttribute : Attribute
|
||||
{
|
||||
public RenderMethod RenderMethod;
|
||||
}
|
||||
|
||||
enum SettingEditor
|
||||
{
|
||||
case Default;
|
||||
case Path;//(bool MultiSelect, bool OpenFolderDialog, StringView Filter);
|
||||
}
|
||||
|
||||
/// Fields with this Attribute will be exposed as settings.
|
||||
[AttributeUsage(.Field, .ReflectAttribute)]
|
||||
struct SettingAttribute : Attribute
|
||||
{
|
||||
public String Category;
|
||||
public String Name;
|
||||
public String Tooltip;
|
||||
public SettingEditor EditorMode;
|
||||
|
||||
public this(String category, String name, String tooltip = "", SettingEditor editorMode = .Default)
|
||||
{
|
||||
Category = category;
|
||||
Name = name;
|
||||
Tooltip = tooltip;
|
||||
EditorMode = editorMode;
|
||||
}
|
||||
}
|
||||
|
||||
/// Fields with this Attribute will be exposed as settings.
|
||||
[AttributeUsage(.Method, .ReflectAttribute | .AlwaysIncludeTarget)]
|
||||
struct CustomSettingRendererAttribute : Attribute
|
||||
{
|
||||
public String Category;
|
||||
|
||||
public this(String category)
|
||||
{
|
||||
Category = category;
|
||||
}
|
||||
}
|
||||
|
||||
[Reflect, BonTarget]
|
||||
class Settings
|
||||
{
|
||||
private bool _areFromDisk = false;
|
||||
|
||||
public bool AreFromDisk => _areFromDisk;
|
||||
|
||||
#if IMGUI
|
||||
[SettingContainer, BonInclude]
|
||||
public readonly ImGuiSettings ImGuiSettings = new .() ~ delete _;
|
||||
#endif
|
||||
[BonIgnore]
|
||||
public Event<EventHandler> OnApplySettings = .() ~ _.Dispose();
|
||||
|
||||
#if DEBUG
|
||||
[SettingContainer, BonInclude]
|
||||
public readonly DevSettings DevSettings = new .() ~ delete _;
|
||||
#endif
|
||||
|
||||
[SettingContainer, BonInclude]
|
||||
public readonly EditorSettings EditorSettings = new .() ~ delete _;
|
||||
|
||||
[SettingContainer, BonInclude]
|
||||
public readonly ScriptSettings ScriptSettings = new .() ~ delete _;
|
||||
|
||||
/*
|
||||
[BonInclude]
|
||||
private List<ISettings> _userSettings ~ ClearAndDeleteItems!(_);
|
||||
*/
|
||||
|
||||
public this()
|
||||
{
|
||||
/*List<ISettings> userSettings = append .();
|
||||
_userSettings = userSettings;*/
|
||||
RegisterEventListeners();
|
||||
}
|
||||
|
||||
protected virtual void RegisterEventListeners()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Apply()
|
||||
{
|
||||
#if IMGUI
|
||||
ImGuiSettings.Apply();
|
||||
#endif
|
||||
|
||||
#if DEBUG
|
||||
DevSettings.Apply();
|
||||
#endif
|
||||
EditorSettings.Apply();
|
||||
ScriptSettings.Apply();
|
||||
|
||||
OnApplySettings.Invoke(this, .Empty);
|
||||
|
||||
/*for (let settings in _userSettings)
|
||||
{
|
||||
settings.Apply();
|
||||
}*/
|
||||
}
|
||||
|
||||
public static void Load()
|
||||
{
|
||||
Settings settings = EditorApp.Instance.Settings;
|
||||
|
||||
if (!File.Exists("./settings.bon"))
|
||||
{
|
||||
settings.Save();
|
||||
}
|
||||
|
||||
var result = Bon.DeserializeFromFile(ref settings, "./settings.bon");
|
||||
|
||||
settings._areFromDisk = true;
|
||||
|
||||
if (result case .Err)
|
||||
Log.EngineLogger.Error("Failed to deserialze settings.");
|
||||
|
||||
settings.Apply();
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
gBonEnv.serializeFlags |= .Verbose;
|
||||
Bon.SerializeIntoFile(this, "./settings.bon");
|
||||
}
|
||||
|
||||
/*
|
||||
/// Registers a instance of a settings interface. Note: Takes ownership of the instance.
|
||||
public void RegisterUserSettings(ISettings settings)
|
||||
{
|
||||
_userSettings.Add(settings);
|
||||
}
|
||||
|
||||
public T GetUserSettings<T>() where T : ISettings, class
|
||||
{
|
||||
for (let v in _userSettings)
|
||||
{
|
||||
if (v is T)
|
||||
{
|
||||
return (T)v;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}*/
|
||||
}
|
||||
|
||||
#if IMGUI
|
||||
[Reflect]
|
||||
class ImGuiSettings
|
||||
{
|
||||
[Setting("UI", "Font Size"), BonInclude]
|
||||
public int32 FontSize = 14;
|
||||
|
||||
[Setting("UI", "Font name"), BonInclude]
|
||||
public readonly String FontName = new .("Fonts/CascadiaCode.ttf") ~ delete _;
|
||||
|
||||
public void Apply()
|
||||
{
|
||||
EditorApp.Instance.[Friend]_imGuiLayer?.SettingsInvalid = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if DEBUG
|
||||
[Reflect]
|
||||
class DevSettings
|
||||
{
|
||||
[Setting("Dev", "Use ScriptCore csproj", "If enabled the Editor will include the csproj of the ScriptCore instead of the compiled dll."), BonInclude]
|
||||
public bool UseScriptCoreDll = true;
|
||||
|
||||
public void Apply()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
[Reflect]
|
||||
enum ScriptIde
|
||||
{
|
||||
Rider,
|
||||
VisualStudio
|
||||
}
|
||||
|
||||
[Reflect]
|
||||
class IdeInstallation
|
||||
{
|
||||
[BonInclude]
|
||||
public ScriptIde Ide;
|
||||
[BonInclude]
|
||||
private String _path ~ delete _;
|
||||
[BonInclude]
|
||||
private String _name ~ delete _;
|
||||
|
||||
public StringView Path
|
||||
{
|
||||
get => _path;
|
||||
set => String.NewOrSet!(_path, value);
|
||||
}
|
||||
|
||||
public StringView Name
|
||||
{
|
||||
get => _name;
|
||||
set => String.NewOrSet!(_name, value);
|
||||
}
|
||||
|
||||
[BonInclude]
|
||||
public bool IsAutoDetected;
|
||||
// TODO: allow custom IDEs? We then need something like "'Open Project' command line", "'Open file' command line" and "'Open file at location' command line"
|
||||
// This would however be quite complicated to implement well, when looking at how different Visual Studio and Rider already are.
|
||||
}
|
||||
|
||||
[Reflect]
|
||||
class ScriptSettings
|
||||
{
|
||||
[BonInclude]
|
||||
private List<IdeInstallation> _ideInstallations = new .() ~ DeleteContainerAndItems!(_);
|
||||
|
||||
// TODO: It's probably overkill to have a lock for the ide list.
|
||||
// If we cared enough about multi threading, InvokeOnMainThread would probably be enough.
|
||||
private Monitor _ideInstallationsLock = new .() ~ delete _;
|
||||
|
||||
[BonInclude]
|
||||
private String _activeIdePath = new String() ~ delete _;
|
||||
|
||||
public IdeInstallation ActiveIde => _ideInstallations.Where((i) => i.Path == _activeIdePath).FirstOrDefault();
|
||||
|
||||
private bool _lookedForVs = false;
|
||||
private bool _lookedForRider = false;
|
||||
|
||||
/// Enters the lock and gets the list of Ide installations.
|
||||
public Monitor.MonitorLockInstance EnterAndGetIdeInstallations(out List<IdeInstallation> ideInstallations)
|
||||
{
|
||||
let lock = _ideInstallationsLock.Enter();
|
||||
ideInstallations = _ideInstallations;
|
||||
return lock;
|
||||
}
|
||||
|
||||
public void Apply()
|
||||
{
|
||||
if (ActiveIde == null)
|
||||
{
|
||||
using (EnterAndGetIdeInstallations(let ideInstallations))
|
||||
{
|
||||
// If no IDE is selected, just select the first one
|
||||
if (ideInstallations?.Count > 0)
|
||||
{
|
||||
_activeIdePath.Set(ideInstallations.Front.Path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[CustomSettingRenderer("Tools")]
|
||||
public bool RenderUI(SettingsWindow.Category category)
|
||||
{
|
||||
bool settingsChanged = false;
|
||||
|
||||
ImGui.Text("IDE:");
|
||||
|
||||
if (ImGui.BeginCombo("##ide", ActiveIde?.Name.ToScopeCStr!()))
|
||||
{
|
||||
using (EnterAndGetIdeInstallations(let installations))
|
||||
{
|
||||
for (let ide in installations)
|
||||
{
|
||||
if (ImGui.Selectable(ide.Name.ToScopeCStr!()) && ActiveIde != ide)
|
||||
{
|
||||
_activeIdePath.Set(ide.Path);
|
||||
settingsChanged = true;
|
||||
}
|
||||
|
||||
ImGui.AttachTooltip(ide.Path);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.EndCombo();
|
||||
}
|
||||
|
||||
if (ImGui.Button("Detect IDEs..."))
|
||||
{
|
||||
//BackgroundTask task = new BackgroundTask();
|
||||
|
||||
EditorApp.Instance.BackgroundTaskManager.StartBackgroundTask(new DetectIDEsBackgroundTask(this));
|
||||
}
|
||||
|
||||
return settingsChanged;
|
||||
}
|
||||
}
|
||||
|
||||
[Reflect]
|
||||
class EditorSettings
|
||||
{
|
||||
[Setting("Editor", "Switch to Player on play", "If checked the editor will automatically switch to the \"Play\" window after starting the game."), BonInclude]
|
||||
public bool SwitchToPlayerOnPlay = true;
|
||||
|
||||
[Setting("Editor", "Switch to Player on simulate", "If checked the editor will automatically switch to the \"Play\" window after starting the simulation."), BonInclude]
|
||||
public bool SwitchToPlayerOnSimulate = true;
|
||||
|
||||
[Setting("Editor", "Switch to Player on continue", "If checked the editor will automatically switch to the \"Play\" window when the game is continued after pausing."), BonInclude]
|
||||
public bool SwitchToPlayerOnResume = false;
|
||||
|
||||
[Setting("Editor", "Switch to Editor on stop", "If checked the editor will automatically switch to the \"Editor\" window after stopping the game."), BonInclude]
|
||||
public bool SwitchToEditorOnStop = true;
|
||||
|
||||
[Setting("Editor", "Switch to Editor on pause", "If checked the editor will automatically switch to the \"Editor\" window when the game is being paused."), BonInclude]
|
||||
public bool SwitchToEditorOnPause = false;
|
||||
|
||||
[Setting("Editor", "Clear log on play", "If checked the message log will be cleared when the play-mode is entered."), BonInclude]
|
||||
public bool ClearLogOnPlay = true;
|
||||
|
||||
[BonInclude]
|
||||
private List<String> _recentProjects ~ DeleteContainerAndItems!(_);
|
||||
|
||||
/// Gets or sets the path of the Project that was last open.
|
||||
public StringView LastOpenedProject
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_recentProjects == null || _recentProjects.Count == 0)
|
||||
return "";
|
||||
|
||||
return _recentProjects[0];
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_recentProjects == null)
|
||||
_recentProjects = new List<String>();
|
||||
|
||||
// Remove duplicates
|
||||
for (var entry in _recentProjects)
|
||||
{
|
||||
if (entry == value)
|
||||
{
|
||||
delete entry;
|
||||
@entry.Remove();
|
||||
}
|
||||
}
|
||||
|
||||
_recentProjects.Insert(0, new String(value));
|
||||
|
||||
if (_recentProjects.Count > 10)
|
||||
{
|
||||
// We only store 10 entries, delete the rest
|
||||
for (int i = 10; i < _recentProjects.Count; i++)
|
||||
{
|
||||
delete _recentProjects[i];
|
||||
}
|
||||
|
||||
_recentProjects.Count = 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> RecentProjects => _recentProjects;
|
||||
|
||||
public void Apply()
|
||||
{
|
||||
}
|
||||
}
|
||||
+55
-91
@@ -6,12 +6,13 @@ using System.Collections;
|
||||
using System.Reflection;
|
||||
using GlitchyEngine.Math;
|
||||
using System.IO;
|
||||
using GlitchyEditor.Settings;
|
||||
|
||||
namespace GlitchyEditor
|
||||
{
|
||||
class SettingsWindow : EditorWindow
|
||||
{
|
||||
class Binding
|
||||
public class Binding
|
||||
{
|
||||
public Object SettingsObject;
|
||||
public String Name ~ delete _;
|
||||
@@ -29,9 +30,22 @@ namespace GlitchyEditor
|
||||
}
|
||||
}
|
||||
|
||||
class Category
|
||||
public struct MethodBinding
|
||||
{
|
||||
public Object SettingsObject;
|
||||
public MethodInfo Method;
|
||||
|
||||
public this(MethodInfo method, Object settingsObject)
|
||||
{
|
||||
Method = method;
|
||||
SettingsObject = settingsObject;
|
||||
}
|
||||
}
|
||||
|
||||
public class Category
|
||||
{
|
||||
public List<Binding> _bindings = new .() ~ DeleteContainerAndItems!(_);
|
||||
public List<MethodBinding> _methodBindings = new .() ~ delete _;
|
||||
|
||||
public String Header ~ delete _;
|
||||
|
||||
@@ -47,6 +61,11 @@ namespace GlitchyEditor
|
||||
Binding binding = new .(name, fieldName, settingsObject ?? SettingsObject, tooltip, editorType);
|
||||
_bindings.Add(binding);
|
||||
}
|
||||
|
||||
public void AddRenderer(MethodInfo method, Object settingsObject = null)
|
||||
{
|
||||
_methodBindings.Add(MethodBinding(method, settingsObject));
|
||||
}
|
||||
}
|
||||
|
||||
Settings _settings;
|
||||
@@ -87,6 +106,8 @@ namespace GlitchyEditor
|
||||
|
||||
void ScanForSettings(Object container)
|
||||
{
|
||||
Log.EngineLogger.Info($"Visiting type: {container.GetType().GetName(.. scope .())}");
|
||||
|
||||
Type type = container.GetType();
|
||||
|
||||
for (var field in type.GetFields())
|
||||
@@ -114,6 +135,18 @@ namespace GlitchyEditor
|
||||
ScanForSettings(childContainer);
|
||||
}
|
||||
}
|
||||
|
||||
for (let method in type.GetMethods(.Instance | .Static | .Public | .NonPublic))
|
||||
{
|
||||
Log.EngineLogger.Info($"Visiting method: {method.Name}");
|
||||
|
||||
Result<CustomSettingRendererAttribute> rendererResult = method.GetCustomAttribute<CustomSettingRendererAttribute>();
|
||||
|
||||
if (rendererResult case .Ok(let rendererInfo))
|
||||
{
|
||||
AddRenderer(rendererInfo.Category, method, container);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AddSetting(String categoryName, String name, StringView fieldName, Object container, StringView tooltip, SettingEditor settingEditor)
|
||||
@@ -123,6 +156,12 @@ namespace GlitchyEditor
|
||||
category.AddSetting(name, fieldName, container, tooltip, settingEditor);
|
||||
}
|
||||
|
||||
void AddRenderer(String categoryName, MethodInfo method, Object container)
|
||||
{
|
||||
Category category = AddCategory(categoryName);
|
||||
category.AddRenderer(method, container);
|
||||
}
|
||||
|
||||
private String _tabToSelect = new String() ~ delete _;
|
||||
private String _SettingToHighlight = new String() ~ delete _;
|
||||
private float _timeToHighlight;
|
||||
@@ -157,6 +196,7 @@ namespace GlitchyEditor
|
||||
if (ImGui.BeginTabItem(category.Header, null, flags))
|
||||
{
|
||||
ShowCategory(category);
|
||||
|
||||
ImGui.EndTabItem();
|
||||
}
|
||||
}
|
||||
@@ -209,6 +249,12 @@ namespace GlitchyEditor
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
|
||||
/*if (setting.EditorType case .Custom(let renderMethod))
|
||||
{
|
||||
renderMethod(setting);
|
||||
continue;
|
||||
}*/
|
||||
|
||||
// Name of setting
|
||||
ImGui.TextUnformatted(setting.Name);
|
||||
|
||||
@@ -370,98 +416,16 @@ namespace GlitchyEditor
|
||||
_SettingToHighlight.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*if (fieldInfo.FieldType.IsEnum)
|
||||
for (let methodBinding in category._methodBindings)
|
||||
{
|
||||
if (methodBinding.Method.Invoke(methodBinding.Method.IsStatic ? null : methodBinding.SettingsObject, category) case .Ok(var returnValue))
|
||||
{
|
||||
uint64 enumValue = 0;
|
||||
_settingsChanged |= returnValue.Get<bool>();
|
||||
|
||||
fieldInfo.GetValueReference(setting.SettingsObject);
|
||||
|
||||
// Assign value
|
||||
switch (fieldInfo.FieldType.Size)
|
||||
{
|
||||
case 1: enumValue = *(uint8*)&enumValue;
|
||||
case 2: *(uint16*)&enumValue = *(uint16*)&enumValue;
|
||||
case 4: *(uint32*)&enumValue = *(uint32*)&enumValue;
|
||||
case 8: *(uint64*)&enumValue = *(uint64*)&enumValue;
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
for (var field in valType.GetFields())
|
||||
{
|
||||
if (field.[Friend]mFieldData.mFlags.HasFlag(.EnumCase) &&
|
||||
*(int64*)&field.[Friend]mFieldData.[Friend]mData == valueData)
|
||||
{
|
||||
writer.Enum(field.Name);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Find field on enum
|
||||
bool found = false;
|
||||
for (var field in valType.GetFields())
|
||||
if (field.[Friend]mFieldData.mFlags.HasFlag(.EnumCase)
|
||||
&& name == field.Name)
|
||||
{
|
||||
// Add value of enum case to current enum value
|
||||
enumValue |= *(int64*)&field.[Friend]mFieldData.[Friend]mData;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
Error!("Enum case not found", reader, valType);
|
||||
|
||||
|
||||
uint64 value = 0;
|
||||
StringView selectedValue;
|
||||
|
||||
for (FieldInfo enumField in fieldInfo.FieldType.GetFields())
|
||||
{
|
||||
if (enumField.[Friend]mFieldData.mFlags.HasFlag(.EnumCase))
|
||||
{
|
||||
hasCaseData = true;
|
||||
|
||||
if (name == enumField.Name)
|
||||
{
|
||||
unionPayload = ValueView(enumField.FieldType, val.dataPtr);
|
||||
|
||||
foundCase = true;
|
||||
break;
|
||||
}
|
||||
|
||||
unionDiscrIndex++;
|
||||
}
|
||||
else if (enumField.[Friend]mFieldData.mFlags.HasFlag(.EnumDiscriminator))
|
||||
{
|
||||
let discrType = enumField.FieldType;
|
||||
Debug.Assert(discrType.IsInteger);
|
||||
discrVal = ValueView(discrType, (uint8*)val.dataPtr + enumField.[Friend]mFieldData.mData);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.BeginCombo(scope $"##{setting.Name}", null);
|
||||
|
||||
for (var v in Enum.GetValues(fieldInfo.FieldType))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ImGui.EndCombo();
|
||||
|
||||
/*// Find field on enum
|
||||
bool found = false;
|
||||
for (var field in valType.GetFields())
|
||||
if (field.[Friend]mFieldData.mFlags.HasFlag(.EnumCase)
|
||||
&& name == field.Name)
|
||||
{
|
||||
// Add value of enum case to current enum value
|
||||
enumValue |= *(int64*)&field.[Friend]mFieldData.[Friend]mData;
|
||||
found = true;
|
||||
break;
|
||||
}*/
|
||||
}*/
|
||||
returnValue.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.EndTable();
|
||||
Reference in New Issue
Block a user