ranger-compiler 3.5.1 → 3.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (338) hide show
  1. package/CHANGELOG.md +1064 -20
  2. package/LICENSE +3 -1
  3. package/README.md +123 -28
  4. package/dist/Lang.rgr +1188 -290
  5. package/dist/api.d.ts +2343 -879
  6. package/dist/api.js +79923 -56921
  7. package/dist/lib/JSON.rgr +102 -91
  8. package/dist/lib/Shell.rgr +4 -4
  9. package/dist/lib/apple/AppleToolchain.rgr +4 -4
  10. package/dist/lib/apple/README.md +5 -4
  11. package/dist/lib/apple/apple_test.rgr +1 -1
  12. package/dist/lib/core/README.md +1 -1
  13. package/dist/lib/evg/EVG.rgr +12 -0
  14. package/dist/lib/evg/EVGA11yFromTree.rgr +302 -0
  15. package/dist/lib/evg/EVGA11yTree.rgr +894 -0
  16. package/dist/lib/evg/EVGBox.rgr +267 -0
  17. package/dist/lib/evg/EVGBoxShorthandTest.rgr +220 -0
  18. package/dist/lib/evg/EVGCodepoint.rgr +316 -0
  19. package/dist/lib/evg/EVGColor.rgr +700 -0
  20. package/dist/lib/evg/EVGCommands.rgr +177 -0
  21. package/dist/lib/evg/EVGComponent.rgr +331 -0
  22. package/dist/lib/evg/EVGComponentTest.rgr +387 -0
  23. package/dist/lib/evg/EVGConnector.rgr +541 -0
  24. package/dist/lib/evg/EVGConnectorTest.rgr +306 -0
  25. package/dist/lib/evg/EVGDisplayList.rgr +4186 -0
  26. package/dist/lib/evg/EVGEasing.rgr +370 -0
  27. package/dist/lib/evg/EVGEffectTest.rgr +253 -0
  28. package/dist/lib/evg/EVGElement.rgr +4914 -0
  29. package/dist/lib/evg/EVGFixedTest.rgr +289 -0
  30. package/dist/lib/evg/EVGFlexRulesTest.rgr +317 -0
  31. package/dist/lib/evg/EVGFlexWrapTest.rgr +246 -0
  32. package/dist/lib/evg/EVGFling.rgr +244 -0
  33. package/dist/lib/evg/EVGFocus.rgr +400 -0
  34. package/dist/lib/evg/EVGFocusTest.rgr +399 -0
  35. package/dist/lib/evg/EVGGradient.rgr +322 -0
  36. package/dist/lib/evg/EVGGrapheme.rgr +190 -0
  37. package/dist/lib/evg/EVGGrid.rgr +975 -0
  38. package/dist/lib/evg/EVGHitTest.rgr +208 -0
  39. package/dist/lib/evg/EVGHoles.rgr +294 -0
  40. package/dist/lib/evg/EVGHostMeasurerTest.rgr +281 -0
  41. package/dist/lib/evg/EVGHostTextMeasurer.rgr +331 -0
  42. package/dist/lib/evg/EVGHostTree.rgr +834 -0
  43. package/dist/lib/evg/EVGHostTreeTest.rgr +447 -0
  44. package/dist/lib/evg/EVGImageDecode.rgr +117 -0
  45. package/dist/lib/evg/EVGImageMeasurer.rgr +86 -0
  46. package/dist/lib/evg/EVGInspect.rgr +883 -0
  47. package/dist/lib/evg/EVGInvalidateTest.rgr +432 -0
  48. package/dist/lib/evg/EVGJsonTest.rgr +535 -0
  49. package/dist/lib/evg/EVGLayout.rgr +4220 -0
  50. package/dist/lib/evg/EVGMeasure.rgr +1267 -0
  51. package/dist/lib/evg/EVGOverlayTest.rgr +626 -0
  52. package/dist/lib/evg/EVGPatch.rgr +1721 -0
  53. package/dist/lib/evg/EVGPatchTest.rgr +692 -0
  54. package/dist/lib/evg/EVGPopoverTest.rgr +395 -0
  55. package/dist/lib/evg/EVGReconcile.rgr +226 -0
  56. package/dist/lib/evg/EVGReconcileTest.rgr +421 -0
  57. package/dist/lib/evg/EVGReject.rgr +126 -0
  58. package/dist/lib/evg/EVGRelayoutTest.rgr +375 -0
  59. package/dist/lib/evg/EVGRtlLayoutTest.rgr +319 -0
  60. package/dist/lib/evg/EVGRuler.rgr +232 -0
  61. package/dist/lib/evg/EVGRulerTest.rgr +172 -0
  62. package/dist/lib/evg/EVGSelectChrome.rgr +178 -0
  63. package/dist/lib/evg/EVGStyleCacheTest.rgr +419 -0
  64. package/dist/lib/evg/EVGStyleSheet.rgr +2148 -0
  65. package/dist/lib/evg/EVGStyleStateTest.rgr +407 -0
  66. package/dist/lib/evg/EVGStyleVarTest.rgr +471 -0
  67. package/dist/lib/evg/EVGText.rgr +105 -0
  68. package/dist/lib/evg/EVGTextEngine.rgr +536 -0
  69. package/dist/lib/evg/EVGTextMeasurer.rgr +720 -0
  70. package/dist/lib/evg/EVGTimingTest.rgr +1417 -0
  71. package/dist/lib/evg/EVGToolbar.rgr +1369 -0
  72. package/dist/lib/evg/EVGTransition.rgr +728 -0
  73. package/dist/lib/evg/EVGTreeJson.rgr +721 -0
  74. package/dist/lib/evg/EVGUnit.rgr +554 -0
  75. package/dist/lib/evg/EVGViewportUnitTest.rgr +236 -0
  76. package/dist/lib/evg/EvgApp.rgr +189 -0
  77. package/dist/lib/evg/EvgBitmapTracer.rgr +4277 -0
  78. package/dist/lib/evg/EvgBitmapTracerTest.rgr +2119 -0
  79. package/dist/lib/evg/EvgHost.rgr +451 -0
  80. package/dist/lib/evg/EvgTest.rgr +91 -0
  81. package/dist/lib/evg/EvgTraceColor.rgr +236 -0
  82. package/dist/lib/evg/EvgTraceCurve.rgr +648 -0
  83. package/dist/lib/evg/EvgTraceFit.rgr +767 -0
  84. package/dist/lib/evg/EvgTracePath.rgr +435 -0
  85. package/dist/lib/evg/EvgTraceTypes.rgr +559 -0
  86. package/dist/lib/evg/EvgViewport.rgr +313 -0
  87. package/dist/lib/evg/FxDemoDoc.rgr +185 -0
  88. package/dist/lib/evg/HOSTS.md +227 -0
  89. package/dist/lib/evg/ISSUES.md +835 -0
  90. package/dist/lib/evg/PLAN_ACCESSIBILITY.md +650 -0
  91. package/dist/lib/evg/PLAN_CSS_LAYOUT_AND_FONTS.md +1124 -0
  92. package/dist/lib/evg/PLAN_EFFECTS.md +431 -0
  93. package/dist/lib/evg/PLAN_EVG.md +518 -0
  94. package/dist/lib/evg/PLAN_EVG_RENDERER.md +1674 -0
  95. package/dist/lib/evg/PLAN_INSPECTOR.md +788 -0
  96. package/dist/lib/evg/PLAN_LINKS_AND_FORMS.md +157 -0
  97. package/dist/lib/evg/PLAN_NATIVE_HOSTS.md +671 -0
  98. package/dist/lib/evg/PLAN_VECTOR_IR.md +664 -0
  99. package/dist/lib/evg/PLAN_VIEW_TRANSFORM.md +322 -0
  100. package/dist/lib/evg/PathBuilder.rgr +333 -0
  101. package/dist/lib/evg/README.md +1236 -0
  102. package/dist/lib/evg/SPEC.md +1252 -0
  103. package/dist/lib/evg/SVGPathParser.rgr +1199 -0
  104. package/dist/lib/evg/SvgParser.rgr +1736 -0
  105. package/dist/lib/evg/TODO_EVG_RENDERER.md +595 -0
  106. package/dist/lib/evg/VectorShapes.rgr +331 -0
  107. package/dist/lib/evg/VectorStroke.rgr +107 -0
  108. package/dist/lib/evg/VectorViewBox.rgr +380 -0
  109. package/dist/lib/evg/agent/README.md +290 -0
  110. package/dist/lib/evg/agent/evg_agent.rgr +899 -0
  111. package/dist/lib/evg/agent/fixtures/broken.evg.json +7 -0
  112. package/dist/lib/evg/agent/fixtures/card.evg.json +9 -0
  113. package/dist/lib/evg/agent/fixtures/connector.css +63 -0
  114. package/dist/lib/evg/agent/fixtures/connector.evg.json +14 -0
  115. package/dist/lib/evg/agent/fixtures/drawn.evg.json +129 -0
  116. package/dist/lib/evg/agent/fixtures/gradient.evg.json +4 -0
  117. package/dist/lib/evg/agent/fixtures/popover.css +99 -0
  118. package/dist/lib/evg/agent/fixtures/popover.evg.json +30 -0
  119. package/dist/lib/evg/agent/roundtrip.sh +74 -0
  120. package/dist/lib/evg/agent/smoke.sh +230 -0
  121. package/dist/lib/evg/android/README.md +97 -0
  122. package/dist/lib/evg/android/androidstubs/AndroidStubs.kt +175 -0
  123. package/dist/lib/evg/android/androidstubs/Annotation.kt +10 -0
  124. package/dist/lib/evg/android/androidstubs/App.kt +30 -0
  125. package/dist/lib/evg/android/androidstubs/Content.kt +41 -0
  126. package/dist/lib/evg/android/androidstubs/ContentRes.kt +12 -0
  127. package/dist/lib/evg/android/androidstubs/InputMethod.kt +46 -0
  128. package/dist/lib/evg/android/androidstubs/Net.kt +6 -0
  129. package/dist/lib/evg/android/androidstubs/Os.kt +30 -0
  130. package/dist/lib/evg/android/androidstubs/Util.kt +15 -0
  131. package/dist/lib/evg/android/androidstubs/View.kt +113 -0
  132. package/dist/lib/evg/android/androidstubs/Widget.kt +16 -0
  133. package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/AndroidEvgSurface.kt +323 -0
  134. package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/AndroidTextMeasurer.kt +56 -0
  135. package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/RippleEffect.kt +262 -0
  136. package/dist/lib/evg/android/src/awt/kotlin/fi/ranger/evg/AwtEvgSurface.kt +238 -0
  137. package/dist/lib/evg/android/src/awt/kotlin/fi/ranger/evg/AwtTextMeasurer.kt +85 -0
  138. package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgEngineThread.kt +115 -0
  139. package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgPainter.kt +231 -0
  140. package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgSurface.kt +117 -0
  141. package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/RecordingSurface.kt +94 -0
  142. package/dist/lib/evg/apple/README.md +128 -0
  143. package/dist/lib/evg/apple/Sources/CoreGraphicsEvgSurface.swift +329 -0
  144. package/dist/lib/evg/apple/Sources/CoreTextMeasurer.swift +70 -0
  145. package/dist/lib/evg/apple/Sources/EvgEngineQueue.swift +105 -0
  146. package/dist/lib/evg/apple/Sources/EvgPainter.swift +225 -0
  147. package/dist/lib/evg/apple/Sources/EvgSurface.swift +163 -0
  148. package/dist/lib/evg/apple/Sources/RecordingSurface.swift +110 -0
  149. package/dist/lib/evg/bench/EvgLayoutBench.rgr +34 -0
  150. package/dist/lib/evg/bench/README.md +64 -0
  151. package/dist/lib/evg/bench/layout-bench.mjs +316 -0
  152. package/dist/lib/evg/bench/layout-cases.mjs +510 -0
  153. package/dist/lib/evg/bench/layout-conformance.mjs +254 -0
  154. package/dist/lib/evg/bin/.gitignore +17 -0
  155. package/dist/lib/evg/evg_test.rgr +313 -0
  156. package/dist/lib/evg/gl/README.md +198 -0
  157. package/dist/lib/evg/gl/a11y-paint-check.mjs +73 -0
  158. package/dist/lib/evg/gl/blur-check.mjs +399 -0
  159. package/dist/lib/evg/gl/boxmodel.json +1 -0
  160. package/dist/lib/evg/gl/demo.html +46 -0
  161. package/dist/lib/evg/gl/effect-presets.css +285 -0
  162. package/dist/lib/evg/gl/effect-presets.js +63 -0
  163. package/dist/lib/evg/gl/effect-shots.mjs +135 -0
  164. package/dist/lib/evg/gl/evg-a11y.js +566 -0
  165. package/dist/lib/evg/gl/evg-binary.js +160 -0
  166. package/dist/lib/evg/gl/evg-engine.js +296 -0
  167. package/dist/lib/evg/gl/evg-fx.js +237 -0
  168. package/dist/lib/evg/gl/evg-gestures.js +209 -0
  169. package/dist/lib/evg/gl/evg-list.js +167 -0
  170. package/dist/lib/evg/gl/evg-measure.js +186 -0
  171. package/dist/lib/evg/gl/evg-textinput.js +303 -0
  172. package/dist/lib/evg/gl/evg-view.js +144 -0
  173. package/dist/lib/evg/gl/evg-webgl.js +3942 -0
  174. package/dist/lib/evg/gl/fx-check.mjs +998 -0
  175. package/dist/lib/evg/gl/fx-demo.html +166 -0
  176. package/dist/lib/evg/gl/fx-demo.js +2 -0
  177. package/dist/lib/evg/gl/fx-serve.mjs +47 -0
  178. package/dist/lib/evg/gl/gestures-check.mjs +189 -0
  179. package/dist/lib/evg/gl/list-binary-check.mjs +152 -0
  180. package/dist/lib/evg/gl/measure-check.mjs +135 -0
  181. package/dist/lib/evg/gl/rotation-check.mjs +207 -0
  182. package/dist/lib/evg/gl/shift-check.mjs +113 -0
  183. package/dist/lib/evg/gl/stroke-check.mjs +168 -0
  184. package/dist/lib/evg/gl/text-snap-check.mjs +139 -0
  185. package/dist/lib/evg/gl/view-check.mjs +343 -0
  186. package/dist/lib/evg/gl/view-policy-check.mjs +184 -0
  187. package/dist/lib/evg/html/evg-dom.js +318 -0
  188. package/dist/lib/evg/html/evg-html.js +600 -0
  189. package/dist/lib/evg/inspect/README.md +415 -0
  190. package/dist/lib/evg/inspect/browser-smoke.mjs +115 -0
  191. package/dist/lib/evg/inspect/evg-inspect.js +947 -0
  192. package/dist/lib/evg/inspect/shots/css.png +0 -0
  193. package/dist/lib/evg/inspect/shots/dashboard.png +0 -0
  194. package/dist/lib/evg/inspect/shots/pptx-slide.png +0 -0
  195. package/dist/lib/evg/inspect/shots/state.png +0 -0
  196. package/dist/lib/evg/inspect/shots.mjs +226 -0
  197. package/dist/lib/evg/oracle/css-blur.json +576 -0
  198. package/dist/lib/evg/oracle/css-box.json +157 -0
  199. package/dist/lib/evg/oracle/css-timing.json +709 -0
  200. package/dist/lib/evg/oracle/css_blur_oracle.mjs +529 -0
  201. package/dist/lib/evg/oracle/css_box_oracle.mjs +106 -0
  202. package/dist/lib/evg/oracle/css_timing_oracle.mjs +389 -0
  203. package/dist/lib/evg/original/EVGColor.clj +310 -0
  204. package/dist/lib/evg/original/EVGColorContext.rgr +178 -0
  205. package/dist/lib/evg/original/SVGPath.rgr +627 -0
  206. package/dist/lib/evg/original/Vec2.crgr +103 -0
  207. package/dist/lib/evg/ranger.json +9 -0
  208. package/dist/lib/evg/showcase/README.md +246 -0
  209. package/dist/lib/evg/showcase/assets/emblem.svg +30 -0
  210. package/dist/lib/evg/showcase/assets/rosette.svg +22 -0
  211. package/dist/lib/evg/showcase/build.mjs +651 -0
  212. package/dist/lib/evg/showcase/pages/album.tsx +30 -0
  213. package/dist/lib/evg/showcase/pages/boxmodel.tsx +37 -0
  214. package/dist/lib/evg/showcase/pages/cards.tsx +41 -0
  215. package/dist/lib/evg/showcase/pages/chart_api.tsx +170 -0
  216. package/dist/lib/evg/showcase/pages/charts.tsx +219 -0
  217. package/dist/lib/evg/showcase/pages/drawing.tsx +160 -0
  218. package/dist/lib/evg/showcase/pages/emoji.tsx +91 -0
  219. package/dist/lib/evg/showcase/pages/flex.tsx +46 -0
  220. package/dist/lib/evg/showcase/pages/more.tsx +332 -0
  221. package/dist/lib/evg/showcase/pages/plots.tsx +262 -0
  222. package/dist/lib/evg/showcase/pages/svg.tsx +63 -0
  223. package/dist/lib/evg/showcase/pages/tables.tsx +209 -0
  224. package/dist/lib/evg/showcase/pages/typography.tsx +43 -0
  225. package/dist/lib/evg/showcase/pages/units.tsx +35 -0
  226. package/dist/lib/evg/showcase/pages/variants.tsx +233 -0
  227. package/dist/lib/evg/showcase/pages/vector.tsx +75 -0
  228. package/dist/lib/evg/showcase/pages/views.tsx +223 -0
  229. package/dist/lib/evg/showcase/tests/chart_api_smoke.mjs +339 -0
  230. package/dist/lib/evg/showcase/tests/gl_smoke.mjs +125 -0
  231. package/dist/lib/evg/showcase/themes/chart_api-default.css +15 -0
  232. package/dist/lib/evg/showcase/themes/charts-default.css +29 -0
  233. package/dist/lib/evg/showcase/themes/drawing-default.css +35 -0
  234. package/dist/lib/evg/showcase/themes/more-default.css +128 -0
  235. package/dist/lib/evg/showcase/themes/plots-default.css +18 -0
  236. package/dist/lib/evg/showcase/themes/showcase.css +508 -0
  237. package/dist/lib/evg/showcase/themes/tables-default.css +123 -0
  238. package/dist/lib/evg/showcase/themes/variants-default.css +15 -0
  239. package/dist/lib/evg/showcase/themes/views-default.css +15 -0
  240. package/dist/lib/evg/tools/bench_vs_potrace.mjs +290 -0
  241. package/dist/lib/evg/tools/evg_image_tool.rgr +488 -0
  242. package/dist/lib/evg/tools/evg_trace_bench.rgr +114 -0
  243. package/dist/lib/evg/tools/evg_trace_cli.rgr +662 -0
  244. package/dist/lib/evg/tools/evg_trace_cpp_bench.rgr +83 -0
  245. package/dist/lib/evg/tools/run_trace_bench.sh +74 -0
  246. package/dist/lib/evg/tools/run_trace_cli_smoke.sh +128 -0
  247. package/dist/lib/evg/web/responsive/EvgResponsiveCheck.rgr +271 -0
  248. package/dist/lib/evg/web/responsive/EvgResponsiveDemo.rgr +531 -0
  249. package/dist/lib/evg/web/responsive/README.md +97 -0
  250. package/dist/lib/evg/web/responsive/build.mjs +90 -0
  251. package/dist/lib/evg/web/responsive/dom-check.mjs +169 -0
  252. package/dist/lib/evg/web/responsive/index.html +177 -0
  253. package/dist/lib/evg/web/responsive/smoke.mjs +221 -0
  254. package/dist/lib/evg/web/tools/assets-client.mjs +95 -0
  255. package/dist/lib/evg/web/tools/boot-bench.mjs +189 -0
  256. package/dist/lib/evg/web/tools/inline-assets.mjs +119 -0
  257. package/dist/lib/evg/web/tools/minify.mjs +60 -0
  258. package/dist/lib/evg/web/tracer/build.mjs +87 -0
  259. package/dist/lib/evg/web/tracer/index.html +2805 -0
  260. package/dist/lib/evg/web/tracer/sample.png +0 -0
  261. package/dist/lib/evg/web/tracer/smoke.mjs +1162 -0
  262. package/dist/lib/evgr/Cargo.lock +21 -0
  263. package/dist/lib/evgr/Cargo.toml +19 -0
  264. package/dist/lib/evgr/README.md +140 -0
  265. package/dist/lib/evgr/bench/NativeBench.rgr +246 -0
  266. package/dist/lib/evgr/bench/compare.mjs +252 -0
  267. package/dist/lib/evgr/bench/speed.mjs +277 -0
  268. package/dist/lib/evgr/bin/.gitignore +3 -0
  269. package/dist/lib/evgr/src/bin/bench.rs +106 -0
  270. package/dist/lib/evgr/src/bin/smoke.rs +13 -0
  271. package/dist/lib/evgr/src/grid.rs +730 -0
  272. package/dist/lib/evgr/src/lib.rs +1003 -0
  273. package/dist/lib/evgr/src/style.rs +468 -0
  274. package/dist/lib/evgr/src/text.rs +83 -0
  275. package/dist/lib/image/BitReader.rgr +171 -0
  276. package/dist/lib/image/Buffer.rgr +173 -0
  277. package/dist/lib/image/DCT.rgr +283 -0
  278. package/dist/lib/image/Deflate.rgr +339 -0
  279. package/dist/lib/image/HuffmanDecoder.rgr +181 -0
  280. package/dist/lib/image/ImageBuffer.rgr +706 -0
  281. package/dist/lib/image/JPEGDecoder.rgr +808 -0
  282. package/dist/lib/image/PNGDecoder.rgr +544 -0
  283. package/dist/lib/image/PNGEncoder.rgr +388 -0
  284. package/dist/lib/image/PPMImage.rgr +224 -0
  285. package/dist/lib/image/ProgressiveJPEGDecoder.rgr +1154 -0
  286. package/dist/lib/image/README.md +33 -0
  287. package/dist/lib/image/RasterBuffer.rgr +294 -0
  288. package/dist/lib/image/VP8BoolDecoder.rgr +163 -0
  289. package/dist/lib/image/WebPDecoder.rgr +433 -0
  290. package/dist/lib/image/WebPLossless.rgr +1035 -0
  291. package/dist/lib/image/WebPLossy.rgr +1929 -0
  292. package/dist/lib/image/ranger.json +9 -0
  293. package/dist/lib/image/testdata/webp/anim_2f_24x16.webp +0 -0
  294. package/dist/lib/image/testdata/webp/fixtures.txt +33 -0
  295. package/dist/lib/image/testdata/webp/gen_fixtures.py +261 -0
  296. package/dist/lib/image/testdata/webp/ll_1x1.webp +0 -0
  297. package/dist/lib/image/testdata/webp/ll_alpha_29x21.webp +0 -0
  298. package/dist/lib/image/testdata/webp/ll_gradient_64x64.webp +0 -0
  299. package/dist/lib/image/testdata/webp/ll_meta_32x32.webp +0 -0
  300. package/dist/lib/image/testdata/webp/ll_noise_17x9.webp +0 -0
  301. package/dist/lib/image/testdata/webp/ll_pal11_23x11.webp +0 -0
  302. package/dist/lib/image/testdata/webp/ll_pal2_19x7.webp +0 -0
  303. package/dist/lib/image/testdata/webp/ll_pal40_16x16.webp +0 -0
  304. package/dist/lib/image/testdata/webp/ll_pal4_13x5.webp +0 -0
  305. package/dist/lib/image/testdata/webp/ll_photo_40x30.webp +0 -0
  306. package/dist/lib/image/testdata/webp/ll_predictors_64x32.webp +0 -0
  307. package/dist/lib/image/testdata/webp/ly_1x1.webp +0 -0
  308. package/dist/lib/image/testdata/webp/ly_alph_raw_f0_21x13.webp +0 -0
  309. package/dist/lib/image/testdata/webp/ly_alph_raw_f1_21x13.webp +0 -0
  310. package/dist/lib/image/testdata/webp/ly_alph_raw_f2_21x13.webp +0 -0
  311. package/dist/lib/image/testdata/webp/ly_alph_raw_f3_21x13.webp +0 -0
  312. package/dist/lib/image/testdata/webp/ly_alph_vp8l_f0_21x13.webp +0 -0
  313. package/dist/lib/image/testdata/webp/ly_alph_vp8l_f1_21x13.webp +0 -0
  314. package/dist/lib/image/testdata/webp/ly_alph_vp8l_f2_21x13.webp +0 -0
  315. package/dist/lib/image/testdata/webp/ly_alph_vp8l_f3_21x13.webp +0 -0
  316. package/dist/lib/image/testdata/webp/ly_alpha_33x17.webp +0 -0
  317. package/dist/lib/image/testdata/webp/ly_exif_15x11.webp +0 -0
  318. package/dist/lib/image/testdata/webp/ly_i16_96x80.webp +0 -0
  319. package/dist/lib/image/testdata/webp/ly_nofilter_24x20.webp +0 -0
  320. package/dist/lib/image/testdata/webp/ly_odd_17x9.webp +0 -0
  321. package/dist/lib/image/testdata/webp/ly_photo_48x40.webp +0 -0
  322. package/dist/lib/image/testdata/webp/ly_q100_20x18.webp +0 -0
  323. package/dist/lib/image/testdata/webp/ly_sharp_40x36.webp +0 -0
  324. package/dist/lib/image/testdata/webp/ly_simple_filter_33x31.webp +0 -0
  325. package/dist/lib/image/testdata/webp/ly_vpx_parts8_37x45.webp +0 -0
  326. package/dist/lib/image/testdata/webp/ly_vpx_skip_96x80.webp +0 -0
  327. package/dist/lib/image/tests/WebPDecodeTool.rgr +81 -0
  328. package/dist/lib/image/tests/WebPDecoderTest.rgr +1162 -0
  329. package/dist/lib/image/tests/run_webp_tests.sh +39 -0
  330. package/dist/lib/rust/RsJson.rgr +468 -0
  331. package/dist/lib/rust/RsPrelude.rgr +1663 -0
  332. package/dist/lib/shell_test.rgr +7 -7
  333. package/dist/lib/stdlib.rgr +21 -5
  334. package/dist/lib/zip/ranger.json +6 -0
  335. package/dist/rgrc.js +87318 -67610
  336. package/package.json +1010 -1040
  337. package/dist/README.md +0 -117
  338. package/dist/package.json +0 -47
@@ -0,0 +1,1369 @@
1
+ ; SPDX-License-Identifier: MIT
2
+
3
+ ; ==============================================================================
4
+ ; EVGToolbar.rgr — a strip of buttons, for any EVG app.
5
+ ; ==============================================================================
6
+ ;
7
+ ; This was the spreadsheet's toolbar and nothing else could have it. Word had
8
+ ; no toolbar at all — no bold, no font, no size, no colour — and the only way
9
+ ; to give it one was to write a second one, which is how two toolbars start
10
+ ; drifting: a fix to the wrapping here, an icon added there, and a year later
11
+ ; the two applications look like they were made by different people.
12
+ ;
13
+ ; So the parts that are not about spreadsheets live here:
14
+ ;
15
+ ; ToolIcon the icon numbers, so a bold button is the same shape wherever
16
+ ; it is drawn
17
+ ; ToolItem what a button IS — the command it runs, how it looks, and the
18
+ ; geometry the layout gave it
19
+ ; EVGToolbar the strip: adding, measuring, wrapping, hit testing, hover
20
+ ;
21
+ ; What stays with each application is the only thing that differs: which
22
+ ; buttons, in which order, and what "pressed" means for a toggle. A spreadsheet
23
+ ; asks the cell under the caret; a document asks the run under the caret;
24
+ ; neither question belongs in here. `ToolItem.pressed` is the answer, set by
25
+ ; the owner before the strip is painted.
26
+ ;
27
+ ; Every entry names a COMMAND — the same string the keyboard and a remote host
28
+ ; use — so a button cannot do something the command surface cannot, and adding
29
+ ; a command to the table is how a button gets its behaviour.
30
+ ;
31
+ ; The layout is measured, not assumed: labels are as wide as their text, groups
32
+ ; are separated, and the strip is as many rows as the buttons need at the width
33
+ ; it is given. Anything that would not fit is simply not drawn — a toolbar that
34
+ ; runs off the edge of its own strip is the bug this started as.
35
+ ; ==============================================================================
36
+
37
+ Import "EVGColor.rgr"
38
+ Import "EVGDisplayList.rgr"
39
+ Import "EVGA11yTree.rgr"
40
+ Import "EVGStyleSheet.rgr"
41
+
42
+ ; The palette the strip draws itself with. An application hands one over rather
43
+ ; than the toolbar reaching into a theme it cannot know about.
44
+ class EVGToolbarTheme {
45
+ def bg:EVGColor (EVGColor.rgb(236 240 246))
46
+ def line:EVGColor (EVGColor.rgb(210 216 224))
47
+ def ink:EVGColor (EVGColor.rgb(30 36 48))
48
+ def dim:EVGColor (EVGColor.rgb(80 92 110))
49
+ ; THE LINE AN ICON IS DRAWN IN, which is not the colour a word is written
50
+ ; in even though it was for years. Every toolbar worth copying draws its
51
+ ; glyphs a step lighter than its text — the Radix reference this gallery
52
+ ; measures against uses a muted purple against near-black type, and the
53
+ ; dashboard uses `#52525b` against `#27272a`. Defaulted to `ink` so the
54
+ ; spreadsheet and the document are exactly where they were.
55
+ def glyphInk:EVGColor (EVGColor.rgb(30 36 48))
56
+ ; The body of a filled icon shape — a row bar, a column bar, a cell block.
57
+ ;
58
+ ; This exists because `line` was being used for it, and `line` is the
59
+ ; HAIRLINE colour: 210,216,224 on a 236,240,246 strip is four steps of
60
+ ; grey. Every icon that drew a solid shape in it — insert row, insert
61
+ ; column, delete row, no-border — came out as a pale smudge you had to
62
+ ; know was there. A fill needs a fill's contrast.
63
+ def glyph:EVGColor (EVGColor.rgb(140 152 170))
64
+ ; The pill under the pointer. This was 232,236,242 on a 236,240,246
65
+ ; strip — FOUR steps of grey, which is the same mistake `glyph` above is
66
+ ; there to explain, and the reason the strip was reported as having no
67
+ ; hover at all. A hover has to be seen without being looked for.
68
+ def hotBg:EVGColor (EVGColor.rgb(218 225 236))
69
+ def pressedBg:EVGColor (EVGColor.rgba(50 120 220 0.22))
70
+ def accent:EVGColor (EVGColor.rgb(40 100 210))
71
+ def warn:EVGColor (EVGColor.rgb(214 69 65))
72
+ ; A link is its own colour everywhere it is drawn, and a toolbar icon that
73
+ ; means "hyperlink" is drawn in it too.
74
+ def link:EVGColor (EVGColor.rgb(21 78 172))
75
+ ; The panel a dropdown's list is drawn on.
76
+ def tipBg:EVGColor (EVGColor.rgb(255 255 255))
77
+ ; The tooltip chip, which is NOT the same thing and used to borrow the
78
+ ; panel's white and an accent border. A white chip on a white page is a
79
+ ; rectangle you have to find; every toolbar worth copying draws this dark
80
+ ; and reversed out, which reads as a label about something rather than
81
+ ; another piece of the page.
82
+ def tooltipBg:EVGColor (EVGColor.rgb(38 43 54))
83
+ def tooltipInk:EVGColor (EVGColor.rgb(246 248 251))
84
+
85
+ Constructor () {
86
+ }
87
+ }
88
+
89
+ class ToolIcon {
90
+ sfn none:int () { return 0 }
91
+ sfn undo:int () { return 1 }
92
+ sfn redo:int () { return 2 }
93
+ sfn bold:int () { return 3 }
94
+ sfn italic:int () { return 4 }
95
+ sfn underline:int () { return 5 }
96
+ sfn strike:int () { return 6 }
97
+ sfn alignLeft:int () { return 7 }
98
+ sfn alignCenter:int () { return 8 }
99
+ sfn alignRight:int () { return 9 }
100
+ sfn valignTop:int () { return 10 }
101
+ sfn valignMid:int () { return 11 }
102
+ sfn valignBot:int () { return 12 }
103
+ ; `wrap` belongs to the text renderer; this one is `wrapCell`.
104
+ sfn wrapCell:int () { return 13 }
105
+ sfn merge:int () { return 14 }
106
+ sfn borderAll:int () { return 15 }
107
+ sfn borderOut:int () { return 16 }
108
+ sfn borderNone:int () { return 17 }
109
+ sfn fill:int () { return 18 }
110
+ sfn textColor:int () { return 19 }
111
+ sfn currency:int () { return 20 }
112
+ sfn percent:int () { return 21 }
113
+ sfn comma:int () { return 22 }
114
+ sfn decMore:int () { return 23 }
115
+ sfn decLess:int () { return 24 }
116
+ sfn sizeUp:int () { return 25 }
117
+ sfn sizeDown:int () { return 26 }
118
+ sfn paint:int () { return 27 }
119
+ sfn clearFmt:int () { return 28 }
120
+ sfn freeze:int () { return 29 }
121
+ ; `filter` is taken by the compiler too.
122
+ sfn funnel:int () { return 30 }
123
+ sfn sortAsc:int () { return 31 }
124
+ sfn sortDesc:int () { return 32 }
125
+ sfn insertRow:int () { return 33 }
126
+ sfn insertCol:int () { return 34 }
127
+ sfn deleteRow:int () { return 35 }
128
+ sfn chart:int () { return 36 }
129
+ sfn link:int () { return 37 }
130
+ sfn comment:int () { return 38 }
131
+ ; `find` is a name the compiler keeps for itself, hence `search`.
132
+ sfn search:int () { return 39 }
133
+ sfn cond:int () { return 40 }
134
+ sfn validate:int () { return 41 }
135
+ sfn save:int () { return 42 }
136
+ sfn openFile:int () { return 43 }
137
+ sfn newFile:int () { return 44 }
138
+ sfn report:int () { return 45 }
139
+ ; The border gallery, as opposed to the three one-click border words.
140
+ sfn borderMore:int () { return 46 }
141
+ ; The database group. A shortcut is not a discoverable feature, and in a
142
+ ; browser it is not even a reliable one: Ctrl+D is the bookmark and Ctrl+Q
143
+ ; quits. A button on the strip works everywhere the grid is drawn.
144
+ sfn database:int () { return 47 }
145
+ sfn sqlBox:int () { return 48 }
146
+ ; A show, as a triangle pointing right — the one icon a presentation tool
147
+ ; cannot do without.
148
+ sfn play:int () { return 49 }
149
+
150
+ ; Lists: a dotted one, a numbered one, and the two that move an item in
151
+ ; and out of its parent.
152
+ sfn bulletList:int () { return 50 }
153
+ sfn numberList:int () { return 51 }
154
+ sfn indentMore:int () { return 52 }
155
+ sfn indentLess:int () { return 53 }
156
+
157
+ ; --- navigation, and the verbs a document or a deck has ------------------
158
+ ;
159
+ ; These exist because the two newer editors were borrowing the
160
+ ; spreadsheet's icons for commands that have nothing to do with them, and
161
+ ; a borrowed icon is not a neutral choice — it is a WRONG picture, which
162
+ ; is worse than a dull one. "Next slide" was drawn as redo. "Previous
163
+ ; page" was drawn as undo, on a bar that also has undo. "Insert an
164
+ ; ellipse" was drawn as conditional formatting, in red and green.
165
+ ;
166
+ ; Five pairs were outright collisions — two different commands with one
167
+ ; picture on the same strip: edit-mode and copy and format-copy all drew
168
+ ; the paint roller; lock and group both drew the freeze cross; delete
169
+ ; shape and delete slide both drew the delete-row bar.
170
+ sfn navFirst:int () { return 54 }
171
+ sfn navPrev:int () { return 55 }
172
+ sfn navNext:int () { return 56 }
173
+ sfn navLast:int () { return 57 }
174
+ ; A pointer: "you are editing this now", as against reading it.
175
+ sfn editMode:int () { return 58 }
176
+ sfn copy:int () { return 59 }
177
+ sfn paste:int () { return 60 }
178
+ sfn shapeRect:int () { return 61 }
179
+ sfn shapeEllipse:int (){ return 62 }
180
+ sfn textBox:int () { return 63 }
181
+ sfn picture:int () { return 64 }
182
+ sfn lock:int () { return 65 }
183
+ sfn flipH:int () { return 66 }
184
+ sfn flipV:int () { return 67 }
185
+ sfn duplicate:int () { return 68 }
186
+ sfn trash:int () { return 69 }
187
+ sfn bringFront:int () { return 70 }
188
+ sfn sendBack:int () { return 71 }
189
+ sfn group:int () { return 72 }
190
+ sfn ungroup:int () { return 73 }
191
+ sfn palette:int () { return 74 }
192
+ sfn slideAdd:int () { return 75 }
193
+ sfn slideCopy:int () { return 76 }
194
+ sfn slideDel:int () { return 77 }
195
+ ; The other half of the format painter: pick it up, put it on. These were
196
+ ; the paint roller and the all-borders grid, and the grid was ALSO the box
197
+ ; tool two buttons away.
198
+ sfn formatPaste:int () { return 78 }
199
+ ; The switch that turns the interface around. Drawn as a paragraph of
200
+ ; ragged lines with an arrow under it pointing LEFT — the direction the
201
+ ; text will run once it is pressed, which is the only thing about this
202
+ ; button a reader needs to guess.
203
+ sfn textRtl:int () { return 79 }
204
+ ; The printer. Every office suite puts one on the strip and it is the one
205
+ ; picture nobody has to be taught.
206
+ sfn printDoc:int () { return 80 }
207
+ ; The chevron a dropdown draws beside its own picture.
208
+ sfn caret:int () { return 81 }
209
+ ; The shape gallery: a square, a circle and a triangle, which is what
210
+ ; every office suite puts on the button that opens one. It had been
211
+ ; borrowing `merge`, two arrows pointing at each other, and a reader who
212
+ ; cannot guess what a button does clicks it to find out — which is exactly
213
+ ; how this one got reported as a broken insert.
214
+ sfn shapes:int () { return 82 }
215
+ ; Scissors. Copy and paste had pictures and cut did not, which is the one
216
+ ; of the three nobody can substitute a word for.
217
+ sfn cut:int () { return 83 }
218
+ ; Justified text: four full-width lines. The other three alignments have
219
+ ; had a picture since the beginning and this one had none, so it could not
220
+ ; be offered.
221
+ sfn textJustify:int () { return 84 }
222
+ ; Lining OBJECTS up against an edge, which is not the same thing as
223
+ ; aligning text and had been borrowing its picture. Two boxes and the rule
224
+ ; they are pushed against; the text ones stay four lines of type.
225
+ sfn arrangeLeft:int () { return 85 }
226
+ sfn arrangeCenter:int () { return 86 }
227
+ sfn arrangeRight:int () { return 87 }
228
+ ; The structure of one page rather than a list of words: a rule down the
229
+ ; side and rows hanging off it, two of them indented under the first.
230
+ ; It is next to the bulleted list on the strip and must not be mistaken
231
+ ; for it — that one makes text into a list, this one shows what is on the
232
+ ; slide.
233
+ sfn outlineList:int () { return 88 }
234
+ }
235
+
236
+ class ToolItem {
237
+ ; The command this runs, and the argument it runs it with.
238
+ def command:string ""
239
+ def arg:string ""
240
+ def label:string ""
241
+ def icon:int 0
242
+ ; A toggle draws itself pressed when its style question answers yes. The
243
+ ; question is `toggleOp` / `toggleArg`, asked of the cell the caret is on.
244
+ def toggle:boolean false
245
+ def toggleOp:string ""
246
+ def toggleArg:string ""
247
+ ; Laid-out geometry, filled by `layout`.
248
+ def x:int 0
249
+ def y:int 0
250
+ def w:int 0
251
+ def h:int 0
252
+ ; A separator is drawn as a rule and cannot be clicked.
253
+ def isSeparator:boolean false
254
+ ; Whether a toggle is currently on. The strip does not know how to answer
255
+ ; that — a spreadsheet asks the cell under the caret and a document asks
256
+ ; the run under it — so the owner sets it before the strip is painted.
257
+ def pressed:boolean false
258
+ ; Wider than an icon: font / size dropdowns show a readable label.
259
+ def textW:int 0
260
+ ; Draw the name beside the picture, not only in the tooltip.
261
+ ;
262
+ ; The commands a beginner reaches for first — paste, copy, print — are
263
+ ; worth the width: a picture has to be learnt and a word does not, and a
264
+ ; strip where the four or five most-used buttons say what they are is a
265
+ ; strip somebody can use on the first day. The rest stay icon-only,
266
+ ; because a band where EVERYTHING is labelled is as unreadable as one
267
+ ; where nothing is.
268
+ def showLabel:boolean false
269
+ ; What the caption measures at, filled in by the view — the strip has no
270
+ ; measurer of its own. See ToolTab.measuredW, same arrangement.
271
+ def labelW:int 0
272
+ ; A button that opens a list under itself instead of running something.
273
+ ; Its own `command` is empty: pressing it opens the list.
274
+ def isMenu:boolean false
275
+ ; For an entry IN such a list, the index of the button that owns it, or
276
+ ; -1. An entry takes no room on the strip while its list is shut, which
277
+ ; is the whole point — five border commands in one slot.
278
+ def menuOwner:int -1
279
+ ; Scratch: where this item was before the array was last rebuilt. Only
280
+ ; `tagPositions` and `reindex` touch it — see `addUnderMenu`.
281
+ def wasAt:int -1
282
+ ; A button drawn big, with its name UNDER the picture rather than beside
283
+ ; it. For the one or two commands on a page that are the reason the page
284
+ ; exists — Insert's text box is what somebody opens Insert to do. A strip
285
+ ; where every command is the same size says every command is equally
286
+ ; likely, and none of them ever is.
287
+ def isLarge:boolean false
288
+
289
+ Constructor () {
290
+ }
291
+ }
292
+
293
+ ; One named page of the strip.
294
+ ;
295
+ ; A spreadsheet has upwards of fifty commands and a deck not many fewer, and a
296
+ ; single strip of them is a wall: everything is present, nothing is findable,
297
+ ; and the buttons a beginner wants are the ones that wrapped onto the second
298
+ ; row or fell off the end. Grouping them under names — File, Home, Insert,
299
+ ; Layout — is what every office suite settled on, LibreOffice's tabbed
300
+ ; notebookbar included, and it is not decoration: it turns "which of these
301
+ ; fifty pictures is it" into "which of five words is it", and a word can be
302
+ ; read.
303
+ ;
304
+ ; A tab owns a RANGE of the item list rather than its own list. The items stay
305
+ ; in one array in the order they were added, so everything that walks them —
306
+ ; the painter, the click, the accessibility tree — keeps working on a strip
307
+ ; with tabs and on one without.
308
+ class ToolTab {
309
+ def name:string ""
310
+ ; The half-open range of `items` this tab owns.
311
+ def first:int 0
312
+ def count:int 0
313
+ ; Laid-out geometry of the tab's own label, filled by `layout`.
314
+ def x:int 0
315
+ def y:int 0
316
+ def w:int 0
317
+ def h:int 0
318
+ ; What the label measures at, when somebody with a measurer has said. The
319
+ ; strip itself has none — it computes geometry from numbers the owner
320
+ ; hands it, which is the same reason `addText` takes a width — so the
321
+ ; view measures the names and writes them here before the layout runs,
322
+ ; and the estimate below is what a host without a view falls back to.
323
+ def measuredW:int 0
324
+
325
+ Constructor () {
326
+ }
327
+ }
328
+
329
+ class EVGToolbar {
330
+ def items:[ToolItem]
331
+ ; The named pages, in the order they are shown. Empty means an untabbed
332
+ ; strip, which is what every caller had before tabs existed and still
333
+ ; works unchanged.
334
+ def tabs:[ToolTab]
335
+ def activeTab:int 0
336
+ ; The height of the row of names above the buttons.
337
+ def tabH:int 28
338
+ ; One row is 30 tall; the strip is as many rows as the buttons need at the
339
+ ; width it is given. A spreadsheet has more buttons than a narrow window
340
+ ; has room for, and dropping the last six of them — which is what a single
341
+ ; fixed row does — loses exactly the ones a beginner is looking for.
342
+ ; A desktop icon set draws its glyphs on a 24-pixel grid in a button of
343
+ ; about 30 — LibreOffice, Breeze, every system tray. This strip drew 16
344
+ ; pixels of glyph in 26, which is why the icons read as small even before
345
+ ; anything about their shapes is discussed. The button grew with them so
346
+ ; the strip does not get denser, only more legible.
347
+ ; Tall enough for a 24-pixel glyph with room around it. The row was 34,
348
+ ; which left 28 for the button and forced the glyph down to 20.
349
+ def rowH:int 40
350
+ def rows:int 1
351
+ def height:int 36
352
+ def pad:int 5
353
+ def iconW:int 34
354
+ ; A row with a large button in it is this tall instead of `rowH`; the
355
+ ; ordinary chips centre themselves in it.
356
+ def largeH:int 54
357
+ ; --- the strip's metrics, from a stylesheet -------------------------------
358
+ ;
359
+ ; The seven numbers above are what a strip on a desktop is. They are wrong
360
+ ; on a phone — a 30-pixel chip is a comfortable mouse target and a poor
361
+ ; finger one, and WCAG 2.2 asks for 44 — and they were wrong in a way that
362
+ ; could only be fixed by an `if` in whatever code laid the strip out, once
363
+ ; per caller.
364
+ ;
365
+ ; So they come from a stylesheet instead, and a stylesheet has `@media`.
366
+ ; The strip is not a tree of elements and this does not make it one: it
367
+ ; reads a fixed handful of classes, each standing for one metric, and
368
+ ; anything it does not find keeps the value it already had. That is enough
369
+ ; for the responsive part — the metrics ARE the layout — without a toolbar
370
+ ; that has to grow a box model.
371
+ ;
372
+ ; .toolbar { height: 34px; padding: 5px } row height, edge pad
373
+ ; .toolbar-tabs { height: 28px }
374
+ ; .toolbar-button { width: 30px; height: 54px } chip width, large row
375
+ ;
376
+ ; The sheet's viewport must already be set; this asks it nothing about the
377
+ ; window, because a toolbar has no window either.
378
+ fn applyStyle:void (sheet:EVGStyleSheet) {
379
+ def strip (EVGElement.createDiv())
380
+ strip.className = "toolbar"
381
+ sheet.applyTree(strip "")
382
+ if (strip.height.isPixels()) {
383
+ def h:int (to_int strip.height.pixels)
384
+ if (h > 0) {
385
+ rowH = h
386
+ }
387
+ }
388
+ if (strip.box.paddingLeft.isPixels()) {
389
+ def p:int (to_int strip.box.paddingLeft.pixels)
390
+ if (p >= 0) {
391
+ pad = p
392
+ }
393
+ }
394
+ def tabsEl (EVGElement.createDiv())
395
+ tabsEl.className = "toolbar-tabs"
396
+ sheet.applyTree(tabsEl "")
397
+ if (tabsEl.height.isPixels()) {
398
+ def th:int (to_int tabsEl.height.pixels)
399
+ if (th > 0) {
400
+ tabH = th
401
+ }
402
+ }
403
+ def btn (EVGElement.createDiv())
404
+ btn.className = "toolbar-button"
405
+ sheet.applyTree(btn "")
406
+ if (btn.width.isPixels()) {
407
+ def bw:int (to_int btn.width.pixels)
408
+ if (bw > 0) {
409
+ iconW = bw
410
+ }
411
+ }
412
+ if (btn.height.isPixels()) {
413
+ def bh:int (to_int btn.height.pixels)
414
+ if (bh > 0) {
415
+ largeH = bh
416
+ }
417
+ }
418
+ ; A row can never be shorter than the chips standing in it.
419
+ if (largeH < rowH) {
420
+ largeH = rowH
421
+ }
422
+ if ((array_length items) > 0) {
423
+ this.layout(lastX 0 lastW)
424
+ }
425
+ }
426
+
427
+ ; Lay the strip out right to left.
428
+ ;
429
+ ; This is the same switch `direction: rtl` is in EVGLayout, for the part
430
+ ; of the interface that does not go through it: a toolbar computes its own
431
+ ; geometry because it is a single row of chips, not a flex tree. The
432
+ ; mirroring is the same reflection, for the same reason — everything that
433
+ ; reads a button's position, the painter, the click and the accessibility
434
+ ; tree, reads `x` and `w` off the item, so turning those around turns all
435
+ ; three around together and none of them has to know.
436
+ def rtl:boolean false
437
+ ; Which dropdown is showing its list, or -1.
438
+ def openMenu:int -1
439
+ ; Which item the pointer is over, or -1. Only chrome, but it is the
440
+ ; difference between a strip of shapes and something that looks clickable.
441
+ def hot:int -1
442
+ ; Where the strip was last laid out. The overlay is drawn in a second
443
+ ; pass, after the app has drawn everything else, and it has to be able to
444
+ ; keep a tooltip inside the window without being told twice.
445
+ def lastX:int 0
446
+ def lastW:int 0
447
+
448
+ Constructor () {
449
+ }
450
+
451
+ Constructor () {
452
+ }
453
+
454
+ fn add:ToolItem (command:string arg:string label:string icon:int) {
455
+ def t (new ToolItem)
456
+ t.command = command
457
+ t.arg = arg
458
+ t.label = label
459
+ t.icon = icon
460
+ push items t
461
+ return t
462
+ }
463
+
464
+ fn addToggle:ToolItem (command:string arg:string label:string icon:int op:string opArg:string) {
465
+ def t:ToolItem (this.add(command arg label icon))
466
+ t.toggle = true
467
+ t.toggleOp = op
468
+ t.toggleArg = opArg
469
+ return t
470
+ }
471
+
472
+ fn addText:ToolItem (command:string arg:string label:string width:int) {
473
+ def t:ToolItem (this.add(command arg label (ToolIcon.none())))
474
+ t.textW = width
475
+ return t
476
+ }
477
+
478
+ fn addSeparator:void () {
479
+ def t (new ToolItem)
480
+ t.isSeparator = true
481
+ push items t
482
+ }
483
+
484
+ ; Start a named page. Everything added after this belongs to it, until the
485
+ ; next one — which is why a strip is still built as one list of `add`
486
+ ; calls, with the tabs marked off inside it rather than built separately.
487
+ ; A button drawn big, with its name under the picture.
488
+ fn addLarge:ToolItem (command:string arg:string label:string icon:int) {
489
+ def t:ToolItem (this.add(command arg label icon))
490
+ t.isLarge = true
491
+ t.showLabel = true
492
+ return t
493
+ }
494
+
495
+ ; Is there a big button on the page being shown? The row has to be tall
496
+ ; enough for it, and the answer decides the height of the whole band.
497
+ fn hasLargeShowing:boolean () {
498
+ def i:int 0
499
+ while (i < (array_length items)) {
500
+ def t:ToolItem (itemAt items i)
501
+ if t.isLarge {
502
+ if (t.menuOwner < 0) {
503
+ if (this.itemOnActiveTab(i)) {
504
+ return true
505
+ }
506
+ }
507
+ }
508
+ i = (i + 1)
509
+ }
510
+ return false
511
+ }
512
+
513
+ ; A button that says what it is. The picture and the word, side by side.
514
+ fn addLabeled:ToolItem (command:string arg:string label:string icon:int) {
515
+ def t:ToolItem (this.add(command arg label icon))
516
+ t.showLabel = true
517
+ return t
518
+ }
519
+
520
+ ; A button that opens a list under itself. Everything added with
521
+ ; `addUnder` after it belongs to that list until the next one.
522
+ fn addDropdown:ToolItem (label:string icon:int) {
523
+ def t:ToolItem (this.add("" "" label icon))
524
+ t.isMenu = true
525
+ t.showLabel = true
526
+ return t
527
+ }
528
+
529
+ ; An entry in the last dropdown's list. It takes no room on the strip
530
+ ; while the list is shut.
531
+ fn addUnder:ToolItem (command:string arg:string label:string icon:int) {
532
+ def owner:int (this.lastMenuIndex())
533
+ def t:ToolItem (this.add(command arg label icon))
534
+ t.menuOwner = owner
535
+ t.showLabel = true
536
+ return t
537
+ }
538
+
539
+ ; Where a dropdown with this label is, or -1. `addUnder` fills the list
540
+ ; that was declared LAST, which is only any use while the strip is being
541
+ ; built; an owner filling a menu in later — the font chooser, whose
542
+ ; entries are the faces the page loaded and whatever the open deck is set
543
+ ; in — has to be able to name the one it means.
544
+ fn indexOfLabel:int (label:string) {
545
+ def i:int 0
546
+ while (i < (array_length items)) {
547
+ def t:ToolItem (itemAt items i)
548
+ if t.isMenu {
549
+ if (t.label == label) {
550
+ return i
551
+ }
552
+ }
553
+ i = (i + 1)
554
+ }
555
+ return (0 - 1)
556
+ }
557
+
558
+ ; Empty a dropdown's list, keeping the button, and add an entry to the end
559
+ ; of one. For a menu whose entries are not known when the strip is built.
560
+ ;
561
+ ; WHY BOTH GO THROUGH `reindex`. An entry names its menu by that menu's
562
+ ; INDEX in this array, so inserting or removing anything moves owners that
563
+ ; had nothing to do with the change. Adjusting them by hand means getting
564
+ ; "how many of the things that went were in front of me" right for every
565
+ ; item, which is the kind of arithmetic that is wrong once and then wrong
566
+ ; forever. So each item is tagged with where it used to be, the array is
567
+ ; rebuilt, and every owner is looked up in that tag — no counting.
568
+ fn clearUnderMenu:void (owner:int) {
569
+ this.tagPositions()
570
+ def keep:[ToolItem]
571
+ def i:int 0
572
+ while (i < (array_length items)) {
573
+ def t:ToolItem (itemAt items i)
574
+ if (t.menuOwner != owner) {
575
+ push keep t
576
+ }
577
+ i = (i + 1)
578
+ }
579
+ ; A list being emptied cannot stay open over it.
580
+ if (openMenu == owner) {
581
+ openMenu = -1
582
+ }
583
+ this.reindex(keep)
584
+ }
585
+
586
+ fn addUnderMenu:ToolItem (owner:int command:string arg:string label:string icon:int) {
587
+ this.tagPositions()
588
+ def t (new ToolItem)
589
+ t.command = command
590
+ t.arg = arg
591
+ t.label = label
592
+ t.icon = icon
593
+ t.showLabel = true
594
+ ; Tagged with the owner's OLD position, which `reindex` then turns
595
+ ; into wherever the owner ends up.
596
+ t.wasAt = -1
597
+ t.menuOwner = owner
598
+ ; Just past the last entry this menu already has, so entries come out
599
+ ; in the order they were added.
600
+ def at:int (owner + 1)
601
+ def k:int (owner + 1)
602
+ while (k < (array_length items)) {
603
+ def other:ToolItem (itemAt items k)
604
+ if (other.menuOwner == owner) {
605
+ at = (k + 1)
606
+ }
607
+ k = (k + 1)
608
+ }
609
+ def out:[ToolItem]
610
+ def m:int 0
611
+ while (m < (array_length items)) {
612
+ if (m == at) {
613
+ push out t
614
+ }
615
+ push out (itemAt items m)
616
+ m = (m + 1)
617
+ }
618
+ if (at >= (array_length items)) {
619
+ push out t
620
+ }
621
+ ; The TABS have to move too. Each owns a half-open range of `items`,
622
+ ; and an entry inserted into the middle of the array pushes every
623
+ ; item after it along by one: the tab the owner is on has one more
624
+ ; item, and every tab after it starts one later. Without this the
625
+ ; ranges stayed where they were, so the last button on the owner's
626
+ ; page fell off it and the next page began one button early — the
627
+ ; strip's font chooser fills itself this way at start-up, and Home's
628
+ ; last button was never drawn on any page that had one.
629
+ def ti:int 0
630
+ while (ti < (array_length tabs)) {
631
+ def tab:ToolTab (itemAt tabs ti)
632
+ if (at <= tab.first) {
633
+ ; Not the owner's tab: an insertion at a later tab's first
634
+ ; index belongs to the tab BEFORE it (the owner is there), so
635
+ ; that tab moves along rather than growing.
636
+ if (owner < tab.first) {
637
+ tab.first = (tab.first + 1)
638
+ } {
639
+ tab.count = (tab.count + 1)
640
+ }
641
+ } {
642
+ if (at < (tab.first + tab.count)) {
643
+ tab.count = (tab.count + 1)
644
+ } {
645
+ ; `at` is exactly the end of this tab's range: the entry
646
+ ; joins it when its owner is on it.
647
+ if ((at == (tab.first + tab.count)) && (owner >= tab.first) && (owner < (tab.first + tab.count))) {
648
+ tab.count = (tab.count + 1)
649
+ }
650
+ }
651
+ }
652
+ ti = (ti + 1)
653
+ }
654
+ this.reindex(out)
655
+ return t
656
+ }
657
+
658
+ ; Remember where every item is now, so an owner can be found again after
659
+ ; the array has been rebuilt. -1 marks an item that was not in it.
660
+ fn tagPositions:void () {
661
+ def i:int 0
662
+ while (i < (array_length items)) {
663
+ def t:ToolItem (itemAt items i)
664
+ t.wasAt = i
665
+ i = (i + 1)
666
+ }
667
+ }
668
+
669
+ ; Adopt a rebuilt array and point every entry back at its own menu.
670
+ fn reindex:void (next:[ToolItem]) {
671
+ def i:int 0
672
+ while (i < (array_length next)) {
673
+ def t:ToolItem (itemAt next i)
674
+ if (t.menuOwner >= 0) {
675
+ def found:int -1
676
+ def j:int 0
677
+ while (j < (array_length next)) {
678
+ def other:ToolItem (itemAt next j)
679
+ if (other.wasAt == t.menuOwner) {
680
+ found = j
681
+ }
682
+ j = (j + 1)
683
+ }
684
+ t.menuOwner = found
685
+ }
686
+ i = (i + 1)
687
+ }
688
+ ; …and the open menu, which is an index into the same array.
689
+ if (openMenu >= 0) {
690
+ def wasOpen:int openMenu
691
+ openMenu = -1
692
+ def k:int 0
693
+ while (k < (array_length next)) {
694
+ ; Bound to a local first: `(itemAt …).field` does not resolve.
695
+ def item:ToolItem (itemAt next k)
696
+ if (item.wasAt == wasOpen) {
697
+ openMenu = k
698
+ }
699
+ k = (k + 1)
700
+ }
701
+ }
702
+ items = next
703
+ }
704
+
705
+ fn lastMenuIndex:int () {
706
+ def i:int ((array_length items) - 1)
707
+ while (i >= 0) {
708
+ def t:ToolItem (itemAt items i)
709
+ if t.isMenu {
710
+ return i
711
+ }
712
+ i = (i - 1)
713
+ }
714
+ return -1
715
+ }
716
+
717
+ ; A press on item `i`, as far as the STRIP is concerned. Answers true when
718
+ ; the strip dealt with it itself — opening or shutting a list — and false
719
+ ; when the owner should run the command. Keeping this here rather than in
720
+ ; three apps is what stops the three from drifting on what a dropdown does.
721
+ fn pressItem:boolean (i:int) {
722
+ if (i < 0) {
723
+ return false
724
+ }
725
+ if (i >= (array_length items)) {
726
+ return false
727
+ }
728
+ def t:ToolItem (itemAt items i)
729
+ if t.isMenu {
730
+ if (openMenu == i) {
731
+ openMenu = -1
732
+ } {
733
+ openMenu = i
734
+ }
735
+ return true
736
+ }
737
+ ; Choosing from a list shuts it. The command still runs — the caller
738
+ ; is told false so that it does.
739
+ if (t.menuOwner >= 0) {
740
+ openMenu = -1
741
+ } {
742
+ openMenu = -1
743
+ }
744
+ return false
745
+ }
746
+
747
+ fn closeMenu:void () {
748
+ openMenu = -1
749
+ }
750
+
751
+ fn addTab:ToolTab (name:string) {
752
+ this.closeTab()
753
+ def t (new ToolTab)
754
+ t.name = name
755
+ t.first = (array_length items)
756
+ t.count = 0
757
+ push tabs t
758
+ return t
759
+ }
760
+
761
+ ; Give the last tab the items added since it was opened. Called by
762
+ ; `addTab` and again by `layout`, so a caller never has to close the last
763
+ ; one by hand — forgetting to would silently hide every button on it.
764
+ fn closeTab:void () {
765
+ def n:int (array_length tabs)
766
+ if (n == 0) {
767
+ return
768
+ }
769
+ def last:ToolTab (itemAt tabs (n - 1))
770
+ last.count = ((array_length items) - last.first)
771
+ }
772
+
773
+ fn hasTabs:boolean () {
774
+ return ((array_length tabs) > 0)
775
+ }
776
+
777
+ fn tabCount:int () {
778
+ return (array_length tabs)
779
+ }
780
+
781
+ fn tabAt:ToolTab (i:int) {
782
+ return (itemAt tabs i)
783
+ }
784
+
785
+ ; How wide a tab's label sits. The measured width when the view has said,
786
+ ; and an estimate when nothing has — a strip with no measurer behind it
787
+ ; still has to lay out, because that is how the tests and the a11y tree
788
+ ; drive it.
789
+ fn tabLabelW:int (t:ToolTab) {
790
+ if (t.measuredW > 0) {
791
+ return (t.measuredW + 22)
792
+ }
793
+ return ((strlen t.name) * 8 + 22)
794
+ }
795
+
796
+ ; Which tab the pointer is over, or -1. Separate from `hit` because a tab
797
+ ; and a button do different things and a caller has to be able to tell
798
+ ; them apart before deciding which.
799
+ fn hitTab:int (px:int py:int) {
800
+ def i:int 0
801
+ while (i < (array_length tabs)) {
802
+ def t:ToolTab (itemAt tabs i)
803
+ if (t.w > 0) {
804
+ if (px >= t.x) {
805
+ if (px < (t.x + t.w)) {
806
+ if (py >= t.y) {
807
+ if (py < (t.y + t.h)) {
808
+ return i
809
+ }
810
+ }
811
+ }
812
+ }
813
+ }
814
+ i = (i + 1)
815
+ }
816
+ return -1
817
+ }
818
+
819
+ ; Show a page. Out-of-range is ignored rather than clamped: a caller
820
+ ; asking for a tab that is not there has a bug, and moving them to a
821
+ ; neighbouring page hides it.
822
+ ; Which page a command's button sits on, or -1 when nothing on the strip
823
+ ; runs it.
824
+ fn tabOwning:int (command:string) {
825
+ def at:int (this.indexOfCommand(command))
826
+ if (at < 0) {
827
+ return -1
828
+ }
829
+ def i:int 0
830
+ def n:int (array_length tabs)
831
+ while (i < n) {
832
+ def t:ToolTab (itemAt tabs i)
833
+ if (at >= t.first) {
834
+ if (at < (t.first + t.count)) {
835
+ return i
836
+ }
837
+ }
838
+ i = (i + 1)
839
+ }
840
+ return -1
841
+ }
842
+
843
+ fn indexOfCommand:int (command:string) {
844
+ def i:int 0
845
+ def n:int (array_length items)
846
+ while (i < n) {
847
+ def t:ToolItem (itemAt items i)
848
+ if (t.command == command) {
849
+ return i
850
+ }
851
+ i = (i + 1)
852
+ }
853
+ return -1
854
+ }
855
+
856
+ ; Bring the page a command lives on to the front. A command run from a
857
+ ; link, a keyboard shortcut or a script leaves the strip showing the
858
+ ; button that would have run it — otherwise the window changes and the
859
+ ; toolbar goes on describing something else.
860
+ fn showTabFor:boolean (command:string) {
861
+ return (this.selectTab((this.tabOwning(command))))
862
+ }
863
+
864
+ fn selectTab:boolean (i:int) {
865
+ if (i < 0) {
866
+ return false
867
+ }
868
+ if (i >= (array_length tabs)) {
869
+ return false
870
+ }
871
+ if (i == activeTab) {
872
+ return false
873
+ }
874
+ activeTab = i
875
+ hot = -1
876
+ ; A list belonging to the page being left has no business staying
877
+ ; open over the page being arrived at.
878
+ openMenu = -1
879
+ return true
880
+ }
881
+
882
+ fn tabNamed:int (name:string) {
883
+ def i:int 0
884
+ while (i < (array_length tabs)) {
885
+ def t:ToolTab (itemAt tabs i)
886
+ if (t.name == name) {
887
+ return i
888
+ }
889
+ i = (i + 1)
890
+ }
891
+ return -1
892
+ }
893
+
894
+ ; Is this item on the page being shown? An untabbed strip shows all of
895
+ ; them, which is what makes tabs an addition rather than a change.
896
+ fn itemOnActiveTab:boolean (i:int) {
897
+ if ((array_length tabs) == 0) {
898
+ return true
899
+ }
900
+ if (activeTab < 0) {
901
+ return false
902
+ }
903
+ if (activeTab >= (array_length tabs)) {
904
+ return false
905
+ }
906
+ def t:ToolTab (itemAt tabs activeTab)
907
+ if (i < t.first) {
908
+ return false
909
+ }
910
+ return (i < (t.first + t.count))
911
+ }
912
+
913
+ fn layout:void (x0:int y0:int width:int) {
914
+ lastX = x0
915
+ lastW = width
916
+ this.closeTab()
917
+ def buttonsY:int y0
918
+ if (this.hasTabs()) {
919
+ this.layoutTabs(x0 y0 width)
920
+ buttonsY = (y0 + tabH)
921
+ }
922
+ def right:int ((x0 + width) - pad)
923
+ def cx:int (x0 + pad)
924
+ ; The row is as tall as the tallest thing on it.
925
+ def useRowH:int rowH
926
+ if (this.hasLargeShowing()) {
927
+ useRowH = largeH
928
+ }
929
+ def row:int 0
930
+ def i:int 0
931
+ while (i < (array_length items)) {
932
+ def t:ToolItem (itemAt items i)
933
+ ; Items on a page that is not showing get NO rectangle. That is
934
+ ; what keeps the painter, the click and the accessibility tree
935
+ ; from having to know about tabs at all: they already skip an item
936
+ ; with no width, because that is how a button that did not fit has
937
+ ; always been marked.
938
+ if ((this.itemOnActiveTab(i)) == false) {
939
+ t.x = 0
940
+ t.y = 0
941
+ t.w = 0
942
+ t.h = 0
943
+ i = (i + 1)
944
+ continue
945
+ }
946
+ ; An entry in a dropdown's list takes no room on the strip. It is
947
+ ; laid out under its owner afterwards, and only while the list is
948
+ ; showing.
949
+ if (t.menuOwner >= 0) {
950
+ t.x = 0
951
+ t.y = 0
952
+ t.w = 0
953
+ t.h = 0
954
+ i = (i + 1)
955
+ continue
956
+ }
957
+ def w:int iconW
958
+ if t.isSeparator {
959
+ w = 9
960
+ }
961
+ if (t.textW > 0) {
962
+ w = t.textW
963
+ }
964
+ if t.showLabel {
965
+ w = ((iconW + (this.labelWidth(t))) + 6)
966
+ }
967
+ ; A big button is as wide as its own name and no wider — the word
968
+ ; sits under the picture rather than beside it.
969
+ if t.isLarge {
970
+ w = ((this.labelWidth(t)) + 12)
971
+ if (w < (iconW + 16)) {
972
+ w = (iconW + 16)
973
+ }
974
+ }
975
+ if t.isMenu {
976
+ ; Room for the chevron that says there is a list under it.
977
+ w = (w + 12)
978
+ }
979
+ if ((cx + w) > right) {
980
+ ; Wrap. A separator never starts a row — it would be a rule
981
+ ; hanging in space.
982
+ row = (row + 1)
983
+ cx = (x0 + pad)
984
+ if t.isSeparator {
985
+ t.x = 0
986
+ t.y = 0
987
+ t.w = 0
988
+ t.h = 0
989
+ i = (i + 1)
990
+ continue
991
+ }
992
+ }
993
+ t.x = cx
994
+ t.y = ((buttonsY + (row * useRowH)) + 3)
995
+ t.w = w
996
+ t.h = (useRowH - 6)
997
+ if (t.isLarge == false) {
998
+ ; An ordinary chip keeps its own height and sits in the middle
999
+ ; of a tall row, rather than stretching to fill it.
1000
+ t.h = (rowH - 6)
1001
+ t.y = (t.y + (idiv (useRowH - rowH) 2))
1002
+ }
1003
+ cx = (cx + w)
1004
+ if (t.isSeparator == false) {
1005
+ cx = (cx + 2)
1006
+ }
1007
+ i = (i + 1)
1008
+ }
1009
+ rows = (row + 1)
1010
+ height = (rows * useRowH)
1011
+ if (this.hasTabs()) {
1012
+ height = (height + tabH)
1013
+ }
1014
+ this.layoutMenu()
1015
+ if rtl {
1016
+ this.mirrorRows(x0 width)
1017
+ this.mirrorTabs(x0 width)
1018
+ }
1019
+ }
1020
+
1021
+ ; What a caption measures at: what the view said, or an estimate for a
1022
+ ; strip with nothing measuring behind it.
1023
+ fn labelWidth:int (t:ToolItem) {
1024
+ if (t.labelW > 0) {
1025
+ return t.labelW
1026
+ }
1027
+ return ((strlen t.label) * 7)
1028
+ }
1029
+
1030
+ ; The open dropdown's entries, in a column under the button that owns
1031
+ ; them. Laid out after the strip so they sit ON TOP of it — a list that
1032
+ ; is laid out among the buttons is a list that pushes them around every
1033
+ ; time it opens.
1034
+ fn layoutMenu:void () {
1035
+ if (openMenu < 0) {
1036
+ return
1037
+ }
1038
+ if (openMenu >= (array_length items)) {
1039
+ openMenu = -1
1040
+ return
1041
+ }
1042
+ def owner:ToolItem (itemAt items openMenu)
1043
+ if (owner.w <= 0) {
1044
+ ; Its own button did not fit, or is on a page that is not
1045
+ ; showing. A list hanging off nothing is worse than no list.
1046
+ openMenu = -1
1047
+ return
1048
+ }
1049
+ ; As wide as the widest entry, so the words do not wrap.
1050
+ def wide:int 0
1051
+ def i:int 0
1052
+ while (i < (array_length items)) {
1053
+ def t:ToolItem (itemAt items i)
1054
+ if (t.menuOwner == openMenu) {
1055
+ def w:int ((iconW + (this.labelWidth(t))) + 12)
1056
+ if (w > wide) {
1057
+ wide = w
1058
+ }
1059
+ }
1060
+ i = (i + 1)
1061
+ }
1062
+ if (wide < owner.w) {
1063
+ wide = owner.w
1064
+ }
1065
+ def y:int (owner.y + owner.h)
1066
+ def j:int 0
1067
+ while (j < (array_length items)) {
1068
+ def e:ToolItem (itemAt items j)
1069
+ if (e.menuOwner == openMenu) {
1070
+ e.x = owner.x
1071
+ e.y = y
1072
+ e.w = wide
1073
+ e.h = 26
1074
+ y = (y + 26)
1075
+ }
1076
+ j = (j + 1)
1077
+ }
1078
+ }
1079
+
1080
+ ; The rectangle the open list covers, as x,y,w,h — for the painter to put
1081
+ ; a panel behind it. Empty when nothing is open.
1082
+ fn menuBox:[int] () {
1083
+ def out:[int]
1084
+ if (openMenu < 0) {
1085
+ return out
1086
+ }
1087
+ def x0:int 0
1088
+ def y0:int 0
1089
+ def x1:int 0
1090
+ def y1:int 0
1091
+ def any:boolean false
1092
+ def i:int 0
1093
+ while (i < (array_length items)) {
1094
+ def t:ToolItem (itemAt items i)
1095
+ if (t.menuOwner == openMenu) {
1096
+ if (t.w > 0) {
1097
+ if (any == false) {
1098
+ x0 = t.x
1099
+ y0 = t.y
1100
+ x1 = (t.x + t.w)
1101
+ y1 = (t.y + t.h)
1102
+ any = true
1103
+ } {
1104
+ if ((t.x + t.w) > x1) {
1105
+ x1 = (t.x + t.w)
1106
+ }
1107
+ if ((t.y + t.h) > y1) {
1108
+ y1 = (t.y + t.h)
1109
+ }
1110
+ }
1111
+ }
1112
+ }
1113
+ i = (i + 1)
1114
+ }
1115
+ if (any == false) {
1116
+ return out
1117
+ }
1118
+ push out x0
1119
+ push out y0
1120
+ push out (x1 - x0)
1121
+ push out (y1 - y0)
1122
+ return out
1123
+ }
1124
+
1125
+ ; The row of names. One line, left to right, in the order they were added
1126
+ ; — a tab strip that wraps is a tab strip nobody can find anything on, so
1127
+ ; names that do not fit are given no rectangle and are not drawn, the same
1128
+ ; way a button that does not fit is not.
1129
+ fn layoutTabs:void (x0:int y0:int width:int) {
1130
+ def right:int ((x0 + width) - pad)
1131
+ def cx:int (x0 + pad)
1132
+ def i:int 0
1133
+ while (i < (array_length tabs)) {
1134
+ def t:ToolTab (itemAt tabs i)
1135
+ def w:int (this.tabLabelW(t))
1136
+ if ((cx + w) > right) {
1137
+ t.x = 0
1138
+ t.y = 0
1139
+ t.w = 0
1140
+ t.h = 0
1141
+ } {
1142
+ t.x = cx
1143
+ t.y = (y0 + 2)
1144
+ t.w = w
1145
+ t.h = (tabH - 2)
1146
+ cx = (cx + w)
1147
+ }
1148
+ i = (i + 1)
1149
+ }
1150
+ }
1151
+
1152
+ ; The tab row turns around with the rest of the strip, about the strip
1153
+ ; rather than about the names' own extent — see `mirrorRows`, which had
1154
+ ; exactly that bug.
1155
+ fn mirrorTabs:void (x0:int width:int) {
1156
+ def span:int ((x0 + x0) + width)
1157
+ def i:int 0
1158
+ while (i < (array_length tabs)) {
1159
+ def t:ToolTab (itemAt tabs i)
1160
+ if (t.w > 0) {
1161
+ t.x = (span - (t.x + t.w))
1162
+ }
1163
+ i = (i + 1)
1164
+ }
1165
+ }
1166
+
1167
+ ; Reflect every placed button about THE STRIP, so the row begins at the
1168
+ ; right-hand edge — where a right-to-left reader's eye and hand start, and
1169
+ ; where the first word of every line in the document below it begins.
1170
+ ;
1171
+ ; About the strip, and not about the extent the buttons happen to occupy.
1172
+ ; That was the first version of this and it was wrong in a way the tests
1173
+ ; did not catch and a screenshot did immediately: reflecting a row about
1174
+ ; its own ends reverses the ORDER of the buttons and leaves the block of
1175
+ ; them exactly where it was, still packed against the left edge with the
1176
+ ; empty space still on the right. Every button had moved and the strip had
1177
+ ; not.
1178
+ ;
1179
+ ; Reflecting about the strip is also what a wrapped row wants. A short
1180
+ ; last row lands against the right edge, which is where a row starts here,
1181
+ ; the same way a short last line of a right-to-left paragraph does.
1182
+ ;
1183
+ ; `pad` is preserved at both ends because it is inside the reflection: a
1184
+ ; row running from x0+pad to some x lands with its far end at
1185
+ ; x0+width-pad.
1186
+ ;
1187
+ ; Buttons that did not fit were given no rectangle and are left alone —
1188
+ ; reflecting a zero-width item at zero would put a phantom click target at
1189
+ ; the far edge.
1190
+ fn mirrorRows:void (x0:int width:int) {
1191
+ def span:int ((x0 + x0) + width)
1192
+ def i:int 0
1193
+ while (i < (array_length items)) {
1194
+ def t:ToolItem (itemAt items i)
1195
+ if (t.w > 0) {
1196
+ t.x = (span - (t.x + t.w))
1197
+ }
1198
+ i = (i + 1)
1199
+ }
1200
+ }
1201
+
1202
+
1203
+
1204
+ ; --- what the strip MEANS -------------------------------------------------
1205
+ ; The same buttons, for something that cannot see them. A screen reader gets
1206
+ ; a toolbar with named buttons instead of a band of coloured rectangles, and
1207
+ ; the geometry is the one `layout` settled — read, not recomputed, so the
1208
+ ; rectangle a reader routes the mouse to is the rectangle that was painted.
1209
+ fn a11y:void (t:EVGA11yTree parentId:string idPrefix:string bx:int by:int bw:int bh:int) {
1210
+ def barId:string (idPrefix + "/toolbar")
1211
+ def bar:EVGA11yNode (t.node(barId parentId (EVGA11yRole.toolbar())))
1212
+ bar.name = "Toolbar"
1213
+ t.place(bar bx by bw bh)
1214
+ ; The pages, when there are any. A reader who cannot see the strip
1215
+ ; needs to know that most of it is somewhere else — a tab list says
1216
+ ; that in one word, where a flat list of the twelve buttons that
1217
+ ; happen to be showing says the program has twelve commands.
1218
+ if (this.hasTabs()) {
1219
+ def listId:string (barId + "/tabs")
1220
+ def list:EVGA11yNode (t.node(listId barId (EVGA11yRole.tabList())))
1221
+ list.name = "Toolbar pages"
1222
+ t.place(list bx by bw tabH)
1223
+ def ti:int 0
1224
+ while (ti < (array_length tabs)) {
1225
+ def tb:ToolTab (itemAt tabs ti)
1226
+ if (tb.w > 0) {
1227
+ def tn:EVGA11yNode (t.node(((listId + "/") + tb.name) listId (EVGA11yRole.tab())))
1228
+ tn.name = tb.name
1229
+ t.place(tn tb.x tb.y tb.w tb.h)
1230
+ tn.selected = (ti == activeTab)
1231
+ }
1232
+ ti = (ti + 1)
1233
+ }
1234
+ }
1235
+ def i:int 0
1236
+ while (i < (array_length items)) {
1237
+ def it:ToolItem (itemAt items i)
1238
+ ; A separator is a rule, and an item that did not fit was given no
1239
+ ; rectangle. Neither is something to announce.
1240
+ def skip:boolean it.isSeparator
1241
+ if (it.w <= 0) {
1242
+ skip = true
1243
+ }
1244
+ if (it.h <= 0) {
1245
+ skip = true
1246
+ }
1247
+ if (skip == false) {
1248
+ def label:string it.label
1249
+ if ((strlen label) == 0) {
1250
+ label = it.command
1251
+ }
1252
+ def nid:string (barId + "/" + it.command)
1253
+ if ((strlen it.arg) > 0) {
1254
+ nid = (nid + ":" + it.arg)
1255
+ }
1256
+ def n:EVGA11yNode (t.button(nid barId label))
1257
+ t.place(n it.x it.y it.w it.h)
1258
+ ; A toggle is a pressed button, not a checkbox: the host writes
1259
+ ; aria-pressed for a button role and aria-checked for the rest.
1260
+ if it.toggle {
1261
+ if it.pressed {
1262
+ n.checked = (EVGA11yTri.yes())
1263
+ } {
1264
+ n.checked = (EVGA11yTri.no())
1265
+ }
1266
+ }
1267
+ }
1268
+ i = (i + 1)
1269
+ }
1270
+ }
1271
+
1272
+ ; The first icon this strip uses for two different commands, or 0.
1273
+ ;
1274
+ ; Two buttons with one picture is not a style problem, it is a strip you
1275
+ ; cannot read: the deck's had five such pairs — edit-mode, copy and
1276
+ ; format-copy all drew the paint roller; lock and group both drew the
1277
+ ; freeze cross; delete-shape and delete-slide both drew the delete-row
1278
+ ; bar. Nobody noticed because nothing was looking. Now the app tests are.
1279
+ ;
1280
+ ; Text buttons are exempt: a font picker and a size picker both show a
1281
+ ; value and neither has an icon at all.
1282
+ fn duplicateIcon:int () {
1283
+ def i:int 0
1284
+ while (i < (array_length items)) {
1285
+ def a:ToolItem (itemAt items i)
1286
+ if (a.isSeparator == false) {
1287
+ if (a.icon > 0) {
1288
+ ; Only ICON-ONLY buttons can be confused for one another:
1289
+ ; a button that draws its name beside the picture is read
1290
+ ; by the word. `textW` says the caption has been measured,
1291
+ ; which only happens once a view with a measurer has laid
1292
+ ; the strip out — headless it stays 0 for everything, so a
1293
+ ; dropdown and its own entries looked like two unlabelled
1294
+ ; buttons sharing a picture and this answered yes for a
1295
+ ; strip that reads perfectly well. `showLabel` is the same
1296
+ ; fact stated when the button is BUILT, so the check now
1297
+ ; gives one answer on screen and off it.
1298
+ if ((a.textW == 0) && (a.showLabel == false)) {
1299
+ def j:int (i + 1)
1300
+ while (j < (array_length items)) {
1301
+ def b:ToolItem (itemAt items j)
1302
+ if (b.isSeparator == false) {
1303
+ if ((b.textW == 0) && (b.showLabel == false)) {
1304
+ if (b.icon == a.icon) {
1305
+ if (b.command != a.command) {
1306
+ return a.icon
1307
+ }
1308
+ }
1309
+ }
1310
+ }
1311
+ j = (j + 1)
1312
+ }
1313
+ }
1314
+ }
1315
+ }
1316
+ i = (i + 1)
1317
+ }
1318
+ return 0
1319
+ }
1320
+
1321
+ fn heightFor:int (width:int) {
1322
+ this.layout(0 0 width)
1323
+ return height
1324
+ }
1325
+
1326
+ fn count:int () {
1327
+ return (array_length items)
1328
+ }
1329
+
1330
+ fn at:ToolItem (i:int) {
1331
+ if (i < 0) {
1332
+ return (new ToolItem)
1333
+ }
1334
+ if (i >= (array_length items)) {
1335
+ return (new ToolItem)
1336
+ }
1337
+ return (itemAt items i)
1338
+ }
1339
+
1340
+ fn hit:int (px:int py:int) {
1341
+ def i:int 0
1342
+ while (i < (array_length items)) {
1343
+ def t:ToolItem (itemAt items i)
1344
+ if (t.isSeparator == false) {
1345
+ if (t.w > 0) {
1346
+ if (px >= t.x) {
1347
+ if (px < (t.x + t.w)) {
1348
+ if (py >= t.y) {
1349
+ if (py < (t.y + t.h)) {
1350
+ return i
1351
+ }
1352
+ }
1353
+ }
1354
+ }
1355
+ }
1356
+ }
1357
+ i = (i + 1)
1358
+ }
1359
+ return -1
1360
+ }
1361
+
1362
+ fn hotLabel:string () {
1363
+ if (hot < 0) {
1364
+ return ""
1365
+ }
1366
+ def t:ToolItem (this.at(hot))
1367
+ return t.label
1368
+ }
1369
+ }