From efc2b73709290e5ff66738d4471ea2d9c2fa0778 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Wed, 14 Oct 2026 13:15:16 -0400 Subject: [PATCH] Add cutting action icons and toolbar shortcuts --- .../Forms/NestReportExportTests.cs | 29 +++++++++++++ OpenNest/Forms/MainForm.cs | 38 ++++++++++++++++++ OpenNest/Properties/Resources.Designer.cs | 21 ++++++++++ OpenNest/Properties/Resources.resx | 9 +++++ OpenNest/Resources/cut_planner.png | Bin 0 -> 908 bytes OpenNest/Resources/manual_sequence.png | Bin 0 -> 1322 bytes OpenNest/Resources/post.png | Bin 0 -> 989 bytes 7 files changed, 97 insertions(+) create mode 100644 OpenNest/Resources/cut_planner.png create mode 100644 OpenNest/Resources/manual_sequence.png create mode 100644 OpenNest/Resources/post.png diff --git a/OpenNest.WinForms.Tests/Forms/NestReportExportTests.cs b/OpenNest.WinForms.Tests/Forms/NestReportExportTests.cs index 965bdb88..a93b8351 100644 --- a/OpenNest.WinForms.Tests/Forms/NestReportExportTests.cs +++ b/OpenNest.WinForms.Tests/Forms/NestReportExportTests.cs @@ -12,6 +12,35 @@ namespace OpenNest.WinForms.Tests.Forms; [Collection("Fill operation lifetime")] public class NestReportExportTests { + [Fact] + public void CuttingToolbarExposesExistingActionsAndOnlyAvailablePosts() => RunSta(() => + { + using var host = new TestMainForm(); + var toolbar = (ToolStrip)typeof(MainForm) + .GetField("toolStrip1", BindingFlags.Instance | BindingFlags.NonPublic)! + .GetValue(host)!; + foreach (var name in new[] { "btnCutPlanner", "btnManualSequence", "btnPost" }) + { + var item = Assert.Single(toolbar.Items.Find(name, false)); + Assert.Equal(24, item.Image!.Width); + Assert.Equal(24, item.Image.Height); + } + + var post = Assert.IsType(toolbar.Items["btnPost"]); + var menu = Menu(host, "mnuNestPost"); + Assert.Equal(menu.Visible, post.Visible); + Assert.Equal(menu.DropDownItems.Count, post.DropDownItems.Count); + // Without a document these toolbar shortcuts must be harmless. + ((ToolStripButton)toolbar.Items["btnCutPlanner"]!).PerformClick(); + ((ToolStripButton)toolbar.Items["btnManualSequence"]!).PerformClick(); + foreach (ToolStripMenuItem item in menu.DropDownItems) + { + var toolbarItem = Assert.Single(post.DropDownItems.OfType(), + candidate => candidate.Text == item.Text); + Assert.Same(item.Tag, toolbarItem.Tag); + } + }); + [Fact] public void MenuFollowsActiveDocument() => RunSta(() => { diff --git a/OpenNest/Forms/MainForm.cs b/OpenNest/Forms/MainForm.cs index c1245bff..de1c7dc1 100644 --- a/OpenNest/Forms/MainForm.cs +++ b/OpenNest/Forms/MainForm.cs @@ -36,6 +36,17 @@ namespace OpenNest.Forms private bool databaseOpenInProgress; private readonly ToolStripMenuItem storageModeMenu = new("Storage Mode..."); private readonly ToolStripMenuItem exportNestMenu = new("Export .nest..."); + private readonly ToolStripDropDownButton postButton = new() + { + Name = "btnPost", + Text = "Post", + ToolTipText = "Post CNC program", + DisplayStyle = ToolStripItemDisplayStyle.Image, + Image = Resources.post, + ImageScaling = ToolStripItemImageScaling.None, + Padding = new Padding(5, 0, 5, 0), + Visible = false, + }; private const float ZoomInFactor = 1.5f; private const float ZoomOutFactor = 1.0f / ZoomInFactor; @@ -93,6 +104,11 @@ namespace OpenNest.Forms public MainForm() { InitializeComponent(); + toolStrip1.Items.Add(CreateActionButton("btnCutPlanner", "Plan Cutting", + Resources.cut_planner, PlatePlanCutting_Click)); + toolStrip1.Items.Add(CreateActionButton("btnManualSequence", "Manual Sequence", + Resources.manual_sequence, ManualSequenceParts_Click)); + toolStrip1.Items.Add(postButton); storageModeMenu.Click += StorageMode_Click; exportNestMenu.Click += ExportNest_Click; mnuFile.DropDownItems.Insert(mnuFile.DropDownItems.IndexOf(mnuFileExport), exportNestMenu); @@ -543,6 +559,23 @@ namespace OpenNest.Forms } } + private static ToolStripButton CreateActionButton(string name, string text, Image image, + EventHandler click) + { + var button = new ToolStripButton + { + Name = name, + Text = text, + ToolTipText = text, + DisplayStyle = ToolStripItemDisplayStyle.Image, + Image = image, + ImageScaling = ToolStripItemImageScaling.None, + Padding = new Padding(5, 0, 5, 0), + }; + button.Click += click; + return button; + } + private void LoadPosts() { var exepath = Assembly.GetEntryAssembly().Location; @@ -565,6 +598,10 @@ namespace OpenNest.Forms postProcessorMenuItem.Click += PostProcessor_Click; mnuNestPost.DropDownItems.Add(postProcessorMenuItem); + var toolbarPost = new ToolStripMenuItem(postProcessor.Name) { Tag = postProcessor }; + toolbarPost.Click += PostProcessor_Click; + postButton.DropDownItems.Add(toolbarPost); + if (postProcessor is IMaterialProvidingPostProcessor materialProvider) PostProcessorMaterials.AddFrom(materialProvider); } @@ -572,6 +609,7 @@ namespace OpenNest.Forms } mnuNestPost.Visible = mnuNestPost.DropDownItems.Count > 0; + postButton.Visible = postButton.DropDownItems.Count > 0; } #region Overrides diff --git a/OpenNest/Properties/Resources.Designer.cs b/OpenNest/Properties/Resources.Designer.cs index 099fce39..893253fe 100644 --- a/OpenNest/Properties/Resources.Designer.cs +++ b/OpenNest/Properties/Resources.Designer.cs @@ -73,6 +73,27 @@ namespace OpenNest.Properties { /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// + internal static System.Drawing.Bitmap cut_planner { + get { + object obj = ResourceManager.GetObject("cut_planner", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap manual_sequence { + get { + object obj = ResourceManager.GetObject("manual_sequence", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap post { + get { + object obj = ResourceManager.GetObject("post", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + internal static System.Drawing.Bitmap clear { get { object obj = ResourceManager.GetObject("clear", resourceCulture); diff --git a/OpenNest/Properties/Resources.resx b/OpenNest/Properties/Resources.resx index 327c3468..786b8dde 100644 --- a/OpenNest/Properties/Resources.resx +++ b/OpenNest/Properties/Resources.resx @@ -190,4 +190,13 @@ ..\Resources\shapes.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\Resources\cut_planner.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\post.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\manual_sequence.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + \ No newline at end of file diff --git a/OpenNest/Resources/cut_planner.png b/OpenNest/Resources/cut_planner.png new file mode 100644 index 0000000000000000000000000000000000000000..925087a8eda73dd9e63d7425d49184dc0f7fd1b0 GIT binary patch literal 908 zcmV;719SX|P))mG}W79f}K4bm2m& zh>{`_(52u;&_F@Dh<0hCEwvG;5!#q^GH+(yyZ5-5WYS?~@?rhMgU92Z|L>f0&wW?u zl<@yZdwskibGh!(udqE$2X31jT3%jJ&>g}CfWyi_R#1CuhChluH(iw)yR96umXgrKYTf_ji`m9Qq4q^Ujj zDPn}$_wPBma#o$mTYzAFkt!xhoFvY3`LDm_gO5HVit1Qv(H=t~B1BQ0J$H@s{IgG! z9(w=`XrXBG%idHq)d@hAFtAitYrOF4aV}h3pc%)g_MA8r$YwH}J~PLG`|oA{-btKO zJ$deCNzZ4dj_&h6RVoz|bOoxiw7kN|$S`le`3m`5mL!Q0q}4bP)H%}W6d#^E#hJOY zr~+cJ7Cm>ZU_PHI{P@z$&;OLm5$WjtjsV6ORF#!$RX|DNm<=z%D`Yci66dyTRTsc}rO{|muQzz+xvAFQ zu}7c)jb@Aj&A5BB+sST32yx;l7IHj#09?K|rI?B%KOrnMgZq4WI&&%IC5^3<3pYbY!@1*}W77 zSpXLVL8ykX4X9KqW_o%yifZ)>Q^(((G}8KAC|h4~?KL8xinZ4K^6UB9?$N=8OMk$` z#DunQxqA*gy!+zit9z1qV@H`^&kL+IL!$$K{yz7^{JQUdHQP$k^#Vw_T;9n&(^Fqh iOeiN#_#5@vw*CdIhD*%T3U@mI0000NkldRxR|$i_iuCnIFZ1m2O~}#HxkX zHc;B4;*X-WLIw(4t3W`MH8hBbE$gVu<$A?#=$75kfuKV-6+}L!LiV7BCS#g0 z43$)BOf<#J9=nV`2&H2<60{l9blOCBup$7uCoNUoxqDA-dU?HaIa8D%VcG2lNrKJ+h=bcoZFMGOooPzu zfDi;YZAi6A=l~ei;K@YVXzJw4TN8;!qc^vIyr(3^Fkm-ySW4mi#cQIur7fK6&FrKa zs6b#7st#RLKoGvX!VJY}>ifpwW4Rvf1oDg1iLN~aV2#T@>CVrstH|!`9Vi+c9DwClW|RT;Xt}eIPuvvE}zu zcczo`X&_8$>GId!igxw(@z1Bu@aJD2;?oW1csLa1s-4yBUQqfH0GOsJwa0^)ruoFw z1`7)rH8pCSfCOt&4ja0zyIoFxVa-a~wYL`a#||h%*C`f`CZ~EZ0S*R(7G?pZSSY3F z#kDKZaPBY4_jzH{G)g8DLenJ0M!%bG$IuMdV+0mEQtSxUpTyq%-(cI;Ef7*bDQ19p zrV0oEV|q-Gup%j>VdxrcHVu_qws2qnAZt=uhN0V7C2cbia9VzG`cOv)14AQZ`Nf2e zYeN7O0II7qW$@eP_^nX1oOG-AzS=45~-`p_%OPwiU1{G0_Lh#AXcx+u0 z$6_(`-RsBNRm*Ymmj?9Te*i<*xO+fFv>mN`M)Ck5XbMyY6;(ccZYBsuwyBZoa&bpj z4|jI;;EUZKAvb3ZTZ)B|qUnh`$Y8T6iGp!pb^}SH(vW|Cx#3+r=Uhoq;c|z=4k-jI z%R(%kKc7*Nawfp4)T(?$V)whioB9Qq$P!aMIlL^v>%| gr^j1ww!s15KR8_|wOv*)asU7T07*qoM6N<$f}+!ehyVZp literal 0 HcmV?d00001 diff --git a/OpenNest/Resources/post.png b/OpenNest/Resources/post.png new file mode 100644 index 0000000000000000000000000000000000000000..02b44d3d7b231c344b9e3317b4880899f0d7c9a3 GIT binary patch literal 989 zcmV<310wv1P)Ad({8ih{+DVzM#D-8AX$#@Wrx&fIf+*tN-KKWr;rxC}FM&;6hC zoBz23v=73~jelL=u^@5hSh8BF^p8p=fx`D|0QklPvRQR#dRo=3!S@rf*u%_h1kl|B zh?P=QtFOy=|aef>qT^-B7S#7Ay>;P~TiVX-LX>Y9{R%Cfw=c~+X&m22{sQO`!J_~$H8s^W82|`v$S6^yocQ*ros4f6^x$iZf!3N85e3E%`toTLXjZ8VGr0{P z`QQE^!vX?901yF&8F%d*#84_hEUbDZ(NjPmP>SFi#;5sTP;dCq)D%!eaQy(MCk|pL zm4Ngl-ba8yU=~CMEWG*XMAv|zMMbF_b989y10ujT2D3jez(^BOOC5$8qdU_WO2ykM zn8J_x9Kh7YNgarY2!6o$>{35OaY#!+1VnVX0RWPPqFj4#{5M3J6%k}ExA*X$mD&Z$ zEv}!>9ts`K&0KfaEhAC@YFEjTkM`_CG4xd|9GzV_Gy24?S7xqk7U_xT^Y4CbB2q0x zAhrz-jYp0Eq&g6J zjYg|dg4nF-^yMOelgnrJeHpgpt#mZ>Vd?d~_re{1Nif5mJD_QWy60ovYjjNJ4Wk5x zuHSe8U1X*u-v|JCwi=#`Q4YMdlZ9Ep%;?og%-pJ0kg!=hXU8L@YqiIsw!9s&RcO^Q zMnH~)o8lV+!bBiD8VR#)Sz=pOzfR%+mRg##OGr7Hlp8u*_k!P-9rJ>MyhDPpcv_l? z00a??WbO-RMl%=2_TRH#QLBJ05(FS%AZ`neS<`X{mhyv9tQw{x0-1-<4@%)Uvv8Q) z;pEE;W!e-g2kxKTu~4dvd7j(pUjSNzJOeo}=*K@f;eMP>ZUYQZYcOmLSehULTy=uk z+lSo;F<)rh>@RYI=zE#F0oGt&z#Lky#G@5os~r(rSK;N;&s|v)0ksM2o}3&=HX;*H zWC5o42Vjzd6RifOWhos-_1npBfD?_aYyTlZ07S3ZHf_W61jIy?K)Rpz8o=(jqXB?~ zn6$439{S+a`KNF(UmjQEPv?Wy5%&L&&J43zmA!?Ha=DzK%W?fTAg|?qt%)&Z00000 LNkvXXu0mjf49d~6 literal 0 HcmV?d00001