Compare commits

...

3 Commits

Author SHA1 Message Date
Leonmmcoset
5c952e7861 增加CodeStudio 2026-03-01 21:34:43 +08:00
Leonmmcoset
6a9d39abd9 增加防傻功能 2026-03-01 19:46:27 +08:00
Leonmmcoset
1f385ac18a 修复GUI的一些bug 2026-03-01 19:17:10 +08:00
13 changed files with 582 additions and 53 deletions

View File

@@ -3,6 +3,7 @@ using Sys = Cosmos.System;
using Cosmos.HAL;
using Cosmos.Core;
using System.Threading;
using System.IO;
namespace CMLeonOS
{
@@ -16,6 +17,10 @@ namespace CMLeonOS
internal static class BootMenu
{
private static bool UserDatExists()
{
return File.Exists(@"0:\system\user.dat");
}
private static void PrintOption(string text, bool selected)
{
Console.SetCursorPosition(1, Console.GetCursorPosition().Top);
@@ -35,8 +40,6 @@ namespace CMLeonOS
uint mem = Cosmos.Core.CPU.GetAmountOfRAM();
Console.WriteLine($"{Version.DisplayVersion} [{mem} MB memory]");
// 这里老显示 unknown谁知道为啥
// Console.WriteLine($"Git Commit: {Version.GitCommit}");
Console.WriteLine($"Build Time: {GetBuildTime()}");
Console.WriteLine();
Console.WriteLine($"Auto-select in {remainingTime} seconds...");
@@ -44,10 +47,16 @@ namespace CMLeonOS
Console.WriteLine("Select an option:");
Console.WriteLine();
PrintOption("Normal Boot", selIdx == 0);
PrintOption("GUI Boot", selIdx == 1);
PrintOption("Reboot", selIdx == 2);
PrintOption("Shutdown", selIdx == 3);
bool userDatExists = UserDatExists();
int optionIndex = 0;
PrintOption("Normal Boot", selIdx == optionIndex++);
if (userDatExists)
{
PrintOption("GUI Boot", selIdx == optionIndex++);
}
PrintOption("Reboot", selIdx == optionIndex++);
PrintOption("Shutdown", selIdx == optionIndex++);
}
private static BootMenuAction Confirm(int selIdx)
@@ -61,21 +70,29 @@ namespace CMLeonOS
Console.CursorVisible = true;
switch (selIdx)
bool userDatExists = UserDatExists();
int optionIndex = 0;
if (selIdx == optionIndex++)
{
case 0:
return BootMenuAction.NormalBoot;
case 1:
return BootMenuAction.GuiBoot;
case 2:
Sys.Power.Reboot();
return BootMenuAction.Reboot;
case 3:
Sys.Power.Shutdown();
return BootMenuAction.Shutdown;
default:
return BootMenuAction.NormalBoot;
return BootMenuAction.NormalBoot;
}
if (userDatExists && selIdx == optionIndex++)
{
return BootMenuAction.GuiBoot;
}
if (selIdx == optionIndex++)
{
Sys.Power.Reboot();
return BootMenuAction.Reboot;
}
if (selIdx == optionIndex++)
{
Sys.Power.Shutdown();
return BootMenuAction.Shutdown;
}
return BootMenuAction.NormalBoot;
}
public static BootMenuAction Show()
@@ -131,12 +148,14 @@ namespace CMLeonOS
}
}
int maxOptionIndex = UserDatExists() ? 3 : 2;
if (selIdx < 0)
{
selIdx = 3;
selIdx = maxOptionIndex;
}
if (selIdx > 3)
if (selIdx > maxOptionIndex)
{
selIdx = 0;
}

View File

@@ -1 +1 @@
2026-03-01 17:02:21
2026-03-01 21:19:06

View File

@@ -1 +1 @@
545f40c
6a9d39a

View File

@@ -120,6 +120,7 @@ namespace CMLeonOS.Gui
RegisterApp(new AppMetadata("Stopwatch", () => { return new Stopwatch(); }, Icons.Icon_Stopwatch, Color.FromArgb(168, 55, 47)));
RegisterApp(new AppMetadata("Paint", () => { return new Apps.Paint.Paint(); }, Icons.Icon_Paint, Color.FromArgb(0, 115, 186)));
RegisterApp(new AppMetadata("Memory Statistics", () => { return new Apps.MemoryStatistics(); }, Icons.Icon_MemoryStatistics, Color.FromArgb(25, 25, 25)));
RegisterApp(new AppMetadata("CodeStudio", () => { return new Apps.CodeStudio.CodeStudio(); }, Icons.Icon_CodeStudio, Color.FromArgb(14, 59, 76)));
Logger.Logger.Instance.Info("AppManager", $"{AppMetadatas.Count} apps were registered.");

View File

@@ -0,0 +1,47 @@
using CMLeonOS;
using CMLeonOS.Gui.UILib;
using System.Drawing;
using Cosmos.System.Graphics;
namespace CMLeonOS.Gui.Apps.CodeStudio
{
internal class CodeStudio : Process
{
internal CodeStudio() : base("CodeStudio", ProcessType.Application) { }
Window splash;
WindowManager wm = ProcessManager.GetProcess<WindowManager>();
[IL2CPU.API.Attribs.ManifestResourceStream(ResourceName = "CMLeonOS.Gui.Resources.CodeStudio.Splash.bmp")]
private static byte[] _splashBytes;
private static Bitmap splashBitmap = new Bitmap(_splashBytes);
private Ide ide;
private bool ideCreated = false;
public override void Start()
{
base.Start();
splash = new Window(this, 372, 250, 535, 300);
wm.AddWindow(splash);
splash.DrawImage(splashBitmap, 0, 0);
//splash.DrawString("Starting...", Color.White, 20, splash.Height - 16 - 20);
wm.Update(splash);
}
public override void Run()
{
if (!ideCreated)
{
ide = new Ide(this, wm);
ide.Start();
wm.RemoveWindow(splash);
ideCreated = true;
}
}
}
}

322
Gui/Apps/CodeStudio/Ide.cs Normal file
View File

@@ -0,0 +1,322 @@
using CMLeonOS;
using CMLeonOS.Gui.UILib;
using Cosmos.System.Graphics;
using System.Drawing;
using System;
using System.Collections.Generic;
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.CodeStudio.Run.bmp")]
private static byte[] _runBytes;
private static Bitmap runBitmap = new Bitmap(_runBytes);
Process process;
WindowManager wm;
AppWindow mainWindow;
Button runButton;
TextBox editor;
TextBox problems;
TextBox output;
private const int headersHeight = 24;
private const int problemsHeight = 128;
private const int outputHeight = 128;
private string? path = null;
private bool modified = false;
private void TextChanged()
{
modified = true;
UpdateTitle();
}
private static class Theme
{
internal static Color Background = Color.FromArgb(68, 76, 84);
internal static Color CodeBackground = Color.FromArgb(41, 46, 51);
}
private void UpdateTitle()
{
if (path == null)
{
mainWindow.Title = "Untitled - Lua CodeStudio";
return;
}
if (modified)
{
mainWindow.Title = $"{Path.GetFileName(path)}* - Lua CodeStudio";
}
else
{
mainWindow.Title = $"{Path.GetFileName(path)} - Lua CodeStudio";
}
}
internal void Open(string newPath, bool readFile = true)
{
if (newPath == null) return;
if (readFile && !File.Exists(newPath))
{
MessageBox messageBox = new MessageBox(process, "Lua CodeStudio", $"No such file '{Path.GetFileName(newPath)}'.");
messageBox.Show();
}
path = newPath;
if (readFile)
{
editor.Text = File.ReadAllText(path);
editor.MarkAllLines();
editor.Render();
modified = false;
}
UpdateTitle();
}
private void OpenFilePrompt()
{
PromptBox prompt = new PromptBox(process, "Open File", "Enter the path to open.", "Path", (string newPath) =>
{
if (!newPath.Contains(':'))
{
newPath = $@"0:\{newPath}";
}
Open(newPath);
});
prompt.Show();
}
private void SaveAsPrompt()
{
PromptBox prompt = new PromptBox(process, "Save As", "Enter the path to save to.", "Path", (string newPath) =>
{
if (!newPath.Contains(':'))
{
newPath = $@"0:\{newPath}";
}
Open(newPath, readFile: false);
// Check if open succeeded.
if (path != null)
{
Save();
}
});
prompt.Show();
}
private void Save()
{
if (path == null)
{
SaveAsPrompt();
return;
}
File.WriteAllText(path, editor.Text);
modified = false;
UpdateTitle();
}
private void RunClicked(int x, int y)
{
try
{
output.Text = "";
ILuaState lua = LuaAPI.NewState();
lua.L_OpenLibs();
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 = Color.LimeGreen;
problems.Text = "Execution successful";
}
else
{
string errorMsg = lua.ToString(-1);
problems.Foreground = Color.Pink;
problems.Text = $"Script execution error: {errorMsg}";
}
}
else
{
string errorMsg = lua.ToString(-1);
problems.Foreground = Color.Pink;
problems.Text = $"Script load error: {errorMsg}";
}
}
catch (Exception e)
{
problems.Foreground = Color.Pink;
problems.Text = e.Message;
}
}
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 = Color.LimeGreen;
problems.Text = "No syntax errors";
}
else
{
string errorMsg = lua.ToString(-1);
if (string.IsNullOrWhiteSpace(errorMsg))
{
problems.Foreground = Color.Pink;
problems.Text = "Script load error: Unknown error";
}
else
{
problems.Foreground = Color.Pink;
problems.Text = $"Script load error: {errorMsg}";
}
}
}
catch (Exception e)
{
problems.Foreground = Color.Pink;
problems.Text = e.Message;
}
}
internal void Start()
{
mainWindow = new AppWindow(process, 96, 96, 800, 600);
mainWindow.Clear(Theme.Background);
mainWindow.Closing = process.TryStop;
UpdateTitle();
wm.AddWindow(mainWindow);
runButton = new Button(mainWindow, 0, 0, 60, headersHeight);
runButton.Background = Theme.Background;
runButton.Border = Theme.Background;
runButton.Foreground = Color.White;
runButton.Text = "Run";
runButton.Image = runBitmap;
runButton.ImageLocation = Button.ButtonImageLocation.Left;
runButton.OnClick = RunClicked;
wm.AddWindow(runButton);
editor = new TextBox(mainWindow, 0, headersHeight, mainWindow.Width, mainWindow.Height - headersHeight - problemsHeight - outputHeight - (headersHeight * 3))
{
Background = Theme.CodeBackground,
Foreground = Color.White,
Text = "print(\"Hello World!\")",
Changed = TextChanged,
MultiLine = true
};
wm.AddWindow(editor);
problems = new TextBox(mainWindow, 0, headersHeight + editor.Height + headersHeight, mainWindow.Width, problemsHeight + (headersHeight * 2))
{
Background = Theme.CodeBackground,
Foreground = Color.Gray,
Text = "Click Evaluate to check your program for syntax errors.",
ReadOnly = true,
MultiLine = true
};
wm.AddWindow(problems);
mainWindow.DrawString("Problems", Color.White, 0, headersHeight + editor.Height);
output = new TextBox(mainWindow, 0, headersHeight + editor.Height + problemsHeight + (headersHeight * 2), mainWindow.Width, outputHeight + (headersHeight * 2))
{
Background = Theme.CodeBackground,
Foreground = Color.White,
Text = "Output will appear here...",
ReadOnly = true,
MultiLine = true
};
wm.AddWindow(output);
mainWindow.DrawString("Output", Color.White, 0, headersHeight + editor.Height + problemsHeight + headersHeight);
var shortcutBar = new ShortcutBar(mainWindow, runButton.Width, 0, mainWindow.Width - runButton.Width, headersHeight)
{
Background = Theme.Background,
Foreground = Color.White
};
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);
wm.Update(mainWindow);
}
}
}

View File

@@ -75,7 +75,6 @@ namespace CMLeonOS.Gui.Apps
("CMLeonOS (0:)", @"0:\"),
("My Home", @$"0:\user\{UserSystem.CurrentLoggedInUser.Username}"),
("Users", @"0:\user"),
("etc", @"0:\etc"),
};
private Bitmap GetFileIcon(string path)

View File

@@ -30,9 +30,8 @@ namespace CMLeonOS.Gui.Apps
window.DrawString($"Memory: {Cosmos.Core.CPU.GetAmountOfRAM()} MB", Color.Black, 12, 80);
window.DrawString("Credits", Color.DarkBlue, 12, 108);
window.DrawString("Cosmos Team - OS tooling", Color.Black, 12, 132);
window.DrawString("Microsoft - .NET Runtime", Color.Black, 12, 156);
window.DrawString("Google Fonts - Font", Color.Black, 12, 180);
window.DrawString("Microsoft - .NET Runtime", Color.Black, 12, 132);
window.DrawString("Google Fonts - Font", Color.Black, 12, 156);
Button button = new Button(window, window.Width - 80 - 12, window.Height - 20 - 12, 80, 20);
button.Text = "OK";

View File

@@ -1,6 +1,7 @@
using Cosmos.System.Graphics;
using CMLeonOS;
using CMLeonOS.Gui.UILib;
using CMLeonOS.Settings;
using System.Drawing;
@@ -16,8 +17,6 @@ namespace CMLeonOS.Gui.Apps
WindowManager wm = ProcessManager.GetProcess<WindowManager>();
SettingsService settingsService = ProcessManager.GetProcess<SettingsService>();
private static class Icons
{
[IL2CPU.API.Attribs.ManifestResourceStream(ResourceName = "CMLeonOS.Gui.Resources.Settings.User.bmp")]
@@ -59,22 +58,22 @@ namespace CMLeonOS.Gui.Apps
private void LeftStartButtonChanged(bool @checked)
{
settingsService.LeftHandStartButton = @checked;
SettingsManager.GUI_LeftHandStartButton = @checked;
}
private void TwelveHourClockChanged(bool @checked)
{
settingsService.TwelveHourClock = @checked;
SettingsManager.GUI_TwelveHourClock = @checked;
}
private void ShowFpsChanged(bool @checked)
{
settingsService.ShowFps = @checked;
SettingsManager.GUI_ShowFps = @checked;
}
private void MouseSensitivityChanged(float value)
{
settingsService.MouseSensitivity = value;
SettingsManager.GUI_MouseSensitivity = value;
}
private void ShowAppearanceCategory()
@@ -90,13 +89,13 @@ namespace CMLeonOS.Gui.Apps
Switch leftStartButton = new Switch(appearance, 12, 40, 244, 16);
leftStartButton.Text = "Left-hand start button";
leftStartButton.Checked = settingsService.LeftHandStartButton;
leftStartButton.Checked = SettingsManager.GUI_LeftHandStartButton;
leftStartButton.CheckBoxChanged = LeftStartButtonChanged;
wm.AddWindow(leftStartButton);
Switch showFps = new Switch(appearance, 12, 68, 244, 16);
showFps.Text = "Show frames per second";
showFps.Checked = settingsService.ShowFps;
showFps.Checked = SettingsManager.GUI_ShowFps;
showFps.CheckBoxChanged = ShowFpsChanged;
wm.AddWindow(showFps);
@@ -116,7 +115,7 @@ namespace CMLeonOS.Gui.Apps
Switch twelveHourClock = new Switch(dateTime, 12, 40, 244, 16);
twelveHourClock.Text = "12-hour clock";
twelveHourClock.Checked = settingsService.TwelveHourClock;
twelveHourClock.Checked = SettingsManager.GUI_TwelveHourClock;
twelveHourClock.CheckBoxChanged = TwelveHourClockChanged;
wm.AddWindow(twelveHourClock);
@@ -152,7 +151,7 @@ namespace CMLeonOS.Gui.Apps
{
Mode mode = wm.AvailableModes[i];
resolutionsTable.Cells.Add(new TableCell($"{mode.Width}x{mode.Height}"));
if (mode.Equals(settingsService.Mode))
if (mode.Width == SettingsManager.GUI_ScreenWidth && mode.Height == SettingsManager.GUI_ScreenHeight)
{
resolutionsTable.SelectedCellIndex = i;
}
@@ -161,8 +160,8 @@ namespace CMLeonOS.Gui.Apps
resolutionsTable.TableCellSelected = (int index) =>
{
Mode mode = wm.AvailableModes[index];
settingsService.Mode = mode;
settingsService.Flush();
SettingsManager.GUI_ScreenWidth = (int)mode.Width;
SettingsManager.GUI_ScreenHeight = (int)mode.Height;
MessageBox messageBox = new MessageBox(this, "Restart Required", "Restart your PC to apply changes.");
messageBox.Show();
@@ -242,7 +241,7 @@ namespace CMLeonOS.Gui.Apps
mouse.DrawString("Mouse sensitivity", Color.Gray, 12, 40);
RangeSlider mouseSensitivity = new RangeSlider(mouse, 12, 68, 244, 30, min: 0.25f, value: settingsService.MouseSensitivity, max: 2f);
RangeSlider mouseSensitivity = new RangeSlider(mouse, 12, 68, 244, 30, min: 0.25f, value: SettingsManager.GUI_MouseSensitivity, max: 2f);
mouseSensitivity.Changed = MouseSensitivityChanged;
wm.AddWindow(mouseSensitivity);

View File

@@ -159,7 +159,7 @@ namespace CMLeonOS.Gui.ShellComponents
}
window = new AppWindow(this, (int)(wm.ScreenWidth / 2 - width / 2), (int)(wm.ScreenHeight / 2 - height / 2), width, height);
window.Title = "CMLeonOS Logon";
window.Title = "CMLeonOS Login";
window.Icon = Images.Icon_Key;
window.CanMove = false;
window.CanClose = false;

View File

@@ -421,7 +421,7 @@ namespace CMLeonOS
}
}
// 我他妈居然成功了,我在没有任何文档的情况下研究出来了
// 我居然成功了,我在没有任何文档的情况下研究出来了
private void CreateMBRandPartitionTable(Disk disk)
{
disk.Clear();

View File

@@ -12,7 +12,14 @@ namespace CMLeonOS.Settings
private static Dictionary<string, string> defaultSettings = new Dictionary<string, string>
{
{ "LoggerEnabled", "true" },
{ "MaxLoginAttempts", "3" }
{ "MaxLoginAttempts", "3" },
{ "GUI_LeftHandStartButton", "false" },
{ "GUI_ShowFps", "true" },
{ "GUI_TwelveHourClock", "false" },
{ "GUI_MouseSensitivity", "1.0" },
{ "GUI_ScreenWidth", "1280" },
{ "GUI_ScreenHeight", "800" },
{ "GUI_DarkNotepad", "false" }
};
public static bool LoggerEnabled
@@ -52,6 +59,134 @@ namespace CMLeonOS.Settings
}
}
public static bool GUI_LeftHandStartButton
{
get
{
if (settings.TryGetValue("GUI_LeftHandStartButton", out string value))
{
return value.ToLower() == "true";
}
return false;
}
set
{
settings["GUI_LeftHandStartButton"] = value ? "true" : "false";
SaveSettings();
}
}
public static bool GUI_ShowFps
{
get
{
if (settings.TryGetValue("GUI_ShowFps", out string value))
{
return value.ToLower() == "true";
}
return true;
}
set
{
settings["GUI_ShowFps"] = value ? "true" : "false";
SaveSettings();
}
}
public static bool GUI_TwelveHourClock
{
get
{
if (settings.TryGetValue("GUI_TwelveHourClock", out string value))
{
return value.ToLower() == "true";
}
return false;
}
set
{
settings["GUI_TwelveHourClock"] = value ? "true" : "false";
SaveSettings();
}
}
public static float GUI_MouseSensitivity
{
get
{
if (settings.TryGetValue("GUI_MouseSensitivity", out string value))
{
if (float.TryParse(value, out float result))
{
return result;
}
}
return 1.0f;
}
set
{
settings["GUI_MouseSensitivity"] = value.ToString();
SaveSettings();
}
}
public static int GUI_ScreenWidth
{
get
{
if (settings.TryGetValue("GUI_ScreenWidth", out string value))
{
if (int.TryParse(value, out int result))
{
return result;
}
}
return 1280;
}
set
{
settings["GUI_ScreenWidth"] = value.ToString();
SaveSettings();
}
}
public static int GUI_ScreenHeight
{
get
{
if (settings.TryGetValue("GUI_ScreenHeight", out string value))
{
if (int.TryParse(value, out int result))
{
return result;
}
}
return 800;
}
set
{
settings["GUI_ScreenHeight"] = value.ToString();
SaveSettings();
}
}
public static bool GUI_DarkNotepad
{
get
{
if (settings.TryGetValue("GUI_DarkNotepad", out string value))
{
return value.ToLower() == "true";
}
return false;
}
set
{
settings["GUI_DarkNotepad"] = value ? "true" : "false";
SaveSettings();
}
}
public static void LoadSettings()
{
settings.Clear();

View File

@@ -15,18 +15,26 @@ namespace CMLeonOS.Utils
string sanitized = path;
sanitized = sanitized.Replace('\\', '/');
sanitized = sanitized.Replace(':', '_');
sanitized = sanitized.Replace('*', '_');
sanitized = sanitized.Replace('?', '_');
sanitized = sanitized.Replace('"', '_');
sanitized = sanitized.Replace('<', '_');
sanitized = sanitized.Replace('>', '_');
sanitized = sanitized.Replace('|', '_');
// 保留驱动器字母和冒号(如 0:
// 检查格式X:\ 其中 X 是单个字符(字母或数字)
bool hasDrive = sanitized.Length >= 2 && sanitized[1] == ':';
string drive = hasDrive ? sanitized.Substring(0, 2) : "";
string rest = hasDrive ? sanitized.Substring(2) : sanitized;
sanitized = sanitized.Trim('/', '\\');
// 不替换反斜杠,因为 Cosmos 文件系统需要反斜杠
rest = rest.Replace(':', '_');
rest = rest.Replace('*', '_');
rest = rest.Replace('?', '_');
rest = rest.Replace('"', '_');
rest = rest.Replace('<', '_');
rest = rest.Replace('>', '_');
rest = rest.Replace('|', '_');
return sanitized;
// 只移除结尾的斜杠,保留开头的斜杠(因为驱动器后面需要斜杠)
rest = rest.TrimEnd('/');
rest = rest.TrimEnd('\\');
return drive + rest;
}
public static string Combine(string path1, string path2)