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.
- package/CHANGELOG.md +1064 -20
- package/LICENSE +3 -1
- package/README.md +123 -28
- package/dist/Lang.rgr +1188 -290
- package/dist/api.d.ts +2343 -879
- package/dist/api.js +79923 -56921
- package/dist/lib/JSON.rgr +102 -91
- package/dist/lib/Shell.rgr +4 -4
- package/dist/lib/apple/AppleToolchain.rgr +4 -4
- package/dist/lib/apple/README.md +5 -4
- package/dist/lib/apple/apple_test.rgr +1 -1
- package/dist/lib/core/README.md +1 -1
- package/dist/lib/evg/EVG.rgr +12 -0
- package/dist/lib/evg/EVGA11yFromTree.rgr +302 -0
- package/dist/lib/evg/EVGA11yTree.rgr +894 -0
- package/dist/lib/evg/EVGBox.rgr +267 -0
- package/dist/lib/evg/EVGBoxShorthandTest.rgr +220 -0
- package/dist/lib/evg/EVGCodepoint.rgr +316 -0
- package/dist/lib/evg/EVGColor.rgr +700 -0
- package/dist/lib/evg/EVGCommands.rgr +177 -0
- package/dist/lib/evg/EVGComponent.rgr +331 -0
- package/dist/lib/evg/EVGComponentTest.rgr +387 -0
- package/dist/lib/evg/EVGConnector.rgr +541 -0
- package/dist/lib/evg/EVGConnectorTest.rgr +306 -0
- package/dist/lib/evg/EVGDisplayList.rgr +4186 -0
- package/dist/lib/evg/EVGEasing.rgr +370 -0
- package/dist/lib/evg/EVGEffectTest.rgr +253 -0
- package/dist/lib/evg/EVGElement.rgr +4914 -0
- package/dist/lib/evg/EVGFixedTest.rgr +289 -0
- package/dist/lib/evg/EVGFlexRulesTest.rgr +317 -0
- package/dist/lib/evg/EVGFlexWrapTest.rgr +246 -0
- package/dist/lib/evg/EVGFling.rgr +244 -0
- package/dist/lib/evg/EVGFocus.rgr +400 -0
- package/dist/lib/evg/EVGFocusTest.rgr +399 -0
- package/dist/lib/evg/EVGGradient.rgr +322 -0
- package/dist/lib/evg/EVGGrapheme.rgr +190 -0
- package/dist/lib/evg/EVGGrid.rgr +975 -0
- package/dist/lib/evg/EVGHitTest.rgr +208 -0
- package/dist/lib/evg/EVGHoles.rgr +294 -0
- package/dist/lib/evg/EVGHostMeasurerTest.rgr +281 -0
- package/dist/lib/evg/EVGHostTextMeasurer.rgr +331 -0
- package/dist/lib/evg/EVGHostTree.rgr +834 -0
- package/dist/lib/evg/EVGHostTreeTest.rgr +447 -0
- package/dist/lib/evg/EVGImageDecode.rgr +117 -0
- package/dist/lib/evg/EVGImageMeasurer.rgr +86 -0
- package/dist/lib/evg/EVGInspect.rgr +883 -0
- package/dist/lib/evg/EVGInvalidateTest.rgr +432 -0
- package/dist/lib/evg/EVGJsonTest.rgr +535 -0
- package/dist/lib/evg/EVGLayout.rgr +4220 -0
- package/dist/lib/evg/EVGMeasure.rgr +1267 -0
- package/dist/lib/evg/EVGOverlayTest.rgr +626 -0
- package/dist/lib/evg/EVGPatch.rgr +1721 -0
- package/dist/lib/evg/EVGPatchTest.rgr +692 -0
- package/dist/lib/evg/EVGPopoverTest.rgr +395 -0
- package/dist/lib/evg/EVGReconcile.rgr +226 -0
- package/dist/lib/evg/EVGReconcileTest.rgr +421 -0
- package/dist/lib/evg/EVGReject.rgr +126 -0
- package/dist/lib/evg/EVGRelayoutTest.rgr +375 -0
- package/dist/lib/evg/EVGRtlLayoutTest.rgr +319 -0
- package/dist/lib/evg/EVGRuler.rgr +232 -0
- package/dist/lib/evg/EVGRulerTest.rgr +172 -0
- package/dist/lib/evg/EVGSelectChrome.rgr +178 -0
- package/dist/lib/evg/EVGStyleCacheTest.rgr +419 -0
- package/dist/lib/evg/EVGStyleSheet.rgr +2148 -0
- package/dist/lib/evg/EVGStyleStateTest.rgr +407 -0
- package/dist/lib/evg/EVGStyleVarTest.rgr +471 -0
- package/dist/lib/evg/EVGText.rgr +105 -0
- package/dist/lib/evg/EVGTextEngine.rgr +536 -0
- package/dist/lib/evg/EVGTextMeasurer.rgr +720 -0
- package/dist/lib/evg/EVGTimingTest.rgr +1417 -0
- package/dist/lib/evg/EVGToolbar.rgr +1369 -0
- package/dist/lib/evg/EVGTransition.rgr +728 -0
- package/dist/lib/evg/EVGTreeJson.rgr +721 -0
- package/dist/lib/evg/EVGUnit.rgr +554 -0
- package/dist/lib/evg/EVGViewportUnitTest.rgr +236 -0
- package/dist/lib/evg/EvgApp.rgr +189 -0
- package/dist/lib/evg/EvgBitmapTracer.rgr +4277 -0
- package/dist/lib/evg/EvgBitmapTracerTest.rgr +2119 -0
- package/dist/lib/evg/EvgHost.rgr +451 -0
- package/dist/lib/evg/EvgTest.rgr +91 -0
- package/dist/lib/evg/EvgTraceColor.rgr +236 -0
- package/dist/lib/evg/EvgTraceCurve.rgr +648 -0
- package/dist/lib/evg/EvgTraceFit.rgr +767 -0
- package/dist/lib/evg/EvgTracePath.rgr +435 -0
- package/dist/lib/evg/EvgTraceTypes.rgr +559 -0
- package/dist/lib/evg/EvgViewport.rgr +313 -0
- package/dist/lib/evg/FxDemoDoc.rgr +185 -0
- package/dist/lib/evg/HOSTS.md +227 -0
- package/dist/lib/evg/ISSUES.md +835 -0
- package/dist/lib/evg/PLAN_ACCESSIBILITY.md +650 -0
- package/dist/lib/evg/PLAN_CSS_LAYOUT_AND_FONTS.md +1124 -0
- package/dist/lib/evg/PLAN_EFFECTS.md +431 -0
- package/dist/lib/evg/PLAN_EVG.md +518 -0
- package/dist/lib/evg/PLAN_EVG_RENDERER.md +1674 -0
- package/dist/lib/evg/PLAN_INSPECTOR.md +788 -0
- package/dist/lib/evg/PLAN_LINKS_AND_FORMS.md +157 -0
- package/dist/lib/evg/PLAN_NATIVE_HOSTS.md +671 -0
- package/dist/lib/evg/PLAN_VECTOR_IR.md +664 -0
- package/dist/lib/evg/PLAN_VIEW_TRANSFORM.md +322 -0
- package/dist/lib/evg/PathBuilder.rgr +333 -0
- package/dist/lib/evg/README.md +1236 -0
- package/dist/lib/evg/SPEC.md +1252 -0
- package/dist/lib/evg/SVGPathParser.rgr +1199 -0
- package/dist/lib/evg/SvgParser.rgr +1736 -0
- package/dist/lib/evg/TODO_EVG_RENDERER.md +595 -0
- package/dist/lib/evg/VectorShapes.rgr +331 -0
- package/dist/lib/evg/VectorStroke.rgr +107 -0
- package/dist/lib/evg/VectorViewBox.rgr +380 -0
- package/dist/lib/evg/agent/README.md +290 -0
- package/dist/lib/evg/agent/evg_agent.rgr +899 -0
- package/dist/lib/evg/agent/fixtures/broken.evg.json +7 -0
- package/dist/lib/evg/agent/fixtures/card.evg.json +9 -0
- package/dist/lib/evg/agent/fixtures/connector.css +63 -0
- package/dist/lib/evg/agent/fixtures/connector.evg.json +14 -0
- package/dist/lib/evg/agent/fixtures/drawn.evg.json +129 -0
- package/dist/lib/evg/agent/fixtures/gradient.evg.json +4 -0
- package/dist/lib/evg/agent/fixtures/popover.css +99 -0
- package/dist/lib/evg/agent/fixtures/popover.evg.json +30 -0
- package/dist/lib/evg/agent/roundtrip.sh +74 -0
- package/dist/lib/evg/agent/smoke.sh +230 -0
- package/dist/lib/evg/android/README.md +97 -0
- package/dist/lib/evg/android/androidstubs/AndroidStubs.kt +175 -0
- package/dist/lib/evg/android/androidstubs/Annotation.kt +10 -0
- package/dist/lib/evg/android/androidstubs/App.kt +30 -0
- package/dist/lib/evg/android/androidstubs/Content.kt +41 -0
- package/dist/lib/evg/android/androidstubs/ContentRes.kt +12 -0
- package/dist/lib/evg/android/androidstubs/InputMethod.kt +46 -0
- package/dist/lib/evg/android/androidstubs/Net.kt +6 -0
- package/dist/lib/evg/android/androidstubs/Os.kt +30 -0
- package/dist/lib/evg/android/androidstubs/Util.kt +15 -0
- package/dist/lib/evg/android/androidstubs/View.kt +113 -0
- package/dist/lib/evg/android/androidstubs/Widget.kt +16 -0
- package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/AndroidEvgSurface.kt +323 -0
- package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/AndroidTextMeasurer.kt +56 -0
- package/dist/lib/evg/android/src/android/kotlin/fi/ranger/evg/RippleEffect.kt +262 -0
- package/dist/lib/evg/android/src/awt/kotlin/fi/ranger/evg/AwtEvgSurface.kt +238 -0
- package/dist/lib/evg/android/src/awt/kotlin/fi/ranger/evg/AwtTextMeasurer.kt +85 -0
- package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgEngineThread.kt +115 -0
- package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgPainter.kt +231 -0
- package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/EvgSurface.kt +117 -0
- package/dist/lib/evg/android/src/main/kotlin/fi/ranger/evg/RecordingSurface.kt +94 -0
- package/dist/lib/evg/apple/README.md +128 -0
- package/dist/lib/evg/apple/Sources/CoreGraphicsEvgSurface.swift +329 -0
- package/dist/lib/evg/apple/Sources/CoreTextMeasurer.swift +70 -0
- package/dist/lib/evg/apple/Sources/EvgEngineQueue.swift +105 -0
- package/dist/lib/evg/apple/Sources/EvgPainter.swift +225 -0
- package/dist/lib/evg/apple/Sources/EvgSurface.swift +163 -0
- package/dist/lib/evg/apple/Sources/RecordingSurface.swift +110 -0
- package/dist/lib/evg/bench/EvgLayoutBench.rgr +34 -0
- package/dist/lib/evg/bench/README.md +64 -0
- package/dist/lib/evg/bench/layout-bench.mjs +316 -0
- package/dist/lib/evg/bench/layout-cases.mjs +510 -0
- package/dist/lib/evg/bench/layout-conformance.mjs +254 -0
- package/dist/lib/evg/bin/.gitignore +17 -0
- package/dist/lib/evg/evg_test.rgr +313 -0
- package/dist/lib/evg/gl/README.md +198 -0
- package/dist/lib/evg/gl/a11y-paint-check.mjs +73 -0
- package/dist/lib/evg/gl/blur-check.mjs +399 -0
- package/dist/lib/evg/gl/boxmodel.json +1 -0
- package/dist/lib/evg/gl/demo.html +46 -0
- package/dist/lib/evg/gl/effect-presets.css +285 -0
- package/dist/lib/evg/gl/effect-presets.js +63 -0
- package/dist/lib/evg/gl/effect-shots.mjs +135 -0
- package/dist/lib/evg/gl/evg-a11y.js +566 -0
- package/dist/lib/evg/gl/evg-binary.js +160 -0
- package/dist/lib/evg/gl/evg-engine.js +296 -0
- package/dist/lib/evg/gl/evg-fx.js +237 -0
- package/dist/lib/evg/gl/evg-gestures.js +209 -0
- package/dist/lib/evg/gl/evg-list.js +167 -0
- package/dist/lib/evg/gl/evg-measure.js +186 -0
- package/dist/lib/evg/gl/evg-textinput.js +303 -0
- package/dist/lib/evg/gl/evg-view.js +144 -0
- package/dist/lib/evg/gl/evg-webgl.js +3942 -0
- package/dist/lib/evg/gl/fx-check.mjs +998 -0
- package/dist/lib/evg/gl/fx-demo.html +166 -0
- package/dist/lib/evg/gl/fx-demo.js +2 -0
- package/dist/lib/evg/gl/fx-serve.mjs +47 -0
- package/dist/lib/evg/gl/gestures-check.mjs +189 -0
- package/dist/lib/evg/gl/list-binary-check.mjs +152 -0
- package/dist/lib/evg/gl/measure-check.mjs +135 -0
- package/dist/lib/evg/gl/rotation-check.mjs +207 -0
- package/dist/lib/evg/gl/shift-check.mjs +113 -0
- package/dist/lib/evg/gl/stroke-check.mjs +168 -0
- package/dist/lib/evg/gl/text-snap-check.mjs +139 -0
- package/dist/lib/evg/gl/view-check.mjs +343 -0
- package/dist/lib/evg/gl/view-policy-check.mjs +184 -0
- package/dist/lib/evg/html/evg-dom.js +318 -0
- package/dist/lib/evg/html/evg-html.js +600 -0
- package/dist/lib/evg/inspect/README.md +415 -0
- package/dist/lib/evg/inspect/browser-smoke.mjs +115 -0
- package/dist/lib/evg/inspect/evg-inspect.js +947 -0
- package/dist/lib/evg/inspect/shots/css.png +0 -0
- package/dist/lib/evg/inspect/shots/dashboard.png +0 -0
- package/dist/lib/evg/inspect/shots/pptx-slide.png +0 -0
- package/dist/lib/evg/inspect/shots/state.png +0 -0
- package/dist/lib/evg/inspect/shots.mjs +226 -0
- package/dist/lib/evg/oracle/css-blur.json +576 -0
- package/dist/lib/evg/oracle/css-box.json +157 -0
- package/dist/lib/evg/oracle/css-timing.json +709 -0
- package/dist/lib/evg/oracle/css_blur_oracle.mjs +529 -0
- package/dist/lib/evg/oracle/css_box_oracle.mjs +106 -0
- package/dist/lib/evg/oracle/css_timing_oracle.mjs +389 -0
- package/dist/lib/evg/original/EVGColor.clj +310 -0
- package/dist/lib/evg/original/EVGColorContext.rgr +178 -0
- package/dist/lib/evg/original/SVGPath.rgr +627 -0
- package/dist/lib/evg/original/Vec2.crgr +103 -0
- package/dist/lib/evg/ranger.json +9 -0
- package/dist/lib/evg/showcase/README.md +246 -0
- package/dist/lib/evg/showcase/assets/emblem.svg +30 -0
- package/dist/lib/evg/showcase/assets/rosette.svg +22 -0
- package/dist/lib/evg/showcase/build.mjs +651 -0
- package/dist/lib/evg/showcase/pages/album.tsx +30 -0
- package/dist/lib/evg/showcase/pages/boxmodel.tsx +37 -0
- package/dist/lib/evg/showcase/pages/cards.tsx +41 -0
- package/dist/lib/evg/showcase/pages/chart_api.tsx +170 -0
- package/dist/lib/evg/showcase/pages/charts.tsx +219 -0
- package/dist/lib/evg/showcase/pages/drawing.tsx +160 -0
- package/dist/lib/evg/showcase/pages/emoji.tsx +91 -0
- package/dist/lib/evg/showcase/pages/flex.tsx +46 -0
- package/dist/lib/evg/showcase/pages/more.tsx +332 -0
- package/dist/lib/evg/showcase/pages/plots.tsx +262 -0
- package/dist/lib/evg/showcase/pages/svg.tsx +63 -0
- package/dist/lib/evg/showcase/pages/tables.tsx +209 -0
- package/dist/lib/evg/showcase/pages/typography.tsx +43 -0
- package/dist/lib/evg/showcase/pages/units.tsx +35 -0
- package/dist/lib/evg/showcase/pages/variants.tsx +233 -0
- package/dist/lib/evg/showcase/pages/vector.tsx +75 -0
- package/dist/lib/evg/showcase/pages/views.tsx +223 -0
- package/dist/lib/evg/showcase/tests/chart_api_smoke.mjs +339 -0
- package/dist/lib/evg/showcase/tests/gl_smoke.mjs +125 -0
- package/dist/lib/evg/showcase/themes/chart_api-default.css +15 -0
- package/dist/lib/evg/showcase/themes/charts-default.css +29 -0
- package/dist/lib/evg/showcase/themes/drawing-default.css +35 -0
- package/dist/lib/evg/showcase/themes/more-default.css +128 -0
- package/dist/lib/evg/showcase/themes/plots-default.css +18 -0
- package/dist/lib/evg/showcase/themes/showcase.css +508 -0
- package/dist/lib/evg/showcase/themes/tables-default.css +123 -0
- package/dist/lib/evg/showcase/themes/variants-default.css +15 -0
- package/dist/lib/evg/showcase/themes/views-default.css +15 -0
- package/dist/lib/evg/tools/bench_vs_potrace.mjs +290 -0
- package/dist/lib/evg/tools/evg_image_tool.rgr +488 -0
- package/dist/lib/evg/tools/evg_trace_bench.rgr +114 -0
- package/dist/lib/evg/tools/evg_trace_cli.rgr +662 -0
- package/dist/lib/evg/tools/evg_trace_cpp_bench.rgr +83 -0
- package/dist/lib/evg/tools/run_trace_bench.sh +74 -0
- package/dist/lib/evg/tools/run_trace_cli_smoke.sh +128 -0
- package/dist/lib/evg/web/responsive/EvgResponsiveCheck.rgr +271 -0
- package/dist/lib/evg/web/responsive/EvgResponsiveDemo.rgr +531 -0
- package/dist/lib/evg/web/responsive/README.md +97 -0
- package/dist/lib/evg/web/responsive/build.mjs +90 -0
- package/dist/lib/evg/web/responsive/dom-check.mjs +169 -0
- package/dist/lib/evg/web/responsive/index.html +177 -0
- package/dist/lib/evg/web/responsive/smoke.mjs +221 -0
- package/dist/lib/evg/web/tools/assets-client.mjs +95 -0
- package/dist/lib/evg/web/tools/boot-bench.mjs +189 -0
- package/dist/lib/evg/web/tools/inline-assets.mjs +119 -0
- package/dist/lib/evg/web/tools/minify.mjs +60 -0
- package/dist/lib/evg/web/tracer/build.mjs +87 -0
- package/dist/lib/evg/web/tracer/index.html +2805 -0
- package/dist/lib/evg/web/tracer/sample.png +0 -0
- package/dist/lib/evg/web/tracer/smoke.mjs +1162 -0
- package/dist/lib/evgr/Cargo.lock +21 -0
- package/dist/lib/evgr/Cargo.toml +19 -0
- package/dist/lib/evgr/README.md +140 -0
- package/dist/lib/evgr/bench/NativeBench.rgr +246 -0
- package/dist/lib/evgr/bench/compare.mjs +252 -0
- package/dist/lib/evgr/bench/speed.mjs +277 -0
- package/dist/lib/evgr/bin/.gitignore +3 -0
- package/dist/lib/evgr/src/bin/bench.rs +106 -0
- package/dist/lib/evgr/src/bin/smoke.rs +13 -0
- package/dist/lib/evgr/src/grid.rs +730 -0
- package/dist/lib/evgr/src/lib.rs +1003 -0
- package/dist/lib/evgr/src/style.rs +468 -0
- package/dist/lib/evgr/src/text.rs +83 -0
- package/dist/lib/image/BitReader.rgr +171 -0
- package/dist/lib/image/Buffer.rgr +173 -0
- package/dist/lib/image/DCT.rgr +283 -0
- package/dist/lib/image/Deflate.rgr +339 -0
- package/dist/lib/image/HuffmanDecoder.rgr +181 -0
- package/dist/lib/image/ImageBuffer.rgr +706 -0
- package/dist/lib/image/JPEGDecoder.rgr +808 -0
- package/dist/lib/image/PNGDecoder.rgr +544 -0
- package/dist/lib/image/PNGEncoder.rgr +388 -0
- package/dist/lib/image/PPMImage.rgr +224 -0
- package/dist/lib/image/ProgressiveJPEGDecoder.rgr +1154 -0
- package/dist/lib/image/README.md +33 -0
- package/dist/lib/image/RasterBuffer.rgr +294 -0
- package/dist/lib/image/VP8BoolDecoder.rgr +163 -0
- package/dist/lib/image/WebPDecoder.rgr +433 -0
- package/dist/lib/image/WebPLossless.rgr +1035 -0
- package/dist/lib/image/WebPLossy.rgr +1929 -0
- package/dist/lib/image/ranger.json +9 -0
- package/dist/lib/image/testdata/webp/anim_2f_24x16.webp +0 -0
- package/dist/lib/image/testdata/webp/fixtures.txt +33 -0
- package/dist/lib/image/testdata/webp/gen_fixtures.py +261 -0
- package/dist/lib/image/testdata/webp/ll_1x1.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_alpha_29x21.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_gradient_64x64.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_meta_32x32.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_noise_17x9.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_pal11_23x11.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_pal2_19x7.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_pal40_16x16.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_pal4_13x5.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_photo_40x30.webp +0 -0
- package/dist/lib/image/testdata/webp/ll_predictors_64x32.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_1x1.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_raw_f0_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_raw_f1_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_raw_f2_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_raw_f3_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_vp8l_f0_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_vp8l_f1_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_vp8l_f2_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alph_vp8l_f3_21x13.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_alpha_33x17.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_exif_15x11.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_i16_96x80.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_nofilter_24x20.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_odd_17x9.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_photo_48x40.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_q100_20x18.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_sharp_40x36.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_simple_filter_33x31.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_vpx_parts8_37x45.webp +0 -0
- package/dist/lib/image/testdata/webp/ly_vpx_skip_96x80.webp +0 -0
- package/dist/lib/image/tests/WebPDecodeTool.rgr +81 -0
- package/dist/lib/image/tests/WebPDecoderTest.rgr +1162 -0
- package/dist/lib/image/tests/run_webp_tests.sh +39 -0
- package/dist/lib/rust/RsJson.rgr +468 -0
- package/dist/lib/rust/RsPrelude.rgr +1663 -0
- package/dist/lib/shell_test.rgr +7 -7
- package/dist/lib/stdlib.rgr +21 -5
- package/dist/lib/zip/ranger.json +6 -0
- package/dist/rgrc.js +87318 -67610
- package/package.json +1010 -1040
- package/dist/README.md +0 -117
- 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
|
+
}
|