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
|
interface IIdeAdapter
|
||||||
{
|
{
|
||||||
static void OpenScript(StringView fileName);
|
void OpenScript(StringView fileName, int lineNumber = 0);
|
||||||
|
|
||||||
static void OpenScript(StringView fileName, int lineNumber);
|
void OpenScriptProject();
|
||||||
|
|
||||||
static void OpenScriptProject();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,29 +6,34 @@ using System.IO;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using GlitchyEditor.EditWindows;
|
using GlitchyEditor.EditWindows;
|
||||||
using ImGui;
|
using ImGui;
|
||||||
|
using GlitchyEditor.Settings;
|
||||||
|
|
||||||
namespace GlitchyEditor.CodeEditors;
|
namespace GlitchyEditor.CodeEditors;
|
||||||
|
|
||||||
class RiderIdeAdapter : IIdeAdapter
|
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 .();
|
String solutionPath = scope .();
|
||||||
Editor.Instance.CurrentProject.GetPathToScriptSolutionFile(solutionPath);
|
Editor.Instance.CurrentProject.GetPathToScriptSolutionFile(solutionPath);
|
||||||
|
|
||||||
ProcessStartInfo startInfo = scope .();
|
ProcessStartInfo startInfo = scope .();
|
||||||
startInfo.SetFileName(EditorApp.Instance.Settings.ScriptSettings.RiderPath);
|
startInfo.SetFileName(_ideInstallation.Path);
|
||||||
startInfo.SetArguments(scope $"{solutionPath} --line {lineNumber} {fileName}");
|
startInfo.SetArguments(scope $"{solutionPath} --line {lineNumber} {fileName}");
|
||||||
|
|
||||||
scope SpawnedProcess().Start(startInfo);
|
scope SpawnedProcess().Start(startInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OpenScriptProject()
|
public void OpenScriptProject()
|
||||||
{
|
{
|
||||||
String solutionPath = scope .();
|
String solutionPath = scope .();
|
||||||
Editor.Instance.CurrentProject.GetPathToScriptSolutionFile(solutionPath);
|
Editor.Instance.CurrentProject.GetPathToScriptSolutionFile(solutionPath);
|
||||||
@@ -39,7 +44,7 @@ class RiderIdeAdapter : IIdeAdapter
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!File.Exists(EditorApp.Instance.Settings.ScriptSettings.RiderPath))
|
if (!File.Exists(_ideInstallation.Path))
|
||||||
{
|
{
|
||||||
Editor.Instance.ShowSettings();
|
Editor.Instance.ShowSettings();
|
||||||
Editor.Instance.SettingsWindow.HighlightSetting("Tools", "Rider path");
|
Editor.Instance.SettingsWindow.HighlightSetting("Tools", "Rider path");
|
||||||
@@ -51,7 +56,7 @@ class RiderIdeAdapter : IIdeAdapter
|
|||||||
}
|
}
|
||||||
|
|
||||||
ProcessStartInfo startInfo = scope .();
|
ProcessStartInfo startInfo = scope .();
|
||||||
startInfo.SetFileName(EditorApp.Instance.Settings.ScriptSettings.RiderPath);
|
startInfo.SetFileName(_ideInstallation.Path);
|
||||||
startInfo.SetArguments(solutionPath);
|
startInfo.SetArguments(solutionPath);
|
||||||
|
|
||||||
scope SpawnedProcess().Start(startInfo);
|
scope SpawnedProcess().Start(startInfo);
|
||||||
|
|||||||
@@ -2,12 +2,22 @@ using System;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using GlitchyEngine;
|
using GlitchyEngine;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
|
using GlitchyEditor.Settings;
|
||||||
|
|
||||||
namespace GlitchyEditor.CodeEditors;
|
namespace GlitchyEditor.CodeEditors;
|
||||||
|
|
||||||
class VisualStudioIdeAdapter : IIdeAdapter
|
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 .();
|
List<Process> processes = scope .();
|
||||||
|
|
||||||
@@ -19,6 +29,7 @@ class VisualStudioIdeAdapter : IIdeAdapter
|
|||||||
|
|
||||||
for (Process process in processes)
|
for (Process process in processes)
|
||||||
{
|
{
|
||||||
|
// TODO?
|
||||||
}
|
}
|
||||||
|
|
||||||
ClearAndDeleteItems!(processes);
|
ClearAndDeleteItems!(processes);
|
||||||
@@ -26,12 +37,12 @@ class VisualStudioIdeAdapter : IIdeAdapter
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OpenScript(StringView fileName)
|
public void OpenScript(StringView fileName, int lineNumber)
|
||||||
{
|
{
|
||||||
if (IsRunning())
|
if (IsRunning())
|
||||||
{
|
{
|
||||||
ProcessStartInfo startInfo = scope .();
|
ProcessStartInfo startInfo = scope .();
|
||||||
startInfo.SetFileName(EditorApp.Instance.Settings.ScriptSettings.VisualStudioPath);
|
startInfo.SetFileName(_ideInstallation.Path);
|
||||||
startInfo.SetArguments(scope $"/Edit {fileName}");
|
startInfo.SetArguments(scope $"/Edit {fileName}");
|
||||||
|
|
||||||
scope SpawnedProcess().Start(startInfo);
|
scope SpawnedProcess().Start(startInfo);
|
||||||
@@ -39,25 +50,20 @@ class VisualStudioIdeAdapter : IIdeAdapter
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
ProcessStartInfo startInfo = scope .();
|
ProcessStartInfo startInfo = scope .();
|
||||||
startInfo.SetFileName(EditorApp.Instance.Settings.ScriptSettings.VisualStudioPath);
|
startInfo.SetFileName(_ideInstallation.Path);
|
||||||
startInfo.SetArguments(scope $"/Edit {fileName}");
|
startInfo.SetArguments(scope $"/Edit {fileName}");
|
||||||
|
|
||||||
scope SpawnedProcess().Start(startInfo);
|
scope SpawnedProcess().Start(startInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OpenScript(StringView fileName, int lineNumber)
|
public void OpenScriptProject()
|
||||||
{
|
|
||||||
OpenScript(fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void OpenScriptProject()
|
|
||||||
{
|
{
|
||||||
String solutionPath = scope .();
|
String solutionPath = scope .();
|
||||||
Editor.Instance.CurrentProject.GetPathToScriptSolutionFile(solutionPath);
|
Editor.Instance.CurrentProject.GetPathToScriptSolutionFile(solutionPath);
|
||||||
|
|
||||||
ProcessStartInfo psi = scope .();
|
ProcessStartInfo psi = scope .();
|
||||||
psi.SetFileName("devenv");
|
psi.SetFileName(_ideInstallation.Path);
|
||||||
psi.SetArguments(solutionPath);
|
psi.SetArguments(solutionPath);
|
||||||
|
|
||||||
scope SpawnedProcess().Start(psi);
|
scope SpawnedProcess().Start(psi);
|
||||||
|
|||||||
@@ -604,13 +604,7 @@ namespace GlitchyEditor.EditWindows
|
|||||||
|
|
||||||
if (ImGui.MenuItem("Open C# Project..."))
|
if (ImGui.MenuItem("Open C# Project..."))
|
||||||
{
|
{
|
||||||
switch (EditorApp.Instance.Settings.ScriptSettings.SelectedIde)
|
Editor.Instance.IdeAdapter.OpenScriptProject();
|
||||||
{
|
|
||||||
case .VisualStudio:
|
|
||||||
VisualStudioIdeAdapter.OpenScriptProject();
|
|
||||||
case .Rider:
|
|
||||||
RiderIdeAdapter.OpenScriptProject();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.AttachTooltip("Opens the C# Solution of this project.");
|
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.
|
// Special treatment for scripts, open them in Visual Studio.
|
||||||
if (entry->Path.EndsWith(".cs"))
|
if (entry->Path.EndsWith(".cs"))
|
||||||
{
|
{
|
||||||
// Obviously windows only
|
Editor.Instance.IdeAdapter.OpenScript(entry->Path);
|
||||||
RiderIdeAdapter.OpenScript(entry->Path);
|
|
||||||
}
|
}
|
||||||
else if (Path.OpenFolder(entry->Path) case .Err)
|
else if (Path.OpenFolder(entry->Path) case .Err)
|
||||||
Log.EngineLogger.Error("Failed to open file.");
|
Log.EngineLogger.Error("Failed to open file.");
|
||||||
|
|||||||
@@ -317,7 +317,7 @@ class LogWindow : EditorWindow
|
|||||||
{
|
{
|
||||||
if (message.Source.MessageOrigin != null)
|
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 GlitchyEditor.EditWindows;
|
||||||
using GlitchyEngine;
|
using GlitchyEngine;
|
||||||
using GlitchyEditor.Assets;
|
using GlitchyEditor.Assets;
|
||||||
|
using GlitchyEditor.CodeEditors;
|
||||||
|
|
||||||
namespace GlitchyEditor
|
namespace GlitchyEditor
|
||||||
{
|
{
|
||||||
@@ -79,6 +80,18 @@ namespace GlitchyEditor
|
|||||||
|
|
||||||
private static Editor s_Instance;
|
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;
|
public static Editor Instance => s_Instance;
|
||||||
|
|
||||||
/// Creates a new editor for the given world
|
/// 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.");
|
Log.EngineLogger.AssertDebug(s_Instance == null, "Cannot create a second instance of a singleton.");
|
||||||
s_Instance = this;
|
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;
|
_activeScene = activeScene;
|
||||||
_editorScene = editorScene;
|
_editorScene = editorScene;
|
||||||
_contentManager = contentManager;
|
_contentManager = contentManager;
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ using GlitchyEditor.Platform;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using GlitchyEditor.ImGui;
|
using GlitchyEditor.ImGui;
|
||||||
using GlitchyEngine.Events;
|
using GlitchyEngine.Events;
|
||||||
|
using GlitchyEditor.Settings;
|
||||||
|
|
||||||
namespace GlitchyEditor
|
namespace GlitchyEditor
|
||||||
{
|
{
|
||||||
@@ -48,6 +49,8 @@ namespace GlitchyEditor
|
|||||||
|
|
||||||
DragDropManager.Init();
|
DragDropManager.Init();
|
||||||
|
|
||||||
|
GlitchyEditor.Settings.Settings.Load();
|
||||||
|
|
||||||
#if IMGUI
|
#if IMGUI
|
||||||
_imGuiLayer = new ImGuiLayer();
|
_imGuiLayer = new ImGuiLayer();
|
||||||
PushOverlay(_imGuiLayer);
|
PushOverlay(_imGuiLayer);
|
||||||
@@ -56,7 +59,6 @@ namespace GlitchyEditor
|
|||||||
_editorLayer = new EditorLayer(args, _contentManager);
|
_editorLayer = new EditorLayer(args, _contentManager);
|
||||||
PushLayer(_editorLayer);
|
PushLayer(_editorLayer);
|
||||||
|
|
||||||
GlitchyEditor.Settings.Load();
|
|
||||||
Settings.OnApplySettings.Add(new (s, e) => {
|
Settings.OnApplySettings.Add(new (s, e) => {
|
||||||
OnEvent(scope SettingsAppliedEvent());
|
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;
|
||||||
using GlitchyEngine.Scripting;
|
using GlitchyEngine.Scripting;
|
||||||
using Xml_Beef;
|
using Xml_Beef;
|
||||||
|
using GlitchyEditor.Settings;
|
||||||
|
|
||||||
namespace GlitchyEditor;
|
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 System.Reflection;
|
||||||
using GlitchyEngine.Math;
|
using GlitchyEngine.Math;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using GlitchyEditor.Settings;
|
||||||
|
|
||||||
namespace GlitchyEditor
|
namespace GlitchyEditor
|
||||||
{
|
{
|
||||||
class SettingsWindow : EditorWindow
|
class SettingsWindow : EditorWindow
|
||||||
{
|
{
|
||||||
class Binding
|
public class Binding
|
||||||
{
|
{
|
||||||
public Object SettingsObject;
|
public Object SettingsObject;
|
||||||
public String Name ~ delete _;
|
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<Binding> _bindings = new .() ~ DeleteContainerAndItems!(_);
|
||||||
|
public List<MethodBinding> _methodBindings = new .() ~ delete _;
|
||||||
|
|
||||||
public String Header ~ delete _;
|
public String Header ~ delete _;
|
||||||
|
|
||||||
@@ -47,6 +61,11 @@ namespace GlitchyEditor
|
|||||||
Binding binding = new .(name, fieldName, settingsObject ?? SettingsObject, tooltip, editorType);
|
Binding binding = new .(name, fieldName, settingsObject ?? SettingsObject, tooltip, editorType);
|
||||||
_bindings.Add(binding);
|
_bindings.Add(binding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void AddRenderer(MethodInfo method, Object settingsObject = null)
|
||||||
|
{
|
||||||
|
_methodBindings.Add(MethodBinding(method, settingsObject));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Settings _settings;
|
Settings _settings;
|
||||||
@@ -87,6 +106,8 @@ namespace GlitchyEditor
|
|||||||
|
|
||||||
void ScanForSettings(Object container)
|
void ScanForSettings(Object container)
|
||||||
{
|
{
|
||||||
|
Log.EngineLogger.Info($"Visiting type: {container.GetType().GetName(.. scope .())}");
|
||||||
|
|
||||||
Type type = container.GetType();
|
Type type = container.GetType();
|
||||||
|
|
||||||
for (var field in type.GetFields())
|
for (var field in type.GetFields())
|
||||||
@@ -114,6 +135,18 @@ namespace GlitchyEditor
|
|||||||
ScanForSettings(childContainer);
|
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)
|
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);
|
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 _tabToSelect = new String() ~ delete _;
|
||||||
private String _SettingToHighlight = new String() ~ delete _;
|
private String _SettingToHighlight = new String() ~ delete _;
|
||||||
private float _timeToHighlight;
|
private float _timeToHighlight;
|
||||||
@@ -157,6 +196,7 @@ namespace GlitchyEditor
|
|||||||
if (ImGui.BeginTabItem(category.Header, null, flags))
|
if (ImGui.BeginTabItem(category.Header, null, flags))
|
||||||
{
|
{
|
||||||
ShowCategory(category);
|
ShowCategory(category);
|
||||||
|
|
||||||
ImGui.EndTabItem();
|
ImGui.EndTabItem();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -209,6 +249,12 @@ namespace GlitchyEditor
|
|||||||
|
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
|
|
||||||
|
/*if (setting.EditorType case .Custom(let renderMethod))
|
||||||
|
{
|
||||||
|
renderMethod(setting);
|
||||||
|
continue;
|
||||||
|
}*/
|
||||||
|
|
||||||
// Name of setting
|
// Name of setting
|
||||||
ImGui.TextUnformatted(setting.Name);
|
ImGui.TextUnformatted(setting.Name);
|
||||||
|
|
||||||
@@ -370,98 +416,16 @@ namespace GlitchyEditor
|
|||||||
_SettingToHighlight.Clear();
|
_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);
|
returnValue.Dispose();
|
||||||
|
}
|
||||||
// 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;
|
|
||||||
}*/
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.EndTable();
|
ImGui.EndTable();
|
||||||
Reference in New Issue
Block a user