mirror of
https://github.com/Leonmmcoset/CMLeonOS.git
synced 2026-04-21 19:24:00 +00:00
通讯录&表格编辑软件
This commit is contained in:
@@ -1 +1 @@
|
|||||||
2026-03-31 22:16:42
|
2026-04-03 21:32:07
|
||||||
@@ -1 +1 @@
|
|||||||
cd2e1e5
|
1d41885
|
||||||
@@ -144,6 +144,8 @@ namespace CMLeonOS.Gui
|
|||||||
RegisterApp(new AppMetadata("Music Editor", () => { return new MusicEditor(); }, Icons.Icon_Default, Color.FromArgb(25, 25, 25)));
|
RegisterApp(new AppMetadata("Music Editor", () => { return new MusicEditor(); }, Icons.Icon_Default, Color.FromArgb(25, 25, 25)));
|
||||||
RegisterApp(new AppMetadata("Function Grapher", () => { return new FunctionGrapher(); }, Icons.Icon_Default, Color.FromArgb(25, 25, 25)));
|
RegisterApp(new AppMetadata("Function Grapher", () => { return new FunctionGrapher(); }, Icons.Icon_Default, Color.FromArgb(25, 25, 25)));
|
||||||
RegisterApp(new AppMetadata("CodeBlocks", () => { return new CodeBlocks(); }, Icons.Icon_Default, Color.FromArgb(25, 25, 25)));
|
RegisterApp(new AppMetadata("CodeBlocks", () => { return new CodeBlocks(); }, Icons.Icon_Default, Color.FromArgb(25, 25, 25)));
|
||||||
|
RegisterApp(new AppMetadata("Contacts", () => { return new Contacts(); }, Icons.Icon_Default, Color.FromArgb(25, 25, 25)));
|
||||||
|
RegisterApp(new AppMetadata("Sheet Editor", () => { return new SheetEditor(); }, Icons.Icon_Default, Color.FromArgb(25, 25, 25)));
|
||||||
|
|
||||||
Logger.Logger.Instance.Info("AppManager", $"{AppMetadatas.Count} apps were registered.");
|
Logger.Logger.Instance.Info("AppManager", $"{AppMetadatas.Count} apps were registered.");
|
||||||
|
|
||||||
|
|||||||
515
Gui/Apps/Contacts.cs
Normal file
515
Gui/Apps/Contacts.cs
Normal file
@@ -0,0 +1,515 @@
|
|||||||
|
using CMLeonOS;
|
||||||
|
using CMLeonOS.Gui.UILib;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace CMLeonOS.Gui.Apps
|
||||||
|
{
|
||||||
|
internal class Contacts : Process
|
||||||
|
{
|
||||||
|
internal Contacts() : base("Contacts", ProcessType.Application) { }
|
||||||
|
|
||||||
|
private sealed class ContactEntry
|
||||||
|
{
|
||||||
|
internal string Name = string.Empty;
|
||||||
|
internal string Phone = string.Empty;
|
||||||
|
internal string Email = string.Empty;
|
||||||
|
internal string Address = string.Empty;
|
||||||
|
internal string Notes = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
private const string DataPath = @"0:\contacts.dat";
|
||||||
|
private const int ToolbarHeight = 34;
|
||||||
|
private const int HeaderHeight = 52;
|
||||||
|
private const int Padding = 8;
|
||||||
|
private const int ButtonWidth = 82;
|
||||||
|
private const int ButtonHeight = 24;
|
||||||
|
private const int LabelWidth = 64;
|
||||||
|
private const int RowHeight = 24;
|
||||||
|
|
||||||
|
private readonly WindowManager wm = ProcessManager.GetProcess<WindowManager>();
|
||||||
|
private readonly List<ContactEntry> contacts = new List<ContactEntry>();
|
||||||
|
|
||||||
|
private AppWindow window;
|
||||||
|
private Window header;
|
||||||
|
private Table contactTable;
|
||||||
|
|
||||||
|
private Button newButton;
|
||||||
|
private Button saveButton;
|
||||||
|
private Button deleteButton;
|
||||||
|
private Button refreshButton;
|
||||||
|
|
||||||
|
private TextBlock nameLabel;
|
||||||
|
private TextBlock phoneLabel;
|
||||||
|
private TextBlock emailLabel;
|
||||||
|
private TextBlock addressLabel;
|
||||||
|
private TextBlock notesLabel;
|
||||||
|
|
||||||
|
private TextBox nameBox;
|
||||||
|
private TextBox phoneBox;
|
||||||
|
private TextBox emailBox;
|
||||||
|
private TextBox addressBox;
|
||||||
|
private TextBox notesBox;
|
||||||
|
|
||||||
|
private string statusText = @"Store file: 0:\contacts.dat";
|
||||||
|
|
||||||
|
private void SetStatus(string text)
|
||||||
|
{
|
||||||
|
statusText = text ?? string.Empty;
|
||||||
|
RenderHeader();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RenderHeader()
|
||||||
|
{
|
||||||
|
header.Clear(Color.FromArgb(235, 241, 248));
|
||||||
|
header.DrawRectangle(0, 0, header.Width, header.Height, Color.FromArgb(180, 192, 208));
|
||||||
|
header.DrawString("Contacts", Color.FromArgb(28, 38, 52), 12, 10);
|
||||||
|
header.DrawString(statusText, Color.FromArgb(97, 110, 126), 12, 30);
|
||||||
|
wm.Update(header);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string Escape(string value)
|
||||||
|
{
|
||||||
|
string v = value ?? string.Empty;
|
||||||
|
v = v.Replace("\\", "\\\\");
|
||||||
|
v = v.Replace("\t", "\\t");
|
||||||
|
v = v.Replace("\n", "\\n");
|
||||||
|
v = v.Replace("\r", string.Empty);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string Unescape(string value)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(value))
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder(value.Length);
|
||||||
|
bool escaped = false;
|
||||||
|
for (int i = 0; i < value.Length; i++)
|
||||||
|
{
|
||||||
|
char c = value[i];
|
||||||
|
if (!escaped)
|
||||||
|
{
|
||||||
|
if (c == '\\')
|
||||||
|
{
|
||||||
|
escaped = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.Append(c);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case 't':
|
||||||
|
sb.Append('\t');
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
sb.Append('\n');
|
||||||
|
break;
|
||||||
|
case '\\':
|
||||||
|
sb.Append('\\');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sb.Append(c);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
escaped = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (escaped)
|
||||||
|
{
|
||||||
|
sb.Append('\\');
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string[] SplitEscapedTabs(string line)
|
||||||
|
{
|
||||||
|
List<string> parts = new List<string>();
|
||||||
|
StringBuilder current = new StringBuilder();
|
||||||
|
bool escaped = false;
|
||||||
|
for (int i = 0; i < line.Length; i++)
|
||||||
|
{
|
||||||
|
char c = line[i];
|
||||||
|
if (escaped)
|
||||||
|
{
|
||||||
|
current.Append('\\');
|
||||||
|
current.Append(c);
|
||||||
|
escaped = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == '\\')
|
||||||
|
{
|
||||||
|
escaped = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == '\t')
|
||||||
|
{
|
||||||
|
parts.Add(current.ToString());
|
||||||
|
current.Clear();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
current.Append(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (escaped)
|
||||||
|
{
|
||||||
|
current.Append('\\');
|
||||||
|
}
|
||||||
|
|
||||||
|
parts.Add(current.ToString());
|
||||||
|
return parts.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadContacts()
|
||||||
|
{
|
||||||
|
contacts.Clear();
|
||||||
|
|
||||||
|
if (!File.Exists(DataPath))
|
||||||
|
{
|
||||||
|
SetStatus(@"No contact file yet. Click Save to create 0:\contacts.dat");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string[] lines = File.ReadAllLines(DataPath);
|
||||||
|
for (int i = 0; i < lines.Length; i++)
|
||||||
|
{
|
||||||
|
string line = lines[i] ?? string.Empty;
|
||||||
|
if (string.IsNullOrWhiteSpace(line))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
string[] fields = SplitEscapedTabs(line);
|
||||||
|
ContactEntry entry = new ContactEntry();
|
||||||
|
entry.Name = fields.Length > 0 ? Unescape(fields[0]) : string.Empty;
|
||||||
|
entry.Phone = fields.Length > 1 ? Unescape(fields[1]) : string.Empty;
|
||||||
|
entry.Email = fields.Length > 2 ? Unescape(fields[2]) : string.Empty;
|
||||||
|
entry.Address = fields.Length > 3 ? Unescape(fields[3]) : string.Empty;
|
||||||
|
entry.Notes = fields.Length > 4 ? Unescape(fields[4]) : string.Empty;
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(entry.Name))
|
||||||
|
{
|
||||||
|
contacts.Add(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SetStatus($"Loaded {contacts.Count} contact(s).");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveContacts()
|
||||||
|
{
|
||||||
|
string[] lines = new string[contacts.Count];
|
||||||
|
for (int i = 0; i < contacts.Count; i++)
|
||||||
|
{
|
||||||
|
ContactEntry c = contacts[i];
|
||||||
|
lines[i] = string.Join("\t", new string[]
|
||||||
|
{
|
||||||
|
Escape(c.Name),
|
||||||
|
Escape(c.Phone),
|
||||||
|
Escape(c.Email),
|
||||||
|
Escape(c.Address),
|
||||||
|
Escape(c.Notes)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
File.WriteAllLines(DataPath, lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PopulateTable(int preferredSelection = -1)
|
||||||
|
{
|
||||||
|
contactTable.Cells.Clear();
|
||||||
|
contactTable.SelectedCellIndex = -1;
|
||||||
|
|
||||||
|
for (int i = 0; i < contacts.Count; i++)
|
||||||
|
{
|
||||||
|
ContactEntry c = contacts[i];
|
||||||
|
string display = string.IsNullOrWhiteSpace(c.Phone) ? c.Name : $"{c.Name} ({c.Phone})";
|
||||||
|
contactTable.Cells.Add(new TableCell(display, c.Name));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preferredSelection >= 0 && preferredSelection < contacts.Count)
|
||||||
|
{
|
||||||
|
contactTable.SelectedCellIndex = preferredSelection;
|
||||||
|
}
|
||||||
|
|
||||||
|
contactTable.Render();
|
||||||
|
ContactSelected(contactTable.SelectedCellIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearEditor()
|
||||||
|
{
|
||||||
|
nameBox.Text = string.Empty;
|
||||||
|
phoneBox.Text = string.Empty;
|
||||||
|
emailBox.Text = string.Empty;
|
||||||
|
addressBox.Text = string.Empty;
|
||||||
|
notesBox.Text = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ContactEntry ReadEditor()
|
||||||
|
{
|
||||||
|
ContactEntry entry = new ContactEntry();
|
||||||
|
entry.Name = (nameBox.Text ?? string.Empty).Trim();
|
||||||
|
entry.Phone = (phoneBox.Text ?? string.Empty).Trim();
|
||||||
|
entry.Email = (emailBox.Text ?? string.Empty).Trim();
|
||||||
|
entry.Address = (addressBox.Text ?? string.Empty).Trim();
|
||||||
|
entry.Notes = notesBox.Text ?? string.Empty;
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadEditor(ContactEntry entry)
|
||||||
|
{
|
||||||
|
if (entry == null)
|
||||||
|
{
|
||||||
|
ClearEditor();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nameBox.Text = entry.Name;
|
||||||
|
phoneBox.Text = entry.Phone;
|
||||||
|
emailBox.Text = entry.Email;
|
||||||
|
addressBox.Text = entry.Address;
|
||||||
|
notesBox.Text = entry.Notes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ContactSelected(int index)
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= contacts.Count)
|
||||||
|
{
|
||||||
|
SetStatus($"Loaded {contacts.Count} contact(s).");
|
||||||
|
ClearEditor();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadEditor(contacts[index]);
|
||||||
|
SetStatus($"Selected: {contacts[index].Name}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void NewClicked(int x, int y)
|
||||||
|
{
|
||||||
|
contactTable.SelectedCellIndex = -1;
|
||||||
|
ClearEditor();
|
||||||
|
SetStatus("Create mode: fill fields and click Save.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveClicked(int x, int y)
|
||||||
|
{
|
||||||
|
ContactEntry updated = ReadEditor();
|
||||||
|
if (string.IsNullOrWhiteSpace(updated.Name))
|
||||||
|
{
|
||||||
|
new MessageBox(this, "Contacts", "Name cannot be empty.").Show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int selected = contactTable.SelectedCellIndex;
|
||||||
|
int targetIndex;
|
||||||
|
if (selected >= 0 && selected < contacts.Count)
|
||||||
|
{
|
||||||
|
contacts[selected] = updated;
|
||||||
|
targetIndex = selected;
|
||||||
|
SetStatus($"Updated: {updated.Name}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
contacts.Add(updated);
|
||||||
|
targetIndex = contacts.Count - 1;
|
||||||
|
SetStatus($"Added: {updated.Name}");
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveContacts();
|
||||||
|
PopulateTable(targetIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeleteClicked(int x, int y)
|
||||||
|
{
|
||||||
|
int selected = contactTable.SelectedCellIndex;
|
||||||
|
if (selected < 0 || selected >= contacts.Count)
|
||||||
|
{
|
||||||
|
SetStatus("Select a contact first.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string name = contacts[selected].Name;
|
||||||
|
contacts.RemoveAt(selected);
|
||||||
|
SaveContacts();
|
||||||
|
|
||||||
|
int next = selected;
|
||||||
|
if (next >= contacts.Count) next = contacts.Count - 1;
|
||||||
|
PopulateTable(next);
|
||||||
|
SetStatus($"Deleted: {name}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshClicked(int x, int y)
|
||||||
|
{
|
||||||
|
LoadContacts();
|
||||||
|
PopulateTable(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Relayout()
|
||||||
|
{
|
||||||
|
int topY = Padding;
|
||||||
|
int right = window.Width - Padding;
|
||||||
|
|
||||||
|
newButton.MoveAndResize(right - ((ButtonWidth * 4) + (Padding * 3)), topY, ButtonWidth, ButtonHeight);
|
||||||
|
saveButton.MoveAndResize(right - ((ButtonWidth * 3) + (Padding * 2)), topY, ButtonWidth, ButtonHeight);
|
||||||
|
deleteButton.MoveAndResize(right - ((ButtonWidth * 2) + Padding), topY, ButtonWidth, ButtonHeight);
|
||||||
|
refreshButton.MoveAndResize(right - ButtonWidth, topY, ButtonWidth, ButtonHeight);
|
||||||
|
|
||||||
|
header.MoveAndResize(0, ToolbarHeight, window.Width, HeaderHeight);
|
||||||
|
|
||||||
|
int contentY = ToolbarHeight + HeaderHeight + Padding;
|
||||||
|
int contentHeight = window.Height - contentY - Padding;
|
||||||
|
int listWidth = Math.Max(220, (window.Width * 2) / 5);
|
||||||
|
int editorX = listWidth + (Padding * 2);
|
||||||
|
int editorWidth = window.Width - editorX - Padding;
|
||||||
|
|
||||||
|
contactTable.MoveAndResize(Padding, contentY, listWidth - Padding, contentHeight);
|
||||||
|
|
||||||
|
int y = contentY;
|
||||||
|
nameLabel.MoveAndResize(editorX, y + 2, LabelWidth, 20);
|
||||||
|
nameBox.MoveAndResize(editorX + LabelWidth, y, editorWidth - LabelWidth, RowHeight);
|
||||||
|
|
||||||
|
y += RowHeight + Padding;
|
||||||
|
phoneLabel.MoveAndResize(editorX, y + 2, LabelWidth, 20);
|
||||||
|
phoneBox.MoveAndResize(editorX + LabelWidth, y, editorWidth - LabelWidth, RowHeight);
|
||||||
|
|
||||||
|
y += RowHeight + Padding;
|
||||||
|
emailLabel.MoveAndResize(editorX, y + 2, LabelWidth, 20);
|
||||||
|
emailBox.MoveAndResize(editorX + LabelWidth, y, editorWidth - LabelWidth, RowHeight);
|
||||||
|
|
||||||
|
y += RowHeight + Padding;
|
||||||
|
addressLabel.MoveAndResize(editorX, y + 2, LabelWidth, 20);
|
||||||
|
addressBox.MoveAndResize(editorX + LabelWidth, y, editorWidth - LabelWidth, RowHeight);
|
||||||
|
|
||||||
|
y += RowHeight + Padding;
|
||||||
|
notesLabel.MoveAndResize(editorX, y + 2, LabelWidth, 20);
|
||||||
|
notesBox.MoveAndResize(editorX + LabelWidth, y, editorWidth - LabelWidth, Math.Max(72, contentY + contentHeight - y));
|
||||||
|
|
||||||
|
newButton.Render();
|
||||||
|
saveButton.Render();
|
||||||
|
deleteButton.Render();
|
||||||
|
refreshButton.Render();
|
||||||
|
RenderHeader();
|
||||||
|
contactTable.Render();
|
||||||
|
nameLabel.Render();
|
||||||
|
phoneLabel.Render();
|
||||||
|
emailLabel.Render();
|
||||||
|
addressLabel.Render();
|
||||||
|
notesLabel.Render();
|
||||||
|
nameBox.Render();
|
||||||
|
phoneBox.Render();
|
||||||
|
emailBox.Render();
|
||||||
|
addressBox.Render();
|
||||||
|
notesBox.Render();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Start()
|
||||||
|
{
|
||||||
|
base.Start();
|
||||||
|
|
||||||
|
window = new AppWindow(this, 190, 100, 900, 520);
|
||||||
|
window.Title = "Contacts";
|
||||||
|
window.Icon = AppManager.DefaultAppIcon;
|
||||||
|
window.CanResize = true;
|
||||||
|
window.UserResized = Relayout;
|
||||||
|
window.Closing = TryStop;
|
||||||
|
wm.AddWindow(window);
|
||||||
|
|
||||||
|
newButton = new Button(window, 0, 0, 1, 1);
|
||||||
|
newButton.Text = "New";
|
||||||
|
newButton.OnClick = NewClicked;
|
||||||
|
wm.AddWindow(newButton);
|
||||||
|
|
||||||
|
saveButton = new Button(window, 0, 0, 1, 1);
|
||||||
|
saveButton.Text = "Save";
|
||||||
|
saveButton.OnClick = SaveClicked;
|
||||||
|
wm.AddWindow(saveButton);
|
||||||
|
|
||||||
|
deleteButton = new Button(window, 0, 0, 1, 1);
|
||||||
|
deleteButton.Text = "Delete";
|
||||||
|
deleteButton.OnClick = DeleteClicked;
|
||||||
|
wm.AddWindow(deleteButton);
|
||||||
|
|
||||||
|
refreshButton = new Button(window, 0, 0, 1, 1);
|
||||||
|
refreshButton.Text = "Refresh";
|
||||||
|
refreshButton.OnClick = RefreshClicked;
|
||||||
|
wm.AddWindow(refreshButton);
|
||||||
|
|
||||||
|
header = new Window(this, window, 0, ToolbarHeight, window.Width, HeaderHeight);
|
||||||
|
wm.AddWindow(header);
|
||||||
|
|
||||||
|
contactTable = new Table(window, 0, 0, 1, 1);
|
||||||
|
contactTable.CellHeight = 24;
|
||||||
|
contactTable.Background = Color.White;
|
||||||
|
contactTable.Foreground = Color.Black;
|
||||||
|
contactTable.Border = Color.FromArgb(185, 194, 207);
|
||||||
|
contactTable.SelectedBackground = Color.FromArgb(216, 231, 255);
|
||||||
|
contactTable.SelectedBorder = Color.FromArgb(94, 138, 216);
|
||||||
|
contactTable.SelectedForeground = Color.Black;
|
||||||
|
contactTable.TableCellSelected = ContactSelected;
|
||||||
|
wm.AddWindow(contactTable);
|
||||||
|
|
||||||
|
nameLabel = new TextBlock(window, 0, 0, LabelWidth, 20);
|
||||||
|
nameLabel.Text = "Name";
|
||||||
|
nameLabel.Foreground = UITheme.TextSecondary;
|
||||||
|
wm.AddWindow(nameLabel);
|
||||||
|
|
||||||
|
phoneLabel = new TextBlock(window, 0, 0, LabelWidth, 20);
|
||||||
|
phoneLabel.Text = "Phone";
|
||||||
|
phoneLabel.Foreground = UITheme.TextSecondary;
|
||||||
|
wm.AddWindow(phoneLabel);
|
||||||
|
|
||||||
|
emailLabel = new TextBlock(window, 0, 0, LabelWidth, 20);
|
||||||
|
emailLabel.Text = "Email";
|
||||||
|
emailLabel.Foreground = UITheme.TextSecondary;
|
||||||
|
wm.AddWindow(emailLabel);
|
||||||
|
|
||||||
|
addressLabel = new TextBlock(window, 0, 0, LabelWidth, 20);
|
||||||
|
addressLabel.Text = "Address";
|
||||||
|
addressLabel.Foreground = UITheme.TextSecondary;
|
||||||
|
wm.AddWindow(addressLabel);
|
||||||
|
|
||||||
|
notesLabel = new TextBlock(window, 0, 0, LabelWidth, 20);
|
||||||
|
notesLabel.Text = "Notes";
|
||||||
|
notesLabel.Foreground = UITheme.TextSecondary;
|
||||||
|
wm.AddWindow(notesLabel);
|
||||||
|
|
||||||
|
nameBox = new TextBox(window, 0, 0, 1, 1);
|
||||||
|
wm.AddWindow(nameBox);
|
||||||
|
|
||||||
|
phoneBox = new TextBox(window, 0, 0, 1, 1);
|
||||||
|
wm.AddWindow(phoneBox);
|
||||||
|
|
||||||
|
emailBox = new TextBox(window, 0, 0, 1, 1);
|
||||||
|
wm.AddWindow(emailBox);
|
||||||
|
|
||||||
|
addressBox = new TextBox(window, 0, 0, 1, 1);
|
||||||
|
wm.AddWindow(addressBox);
|
||||||
|
|
||||||
|
notesBox = new TextBox(window, 0, 0, 1, 1);
|
||||||
|
notesBox.MultiLine = true;
|
||||||
|
wm.AddWindow(notesBox);
|
||||||
|
|
||||||
|
LoadContacts();
|
||||||
|
PopulateTable(-1);
|
||||||
|
Relayout();
|
||||||
|
wm.Update(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Run()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
579
Gui/Apps/SheetEditor.cs
Normal file
579
Gui/Apps/SheetEditor.cs
Normal file
@@ -0,0 +1,579 @@
|
|||||||
|
using CMLeonOS;
|
||||||
|
using CMLeonOS.Gui.UILib;
|
||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace CMLeonOS.Gui.Apps
|
||||||
|
{
|
||||||
|
internal class SheetEditor : Process
|
||||||
|
{
|
||||||
|
internal SheetEditor() : base("Sheet Editor", ProcessType.Application) { }
|
||||||
|
|
||||||
|
internal SheetEditor(string path) : base("Sheet Editor", ProcessType.Application)
|
||||||
|
{
|
||||||
|
startupPath = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
private const int MaxRows = 200;
|
||||||
|
private const int MaxCols = 52;
|
||||||
|
private const int ToolbarHeight = 30;
|
||||||
|
private const int StatusHeight = 24;
|
||||||
|
private const int Padding = 6;
|
||||||
|
private const int ButtonWidth = 82;
|
||||||
|
private const int RowHeaderWidth = 44;
|
||||||
|
private const int ColHeaderHeight = 22;
|
||||||
|
private const int CellWidth = 92;
|
||||||
|
private const int CellHeight = 22;
|
||||||
|
private const string FileMagic = "CMSHEET1";
|
||||||
|
|
||||||
|
private readonly WindowManager wm = ProcessManager.GetProcess<WindowManager>();
|
||||||
|
|
||||||
|
private AppWindow window;
|
||||||
|
private Window grid;
|
||||||
|
private TextBox editorBox;
|
||||||
|
private TextBlock cellLabel;
|
||||||
|
private StatusBar statusBar;
|
||||||
|
|
||||||
|
private Button newButton;
|
||||||
|
private Button openButton;
|
||||||
|
private Button saveButton;
|
||||||
|
private Button saveAsButton;
|
||||||
|
private Button applyButton;
|
||||||
|
private Button leftButton;
|
||||||
|
private Button rightButton;
|
||||||
|
private Button upButton;
|
||||||
|
private Button downButton;
|
||||||
|
|
||||||
|
private FileBrowser fileBrowser;
|
||||||
|
|
||||||
|
private readonly string[] cells = new string[MaxRows * MaxCols];
|
||||||
|
private int selectedRow = 0;
|
||||||
|
private int selectedCol = 0;
|
||||||
|
private int startRow = 0;
|
||||||
|
private int startCol = 0;
|
||||||
|
private string currentPath = null;
|
||||||
|
private bool modified = false;
|
||||||
|
private readonly string startupPath = null;
|
||||||
|
|
||||||
|
private static int Index(int row, int col)
|
||||||
|
{
|
||||||
|
return (row * MaxCols) + col;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string Escape(string value)
|
||||||
|
{
|
||||||
|
string v = value ?? string.Empty;
|
||||||
|
v = v.Replace("\\", "\\\\");
|
||||||
|
v = v.Replace("\t", "\\t");
|
||||||
|
v = v.Replace("\n", "\\n");
|
||||||
|
v = v.Replace("\r", string.Empty);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string Unescape(string value)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(value))
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder(value.Length);
|
||||||
|
bool esc = false;
|
||||||
|
for (int i = 0; i < value.Length; i++)
|
||||||
|
{
|
||||||
|
char c = value[i];
|
||||||
|
if (!esc)
|
||||||
|
{
|
||||||
|
if (c == '\\')
|
||||||
|
{
|
||||||
|
esc = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.Append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case 't': sb.Append('\t'); break;
|
||||||
|
case 'n': sb.Append('\n'); break;
|
||||||
|
case '\\': sb.Append('\\'); break;
|
||||||
|
default: sb.Append(c); break;
|
||||||
|
}
|
||||||
|
esc = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (esc) sb.Append('\\');
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string[] SplitTabs(string line)
|
||||||
|
{
|
||||||
|
string[] parts = new string[3];
|
||||||
|
int part = 0;
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
bool esc = false;
|
||||||
|
for (int i = 0; i < line.Length; i++)
|
||||||
|
{
|
||||||
|
char c = line[i];
|
||||||
|
if (!esc)
|
||||||
|
{
|
||||||
|
if (c == '\\')
|
||||||
|
{
|
||||||
|
esc = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (c == '\t' && part < 2)
|
||||||
|
{
|
||||||
|
parts[part++] = sb.ToString();
|
||||||
|
sb.Clear();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
sb.Append(c);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.Append('\\');
|
||||||
|
sb.Append(c);
|
||||||
|
esc = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parts[part] = sb.ToString();
|
||||||
|
if (parts[0] == null) parts[0] = string.Empty;
|
||||||
|
if (parts[1] == null) parts[1] = string.Empty;
|
||||||
|
if (parts[2] == null) parts[2] = string.Empty;
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetStatus(string text)
|
||||||
|
{
|
||||||
|
statusBar.Text = text;
|
||||||
|
statusBar.DetailText = currentPath ?? "Unsaved (.cws)";
|
||||||
|
statusBar.Render();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateTitle()
|
||||||
|
{
|
||||||
|
string baseName = currentPath == null ? "Untitled.cws" : Path.GetFileName(currentPath);
|
||||||
|
window.Title = modified ? (baseName + "* - Sheet Editor") : (baseName + " - Sheet Editor");
|
||||||
|
cellLabel.Text = CellName(selectedRow, selectedCol);
|
||||||
|
cellLabel.Render();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ColumnName(int col)
|
||||||
|
{
|
||||||
|
col = Math.Max(0, Math.Min(MaxCols - 1, col));
|
||||||
|
if (col < 26)
|
||||||
|
{
|
||||||
|
return ((char)('A' + col)).ToString();
|
||||||
|
}
|
||||||
|
return ((char)('A' + ((col / 26) - 1))).ToString() + ((char)('A' + (col % 26))).ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string CellName(int row, int col)
|
||||||
|
{
|
||||||
|
return ColumnName(col) + (row + 1).ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ClipText(string text, int maxChars)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(text)) return string.Empty;
|
||||||
|
string singleLine = text.Replace('\n', ' ');
|
||||||
|
if (singleLine.Length <= maxChars) return singleLine;
|
||||||
|
if (maxChars <= 1) return ".";
|
||||||
|
return singleLine.Substring(0, maxChars - 1) + ".";
|
||||||
|
}
|
||||||
|
|
||||||
|
private int VisibleCols()
|
||||||
|
{
|
||||||
|
int v = (grid.Width - RowHeaderWidth) / CellWidth;
|
||||||
|
return Math.Max(1, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int VisibleRows()
|
||||||
|
{
|
||||||
|
int v = (grid.Height - ColHeaderHeight) / CellHeight;
|
||||||
|
return Math.Max(1, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureSelectionVisible()
|
||||||
|
{
|
||||||
|
int visCols = VisibleCols();
|
||||||
|
int visRows = VisibleRows();
|
||||||
|
if (selectedCol < startCol) startCol = selectedCol;
|
||||||
|
if (selectedCol >= startCol + visCols) startCol = selectedCol - visCols + 1;
|
||||||
|
if (selectedRow < startRow) startRow = selectedRow;
|
||||||
|
if (selectedRow >= startRow + visRows) startRow = selectedRow - visRows + 1;
|
||||||
|
if (startCol < 0) startCol = 0;
|
||||||
|
if (startRow < 0) startRow = 0;
|
||||||
|
if (startCol > MaxCols - 1) startCol = MaxCols - 1;
|
||||||
|
if (startRow > MaxRows - 1) startRow = MaxRows - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RenderGrid()
|
||||||
|
{
|
||||||
|
grid.Clear(Color.White);
|
||||||
|
|
||||||
|
int visCols = VisibleCols();
|
||||||
|
int visRows = VisibleRows();
|
||||||
|
int maxCol = Math.Min(MaxCols, startCol + visCols);
|
||||||
|
int maxRow = Math.Min(MaxRows, startRow + visRows);
|
||||||
|
|
||||||
|
grid.DrawFilledRectangle(0, 0, grid.Width, ColHeaderHeight, Color.FromArgb(240, 245, 252));
|
||||||
|
grid.DrawFilledRectangle(0, 0, RowHeaderWidth, grid.Height, Color.FromArgb(240, 245, 252));
|
||||||
|
grid.DrawRectangle(0, 0, grid.Width, grid.Height, Color.FromArgb(182, 194, 210));
|
||||||
|
|
||||||
|
for (int c = startCol; c < maxCol; c++)
|
||||||
|
{
|
||||||
|
int x = RowHeaderWidth + ((c - startCol) * CellWidth);
|
||||||
|
string label = ColumnName(c);
|
||||||
|
grid.DrawRectangle(x, 0, CellWidth, ColHeaderHeight, Color.FromArgb(198, 208, 223));
|
||||||
|
grid.DrawString(label, Color.FromArgb(44, 60, 82), x + (CellWidth / 2) - (label.Length * 4), 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int r = startRow; r < maxRow; r++)
|
||||||
|
{
|
||||||
|
int y = ColHeaderHeight + ((r - startRow) * CellHeight);
|
||||||
|
string label = (r + 1).ToString();
|
||||||
|
grid.DrawRectangle(0, y, RowHeaderWidth, CellHeight, Color.FromArgb(198, 208, 223));
|
||||||
|
grid.DrawString(label, Color.FromArgb(44, 60, 82), RowHeaderWidth - 6 - (label.Length * 8), y + 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int r = startRow; r < maxRow; r++)
|
||||||
|
{
|
||||||
|
int y = ColHeaderHeight + ((r - startRow) * CellHeight);
|
||||||
|
for (int c = startCol; c < maxCol; c++)
|
||||||
|
{
|
||||||
|
int x = RowHeaderWidth + ((c - startCol) * CellWidth);
|
||||||
|
bool selected = (r == selectedRow && c == selectedCol);
|
||||||
|
Color border = selected ? Color.FromArgb(94, 138, 216) : Color.FromArgb(204, 214, 226);
|
||||||
|
Color back = selected ? Color.FromArgb(227, 238, 255) : Color.White;
|
||||||
|
|
||||||
|
grid.DrawFilledRectangle(x, y, CellWidth, CellHeight, border);
|
||||||
|
grid.DrawFilledRectangle(x + 1, y + 1, CellWidth - 2, CellHeight - 2, back);
|
||||||
|
|
||||||
|
string cellText = cells[Index(r, c)] ?? string.Empty;
|
||||||
|
if (!string.IsNullOrWhiteSpace(cellText))
|
||||||
|
{
|
||||||
|
grid.DrawString(ClipText(cellText, 10), Color.FromArgb(28, 38, 52), x + 4, y + 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wm.Update(grid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SelectCell(int row, int col)
|
||||||
|
{
|
||||||
|
selectedRow = Math.Max(0, Math.Min(MaxRows - 1, row));
|
||||||
|
selectedCol = Math.Max(0, Math.Min(MaxCols - 1, col));
|
||||||
|
EnsureSelectionVisible();
|
||||||
|
editorBox.Text = cells[Index(selectedRow, selectedCol)] ?? string.Empty;
|
||||||
|
UpdateTitle();
|
||||||
|
RenderGrid();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GridDown(int x, int y)
|
||||||
|
{
|
||||||
|
if (x < RowHeaderWidth || y < ColHeaderHeight)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int col = startCol + ((x - RowHeaderWidth) / CellWidth);
|
||||||
|
int row = startRow + ((y - ColHeaderHeight) / CellHeight);
|
||||||
|
if (row < 0 || row >= MaxRows || col < 0 || col >= MaxCols)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SelectCell(row, col);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyEdit()
|
||||||
|
{
|
||||||
|
int idx = Index(selectedRow, selectedCol);
|
||||||
|
string before = cells[idx] ?? string.Empty;
|
||||||
|
string after = editorBox.Text ?? string.Empty;
|
||||||
|
if (before == after) return;
|
||||||
|
cells[idx] = after;
|
||||||
|
modified = true;
|
||||||
|
UpdateTitle();
|
||||||
|
RenderGrid();
|
||||||
|
SetStatus("Cell updated: " + CellName(selectedRow, selectedCol));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearSheet()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < cells.Length; i++)
|
||||||
|
{
|
||||||
|
cells[i] = string.Empty;
|
||||||
|
}
|
||||||
|
selectedRow = 0;
|
||||||
|
selectedCol = 0;
|
||||||
|
startRow = 0;
|
||||||
|
startCol = 0;
|
||||||
|
editorBox.Text = string.Empty;
|
||||||
|
modified = false;
|
||||||
|
currentPath = null;
|
||||||
|
UpdateTitle();
|
||||||
|
RenderGrid();
|
||||||
|
SetStatus("New sheet.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveToPath(string path)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(path))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.AppendLine(FileMagic);
|
||||||
|
sb.AppendLine(MaxRows.ToString() + "\t" + MaxCols.ToString());
|
||||||
|
|
||||||
|
for (int r = 0; r < MaxRows; r++)
|
||||||
|
{
|
||||||
|
for (int c = 0; c < MaxCols; c++)
|
||||||
|
{
|
||||||
|
string val = cells[Index(r, c)] ?? string.Empty;
|
||||||
|
if (val.Length == 0) continue;
|
||||||
|
sb.Append(r.ToString());
|
||||||
|
sb.Append('\t');
|
||||||
|
sb.Append(c.ToString());
|
||||||
|
sb.Append('\t');
|
||||||
|
sb.AppendLine(Escape(val));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
File.WriteAllText(path, sb.ToString());
|
||||||
|
currentPath = path;
|
||||||
|
modified = false;
|
||||||
|
UpdateTitle();
|
||||||
|
SetStatus("Saved.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Save()
|
||||||
|
{
|
||||||
|
if (currentPath == null)
|
||||||
|
{
|
||||||
|
SaveAs();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SaveToPath(currentPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveAs()
|
||||||
|
{
|
||||||
|
fileBrowser = new FileBrowser(this, wm, (selectedPath) =>
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(selectedPath)) return;
|
||||||
|
SaveToPath(selectedPath);
|
||||||
|
}, selectDirectoryOnly: true);
|
||||||
|
fileBrowser.Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OpenFromPath(string path)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(path) || !File.Exists(path))
|
||||||
|
{
|
||||||
|
new MessageBox(this, "Sheet Editor", "File does not exist.").Show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string[] lines = File.ReadAllLines(path);
|
||||||
|
if (lines.Length < 2 || lines[0] != FileMagic)
|
||||||
|
{
|
||||||
|
new MessageBox(this, "Sheet Editor", "Unsupported file format.").Show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < cells.Length; i++)
|
||||||
|
{
|
||||||
|
cells[i] = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 2; i < lines.Length; i++)
|
||||||
|
{
|
||||||
|
string line = lines[i] ?? string.Empty;
|
||||||
|
if (string.IsNullOrWhiteSpace(line)) continue;
|
||||||
|
string[] parts = SplitTabs(line);
|
||||||
|
|
||||||
|
if (!int.TryParse(parts[0], out int r)) continue;
|
||||||
|
if (!int.TryParse(parts[1], out int c)) continue;
|
||||||
|
if (r < 0 || r >= MaxRows || c < 0 || c >= MaxCols) continue;
|
||||||
|
|
||||||
|
cells[Index(r, c)] = Unescape(parts[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentPath = path;
|
||||||
|
modified = false;
|
||||||
|
selectedRow = 0;
|
||||||
|
selectedCol = 0;
|
||||||
|
startRow = 0;
|
||||||
|
startCol = 0;
|
||||||
|
editorBox.Text = string.Empty;
|
||||||
|
UpdateTitle();
|
||||||
|
RenderGrid();
|
||||||
|
SetStatus("Opened.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OpenPrompt()
|
||||||
|
{
|
||||||
|
fileBrowser = new FileBrowser(this, wm, (selectedPath) =>
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(selectedPath)) return;
|
||||||
|
OpenFromPath(selectedPath);
|
||||||
|
});
|
||||||
|
fileBrowser.Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Layout()
|
||||||
|
{
|
||||||
|
int topY = Padding;
|
||||||
|
int x = Padding;
|
||||||
|
newButton.MoveAndResize(x, topY, ButtonWidth, 22); x += ButtonWidth + 4;
|
||||||
|
openButton.MoveAndResize(x, topY, ButtonWidth, 22); x += ButtonWidth + 4;
|
||||||
|
saveButton.MoveAndResize(x, topY, ButtonWidth, 22); x += ButtonWidth + 4;
|
||||||
|
saveAsButton.MoveAndResize(x, topY, ButtonWidth, 22); x += ButtonWidth + 6;
|
||||||
|
|
||||||
|
cellLabel.MoveAndResize(x, topY + 3, 52, 20); x += 54;
|
||||||
|
editorBox.MoveAndResize(x, topY, Math.Max(180, window.Width - x - 210), 22);
|
||||||
|
applyButton.MoveAndResize(window.Width - 198, topY, 70, 22);
|
||||||
|
|
||||||
|
leftButton.MoveAndResize(window.Width - 122, topY, 26, 22);
|
||||||
|
rightButton.MoveAndResize(window.Width - 92, topY, 26, 22);
|
||||||
|
upButton.MoveAndResize(window.Width - 62, topY, 26, 22);
|
||||||
|
downButton.MoveAndResize(window.Width - 32, topY, 26, 22);
|
||||||
|
|
||||||
|
int gridY = ToolbarHeight;
|
||||||
|
grid.MoveAndResize(0, gridY, window.Width, window.Height - gridY - StatusHeight);
|
||||||
|
statusBar.MoveAndResize(0, window.Height - StatusHeight, window.Width, StatusHeight);
|
||||||
|
|
||||||
|
EnsureSelectionVisible();
|
||||||
|
RenderGrid();
|
||||||
|
statusBar.Render();
|
||||||
|
cellLabel.Render();
|
||||||
|
editorBox.Render();
|
||||||
|
newButton.Render();
|
||||||
|
openButton.Render();
|
||||||
|
saveButton.Render();
|
||||||
|
saveAsButton.Render();
|
||||||
|
applyButton.Render();
|
||||||
|
leftButton.Render();
|
||||||
|
rightButton.Render();
|
||||||
|
upButton.Render();
|
||||||
|
downButton.Render();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Start()
|
||||||
|
{
|
||||||
|
base.Start();
|
||||||
|
|
||||||
|
window = new AppWindow(this, 150, 80, 980, 560);
|
||||||
|
window.Title = "Sheet Editor - Untitled.cws";
|
||||||
|
window.Icon = AppManager.DefaultAppIcon;
|
||||||
|
window.CanResize = true;
|
||||||
|
window.UserResized = Layout;
|
||||||
|
window.Closing = TryStop;
|
||||||
|
wm.AddWindow(window);
|
||||||
|
|
||||||
|
newButton = new Button(window, 0, 0, 1, 1);
|
||||||
|
newButton.Text = "New";
|
||||||
|
newButton.OnClick = (_, _) => ClearSheet();
|
||||||
|
wm.AddWindow(newButton);
|
||||||
|
|
||||||
|
openButton = new Button(window, 0, 0, 1, 1);
|
||||||
|
openButton.Text = "Open";
|
||||||
|
openButton.OnClick = (_, _) => OpenPrompt();
|
||||||
|
wm.AddWindow(openButton);
|
||||||
|
|
||||||
|
saveButton = new Button(window, 0, 0, 1, 1);
|
||||||
|
saveButton.Text = "Save";
|
||||||
|
saveButton.OnClick = (_, _) => Save();
|
||||||
|
wm.AddWindow(saveButton);
|
||||||
|
|
||||||
|
saveAsButton = new Button(window, 0, 0, 1, 1);
|
||||||
|
saveAsButton.Text = "Save As";
|
||||||
|
saveAsButton.OnClick = (_, _) => SaveAs();
|
||||||
|
wm.AddWindow(saveAsButton);
|
||||||
|
|
||||||
|
cellLabel = new TextBlock(window, 0, 0, 1, 1);
|
||||||
|
cellLabel.Text = "A1";
|
||||||
|
cellLabel.Foreground = UITheme.TextPrimary;
|
||||||
|
wm.AddWindow(cellLabel);
|
||||||
|
|
||||||
|
editorBox = new TextBox(window, 0, 0, 1, 1);
|
||||||
|
editorBox.PlaceholderText = "Cell value";
|
||||||
|
editorBox.Submitted = ApplyEdit;
|
||||||
|
wm.AddWindow(editorBox);
|
||||||
|
|
||||||
|
applyButton = new Button(window, 0, 0, 1, 1);
|
||||||
|
applyButton.Text = "Apply";
|
||||||
|
applyButton.OnClick = (_, _) => ApplyEdit();
|
||||||
|
wm.AddWindow(applyButton);
|
||||||
|
|
||||||
|
leftButton = new Button(window, 0, 0, 1, 1);
|
||||||
|
leftButton.Text = "<";
|
||||||
|
leftButton.OnClick = (_, _) =>
|
||||||
|
{
|
||||||
|
startCol = Math.Max(0, startCol - 1);
|
||||||
|
RenderGrid();
|
||||||
|
};
|
||||||
|
wm.AddWindow(leftButton);
|
||||||
|
|
||||||
|
rightButton = new Button(window, 0, 0, 1, 1);
|
||||||
|
rightButton.Text = ">";
|
||||||
|
rightButton.OnClick = (_, _) =>
|
||||||
|
{
|
||||||
|
startCol = Math.Min(MaxCols - 1, startCol + 1);
|
||||||
|
RenderGrid();
|
||||||
|
};
|
||||||
|
wm.AddWindow(rightButton);
|
||||||
|
|
||||||
|
upButton = new Button(window, 0, 0, 1, 1);
|
||||||
|
upButton.Text = "^";
|
||||||
|
upButton.OnClick = (_, _) =>
|
||||||
|
{
|
||||||
|
startRow = Math.Max(0, startRow - 1);
|
||||||
|
RenderGrid();
|
||||||
|
};
|
||||||
|
wm.AddWindow(upButton);
|
||||||
|
|
||||||
|
downButton = new Button(window, 0, 0, 1, 1);
|
||||||
|
downButton.Text = "v";
|
||||||
|
downButton.OnClick = (_, _) =>
|
||||||
|
{
|
||||||
|
startRow = Math.Min(MaxRows - 1, startRow + 1);
|
||||||
|
RenderGrid();
|
||||||
|
};
|
||||||
|
wm.AddWindow(downButton);
|
||||||
|
|
||||||
|
grid = new Window(this, window, 0, ToolbarHeight, window.Width, window.Height - ToolbarHeight - StatusHeight);
|
||||||
|
grid.OnDown = GridDown;
|
||||||
|
wm.AddWindow(grid);
|
||||||
|
|
||||||
|
statusBar = new StatusBar(window, 0, window.Height - StatusHeight, window.Width, StatusHeight);
|
||||||
|
statusBar.Text = "Ready";
|
||||||
|
statusBar.DetailText = "Unsaved (.cws)";
|
||||||
|
wm.AddWindow(statusBar);
|
||||||
|
|
||||||
|
ClearSheet();
|
||||||
|
Layout();
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(startupPath))
|
||||||
|
{
|
||||||
|
OpenFromPath(startupPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
wm.Update(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Run()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user