From 9f0f2a165c52df1c129e83b2924e1a0512372cf1 Mon Sep 17 00:00:00 2001 From: Anthony West Date: Thu, 29 Oct 2026 00:08:39 -0700 Subject: [PATCH 1/2] Add icon resource image files and ImageMagick script --- resource/ASWUnitTests.ico | Bin 0 -> 52221 bytes resource/icon_16.png | Bin 0 -> 435 bytes resource/icon_20.png | Bin 0 -> 537 bytes resource/icon_24.png | Bin 0 -> 674 bytes resource/icon_256.png | Bin 0 -> 8115 bytes resource/icon_32.png | Bin 0 -> 905 bytes resource/icon_40.png | Bin 0 -> 1051 bytes resource/icon_48.png | Bin 0 -> 1596 bytes resource/icon_64.png | Bin 0 -> 2010 bytes resource/make_icon.ps1 | 141 ++++++++++++++++++++++++++++++++++++++ 10 files changed, 141 insertions(+) create mode 100644 resource/ASWUnitTests.ico create mode 100644 resource/icon_16.png create mode 100644 resource/icon_20.png create mode 100644 resource/icon_24.png create mode 100644 resource/icon_256.png create mode 100644 resource/icon_32.png create mode 100644 resource/icon_40.png create mode 100644 resource/icon_48.png create mode 100644 resource/icon_64.png create mode 100644 resource/make_icon.ps1 diff --git a/resource/ASWUnitTests.ico b/resource/ASWUnitTests.ico new file mode 100644 index 0000000000000000000000000000000000000000..7904744413426b9c6b170b3b3196f0eea48caf1b GIT binary patch literal 52221 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 i4RG|@-~Z7Jgy$<>;>;5&cCgbqf|+NB$p|5krUo literal 0 HcmV?d00001 diff --git a/resource/icon_16.png b/resource/icon_16.png new file mode 100644 index 0000000000000000000000000000000000000000..91adc467ad73abdb632b57588caf93f986c17c62 GIT binary patch literal 435 zcmV;k0ZjghP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0Z2(iK~y+TV*ml2 z;=1_^S_!M~1Mz<{@plHz*k%7o z!TJR=|Ldg9hw~ZKqZa)q7Te9O`0sr*?!VWeg#TJ8bHHK@Y7z7P6N7E%lz}zFuwKCg zusDNC=&b*QufHPAZ*@Sf}+@YT{;TiZhkIU9Xe)^_v-u)!`9RD zQ8dA@{o?HZ3Vt1Mb;y`O&b{?NGPatQi=x?XQPzKX?-mp_$e2OKwedeXHto#+@4hqf zzs1Dd|FWJ981l%NLCUG-y|h#9e{wN{q+P{)N&AZbWMR9C{S5N<`2rHQCC7pIKWVu5 dytsY20sw;f5Y&(-`V9a8002ovPDHLkV1jRF$}9i? literal 0 HcmV?d00001 diff --git a/resource/icon_20.png b/resource/icon_20.png new file mode 100644 index 0000000000000000000000000000000000000000..749d60080051f79f9a0bd76033c2565ed3c97bee GIT binary patch literal 537 zcmV+!0_OdRP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0j^0zK~y+Tt<=d& z0&x@v@b_0_n@UBEh8jdfF;ke(h6qs+(P9qir4Cx=8imnfU^`LSLSbMF1(i^A>eRVo z$BrF4c6|98m}#6D56TRD4)fmkeJ=w8&wttE z1FE5(lp5K)mwNUd)nH`rKf&O<{xMG6pjyB%md0W=Ms_*k8BilBER$Jf9QpdUhiyr(W5dKJsxlN`3l^sjbhh8DC1a!UYDAo`T zD;5#$ms6;B#B=gepzU5oDN8UYFH23K&b}hcNSvr)j(8no)45+BOm3~D5~TeG;JYQX?qCOknM_be{Bst8!n;!zZzx^i&eq| b;s5}DVJJ7N-;D(N00000NkvXXu0mjfw8rF` literal 0 HcmV?d00001 diff --git a/resource/icon_24.png b/resource/icon_24.png new file mode 100644 index 0000000000000000000000000000000000000000..ae92faeb1115fb90c2c7b7f9c9e5bf92108be476 GIT binary patch literal 674 zcmV;T0$u%yP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0yjxSK~zXfwbn~d z6JZnv;Ink;M%$vaO6i@dQDaQnPTLqmZHX}&L>o$5Y8SGA-mFw1Qb1~q2}VT<4Hq#8 z2@xdd!i6@P7?&>j3H*dEUuPO;=yaxDYCFl3Nxtu#_k5X|#Snj4Z|%ga(f37bbf?7L zQJ-=zoADv12^J6hiYDsl-!67Uw$VtI$h+N>!L5U24sQ-y`Zv)O;kUD73T^x_hc?g* zZ1x_R0_!DH-#VIwjJ>O9gwvU)I5+hOmgu5roecih_#ei=E4bGZ@O|qm^L`DL*Z6(X z-&{t$484nNcw7kQoz3Qi8q)PFpdNaE0j{-}B%EW4&IonX-d(`I(%qPY>y3Q4J%3kJ zOWJFB@bvx{IFlWNZ8i;abXr)i)PDUXT&qL!;qxg;J+)n(0nf3p|2(;uWlcO3_Bh(^ z=WO_(D3oWPPl{@J)OO_=cwA^@Q+Y^iLfCgM*EsoHLa3=m$1hKV$1F)t=lv3dbtns4 zUi!6gxb@16YILmY33$fHBs`zPvO#ie`IyjK3u(NMQOOV<|EnSP!l0nKT+n;kZ+3ai&g{|0=j43R`kZVNaq#z8|*5&l3^-1mXsug=hr1%m4rY07*qo IM6N<$f(>Rc+W-In literal 0 HcmV?d00001 diff --git a/resource/icon_256.png b/resource/icon_256.png new file mode 100644 index 0000000000000000000000000000000000000000..f439c30e3c1c11411ab216b73770d5918c9f2c6c GIT binary patch literal 8115 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 c*a^|+;o%gclkp7vK|7$OuA^3T(<r{=Kufz literal 0 HcmV?d00001 diff --git a/resource/icon_32.png b/resource/icon_32.png new file mode 100644 index 0000000000000000000000000000000000000000..64af3ae57c11d925696a44cf0a71bc6ac9d7c31e GIT binary patch literal 905 zcmV;419tq0P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D10G34K~z{r&6Z0{ zQ$ZAl&(fs}Yg>F!9wM)%EDQzux}*vOL_`px1gP{v0TD`Dp!h%y3dl=*5CT|Z5GV<; zRcPF}ATcp6S-NCpSh8e!oWj)f_D-ROwm8YR>HW|7&dl^Sh5v44^J}e2-sjHdeOt+7L zRipPL)dQqAezIoX@nBqO^1P&afK1b>mw@pk!?e0d_2dNVS9UTQRxsXBJwQhNGRA@& z#|-9;4xzH$#e7cn3^P&vZ16;hlfYC(nath>r@~57Na#RnH zcIyL13!SS^B=@6oOo_5o4{)LOJw^lFyI!W8b9XcIR8Iz|na87|o9|)Em#t*Cv&N0#H3b@{MT>CgtWEEd6d0HPz191v&M&@Kp=IEp_ho2+p#g zdNROu5)L8%HTw&towe;fSC6rey>O+2?CzDQ9}B7nNUE5^P?D=A(Xe=rDKUC2VLwl; znGp2~2h{^8$|sRUQso30qJt?5#ptnMO7$4CPdKQa{2wSA$8abr#?bKb;315j7ILRA znDqxi^<;ojQhWf4Ya?WcyW!=PL(ICvpn8DwCBt|aiC2eEztA2cZzgy7oSBaVswV>! zlO_?6SoVy(9i8L{rWJEXyNP^|nTr&v2RK(Wh{pld1Dw756j>-LotQIV{wE$8>I4*U zc4lusR1c6~9N0Z$e2l>)*V+X49Vx#{%0);uRzdFoR8Iyde7HftVA5JznS#S$4Is&F z7xboVQ9T)ea}FnP{TPZw-G-+w@kc_E+lF$3Q`B3|Io(tbATc;(a(&;n+|WnDu}7xw z-IW{q)2V)c$+V6VnYL#~rt862BGo!}r8mTK)J97Qzt9fDV$ fZxinPco)DQSgJH|LPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1F%U%K~z{r)zr&R zQ(F|k;WhLhxYE|Ehzhhltxj&P8V;qc4=TnN_vV74v_OmaL`%`4fZ&6mMnyp@sKHl! zp;na0Orwcm$k3r96y|3p`0>7-@F@w_Zd_oN4Y)~3T-`oA8D<~&^E%Qbfih(Ix!zj)+r6KC1 ztglO{k4^}v4^KbjdxyV%+*a>npSA7@>WQW9Nf+e-SbQVHR^JHf%cIwn2Vky!Flq5V zL_HBrQBgzwRaQi*{Rg}OmmR@T|_sBsk8R!yk>n=MdQJx5_5ktR#ZPzs{ zAK!`V-_`!l^&rU3N|XmsyzMsTn$k2R;u_=PV&ooohVlT4w%)>AvhEyA7$@0wN7!?e zCjzU7F;}dOk)(0%N6qXxiShsnt8QQ}SOS-m#>qW7!=05VPXzvV9e+0Sj;pET`~&RS z7|H`!^6wBPvaIDax*qP={N3Bh&U~-9<_fNUwu=pu%Etp}uJ32hev9$|7XNbAh4myxrwpAVGO!Irq$!s<)7DoLE@RUH=l&c~feh4Mt;=6>W@>d#`uTOWI&qz(qP zxx%WxlyTO^(=(o*3grP5Y`Tbv*bf}XiZ_WPOGISZ-F@tyB;zcBUiR6kP@V|fcmb2K zH+PFjFB_1I7U%C_pP3HI16a7>9HwI1e=MU&8E5vNWS^BT$^*z>e-_iB?CVS`Qu{&F zpJ1Pn3Ca_J6=yJA%HEEok#bMYaAz|`c>oL6^wi-R#Vdpga+{_5|t?p^?kzLiaaCf4qlvv!GP>F)DG$I{ z))toEGd(dn+ai<)@JCt5V045(8Oy?`2L{LCY4Os`l=o#B)R4#EI3zY_UtVb_Oj+?u zZ1@an>y)9i6=y@98k$ZR7hCe<&QpUCVKPr`5r=YbuG;c`Zb|UIab+<6W7Yox_zbgq VhxiH+yr=*G002ovPDHLkV1gbg`ojPK literal 0 HcmV?d00001 diff --git a/resource/icon_48.png b/resource/icon_48.png new file mode 100644 index 0000000000000000000000000000000000000000..42cd3d05cbe248c38db561002f637df761f6d1da GIT binary patch literal 1596 zcmV-C2E+M@P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1<^@FK~!i%)tXsQ zQ&$+r^XPOW2_OPN1QC*ZZ$c@n3k3l&5Da8t3CJc8ASlRUzy!1+DlSYd)mGfc)-H~1 zZEZVkXWEBOJDooE(Klz9Xy$p#4I%>42IXZih4&OIl0?v(^8 z5sYbT1Y;P8P#MOT6*8twWhk202(3t!rt(OQTIa-G8*e z&L8R^=Sn$neaEQv5p?zhC?)|KKUopf!-KHxRs}CErsDTz$ez&x+de~WhtBQ*iee|I z_S{-_xZwe+yg<=CGjM|=)VAm>3g94r{AgL>`W>+KV;xmoK>Kq&s7Jf0?a|p2pvVEr zhMUU<+cySzH?;gRp@|Fd-v9|p>jRxd0SHMv_KgN5ulubDGT+mIs__!FA9QvH zh~F_nezIPF?J&L0pyjhKl>yT^N$m$gXYT-Y*ZiVvo(7fe9U?)lm$oTt9e-fEkmS3L zQTq|_4p2A@spJO67fk;kcC=z5Gm&Ke4DiP!y(Bld+e`_8whk3 z1sH@B;{__rP!}&q6A&YWNV7UZleZnDjvEMcb_a;fAAn7U_vx6W)PsWcYvyCpQMbBu!RIDn_Lh{`UyxssrNeU@Q}h~!`K_B zZI%Lbb_a;rvJVo=PAnYV1X<&)ka4a8a<6HDJ7R>CmIL&(EUmmStvAlG4X{Cs*3Rs;V$Wqq)Fuq_I6h)(915P?lJ|95FR*y$zwm**F;ec=;I@k7G&Q;&#<%L2* z++V+~AL0rRt!iTPhd|0z|LvZAp{!M!8JW7~;ri_Zq~h?ZB$nLB%Qcq&e7-}0FLsdY zVKzFe<7r%h9aa<3Io9Vg&irrS7eJC)*F><@=kX$utLt1*S#7YA$jNRaWz0|HZ1vq& umW{u{zdJ$F;$g9p;9CSfU+|DfB>w|QQcyi7S0VBM0000ghQE literal 0 HcmV?d00001 diff --git a/resource/icon_64.png b/resource/icon_64.png new file mode 100644 index 0000000000000000000000000000000000000000..8241b52833e0d69474c341fccfdccc95212b0498 GIT binary patch literal 2010 zcmV<02POE4P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D2X9G4K~#8N?VD*( z6K5F5`Pk`bLcF-@Nj7A&B+4irD2H5v$Pp4wxeo;oL?S`PqR|*PaiB#+atk7v59yxzDFBsu@IPuG$B_h_yeZ zV0soP6#S^r8Ve;2*9WGk(ih#DzPN5MJog0XYd@M$XbLYtP|TJmfsuvZy{^j(mb9E3 zIP2`dU+1hjIuu!R7bY@l(`jH6`B16LtCjQ|50sx8Rr!Q3yAF}ncl-if#rrTLrh%=7 z{8mYm9=Pmr=J~Q~?7$EG3Ze2Qs1}qwnxx6qO1kz#r7yTOJLAZZP<0#p5iV~4@Zu=x zjvPw8Id8_2`bR?5Ew;1&5}MHRl-(toB<&YN6`&@?qm=eJIMVj z0g(Okhik>n^G|xUHu{Qcq`~i1klPhUh!Fk=0=C| zR+2u+2mPC7vW69OwF@AWy%0kW8Wf6gVeH3vkvg0tjySJAH9D_+3xp@x!_!zN$aN7fw;9Y9<&sPhKKgey7; zLTe=$j>U%Kvy~d^V<&L z3Qw@4(_z__)g$i7c<)_)JrndtlOdv}lRVZDc#PZfJt?3mKTOt@gA5=zt_i};XC0zr zGr#O=2H6aP4bAUM1Wio`c{~N~>xn#9-bL1R1iIP>5X%BEosmUd&@V1^oNcNhD=$LW zL)IA%?&Fbs*&(v-SfDErz;+1FJuQo-dN(Y&m_{}<8e0hL7^)V6%^P}>-IPzZ$P;y) z0bR)e=H;G}g{y57A2O(>D6AoHECjn+Wd}tG+=n_=4txu2kw0ix(-r6dR&0ZL#*?zB zi@U`8Gu4 zlJ7(LR&pH9V2vIkE$Z431e-joO`X#lper4~nr@fjY8$*3f=!;)q|W6n z$N*+6+YGZaj=F`~3n5tLu@-d>Z-TCL0IQFGam)~G@|&v2 z8siDN+6SYPOE$pFRR_GvytR#>Kb!)K zFQh{3jVuxTIL*+V1mRWQKcBOpD;+@Un_g#D27CKj+5p_fN|Eq#w$j$@A@_Y*$OKT& zHtu^RKv(+!7T3WWDSKZD(3K7#xzj0TWwb-|kxEFu>-YmwZ}1>3(;@d$vfzAz8T3gl zWUmxt0Mnw^Ls(LWQ!MVSAe$WzC~1m2iAJ(l3UnP2fFdHNZc0?;Utx)Rz!_3M9silP zLdnWO^-5tM0RAe(uy!ykaW~sxY+^6c&5j3@D8>EcEV5S$oNSzQNk1R}ze27Q@}7jQ zYzJqUle-NPZ@!#WK*^5g2X-AW#;qfJrBFo{4&t|CC4I|K@TLdBE1DoQq0O%eiEjY~ zpZf>?2uxD93?*;soDx<2G$g*2?c~2qjjkIC)R}Hc+QSC^EKX8{8XSiq_zzQ~YloD) z`7=q=@tDk;`q@etoUyD?Ts9M#pcUJ3<{v9{rmyTSe2&Ist#K*4TK+sWs`{Ux<=a@H zrU}Qi=sIB6wTDV=j&r{mv>PljTn!37=kGvv?_>`U_z>aqsPsjT6] +param([string]$Ico = '') +Add-Type -AssemblyName System.Drawing +$out = "$env:TEMP\asw-check\icon" +New-Item -ItemType Directory -Force $out | Out-Null + +$sizes = 16, 20, 24, 32, 40, 48, 64, 256 + +function C([string]$hex) { return [System.Drawing.ColorTranslator]::FromHtml($hex) } +$blueTop = C '#2D66B5' +$blueBottom = C '#173B72' +$edge = C '#0E2850' +$green = C '#4BC55E' +$greenDark = C '#2E8F3E' +$red = C '#E5484D' +$track = C '#0E2850' + +function RoundedRect([float]$x, [float]$y, [float]$w, [float]$h, [float]$r) +{ + $p = New-Object System.Drawing.Drawing2D.GraphicsPath + $d = 2 * $r + $p.AddArc($x, $y, $d, $d, 180, 90) + $p.AddArc($x + $w - $d, $y, $d, $d, 270, 90) + $p.AddArc($x + $w - $d, $y + $h - $d, $d, $d, 0, 90) + $p.AddArc($x, $y + $h - $d, $d, $d, 90, 90) + $p.CloseFigure() + return $p +} + +function Draw([int]$size) +{ + $s = $size / 256.0 + $bmp = New-Object System.Drawing.Bitmap($size, $size, [System.Drawing.Imaging.PixelFormat]::Format32bppArgb) + $g = [System.Drawing.Graphics]::FromImage($bmp) + $g.SmoothingMode = 'AntiAlias' + $g.PixelOffsetMode = 'HighQuality' + $g.Clear([System.Drawing.Color]::Transparent) + + $withBar = ($size -ge 32) + + # Badge: the full square at 16-24 px, so the check mark gets every pixel. + $margin = if ($size -lt 32) { 0 } else { [Math]::Floor(8 * $s) } + $radius = [Math]::Max(2.5, 44 * $s) + $side = $size - 2 * $margin + $badge = RoundedRect $margin $margin $side $side $radius + $bottom = $margin + $side + $brush = New-Object System.Drawing.Drawing2D.LinearGradientBrush( + (New-Object System.Drawing.PointF(0, $margin)), (New-Object System.Drawing.PointF(0, $bottom)), + $blueTop, $blueBottom) + $g.FillPath($brush, $badge) + if ($size -ge 48) + { + $pen = New-Object System.Drawing.Pen($edge, [Math]::Max(1, 4 * $s)) + $g.DrawPath($pen, $badge) + } + + # Check mark: thicker, relative to the size, when it's small. + if ($withBar) + { + $pts = @((64, 120), (108, 164), (194, 76)); $width = 36 + } + else + { + $pts = @((54, 132), (104, 182), (204, 80)); $width = 48 + } + $points = [System.Drawing.PointF[]]@($pts | ForEach-Object { New-Object System.Drawing.PointF(($_[0] * $s), ($_[1] * $s)) }) + if ($size -ge 48) + { + # A darker green rim, for definition against the blue. + $rim = New-Object System.Drawing.Pen($greenDark, (($width + 8) * $s)) + $rim.StartCap = 'Round'; $rim.EndCap = 'Round'; $rim.LineJoin = 'Round' + $g.DrawLines($rim, $points) + } + $checkPen = New-Object System.Drawing.Pen($green, ($width * $s)) + $checkPen.StartCap = 'Round'; $checkPen.EndCap = 'Round'; $checkPen.LineJoin = 'Round' + $g.DrawLines($checkPen, $points) + + # Progress bar: mostly green, ending red, as the GUI's shows passes and a failure. + if ($withBar) + { + $bx = 52 * $s; $bw = 152 * $s + $bh = [Math]::Max(3, 20 * $s); $by = 196 * $s + $br = $bh / 2 + $g.FillPath((New-Object System.Drawing.SolidBrush($track)), (RoundedRect $bx $by $bw $bh $br)) + $split = $bx + $bw * 0.72 + $g.SetClip((New-Object System.Drawing.RectangleF($bx, $by, ($split - $bx), $bh))) + $g.FillPath((New-Object System.Drawing.SolidBrush($green)), (RoundedRect $bx $by $bw $bh $br)) + $g.SetClip((New-Object System.Drawing.RectangleF($split, $by, ($bx + $bw - $split), $bh))) + $g.FillPath((New-Object System.Drawing.SolidBrush($red)), (RoundedRect $bx $by $bw $bh $br)) + $g.ResetClip() + } + + $g.Dispose() + $bmp.Save("$out\icon_$size.png", [System.Drawing.Imaging.ImageFormat]::Png) + return $bmp +} + +$images = @{} +foreach ($size in $sizes) { $images[$size] = Draw $size } + +# Preview sheet: each size at 1x on a light and a dark band, then 16-48 px magnified 6x on both, then 256 px. +$sheetW = 1400; $sheetH = 700 +$sheet = New-Object System.Drawing.Bitmap($sheetW, $sheetH) +$g = [System.Drawing.Graphics]::FromImage($sheet) +$g.Clear((C '#FFFFFF')) +$g.FillRectangle((New-Object System.Drawing.SolidBrush((C '#F3F3F3'))), 0, 0, 700, 400) +$g.FillRectangle((New-Object System.Drawing.SolidBrush((C '#202020'))), 700, 0, 700, 400) +$x = 20 +foreach ($size in 16, 20, 24, 32, 40, 48, 64) +{ + $g.DrawImageUnscaled($images[$size], $x, 20) + $g.DrawImageUnscaled($images[$size], $x + 700, 20) + $x += $size + 20 +} +$g.InterpolationMode = 'NearestNeighbor' +$g.PixelOffsetMode = 'Half' +$x = 20 +foreach ($size in 16, 20, 24, 32) +{ + $g.DrawImage($images[$size], $x, 110, $size * 6, $size * 6) + $g.DrawImage($images[$size], $x + 700, 110, $size * 6, $size * 6) + $x += $size * 6 + 16 +} +$g.InterpolationMode = 'HighQualityBicubic' +$g.DrawImage($images[256], 20, 420, 256, 256) +$g.DrawImage($images[48], 300, 420, 288, 288) +$g.FillRectangle((New-Object System.Drawing.SolidBrush((C '#202020'))), 700, 400, 700, 300) +$g.DrawImage($images[256], 720, 420, 256, 256) +$g.Dispose() +$sheet.Save("$out\preview.png", [System.Drawing.Imaging.ImageFormat]::Png) +"wrote $out" + +if ($Ico -ne '') +{ + $pngs = $sizes | ForEach-Object { "$out\icon_$_.png" } + & magick @pngs $Ico + & magick identify $Ico +} From 6a90a27bbf4e8dcdd2fa626224bc7b82d120dcda Mon Sep 17 00:00:00 2001 From: Anthony West Date: Thu, 29 Oct 2026 00:33:18 -0700 Subject: [PATCH 2/2] Give the console and GUI runners the ASWUnitTests icon --- .gitattributes | 1 + CHANGELOG.md | 7 +++++ Deploy.bat | 6 ++++- README.md | 20 +++++++++++--- cmake/CMakeLists.txt | 4 +++ rad370/ASWUnitTests.cbproj | 1 + resource/ASWUnitTests_Icon.rc | 27 +++++++++++++++++++ resource/make_icon.ps1 | 25 ++++++++--------- .../rad370/ASWUnitTests_VCL_Console.cbproj | 1 + vcl/gui/rad370/ASWUnitTests_VCL_GUI.cbproj | 2 +- .../gui/rad370/ASWUnitTests_Win32_GUI.cbproj | 1 + .../src/ASWUnitTests_Win32GUI_MainWindow.cpp | 2 ++ .../src/ASWUnitTests_Win32GUI_Sources.cmake | 25 ++++++++++++++--- .../gui/src/ASWUnitTests_Win32GUI_System.cpp | 5 ++++ win32/gui/src/ASWUnitTests_Win32GUI_System.h | 3 +++ .../src/ASWUnitTests_Win32GUI_TextDialog.cpp | 4 +++ .../Test_ASWUnitTests_Win32GUI_System.cpp | 13 +++++++++ .../tests/Test_ASWUnitTests_Win32GUI_System.h | 1 + 18 files changed, 124 insertions(+), 24 deletions(-) create mode 100644 resource/ASWUnitTests_Icon.rc diff --git a/.gitattributes b/.gitattributes index 51ddc4a..90f3e68 100644 --- a/.gitattributes +++ b/.gitattributes @@ -16,4 +16,5 @@ *.jpg -text *.png -text *.gif -text +*.ico -text *.pdf -text diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d42fb6..4296f19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,13 @@ versioned baseline. VCL style, so a project of your own that runs its tests in the VCL GUI runner needs Carbon among its custom styles (Project > Options > Application > Appearance) for it, and a `WinMain()` that applies the saved theme as `vcl\gui\rad370\ASWUnitTests_VCL_GUI.cpp` does. +- An ASWUnitTests icon, `resource\ASWUnitTests.ico`, which this repository's console and GUI + runners now have on Windows, with the PNGs it's made from and `resource\make_icon.ps1`, which + draws them. `ASWUNITTESTS_WIN32_GUI_SOURCES` includes `resource\ASWUnitTests_Icon.rc`, so a Win32 + GUI runner of your own built with CMake gets the icon too, in Explorer, its title bar and the + taskbar; to use your own, remove that script from the list (the README's "Win32 GUI Runner" shows + how) and name your icon `MAINICON`. `ASWUNITTESTS_RESOURCE_DIR` is the `resource` folder, which + `ASWUNITTESTS_WIN32_GUI_INCLUDE_DIRS` now includes. - A `TCLIParser::ParseArguments()` overload that takes the arguments as a `std::vector`, the program name first, as in `argv`. - `UTF8ToWide()` in `ASWUnitTests_Utils.h`, which converts UTF-8 to wide text (UTF-16 on Windows, diff --git a/Deploy.bat b/Deploy.bat index 6c65469..c58f69c 100644 --- a/Deploy.bat +++ b/Deploy.bat @@ -22,7 +22,7 @@ if not exist "%TARGET_DIR%\." ( ) ) -for %%D in (build cmake gui\src gui\tests rad370 src tests toTest vcl\console\rad370 vcl\gui\rad370 vcl\gui\src vcl\tests win32\gui\rad370 win32\gui\src win32\tests) do ( +for %%D in (build cmake gui\src gui\tests rad370 resource src tests toTest vcl\console\rad370 vcl\gui\rad370 vcl\gui\src vcl\tests win32\gui\rad370 win32\gui\src win32\tests) do ( if not exist "%TARGET_DIR%\%%D\." ( echo Creating %%D folder... mkdir "%TARGET_DIR%\%%D" @@ -145,6 +145,10 @@ call :CopyFile "rad370\Build_Win32_Debug.bat" "rad370\Build_Win32_Debug.bat" if errorlevel 1 exit /b 1 call :CopyFile "rad370\Build_Win32_Release.bat" "rad370\Build_Win32_Release.bat" if errorlevel 1 exit /b 1 +call :CopyFile "resource\ASWUnitTests.ico" "resource\ASWUnitTests.ico" +if errorlevel 1 exit /b 1 +call :CopyFile "resource\ASWUnitTests_Icon.rc" "resource\ASWUnitTests_Icon.rc" +if errorlevel 1 exit /b 1 call :CopyFile "src\ASWUnitTests_CLI.cpp" "src\ASWUnitTests_CLI.cpp" if errorlevel 1 exit /b 1 call :CopyFile "src\ASWUnitTests_CLI.h" "src\ASWUnitTests_CLI.h" diff --git a/README.md b/README.md index f1cef5e..a8c612a 100644 --- a/README.md +++ b/README.md @@ -466,7 +466,9 @@ files in `gui\src` (shared by the GUI runners) and `vcl\gui\src`, including a `WinMain()` modeled on `vcl\gui\rad370\ASWUnitTests_VCL_GUI.cpp`, which parses the command line, applies the saved theme and starts the main form. For the dark theme, also add RAD Studio's Carbon style to the application (Project > Options > Application > Appearance, which this repository's -project has as `Custom_Styles`); without it, Dark shows the light look. +project has as `Custom_Styles`); without it, Dark shows the light look. For the ASWUnitTests icon, +set the application's icon to `resource\ASWUnitTests.ico` there too, as this repository's project +does. ### Win32 GUI Runner @@ -485,8 +487,9 @@ and its `Build_*.bat` scripts write the executable to `win32\gui\rad370\true $(BDSLIB)\$(PLATFORM)\release\$(LANGDIR);$(ILINK_TranslatedLibraryPath) CppConsoleApplication + ..\resource\ASWUnitTests.ico System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) true ASWUnitTests diff --git a/resource/ASWUnitTests_Icon.rc b/resource/ASWUnitTests_Icon.rc new file mode 100644 index 0000000..b56d59e --- /dev/null +++ b/resource/ASWUnitTests_Icon.rc @@ -0,0 +1,27 @@ +/* ************************************************************************** +ASWUnitTests_Icon.rc +Author: Anthony S. West - ASW Software + +Embeds the ASWUnitTests icon in the CMake builds of the console runner and +the Win32 GUI runner (MSVC, MinGW and Clang). The RAD Studio projects embed +the same .ico themselves (Icon_MainIcon), also as MAINICON, which is the +name the Win32 GUI runner loads it by. The .ico is found through the +include path, which has this folder. + +Copyright 2026 ASW Software + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************** */ + +MAINICON ICON "ASWUnitTests.ico" diff --git a/resource/make_icon.ps1 b/resource/make_icon.ps1 index cc646e3..3b9cc0b 100644 --- a/resource/make_icon.ps1 +++ b/resource/make_icon.ps1 @@ -1,11 +1,11 @@ # Draws the ASWUnitTests icon (a blue rounded badge with a green check mark and, from 32 px up, a green and red -# progress bar) at each icon size, writes the PNGs and a preview sheet to %TEMP%\asw-check\icon, and with -Ico -# packs them into an .ico (BMP entries below 256 px, PNG at 256, via ImageMagick). -# Usage: & plans\tools\make_icon.ps1 [-Ico ] -param([string]$Ico = '') +# progress bar) at each icon size, and writes icon_.png and ASWUnitTests.ico (BMP entries below 256 px, PNG +# at 256) beside this script, replacing them; the same drawing gives the same files. ImageMagick's magick packs +# the .ico, so it must be on the PATH. Also writes a preview sheet of every size on light and dark backgrounds to +# %TEMP%\ASWUnitTests_icon_preview.png. +# Usage (Windows PowerShell): & resource\make_icon.ps1 Add-Type -AssemblyName System.Drawing -$out = "$env:TEMP\asw-check\icon" -New-Item -ItemType Directory -Force $out | Out-Null +$out = $PSScriptRoot $sizes = 16, 20, 24, 32, 40, 48, 64, 256 @@ -130,12 +130,9 @@ $g.DrawImage($images[48], 300, 420, 288, 288) $g.FillRectangle((New-Object System.Drawing.SolidBrush((C '#202020'))), 700, 400, 700, 300) $g.DrawImage($images[256], 720, 420, 256, 256) $g.Dispose() -$sheet.Save("$out\preview.png", [System.Drawing.Imaging.ImageFormat]::Png) -"wrote $out" +$sheet.Save("$env:TEMP\ASWUnitTests_icon_preview.png", [System.Drawing.Imaging.ImageFormat]::Png) -if ($Ico -ne '') -{ - $pngs = $sizes | ForEach-Object { "$out\icon_$_.png" } - & magick @pngs $Ico - & magick identify $Ico -} +$pngs = $sizes | ForEach-Object { "$out\icon_$_.png" } +& magick @pngs "$out\ASWUnitTests.ico" +& magick identify "$out\ASWUnitTests.ico" +"wrote the PNGs and ASWUnitTests.ico to $out, and the preview to $env:TEMP\ASWUnitTests_icon_preview.png" diff --git a/vcl/console/rad370/ASWUnitTests_VCL_Console.cbproj b/vcl/console/rad370/ASWUnitTests_VCL_Console.cbproj index 2b8e65c..a05e556 100644 --- a/vcl/console/rad370/ASWUnitTests_VCL_Console.cbproj +++ b/vcl/console/rad370/ASWUnitTests_VCL_Console.cbproj @@ -59,6 +59,7 @@ true $(BDSLIB)\$(PLATFORM)\release\$(LANGDIR);$(ILINK_TranslatedLibraryPath) CppConsoleApplication + ..\..\..\resource\ASWUnitTests.ico System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace) rtl.lib;vcl.lib true diff --git a/vcl/gui/rad370/ASWUnitTests_VCL_GUI.cbproj b/vcl/gui/rad370/ASWUnitTests_VCL_GUI.cbproj index e9fe852..4b33a31 100644 --- a/vcl/gui/rad370/ASWUnitTests_VCL_GUI.cbproj +++ b/vcl/gui/rad370/ASWUnitTests_VCL_GUI.cbproj @@ -88,7 +88,7 @@ rtl.lib;vcl.lib <_TCHARMapping>wchar_t true - $(BDS)\bin\cbuilder_PROJECTICON.ico + ..\..\..\resource\ASWUnitTests.ico $(BDS)\bin\Artwork\Windows\UWP\cppreg_UwpDefault_44.png $(BDS)\bin\Artwork\Windows\UWP\cppreg_UwpDefault_150.png ASWUnitTests_VCL_GUI diff --git a/win32/gui/rad370/ASWUnitTests_Win32_GUI.cbproj b/win32/gui/rad370/ASWUnitTests_Win32_GUI.cbproj index d493c9d..f43e523 100644 --- a/win32/gui/rad370/ASWUnitTests_Win32_GUI.cbproj +++ b/win32/gui/rad370/ASWUnitTests_Win32_GUI.cbproj @@ -54,6 +54,7 @@ true $(BDSLIB)\$(PLATFORM)\release\$(LANGDIR);$(ILINK_TranslatedLibraryPath) CppVCLApplication + ..\..\..\resource\ASWUnitTests.ico System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) true ASWUnitTests_Win32_GUI diff --git a/win32/gui/src/ASWUnitTests_Win32GUI_MainWindow.cpp b/win32/gui/src/ASWUnitTests_Win32GUI_MainWindow.cpp index d68854a..cb5dbd7 100644 --- a/win32/gui/src/ASWUnitTests_Win32GUI_MainWindow.cpp +++ b/win32/gui/src/ASWUnitTests_Win32GUI_MainWindow.cpp @@ -804,6 +804,8 @@ void TWin32GUIMainWindow::Create() windowClass.cbSize = sizeof(windowClass); windowClass.lpfnWndProc = &WindowProc; windowClass.hInstance = instance; + // With no small icon given, Windows takes the title bar's from the same resource, at the size it needs. + windowClass.hIcon = MainIcon(); windowClass.hCursor = LoadCursorW(nullptr, MAKEINTRESOURCEW(32512)); // IDC_ARROW windowClass.hbrBackground = reinterpret_cast(COLOR_BTNFACE + 1); windowClass.lpszClassName = MainWindowClassName; diff --git a/win32/gui/src/ASWUnitTests_Win32GUI_Sources.cmake b/win32/gui/src/ASWUnitTests_Win32GUI_Sources.cmake index ec1afa9..7eb6f4d 100644 --- a/win32/gui/src/ASWUnitTests_Win32GUI_Sources.cmake +++ b/win32/gui/src/ASWUnitTests_Win32GUI_Sources.cmake @@ -7,13 +7,21 @@ # ASWUNITTESTS_WIN32_GUI_SOURCES The framework's core sources (all # but main.cpp), the GUI runners' # shared core, this folder's units -# including WinMain, and the -# manifest: the .manifest file +# including WinMain, the +# manifest (the .manifest file # itself for MSVC, or the resource # script that embeds it for other -# compilers. +# compilers), and +# ASWUnitTests_Icon.rc, which +# embeds the ASWUnitTests icon. A +# project with an icon of its own +# removes that script from the list +# (see the usage below); the window +# shows its icon if it's named +# MAINICON. # ASWUNITTESTS_WIN32_GUI_INCLUDE_DIRS Their directories, for the include -# path. +# path (the resource compiler finds +# the icon through it). # ASWUNITTESTS_WIN32_GUI_LIBRARIES The Windows libraries it links. # ASWUNITTESTS_WIN32_GUI_LINK_OPTIONS Linker options it needs: with MSVC, # leaving the execution level to the @@ -22,6 +30,8 @@ # or later, which gives its windows # the same frame as other programs. # ASWUNITTESTS_WIN32_GUI_SOURCE_DIR This folder. +# ASWUNITTESTS_RESOURCE_DIR The repository's resource folder, +# which has the icon. # # It includes src/ASWUnitTests_Sources.cmake and # gui/src/ASWUnitTests_GUI_Sources.cmake, so their variables are also set. @@ -38,11 +48,16 @@ # target_include_directories(MyTestsGUI PRIVATE ${ASWUNITTESTS_WIN32_GUI_INCLUDE_DIRS} tests) # target_link_libraries(MyTestsGUI PRIVATE ${ASWUNITTESTS_WIN32_GUI_LIBRARIES}) # target_link_options(MyTestsGUI PRIVATE ${ASWUNITTESTS_WIN32_GUI_LINK_OPTIONS}) +# +# To use an icon of your own instead, before add_executable(): +# +# list(REMOVE_ITEM ASWUNITTESTS_WIN32_GUI_SOURCES "${ASWUNITTESTS_RESOURCE_DIR}/ASWUnitTests_Icon.rc") include("${CMAKE_CURRENT_LIST_DIR}/../../../src/ASWUnitTests_Sources.cmake") include("${CMAKE_CURRENT_LIST_DIR}/../../../gui/src/ASWUnitTests_GUI_Sources.cmake") set(ASWUNITTESTS_WIN32_GUI_SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}") +get_filename_component(ASWUNITTESTS_RESOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../resource" ABSOLUTE) set(ASWUNITTESTS_WIN32_GUI_SOURCES ${ASWUNITTESTS_CORE_SOURCES} @@ -56,12 +71,14 @@ set(ASWUNITTESTS_WIN32_GUI_SOURCES "${ASWUNITTESTS_WIN32_GUI_SOURCE_DIR}/ASWUnitTests_Win32GUI_System.cpp" "${ASWUNITTESTS_WIN32_GUI_SOURCE_DIR}/ASWUnitTests_Win32GUI_TextDialog.cpp" "${ASWUNITTESTS_WIN32_GUI_SOURCE_DIR}/ASWUnitTests_Win32GUI_WinMain.cpp" + "${ASWUNITTESTS_RESOURCE_DIR}/ASWUnitTests_Icon.rc" ) set(ASWUNITTESTS_WIN32_GUI_INCLUDE_DIRS "${ASWUNITTESTS_SOURCE_DIR}" "${ASWUNITTESTS_GUI_SOURCE_DIR}" "${ASWUNITTESTS_WIN32_GUI_SOURCE_DIR}" + "${ASWUNITTESTS_RESOURCE_DIR}" ) set(ASWUNITTESTS_WIN32_GUI_LIBRARIES diff --git a/win32/gui/src/ASWUnitTests_Win32GUI_System.cpp b/win32/gui/src/ASWUnitTests_Win32GUI_System.cpp index b41dcda..1c1e7ee 100644 --- a/win32/gui/src/ASWUnitTests_Win32GUI_System.cpp +++ b/win32/gui/src/ASWUnitTests_Win32GUI_System.cpp @@ -260,6 +260,11 @@ std::string GUISettingsFolderPath() return WideToUTF8(appData) + "\\ASWUnitTests"; } //--------------------------------------------------------------------------- +HICON MainIcon() +{ + return LoadIconW(GetModuleHandleW(nullptr), L"MAINICON"); +} +//--------------------------------------------------------------------------- std::vector ProcessArguments() { return CommandLineArguments(GetCommandLineW()); diff --git a/win32/gui/src/ASWUnitTests_Win32GUI_System.h b/win32/gui/src/ASWUnitTests_Win32GUI_System.h index 43f4aa3..fb29896 100644 --- a/win32/gui/src/ASWUnitTests_Win32GUI_System.h +++ b/win32/gui/src/ASWUnitTests_Win32GUI_System.h @@ -64,6 +64,9 @@ std::string GUISelectionFilePath(); // The folder that holds the GUI runners' layout and selection files, %APPDATA%\ASWUnitTests. It may not exist // yet (see CreateFolderPath()). std::string GUISettingsFolderPath(); +// This executable's icon, its MAINICON resource (resource/ASWUnitTests_Icon.rc, or Icon_MainIcon in a RAD Studio +// project), at the standard icon size; nullptr if it has none. Shared, so it's never destroyed. +HICON MainIcon(); // This process's command-line arguments, without the program name, converted to UTF-8, for // TGUICommandLine::Parse(). std::vector ProcessArguments(); diff --git a/win32/gui/src/ASWUnitTests_Win32GUI_TextDialog.cpp b/win32/gui/src/ASWUnitTests_Win32GUI_TextDialog.cpp index 75a9799..46df46b 100644 --- a/win32/gui/src/ASWUnitTests_Win32GUI_TextDialog.cpp +++ b/win32/gui/src/ASWUnitTests_Win32GUI_TextDialog.cpp @@ -135,6 +135,10 @@ INT_PTR HandleDialogMessage(HWND dialog, UINT message, WPARAM wParam, LPARAM lPa SendMessageW(textBox, EM_SETLIMITTEXT, 0, 0); SetWindowTextW(textBox, state->Text.c_str()); + // Shown before the main window exists (e.g. for --help), it has a taskbar button of its own. + if (GetWindow(dialog, GW_OWNER) == nullptr) + SendMessageW(dialog, WM_SETICON, ICON_BIG, reinterpret_cast(MainIcon())); + SizeAndCenterDialog(dialog); ApplyDPI(dialog); diff --git a/win32/tests/Test_ASWUnitTests_Win32GUI_System.cpp b/win32/tests/Test_ASWUnitTests_Win32GUI_System.cpp index 42ba17a..514e842 100644 --- a/win32/tests/Test_ASWUnitTests_Win32GUI_System.cpp +++ b/win32/tests/Test_ASWUnitTests_Win32GUI_System.cpp @@ -120,6 +120,8 @@ TTest_ASWUnitTests_Win32GUI_System::TTest_ASWUnitTests_Win32GUI_System() "GUILayoutFilePath_IsPerExeUnderAppData"); RegisterTest(&TTest_ASWUnitTests_Win32GUI_System::Test_GUISelectionFilePath_IsBesideTheLayoutFile, "GUISelectionFilePath_IsBesideTheLayoutFile"); + RegisterTest(&TTest_ASWUnitTests_Win32GUI_System::Test_MainIcon_IsThisExecutablesSharedIcon, + "MainIcon_IsThisExecutablesSharedIcon"); RegisterTest(&TTest_ASWUnitTests_Win32GUI_System::Test_ReadFileBytes_ReadsWhatWriteFileBytesWrote, "ReadFileBytes_ReadsWhatWriteFileBytesWrote"); RegisterTest(&TTest_ASWUnitTests_Win32GUI_System::Test_ReadFileBytes_ThrowsForAMissingFile, @@ -313,6 +315,17 @@ void TTest_ASWUnitTests_Win32GUI_System::Test_GUISelectionFilePath_IsBesideTheLa "the layout file's path with a .selection extension"); } //--------------------------------------------------------------------------- +void TTest_ASWUnitTests_Win32GUI_System::Test_MainIcon_IsThisExecutablesSharedIcon() +{ + // Act: every runner that compiles this test embeds the ASWUnitTests icon. + HICON const icon = MainIcon(); + HICON const again = MainIcon(); + + // Assert + CheckTrue(icon != nullptr, __func__, __LINE__, "this executable has a MAINICON resource"); + CheckTrue(again == icon, __func__, __LINE__, "the same, shared, icon each time"); +} +//--------------------------------------------------------------------------- void TTest_ASWUnitTests_Win32GUI_System::Test_ReadFileBytes_ReadsWhatWriteFileBytesWrote() { // Arrange: a file name with a character outside every ANSI code page but UTF-8's (U+4E2D), and bytes a diff --git a/win32/tests/Test_ASWUnitTests_Win32GUI_System.h b/win32/tests/Test_ASWUnitTests_Win32GUI_System.h index 37ef5f1..f2e0185 100644 --- a/win32/tests/Test_ASWUnitTests_Win32GUI_System.h +++ b/win32/tests/Test_ASWUnitTests_Win32GUI_System.h @@ -61,6 +61,7 @@ class TTest_ASWUnitTests_Win32GUI_System : public TTestGroupBase void Test_FileExists_IsTrueOnlyForAFile(); void Test_GUILayoutFilePath_IsPerExeUnderAppData(); void Test_GUISelectionFilePath_IsBesideTheLayoutFile(); + void Test_MainIcon_IsThisExecutablesSharedIcon(); void Test_ReadFileBytes_ReadsWhatWriteFileBytesWrote(); void Test_ReadFileBytes_ThrowsForAMissingFile(); void Test_RemoveFile_DeletesOnlyAnExistingFile();