Paint增加保存,重写StartMenu的UI,修复部分渲染bug

This commit is contained in:
2026-03-24 18:42:49 +08:00
parent fc61afb6a7
commit d044e97cea
8 changed files with 363 additions and 76 deletions

View File

@@ -15,9 +15,13 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
using Cosmos.System;
using Cosmos.System.Graphics;
using CMLeonOS;
using CMLeonOS.Gui.UILib;
using CMLeonOS.Utils;
using System;
using System.Drawing;
using System.IO;
namespace CMLeonOS.Gui.Apps.Paint
{
@@ -30,14 +34,24 @@ namespace CMLeonOS.Gui.Apps.Paint
AppWindow window;
Window canvas;
Button openButton;
Button saveButton;
Button saveAsButton;
ToolBox toolBox;
ColourPicker colourPicker;
FileBrowser fileBrowser;
WindowManager wm = ProcessManager.GetProcess<WindowManager>();
private bool down = false;
private const int sidePanelWidth = 128;
private const int topBarHeight = 40;
private const int topButtonWidth = 84;
private const int topButtonHeight = 24;
private const int topButtonGap = 8;
private string currentFilePath = string.Empty;
internal Color SelectedColor { get; set; } = Color.Black;
@@ -54,6 +68,190 @@ namespace CMLeonOS.Gui.Apps.Paint
down = true;
}
private void ShowMessage(string title, string text)
{
MessageBox messageBox = new MessageBox(this, title, text);
messageBox.Show();
}
private void Relayout()
{
toolBox.MoveAndResize(0, 0, sidePanelWidth, window.Height);
colourPicker.MoveAndResize(window.Width - sidePanelWidth, 0, sidePanelWidth, window.Height);
int buttonsX = sidePanelWidth + 12;
openButton.MoveAndResize(buttonsX, 8, topButtonWidth, topButtonHeight);
saveButton.MoveAndResize(buttonsX + topButtonWidth + topButtonGap, 8, topButtonWidth, topButtonHeight);
saveAsButton.MoveAndResize(buttonsX + (topButtonWidth + topButtonGap) * 2, 8, topButtonWidth, topButtonHeight);
openButton.Render();
saveButton.Render();
saveAsButton.Render();
int canvasX = sidePanelWidth + 12;
int canvasY = topBarHeight + 8;
int canvasWidth = window.Width - sidePanelWidth * 2 - 24;
int canvasHeight = window.Height - canvasY - 12;
canvas.MoveAndResize(canvasX, canvasY, canvasWidth, canvasHeight);
if (canvasWidth > 0 && canvasHeight > 0)
{
canvas.DrawRectangle(0, 0, canvas.Width, canvas.Height, Color.Black);
wm.Update(canvas);
}
}
private void UpdateWindowTitle()
{
if (string.IsNullOrWhiteSpace(currentFilePath))
{
window.Title = "Paint";
return;
}
window.Title = $"Paint - {Path.GetFileName(currentFilePath)}";
}
private byte[] BuildBmpBytes()
{
int width = canvas.Width;
int height = canvas.Height;
int bytesPerPixel = 4;
int pixelDataSize = width * height * bytesPerPixel;
int fileSize = 54 + pixelDataSize;
byte[] bytes = new byte[fileSize];
bytes[0] = (byte)'B';
bytes[1] = (byte)'M';
BitConverter.GetBytes(fileSize).CopyTo(bytes, 2);
BitConverter.GetBytes(54).CopyTo(bytes, 10);
BitConverter.GetBytes(40).CopyTo(bytes, 14);
BitConverter.GetBytes(width).CopyTo(bytes, 18);
BitConverter.GetBytes(height).CopyTo(bytes, 22);
BitConverter.GetBytes((short)1).CopyTo(bytes, 26);
BitConverter.GetBytes((short)32).CopyTo(bytes, 28);
BitConverter.GetBytes(pixelDataSize).CopyTo(bytes, 34);
int offset = 54;
for (int y = height - 1; y >= 0; y--)
{
for (int x = 0; x < width; x++)
{
Color color = canvas.GetPixel(x, y);
bytes[offset++] = color.B;
bytes[offset++] = color.G;
bytes[offset++] = color.R;
bytes[offset++] = color.A;
}
}
return bytes;
}
private void SaveCanvas(string path)
{
try
{
string sanitizedPath = PathUtil.Sanitize(path.Trim());
if (!sanitizedPath.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase))
{
sanitizedPath += ".bmp";
}
string directory = Path.GetDirectoryName(sanitizedPath);
if (!string.IsNullOrWhiteSpace(directory))
{
Directory.CreateDirectory(directory);
}
File.WriteAllBytes(sanitizedPath, BuildBmpBytes());
currentFilePath = sanitizedPath;
UpdateWindowTitle();
ShowMessage("Paint", "Saved BMP successfully.");
}
catch (Exception ex)
{
ShowMessage("Paint", $"Save failed: {ex.Message}");
}
}
private void OpenImage(string path)
{
try
{
string sanitizedPath = PathUtil.Sanitize(path.Trim());
if (!File.Exists(sanitizedPath))
{
ShowMessage("Paint", "File not found.");
return;
}
if (!sanitizedPath.EndsWith(".bmp", StringComparison.OrdinalIgnoreCase))
{
ShowMessage("Paint", "Only BMP files are supported.");
return;
}
Bitmap bitmap = new Bitmap(File.ReadAllBytes(sanitizedPath));
canvas.Clear(Color.White);
int drawWidth = Math.Min(canvas.Width, (int)bitmap.Width);
int drawHeight = Math.Min(canvas.Height, (int)bitmap.Height);
for (int y = 0; y < drawHeight; y++)
{
int rowOffset = y * (int)bitmap.Width;
for (int x = 0; x < drawWidth; x++)
{
canvas.DrawPoint(x, y, Color.FromArgb(bitmap.RawData[rowOffset + x]));
}
}
canvas.DrawRectangle(0, 0, canvas.Width, canvas.Height, Color.Black);
wm.Update(canvas);
currentFilePath = sanitizedPath;
UpdateWindowTitle();
}
catch (Exception ex)
{
ShowMessage("Paint", $"Open failed: {ex.Message}");
}
}
private void OpenClicked(int x, int y)
{
fileBrowser = new FileBrowser(this, wm, (string selectedPath) =>
{
if (!string.IsNullOrWhiteSpace(selectedPath))
{
OpenImage(selectedPath);
}
}, selectDirectoryOnly: false);
fileBrowser.Show();
}
private void SaveAsClicked(int x, int y)
{
fileBrowser = new FileBrowser(this, wm, (string selectedPath) =>
{
if (!string.IsNullOrWhiteSpace(selectedPath))
{
SaveCanvas(selectedPath);
}
}, selectDirectoryOnly: true);
fileBrowser.Show();
}
private void SaveClicked(int x, int y)
{
if (string.IsNullOrWhiteSpace(currentFilePath))
{
SaveAsClicked(x, y);
return;
}
SaveCanvas(currentFilePath);
}
public override void Start()
{
base.Start();
@@ -61,22 +259,39 @@ namespace CMLeonOS.Gui.Apps.Paint
window.Title = "Paint";
window.Icon = AppManager.GetAppMetadata("Paint").Icon;
window.Closing = TryStop;
window.UserResized = Relayout;
window.Clear(Color.FromArgb(73, 73, 73));
wm.AddWindow(window);
int canvasWidth = 384;
int canvasHeight = 256;
canvas = new Window(this, (window.Width / 2) - (canvasWidth / 2), (window.Height / 2) - (canvasHeight / 2), canvasWidth, canvasHeight);
canvas = new Window(this, 0, 0, 1, 1);
canvas.RelativeTo = window;
canvas.OnDown = CanvasDown;
canvas.Clear(Color.White);
wm.AddWindow(canvas);
toolBox = new ToolBox(this, 0, 0, 128, window.Height);
openButton = new Button(window, 0, 0, 1, 1);
openButton.Text = "Open";
openButton.OnClick = OpenClicked;
wm.AddWindow(openButton);
saveButton = new Button(window, 0, 0, 1, 1);
saveButton.Text = "Save";
saveButton.OnClick = SaveClicked;
wm.AddWindow(saveButton);
saveAsButton = new Button(window, 0, 0, 1, 1);
saveAsButton.Text = "Save As";
saveAsButton.OnClick = SaveAsClicked;
wm.AddWindow(saveAsButton);
toolBox = new ToolBox(this, 0, 0, sidePanelWidth, window.Height);
toolBox.RelativeTo = window;
colourPicker = new ColourPicker(this, window.Width - 128, 0, 128, window.Height);
colourPicker = new ColourPicker(this, window.Width - sidePanelWidth, 0, sidePanelWidth, window.Height);
colourPicker.RelativeTo = window;
UpdateWindowTitle();
Relayout();
wm.Update(window);
}