mirror of
https://github.com/Leonmmcoset/CMLeonOS.git
synced 2026-04-21 10:53:59 +00:00
511 lines
18 KiB
C#
511 lines
18 KiB
C#
using CMLeonOS;
|
|
using CMLeonOS.Gui.UILib;
|
|
using Cosmos.System.Graphics;
|
|
using System;
|
|
using System.Drawing;
|
|
using System.IO;
|
|
using UniLua;
|
|
|
|
namespace CMLeonOS.Gui.Apps.CodeStudio
|
|
{
|
|
internal class Ide
|
|
{
|
|
internal Ide(Process process, WindowManager wm)
|
|
{
|
|
this.process = process;
|
|
this.wm = wm;
|
|
}
|
|
|
|
[IL2CPU.API.Attribs.ManifestResourceStream(ResourceName = "CMLeonOS.Gui.Resources.AppIcons.CodeStudio.bmp")]
|
|
private static byte[] _iconBytes;
|
|
private static Bitmap iconBitmap = new Bitmap(_iconBytes);
|
|
|
|
[IL2CPU.API.Attribs.ManifestResourceStream(ResourceName = "CMLeonOS.Gui.Resources.CodeStudio.Run.bmp")]
|
|
private static byte[] _runBytes;
|
|
private static Bitmap runBitmap = new Bitmap(_runBytes);
|
|
|
|
private readonly Process process;
|
|
private readonly WindowManager wm;
|
|
|
|
private AppWindow mainWindow;
|
|
private Button runButton;
|
|
private ShortcutBar shortcutBar;
|
|
private CodeEditor editor;
|
|
private TextBox problems;
|
|
private TextBox output;
|
|
private TextBox statusBar;
|
|
private Table sideTable;
|
|
private FileBrowser fileBrowser;
|
|
|
|
private string path = null;
|
|
private bool modified = false;
|
|
|
|
private static class Layout
|
|
{
|
|
internal const int TopBarHeight = 30;
|
|
internal const int BottomBarHeight = 24;
|
|
internal const int Gap = 6;
|
|
internal const int Side = 8;
|
|
internal const int RunWidth = 72;
|
|
internal const int SidePanelWidth = 190;
|
|
internal const int PanelTitleHeight = 18;
|
|
internal const int BottomPanelHeight = 150;
|
|
}
|
|
|
|
private static class Theme
|
|
{
|
|
internal static readonly Color FrameBg = Color.FromArgb(34, 40, 52);
|
|
internal static readonly Color ToolbarBg = Color.FromArgb(27, 33, 44);
|
|
internal static readonly Color EditorBg = Color.FromArgb(19, 24, 34);
|
|
internal static readonly Color PanelBg = Color.FromArgb(23, 29, 39);
|
|
internal static readonly Color PanelBorder = Color.FromArgb(66, 78, 97);
|
|
internal static readonly Color TitleText = Color.FromArgb(220, 228, 240);
|
|
internal static readonly Color HintText = Color.FromArgb(157, 171, 190);
|
|
internal static readonly Color OkText = Color.FromArgb(142, 219, 160);
|
|
internal static readonly Color ErrorText = Color.FromArgb(255, 153, 164);
|
|
}
|
|
|
|
private void UpdateTitle()
|
|
{
|
|
if (path == null)
|
|
{
|
|
mainWindow.Title = "Untitled - CodeStudio";
|
|
return;
|
|
}
|
|
|
|
string fileName = Path.GetFileName(path);
|
|
mainWindow.Title = modified ? $"{fileName}* - CodeStudio" : $"{fileName} - CodeStudio";
|
|
}
|
|
|
|
private void SetStatus(string text)
|
|
{
|
|
statusBar.Text = text;
|
|
statusBar.Render();
|
|
wm.Update(statusBar);
|
|
}
|
|
|
|
private void TextChanged()
|
|
{
|
|
modified = true;
|
|
UpdateTitle();
|
|
SetStatus("Edited");
|
|
}
|
|
|
|
private void RenderChrome()
|
|
{
|
|
mainWindow.Clear(Theme.FrameBg);
|
|
mainWindow.DrawFilledRectangle(0, 0, mainWindow.Width, Layout.TopBarHeight, Theme.ToolbarBg);
|
|
mainWindow.DrawRectangle(0, 0, mainWindow.Width, Layout.TopBarHeight, Theme.PanelBorder);
|
|
|
|
int contentY = Layout.TopBarHeight + Layout.Gap;
|
|
int contentH = mainWindow.Height - contentY - Layout.BottomBarHeight - Layout.Gap;
|
|
int leftW = Layout.SidePanelWidth;
|
|
int rightX = Layout.Side + leftW + Layout.Gap;
|
|
int rightW = mainWindow.Width - rightX - Layout.Side;
|
|
|
|
int editorTop = contentY + Layout.PanelTitleHeight;
|
|
int editorH = contentH - Layout.BottomPanelHeight - Layout.Gap - Layout.PanelTitleHeight;
|
|
int bottomY = editorTop + editorH + Layout.Gap + Layout.PanelTitleHeight;
|
|
int halfW = (rightW - Layout.Gap) / 2;
|
|
|
|
mainWindow.DrawString("Workspace", Theme.TitleText, Layout.Side + 2, contentY + 2);
|
|
mainWindow.DrawString("Editor", Theme.TitleText, rightX + 2, contentY + 2);
|
|
mainWindow.DrawString("Problems", Theme.TitleText, rightX + 2, bottomY - Layout.PanelTitleHeight + 2);
|
|
mainWindow.DrawString("Output", Theme.TitleText, rightX + halfW + Layout.Gap + 2, bottomY - Layout.PanelTitleHeight + 2);
|
|
}
|
|
|
|
private int GetEditorHeight()
|
|
{
|
|
int contentY = Layout.TopBarHeight + Layout.Gap;
|
|
int contentH = mainWindow.Height - contentY - Layout.BottomBarHeight - Layout.Gap;
|
|
int available = contentH - Layout.BottomPanelHeight - Layout.Gap - Layout.PanelTitleHeight;
|
|
if (available < 80)
|
|
{
|
|
return 80;
|
|
}
|
|
return available;
|
|
}
|
|
|
|
private void ReLayout()
|
|
{
|
|
int side = Layout.Side;
|
|
int width = mainWindow.Width - (side * 2);
|
|
int y = 0;
|
|
|
|
runButton.MoveAndResize(side, y, Layout.RunWidth, Layout.TopBarHeight, sendWMEvent: false);
|
|
shortcutBar.MoveAndResize(side + Layout.RunWidth + Layout.Gap, y, width - Layout.RunWidth - Layout.Gap, Layout.TopBarHeight, sendWMEvent: false);
|
|
shortcutBar.Render();
|
|
|
|
int contentY = Layout.TopBarHeight + Layout.Gap;
|
|
int contentH = mainWindow.Height - contentY - Layout.BottomBarHeight - Layout.Gap;
|
|
int leftW = Layout.SidePanelWidth;
|
|
int rightX = side + leftW + Layout.Gap;
|
|
int rightW = mainWindow.Width - rightX - side;
|
|
|
|
sideTable.MoveAndResize(side, contentY + Layout.PanelTitleHeight, leftW, contentH - Layout.PanelTitleHeight, sendWMEvent: false);
|
|
sideTable.Render();
|
|
|
|
int editorY = contentY + Layout.PanelTitleHeight;
|
|
int editorH = GetEditorHeight();
|
|
editor.MoveAndResize(rightX, editorY, rightW, editorH, sendWMEvent: false);
|
|
editor.MarkAllLines();
|
|
editor.Render();
|
|
|
|
int bottomY = editorY + editorH + Layout.Gap + Layout.PanelTitleHeight;
|
|
int halfW = (rightW - Layout.Gap) / 2;
|
|
problems.MoveAndResize(rightX, bottomY, halfW, Layout.BottomPanelHeight, sendWMEvent: false);
|
|
problems.MarkAllLines();
|
|
problems.Render();
|
|
|
|
output.MoveAndResize(rightX + halfW + Layout.Gap, bottomY, rightW - halfW - Layout.Gap, Layout.BottomPanelHeight, sendWMEvent: false);
|
|
output.MarkAllLines();
|
|
output.Render();
|
|
|
|
int statusY = mainWindow.Height - Layout.BottomBarHeight;
|
|
statusBar.MoveAndResize(0, statusY, mainWindow.Width, Layout.BottomBarHeight, sendWMEvent: false);
|
|
statusBar.Render();
|
|
|
|
RenderChrome();
|
|
wm.Update(mainWindow);
|
|
}
|
|
|
|
internal void Open(string newPath, bool readFile = true)
|
|
{
|
|
if (newPath == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (readFile && !File.Exists(newPath))
|
|
{
|
|
MessageBox messageBox = new MessageBox(process, "CodeStudio", $"No such file '{Path.GetFileName(newPath)}'.");
|
|
messageBox.Show();
|
|
return;
|
|
}
|
|
|
|
path = newPath;
|
|
|
|
if (readFile)
|
|
{
|
|
editor.Text = File.ReadAllText(path);
|
|
|
|
string extension = Path.GetExtension(path).ToLower();
|
|
editor.SetSyntaxHighlighting(extension == ".lua", extension);
|
|
editor.MarkAllLines();
|
|
editor.Render();
|
|
|
|
modified = false;
|
|
}
|
|
|
|
UpdateTitle();
|
|
SetStatus("Opened: " + Path.GetFileName(path));
|
|
}
|
|
|
|
private void OpenFilePrompt()
|
|
{
|
|
fileBrowser = new FileBrowser(process, wm, (string selectedPath) =>
|
|
{
|
|
if (selectedPath != null)
|
|
{
|
|
Open(selectedPath);
|
|
}
|
|
});
|
|
fileBrowser.Show();
|
|
}
|
|
|
|
private void SaveAsPrompt()
|
|
{
|
|
fileBrowser = new FileBrowser(process, wm, (string selectedPath) =>
|
|
{
|
|
if (selectedPath != null)
|
|
{
|
|
path = selectedPath;
|
|
Save();
|
|
}
|
|
}, selectDirectoryOnly: true);
|
|
fileBrowser.Show();
|
|
}
|
|
|
|
private void Save()
|
|
{
|
|
if (path == null)
|
|
{
|
|
SaveAsPrompt();
|
|
return;
|
|
}
|
|
|
|
File.WriteAllText(path, editor.Text);
|
|
modified = false;
|
|
UpdateTitle();
|
|
SetStatus("Saved: " + Path.GetFileName(path));
|
|
}
|
|
|
|
private void SideCommandSelected(int index)
|
|
{
|
|
if (index < 0 || index >= sideTable.Cells.Count)
|
|
{
|
|
return;
|
|
}
|
|
|
|
string cmd = sideTable.Cells[index].Text;
|
|
if (cmd == "Open File")
|
|
{
|
|
OpenFilePrompt();
|
|
}
|
|
else if (cmd == "Save File")
|
|
{
|
|
Save();
|
|
}
|
|
else if (cmd == "Save As")
|
|
{
|
|
SaveAsPrompt();
|
|
}
|
|
else if (cmd == "Evaluate")
|
|
{
|
|
Evaluate();
|
|
}
|
|
else if (cmd == "Run Script")
|
|
{
|
|
RunClicked(0, 0);
|
|
}
|
|
else if (cmd == "Insert: Hello")
|
|
{
|
|
editor.Text += "\nprint(\"Hello from CodeStudio\")";
|
|
editor.MarkAllLines();
|
|
editor.Render();
|
|
wm.Update(editor);
|
|
TextChanged();
|
|
}
|
|
else if (cmd == "Insert: GUI Demo")
|
|
{
|
|
editor.Text += "\nlocal w = gui.window(\"Demo\", 100, 100, 260, 120)\n";
|
|
editor.MarkAllLines();
|
|
editor.Render();
|
|
wm.Update(editor);
|
|
TextChanged();
|
|
}
|
|
}
|
|
|
|
private void RunClicked(int x, int y)
|
|
{
|
|
try
|
|
{
|
|
output.Text = "";
|
|
|
|
ILuaState lua = LuaAPI.NewState();
|
|
lua.L_OpenLibs();
|
|
|
|
LuaGuiLibrary.Initialize(wm, process);
|
|
LuaGuiLibrary.Register(lua);
|
|
|
|
lua.PushCSharpFunction(L =>
|
|
{
|
|
int n = L.GetTop();
|
|
string result = "";
|
|
for (int i = 1; i <= n; i++)
|
|
{
|
|
if (i > 1)
|
|
{
|
|
result += "\t";
|
|
}
|
|
LuaType type = L.Type(i);
|
|
if (type == LuaType.LUA_TSTRING)
|
|
{
|
|
result += L.ToString(i);
|
|
}
|
|
else if (type == LuaType.LUA_TBOOLEAN)
|
|
{
|
|
result += L.ToBoolean(i) ? "true" : "false";
|
|
}
|
|
else if (type == LuaType.LUA_TNUMBER)
|
|
{
|
|
result += L.ToNumber(i).ToString();
|
|
}
|
|
else if (type == LuaType.LUA_TNIL)
|
|
{
|
|
result += "nil";
|
|
}
|
|
}
|
|
output.Text += result + "\n";
|
|
return 0;
|
|
});
|
|
lua.SetGlobal("print");
|
|
|
|
UniLua.ThreadStatus loadResult = lua.L_LoadString(editor.Text);
|
|
|
|
if (loadResult == UniLua.ThreadStatus.LUA_OK)
|
|
{
|
|
UniLua.ThreadStatus callResult = lua.PCall(0, 0, 0);
|
|
if (callResult == UniLua.ThreadStatus.LUA_OK)
|
|
{
|
|
problems.Foreground = Theme.OkText;
|
|
problems.Text = "Execution successful.";
|
|
SetStatus("Run succeeded");
|
|
}
|
|
else
|
|
{
|
|
string errorMsg = lua.ToString(-1);
|
|
problems.Foreground = Theme.ErrorText;
|
|
problems.Text = "Script execution error: " + errorMsg;
|
|
SetStatus("Run failed");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
string errorMsg = lua.ToString(-1);
|
|
problems.Foreground = Theme.ErrorText;
|
|
problems.Text = "Script load error: " + errorMsg;
|
|
SetStatus("Run failed");
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
problems.Foreground = Theme.ErrorText;
|
|
problems.Text = e.Message;
|
|
SetStatus("Run failed");
|
|
}
|
|
|
|
problems.MarkAllLines();
|
|
problems.Render();
|
|
output.MarkAllLines();
|
|
output.Render();
|
|
wm.Update(problems);
|
|
wm.Update(output);
|
|
}
|
|
|
|
private void Evaluate()
|
|
{
|
|
try
|
|
{
|
|
ILuaState lua = LuaAPI.NewState();
|
|
lua.L_OpenLibs();
|
|
UniLua.ThreadStatus loadResult = lua.L_LoadString(editor.Text);
|
|
|
|
if (loadResult == UniLua.ThreadStatus.LUA_OK)
|
|
{
|
|
problems.Foreground = Theme.OkText;
|
|
problems.Text = "No syntax errors.";
|
|
SetStatus("Evaluate passed");
|
|
}
|
|
else
|
|
{
|
|
string errorMsg = lua.ToString(-1);
|
|
problems.Foreground = Theme.ErrorText;
|
|
if (string.IsNullOrWhiteSpace(errorMsg))
|
|
{
|
|
problems.Text = "Script load error: Unknown error";
|
|
}
|
|
else
|
|
{
|
|
problems.Text = "Script load error: " + errorMsg;
|
|
}
|
|
SetStatus("Evaluate failed");
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
problems.Foreground = Theme.ErrorText;
|
|
problems.Text = e.Message;
|
|
SetStatus("Evaluate failed");
|
|
}
|
|
|
|
problems.MarkAllLines();
|
|
problems.Render();
|
|
wm.Update(problems);
|
|
}
|
|
|
|
internal void Start()
|
|
{
|
|
mainWindow = new AppWindow(process, 96, 96, 920, 640);
|
|
mainWindow.Icon = iconBitmap;
|
|
mainWindow.Closing = process.TryStop;
|
|
mainWindow.CanResize = true;
|
|
mainWindow.UserResized = ReLayout;
|
|
wm.AddWindow(mainWindow);
|
|
UpdateTitle();
|
|
|
|
runButton = new Button(mainWindow, Layout.Side, 0, Layout.RunWidth, Layout.TopBarHeight);
|
|
runButton.Background = Theme.ToolbarBg;
|
|
runButton.Border = Theme.PanelBorder;
|
|
runButton.Foreground = Theme.TitleText;
|
|
runButton.Text = "Run";
|
|
runButton.Image = runBitmap;
|
|
runButton.ImageLocation = Button.ButtonImageLocation.Left;
|
|
runButton.OnClick = RunClicked;
|
|
wm.AddWindow(runButton);
|
|
|
|
shortcutBar = new ShortcutBar(mainWindow, Layout.Side + Layout.RunWidth + Layout.Gap, 0, 300, Layout.TopBarHeight);
|
|
shortcutBar.Background = Theme.ToolbarBg;
|
|
shortcutBar.Foreground = Theme.TitleText;
|
|
shortcutBar.Cells.Add(new ShortcutBarCell("Open", OpenFilePrompt));
|
|
shortcutBar.Cells.Add(new ShortcutBarCell("Save", Save));
|
|
shortcutBar.Cells.Add(new ShortcutBarCell("Save As", SaveAsPrompt));
|
|
shortcutBar.Cells.Add(new ShortcutBarCell("Evaluate", Evaluate));
|
|
shortcutBar.Render();
|
|
wm.AddWindow(shortcutBar);
|
|
|
|
editor = new CodeEditor(mainWindow, Layout.Side, Layout.TopBarHeight + Layout.Gap, 300, 200)
|
|
{
|
|
Background = Theme.EditorBg,
|
|
Foreground = Color.White,
|
|
Text = "print(\"Hello World!\")",
|
|
Changed = TextChanged,
|
|
MultiLine = true
|
|
};
|
|
editor.SetSyntaxHighlighting(true, ".lua");
|
|
wm.AddWindow(editor);
|
|
|
|
sideTable = new Table(mainWindow, Layout.Side, Layout.TopBarHeight + Layout.Gap + Layout.PanelTitleHeight, Layout.SidePanelWidth, 300);
|
|
sideTable.CellHeight = 24;
|
|
sideTable.AllowDeselection = false;
|
|
sideTable.Background = Theme.PanelBg;
|
|
sideTable.Border = Theme.PanelBorder;
|
|
sideTable.Foreground = Theme.TitleText;
|
|
sideTable.SelectedBackground = Color.FromArgb(54, 72, 103);
|
|
sideTable.SelectedBorder = Color.FromArgb(88, 112, 152);
|
|
sideTable.SelectedForeground = Color.White;
|
|
sideTable.TableCellSelected = SideCommandSelected;
|
|
sideTable.Cells.Add(new TableCell("Open File"));
|
|
sideTable.Cells.Add(new TableCell("Save File"));
|
|
sideTable.Cells.Add(new TableCell("Save As"));
|
|
sideTable.Cells.Add(new TableCell("Evaluate"));
|
|
sideTable.Cells.Add(new TableCell("Run Script"));
|
|
sideTable.Cells.Add(new TableCell("Insert: Hello"));
|
|
sideTable.Cells.Add(new TableCell("Insert: GUI Demo"));
|
|
wm.AddWindow(sideTable);
|
|
|
|
problems = new TextBox(mainWindow, Layout.Side, 280, 300, Layout.BottomPanelHeight)
|
|
{
|
|
Background = Theme.PanelBg,
|
|
Foreground = Theme.HintText,
|
|
Text = "Click Evaluate to check syntax.",
|
|
ReadOnly = true,
|
|
MultiLine = true
|
|
};
|
|
wm.AddWindow(problems);
|
|
|
|
output = new TextBox(mainWindow, Layout.Side, 430, 300, Layout.BottomPanelHeight)
|
|
{
|
|
Background = Theme.PanelBg,
|
|
Foreground = Theme.TitleText,
|
|
Text = "Output will appear here...",
|
|
ReadOnly = true,
|
|
MultiLine = true
|
|
};
|
|
wm.AddWindow(output);
|
|
|
|
statusBar = new TextBox(mainWindow, 0, mainWindow.Height - Layout.BottomBarHeight, mainWindow.Width, Layout.BottomBarHeight)
|
|
{
|
|
Background = Theme.ToolbarBg,
|
|
Foreground = Theme.HintText,
|
|
ReadOnly = true,
|
|
Text = "Ready"
|
|
};
|
|
wm.AddWindow(statusBar);
|
|
|
|
ReLayout();
|
|
wm.Update(mainWindow);
|
|
}
|
|
}
|
|
}
|