TODO feature: Undo/Redo for canvas changes

This commit is contained in:
2025-05-22 01:10:38 +03:00
parent ce3b0ddd94
commit 038fd42841
4 changed files with 104 additions and 63 deletions

View File

@@ -12,11 +12,13 @@ namespace McBitFont {
public int Depth { get; set; }
public int Index { get; set; }
public int Count {
get { return stack.Count; }
get { return stack.Count - 1; }
}
public int Redos {
get {
return Count - Index - 1;
var r = Count - Index - 1;
return r < 0 ? 0 : r;
}
}
public int Undos {
@@ -26,9 +28,9 @@ namespace McBitFont {
}
public CanvasHistory(int depth = 5, int index = -1) {
public CanvasHistory(int depth = 50) {
Depth = depth;
Index = index;
Index = -1;
stack = [];
}
@@ -37,38 +39,47 @@ namespace McBitFont {
Index = -1;
}
public void Add(bool[,] data, bool useIndex = true) {
public void AddPre(MainForm.FrameMiniature f, bool useIndex = true) {
if (Count < 0) stack.Add(new bool[f.width, f.height]);
if (Index < Count - 1) {
stack.RemoveRange(Index + 1, Count - Index - 1);
}
stack.Add(data);
bool[,] d = new bool[f.width, f.height];
Array.Copy(f.data, d, f.data.Length);
stack.Insert(Count, d);
if (useIndex) {
if (Count > Depth) stack.RemoveAt(0);
else Index++;
}
}
public void AddPost(MainForm.FrameMiniature f) {
var d = stack.ElementAt(Count);
Array.Copy(f.data, d, f.data.Length);
}
public void ApplyAdded() {
if (Count > Depth)
while (Count > Depth) stack.RemoveAt(0);
else Index = Count - 1;
while (Count > Depth) stack.RemoveAt(0);
Index = Count - 1;
}
public void Remove(bool useIndex = true) {
stack.RemoveAt(stack.Count - 1);
stack.RemoveAt(Count - 1);
if (useIndex) Index--;
}
public void Undo(ref bool[,] data) {
public void Undo(MainForm.FrameMiniature f) {
if (Index < 0) return;
data = stack.ElementAt(Index);
var d = stack.ElementAt(Index);
Array.Copy(d, f.data, d.Length);
Index--;
}
public void Redo(ref bool[,] data) {
public void Redo(MainForm.FrameMiniature f) {
if (Index >= Count - 1) return;
Index++;
data = stack.ElementAt(Index);
var d = stack.ElementAt(Index + 1);
Array.Copy(d, f.data, d.Length);
}
}
}

View File

@@ -85,7 +85,6 @@
toolTip1 = new System.Windows.Forms.ToolTip(components);
chkLeftSide = new System.Windows.Forms.CheckBox();
chkTopSide = new System.Windows.Forms.CheckBox();
label3 = new System.Windows.Forms.Label();
((System.ComponentModel.ISupportInitialize)nudX).BeginInit();
((System.ComponentModel.ISupportInitialize)nudY).BeginInit();
panel1.SuspendLayout();
@@ -346,7 +345,7 @@
miniList.TabStop = false;
miniList.TileSize = new System.Drawing.Size(50, 50);
miniList.UseCompatibleStateImageBehavior = false;
miniList.SelectedIndexChanged += miniList_SelectedIndexChanged;
miniList.SelectedIndexChanged += MiniList_SelectedIndexChanged;
//
// ilMiniatures
//
@@ -457,7 +456,7 @@
saveAsToolStripMenuItem.Size = new System.Drawing.Size(184, 22);
saveAsToolStripMenuItem.Text = "Save as";
saveAsToolStripMenuItem.ToolTipText = "Save changes to another file";
saveAsToolStripMenuItem.Click += saveToolStripMenuItem_Click;
saveAsToolStripMenuItem.Click += SaveToolStripMenuItem_Click;
//
// exitToolStripMenuItem
//
@@ -474,7 +473,7 @@
//
editToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { undoToolStripMenuItem, redoToolStripMenuItem, copyToolStripMenuItem, pasteToolStripMenuItem, prependSymbolToolStripMenuItem, appendSymbolToolStripMenuItem, removeSymbolToolStripMenuItem, removeBeforeToolStripMenuItem, removeAfterToolStripMenuItem, applyToolStripMenuItem });
editToolStripMenuItem.Name = "editToolStripMenuItem";
editToolStripMenuItem.Size = new System.Drawing.Size(122, 20);
editToolStripMenuItem.Size = new System.Drawing.Size(39, 20);
editToolStripMenuItem.Text = "Edit";
editToolStripMenuItem.DropDownOpening += editToolStripMenuItem_DropDownOpening;
//
@@ -496,6 +495,7 @@
redoToolStripMenuItem.Size = new System.Drawing.Size(215, 22);
redoToolStripMenuItem.Text = "Redo";
redoToolStripMenuItem.ToolTipText = "Redo canvas change";
redoToolStripMenuItem.Click += redoToolStripMenuItem_Click;
//
// copyToolStripMenuItem
//
@@ -607,7 +607,7 @@
// ClearToolStripMenuItem
//
ClearToolStripMenuItem.Name = "ClearToolStripMenuItem";
ClearToolStripMenuItem.ShortcutKeyDisplayString = "Ctrl+W";
ClearToolStripMenuItem.ShortcutKeyDisplayString = "";
ClearToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.W;
ClearToolStripMenuItem.Size = new System.Drawing.Size(197, 22);
ClearToolStripMenuItem.Text = "Clear canvas";
@@ -618,7 +618,7 @@
//
shiftUpToolStripMenuItem.Image = Properties.Resources.arrow_top;
shiftUpToolStripMenuItem.Name = "shiftUpToolStripMenuItem";
shiftUpToolStripMenuItem.ShortcutKeyDisplayString = "Ctrl+Up";
shiftUpToolStripMenuItem.ShortcutKeyDisplayString = "";
shiftUpToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Up;
shiftUpToolStripMenuItem.Size = new System.Drawing.Size(197, 22);
shiftUpToolStripMenuItem.Text = "Shift Up";
@@ -629,7 +629,7 @@
//
shiftDownToolStripMenuItem.Image = Properties.Resources.arrow_down;
shiftDownToolStripMenuItem.Name = "shiftDownToolStripMenuItem";
shiftDownToolStripMenuItem.ShortcutKeyDisplayString = "Ctrl+Down";
shiftDownToolStripMenuItem.ShortcutKeyDisplayString = "";
shiftDownToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Down;
shiftDownToolStripMenuItem.Size = new System.Drawing.Size(197, 22);
shiftDownToolStripMenuItem.Text = "Shift Down";
@@ -640,7 +640,7 @@
//
shiftLeftToolStripMenuItem.Image = Properties.Resources.arrow_back;
shiftLeftToolStripMenuItem.Name = "shiftLeftToolStripMenuItem";
shiftLeftToolStripMenuItem.ShortcutKeyDisplayString = "Ctrl+Left";
shiftLeftToolStripMenuItem.ShortcutKeyDisplayString = "";
shiftLeftToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Left;
shiftLeftToolStripMenuItem.Size = new System.Drawing.Size(197, 22);
shiftLeftToolStripMenuItem.Text = "Shift left";
@@ -651,7 +651,7 @@
//
shiftRightToolStripMenuItem.Image = Properties.Resources.arrow_next;
shiftRightToolStripMenuItem.Name = "shiftRightToolStripMenuItem";
shiftRightToolStripMenuItem.ShortcutKeyDisplayString = "Ctrl+Right";
shiftRightToolStripMenuItem.ShortcutKeyDisplayString = "";
shiftRightToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Right;
shiftRightToolStripMenuItem.Size = new System.Drawing.Size(197, 22);
shiftRightToolStripMenuItem.Text = "Shift Right";
@@ -662,7 +662,7 @@
//
invertToolStripMenuItem.Image = Properties.Resources.Ionic_Ionicons_Invert_mode_outline_16;
invertToolStripMenuItem.Name = "invertToolStripMenuItem";
invertToolStripMenuItem.ShortcutKeyDisplayString = "Ctrl+I";
invertToolStripMenuItem.ShortcutKeyDisplayString = "";
invertToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.I;
invertToolStripMenuItem.Size = new System.Drawing.Size(197, 22);
invertToolStripMenuItem.Text = "Invert";
@@ -673,8 +673,7 @@
//
mirrorXToolStripMenuItem.Image = Properties.Resources.Famfamfam_Silk_Shape_flip_horizontal_16;
mirrorXToolStripMenuItem.Name = "mirrorXToolStripMenuItem";
mirrorXToolStripMenuItem.ShortcutKeyDisplayString = "Ctrl+X";
mirrorXToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.X;
mirrorXToolStripMenuItem.ShortcutKeyDisplayString = "";
mirrorXToolStripMenuItem.Size = new System.Drawing.Size(197, 22);
mirrorXToolStripMenuItem.Text = "Mirror X";
mirrorXToolStripMenuItem.ToolTipText = "Mirror by X axis (horizontal)";
@@ -684,8 +683,7 @@
//
mirrorYToolStripMenuItem.Image = Properties.Resources.Famfamfam_Silk_Shape_flip_vertical_16;
mirrorYToolStripMenuItem.Name = "mirrorYToolStripMenuItem";
mirrorYToolStripMenuItem.ShortcutKeyDisplayString = "Ctrl+Y";
mirrorYToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Y;
mirrorYToolStripMenuItem.ShortcutKeyDisplayString = "";
mirrorYToolStripMenuItem.Size = new System.Drawing.Size(197, 22);
mirrorYToolStripMenuItem.Text = "Mirror Y";
mirrorYToolStripMenuItem.ToolTipText = "Mirror by Y axis (vertical)";
@@ -694,7 +692,7 @@
// exportToolStripMenuItem
//
exportToolStripMenuItem.Name = "exportToolStripMenuItem";
exportToolStripMenuItem.ShortcutKeyDisplayString = "Ctrl+E";
exportToolStripMenuItem.ShortcutKeyDisplayString = "";
exportToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.E;
exportToolStripMenuItem.Size = new System.Drawing.Size(197, 22);
exportToolStripMenuItem.Text = "Export";
@@ -761,21 +759,11 @@
toolTip1.SetToolTip(chkTopSide, "Height changes will be made on Top/Bottom side");
chkTopSide.UseVisualStyleBackColor = true;
//
// label3
//
label3.AutoSize = true;
label3.Location = new System.Drawing.Point(645, 78);
label3.Name = "label3";
label3.Size = new System.Drawing.Size(17, 15);
label3.TabIndex = 21;
label3.Text = "h:";
//
// MainForm
//
AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
ClientSize = new System.Drawing.Size(915, 647);
Controls.Add(label3);
Controls.Add(chkTopSide);
Controls.Add(chkLeftSide);
Controls.Add(btnBaseline);
@@ -874,7 +862,6 @@
private System.Windows.Forms.ToolStripMenuItem removeBeforeToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem removeAfterToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem makeVarWidthToolStripMenuItem;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.ToolStripMenuItem undoToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem redoToolStripMenuItem;
}

View File

@@ -68,7 +68,7 @@ namespace McBitFont {
public MainForm() {
InitializeComponent();
this.dotPanel.MouseWheel += new MouseEventHandler(this.dotPanel_MouseWheel);
this.dotPanel.MouseWheel += new MouseEventHandler(this.DotPanel_MouseWheel);
}
private void Form1_Load(object sender, EventArgs e) {
@@ -89,7 +89,7 @@ namespace McBitFont {
miniList.Refresh();
miniList.Items[0].Selected = true;
miniList.Select();
f = copyFrame(frames.Find(x => x.code == 0));
f = CopyFrame(frames.Find(x => x.code == 0));
ListViewItem_SetSpacing(miniList, 50 + 2, 50 + 22);
@@ -99,7 +99,7 @@ namespace McBitFont {
// Chek for arguments
if (Environment.GetCommandLineArgs().Length > 1) {
loadProject(Environment.GetCommandLineArgs()[1]);
LoadProject(Environment.GetCommandLineArgs()[1]);
}
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
@@ -112,7 +112,7 @@ namespace McBitFont {
return (int)(((ushort)lowPart) | (uint)(highPart << 16));
}
FrameMiniature copyFrame(FrameMiniature frame) {
FrameMiniature CopyFrame(FrameMiniature frame) {
var ff = new FrameMiniature(frame.code, frame.width, frame.height);
Array.Copy(frame.data, ff.data, frame.data.Length);
return ff;
@@ -124,7 +124,7 @@ namespace McBitFont {
SendMessage(listview.Handle, LVM_SETICONSPACING, IntPtr.Zero, (IntPtr)MakeLong(leftPadding, topPadding));
}
private void dotPanel_MouseWheel(object sender, MouseEventArgs e) {
private void DotPanel_MouseWheel(object sender, MouseEventArgs e) {
int t = e.Delta / 120;
if (e.Delta == 0) return;
if (ModifierKeys.HasFlag(Keys.Control)) {
@@ -217,6 +217,9 @@ namespace McBitFont {
w = pixelOffset + dotWidth * (cellSize + gap);
h = pixelOffset + dotHeight * (cellSize + gap);
cbZoom_SelectedIndexChanged(cbZoom, null);
// Re-create history object
history = new CanvasHistory();
}
private void cbZoom_SelectedIndexChanged(object sender, EventArgs e) {
@@ -248,6 +251,8 @@ namespace McBitFont {
private void btnShiftLeft_Click(object sender, EventArgs e) {
bool c;
history.AddPre(f);
for (int j = 0; j < dotHeight; j++) {
c = f.data[0, j];
for (int i = 0; i < dotWidth; i++) {
@@ -258,12 +263,16 @@ namespace McBitFont {
}
}
}
history.AddPost(f);
CheckHistoryButtons();
modified = true;
dotPanel.Refresh();
}
private void btnShiftRight_Click(object sender, EventArgs e) {
bool c;
history.AddPre(f);
for (int j = 0; j < dotHeight; j++) {
c = f.data[dotWidth - 1, j];
for (int i = dotWidth - 1; i >= 0; i--) {
@@ -274,6 +283,8 @@ namespace McBitFont {
}
}
}
history.AddPost(f);
CheckHistoryButtons();
modified = true;
dotPanel.Refresh();
}
@@ -313,8 +324,7 @@ namespace McBitFont {
// history management
if (e.Button != MouseButtons.None && !mouseDown) {
mouseDown = true;
history.Add(f.data, false);
label3.Text += history.Count.ToString();
history.AddPre(f, false);
}
if (e.Button == MouseButtons.None && mouseDown) {
mouseDown = false;
@@ -323,8 +333,9 @@ namespace McBitFont {
} else {
fChanged = false;
history.ApplyAdded();
history.AddPost(f);
}
label3.Text += history.Count.ToString();
CheckHistoryButtons();
}
// Paint black / white
@@ -351,6 +362,8 @@ namespace McBitFont {
private void btnShiftUp_Click(object sender, EventArgs e) {
bool c;
history.AddPre(f);
for (int i = 0; i < dotWidth; i++) {
c = f.data[i, 0];
for (int j = 0; j < dotHeight; j++) {
@@ -361,12 +374,16 @@ namespace McBitFont {
}
}
}
history.AddPost(f);
CheckHistoryButtons();
modified = true;
dotPanel.Refresh();
}
private void btnShiftDown_Click(object sender, EventArgs e) {
bool c;
history.AddPre(f);
for (int i = 0; i < dotWidth; i++) {
c = f.data[i, dotHeight - 1];
for (int j = dotHeight - 1; j >= 0; j--) {
@@ -377,16 +394,21 @@ namespace McBitFont {
}
}
}
history.AddPost(f);
CheckHistoryButtons();
modified = true;
dotPanel.Refresh();
}
private void btnInvert_Click(object sender, EventArgs e) {
history.AddPre(f);
for (int i = 0; i < dotWidth; i++) {
for (int j = 0; j < dotHeight; j++) {
f.data[i, j] = !f.data[i, j];
}
}
history.AddPost(f);
CheckHistoryButtons();
modified = true;
dotPanel.Refresh();
}
@@ -395,6 +417,7 @@ namespace McBitFont {
int a, b, j;
bool c;
history.AddPre(f);
for (j = 0; j < dotHeight; j++) {
a = 0;
b = dotWidth - 1;
@@ -406,6 +429,8 @@ namespace McBitFont {
b--;
}
}
history.AddPost(f);
CheckHistoryButtons();
modified = true;
dotPanel.Refresh();
}
@@ -414,6 +439,7 @@ namespace McBitFont {
int a, b, i;
bool c;
history.AddPre(f);
for (i = 0; i < dotWidth; i++) {
a = 0;
b = dotHeight - 1;
@@ -425,6 +451,8 @@ namespace McBitFont {
b--;
}
}
history.AddPost(f);
CheckHistoryButtons();
modified = true;
dotPanel.Refresh();
}
@@ -434,7 +462,7 @@ namespace McBitFont {
if (MessageBox.Show("Current symbol is modified.\nDo you want to save the changes?", "Symbol was modified!", MessageBoxButtons.YesNo) == DialogResult.Yes) {
saveFrame();
} else {
f = copyFrame(frames.Find(x => x.code == f.code));
f = CopyFrame(frames.Find(x => x.code == f.code));
}
modified = false;
}
@@ -609,7 +637,7 @@ namespace McBitFont {
var sss = decodeSymbol(ff.code);
miniList.Items.Add(s, s + ' ' + append + sss, s);
}
f = copyFrame(frames.First());
f = CopyFrame(frames.First());
dotPanel.Refresh();
miniList.Refresh();
form.Dispose();
@@ -626,7 +654,7 @@ namespace McBitFont {
}
}
private void miniList_SelectedIndexChanged(object sender, EventArgs e) {
private void MiniList_SelectedIndexChanged(object sender, EventArgs e) {
if (miniList.FocusedItem == null) return;
checkModifiedFrame();
if (miniList.SelectedItems.Count == 0) {
@@ -644,7 +672,7 @@ namespace McBitFont {
var sel = miniList.SelectedItems[0];
int code = Convert.ToInt32(sel.ImageKey);
FrameMiniature ff = copyFrame(frames.Find(x => x.code == code));
FrameMiniature ff = CopyFrame(frames.Find(x => x.code == code));
nudX.Value = ff.width;
nudY.Value = ff.height;
f = ff;
@@ -671,14 +699,14 @@ namespace McBitFont {
else pasteToolStripMenuItem.Enabled = false;
}
private void saveToolStripMenuItem_Click(object sender, EventArgs e) {
private void SaveToolStripMenuItem_Click(object sender, EventArgs e) {
checkModifiedFrame();
if (dlgSave.ShowDialog() == DialogResult.OK) {
saveProject(dlgSave.FileName);
SaveProject(dlgSave.FileName);
}
}
private void loadProject(string filename) {
private void LoadProject(string filename) {
SaveBlock sav;
using (FileStream fs = File.Open(filename, FileMode.Open)) {
@@ -706,7 +734,7 @@ namespace McBitFont {
dotResize((int)nudX.Value, (int)nudY.Value);
nudX.ValueChanged += nudX_ValueChanged;
nudY.ValueChanged += nudY_ValueChanged;
f = copyFrame(frames.First());
f = CopyFrame(frames.First());
dotPanel.Refresh();
miniList.Refresh();
modified = false;
@@ -719,7 +747,7 @@ namespace McBitFont {
fbuffer = false;
}
private void saveProject(string filename) {
private void SaveProject(string filename) {
SaveBlock sav;
sav.monospaced = monospaced;
sav.frames = frames;
@@ -744,7 +772,7 @@ namespace McBitFont {
}
}
if (dlgOpen.ShowDialog() == DialogResult.OK) {
loadProject(dlgOpen.FileName);
LoadProject(dlgOpen.FileName);
}
}
@@ -792,7 +820,7 @@ namespace McBitFont {
private void copyToolStripMenuItem_Click(object sender, EventArgs e) {
fbuffer = true;
fbuf = copyFrame(f);
fbuf = CopyFrame(f);
pasteToolStripMenuItem.Enabled = true;
}
@@ -838,13 +866,16 @@ namespace McBitFont {
saveAsToolStripMenuItem.PerformClick();
} else {
checkModifiedFrame();
saveProject(prjFileName);
SaveProject(prjFileName);
}
}
private void btnClear_Click(object sender, EventArgs e) {
history.AddPre(f);
Array.Clear(f.data, 0, f.data.Length);
history.AddPost(f);
CheckHistoryButtons();
modified = true;
dotPanel.Refresh();
}
@@ -901,7 +932,7 @@ namespace McBitFont {
prjModified = true;
}
private void editToolStripMenuItem_DropDownOpening(object sender, EventArgs e) {
public void CheckHistoryButtons() {
undoToolStripMenuItem.Enabled = history.Undos > 0;
redoToolStripMenuItem.Enabled = history.Redos > 0;
@@ -909,9 +940,20 @@ namespace McBitFont {
redoToolStripMenuItem.Text = "Redo (" + history.Redos + ")";
}
private void editToolStripMenuItem_DropDownOpening(object sender, EventArgs e) {
CheckHistoryButtons();
}
private void undoToolStripMenuItem_Click(object sender, EventArgs e) {
history.Undo(ref f.data);
history.Undo(f);
dotPanel.Refresh();
CheckHistoryButtons();
}
private void redoToolStripMenuItem_Click(object sender, EventArgs e) {
history.Redo(f);
dotPanel.Refresh();
CheckHistoryButtons();
}
}
}

View File

@@ -1,5 +1,6 @@
Application:
V Migrate from .Net Framework 4.7 to .NET 9
V New Save file format! Use McBitFont v1.7 to convert old save files to the new format.
V Better quality pictures in symbol list
Functionality:
@@ -8,7 +9,7 @@ V Delete symbols before/after selected
- Shift all symbols on code line (change symbol codes)
- Specify starting code (extends the shift)
V Ability to make monospaced font a variable width one
- Undo/Redo
V Undo/Redo for canvas changes
- Image import from a file
- Import from a text array
- Rectangle selection to mass-paint, shift and mirror pixels