From 038fd42841a0df4e43942fa9c500a79c3223e2a9 Mon Sep 17 00:00:00 2001 From: Anton Mukhin Date: Thu, 22 May 2025 01:10:38 +0300 Subject: [PATCH] TODO feature: Undo/Redo for canvas changes --- McBitFont/CanvasHistory.cs | 39 +++++++++++------ McBitFont/Form1.Designer.cs | 39 ++++++----------- McBitFont/Form1.cs | 86 +++++++++++++++++++++++++++---------- TODO.txt | 3 +- 4 files changed, 104 insertions(+), 63 deletions(-) diff --git a/McBitFont/CanvasHistory.cs b/McBitFont/CanvasHistory.cs index a096334..887d258 100644 --- a/McBitFont/CanvasHistory.cs +++ b/McBitFont/CanvasHistory.cs @@ -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); } } } diff --git a/McBitFont/Form1.Designer.cs b/McBitFont/Form1.Designer.cs index 83a1b9e..d38543d 100644 --- a/McBitFont/Form1.Designer.cs +++ b/McBitFont/Form1.Designer.cs @@ -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; } diff --git a/McBitFont/Form1.cs b/McBitFont/Form1.cs index 0ec7e42..666e0fa 100644 --- a/McBitFont/Form1.cs +++ b/McBitFont/Form1.cs @@ -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(); } } } diff --git a/TODO.txt b/TODO.txt index c9a6121..32940e3 100644 --- a/TODO.txt +++ b/TODO.txt @@ -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