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,395 @@
|
|
|
1
|
+
; SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
; ==============================================================================
|
|
4
|
+
; EVGPopoverTest — surfaces: named anchors, fallbacks, fit and presentation
|
|
5
|
+
; ==============================================================================
|
|
6
|
+
;
|
|
7
|
+
; EVG has had overlay surfaces for a while: out of flow, drawn in the top layer
|
|
8
|
+
; after the whole normal tree, placed against an anchor and flipped when they
|
|
9
|
+
; ran off the page. What it did not have is the four things that turn that into
|
|
10
|
+
; a menu system, and this is the test for those four.
|
|
11
|
+
;
|
|
12
|
+
; * `position-anchor: --file` — the anchor NAMED, rather than found among the
|
|
13
|
+
; surface's own siblings. A menu no longer has to be declared beside its
|
|
14
|
+
; trigger, which is what a portal is for in every other toolkit.
|
|
15
|
+
; * `position-try-fallbacks` — an ordered list of `position-area`s, tried
|
|
16
|
+
; until one is wholly on the page, with `position-try-order: most-space`
|
|
17
|
+
; to take the roomiest instead of the first.
|
|
18
|
+
; * `fit-viewport` — take the room there is rather than running off the page,
|
|
19
|
+
; and scroll for the rest.
|
|
20
|
+
; * `presentation: sheet | fullscreen` — on a phone an anchored menu is the
|
|
21
|
+
; wrong widget, not a badly placed one.
|
|
22
|
+
;
|
|
23
|
+
; The old behaviour is asserted by EVGOverlayTest and is deliberately not
|
|
24
|
+
; repeated here; what this file checks is that the new parts do what they say
|
|
25
|
+
; and that a surface that declares none of them is untouched.
|
|
26
|
+
; ==============================================================================
|
|
27
|
+
|
|
28
|
+
Import "EVGElement.rgr"
|
|
29
|
+
Import "EVGUnit.rgr"
|
|
30
|
+
Import "EVGLayout.rgr"
|
|
31
|
+
|
|
32
|
+
class PopCheck {
|
|
33
|
+
def passed:int 0
|
|
34
|
+
def failed:int 0
|
|
35
|
+
|
|
36
|
+
fn ok:void (name:string cond:boolean) {
|
|
37
|
+
if cond {
|
|
38
|
+
passed = (passed + 1)
|
|
39
|
+
print (" PASS " + name)
|
|
40
|
+
} {
|
|
41
|
+
failed = (failed + 1)
|
|
42
|
+
print (" FAIL " + name)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
fn eqInt:void (name:string got:int want:int) {
|
|
47
|
+
def good:boolean (got == want)
|
|
48
|
+
if (good == false) {
|
|
49
|
+
print (" got " + (to_string got) + " want " + (to_string want))
|
|
50
|
+
}
|
|
51
|
+
this.ok(name good)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
fn eqStr:void (name:string got:string want:string) {
|
|
55
|
+
def good:boolean (got == want)
|
|
56
|
+
if (good == false) {
|
|
57
|
+
print (" got \"" + got + "\" want \"" + want + "\"")
|
|
58
|
+
}
|
|
59
|
+
this.ok(name good)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
class EVGPopoverTest {
|
|
64
|
+
|
|
65
|
+
sfn box:EVGElement (id:string w:double h:double) {
|
|
66
|
+
def el (EVGElement.createDiv())
|
|
67
|
+
el.id = id
|
|
68
|
+
el.setAttribute("width" ((to_string (to_int w)) + "px"))
|
|
69
|
+
el.setAttribute("height" ((to_string (to_int h)) + "px"))
|
|
70
|
+
return el
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
sfn laidOut:EVGLayout (root:EVGElement w:double h:double) {
|
|
74
|
+
def lay (new EVGLayout())
|
|
75
|
+
lay.setPageSize(w h)
|
|
76
|
+
lay.layout(root)
|
|
77
|
+
return lay
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
; A page with a bar at `barY`, a trigger in it named `--file`, and a body
|
|
81
|
+
; below. The surface is added by the caller.
|
|
82
|
+
sfn buildPage:EVGElement (barY:double) {
|
|
83
|
+
def page:EVGElement (EVGPopoverTest.box("page" 400.0 800.0))
|
|
84
|
+
page.setAttribute("flex-direction" "column")
|
|
85
|
+
def spacer:EVGElement (EVGPopoverTest.box("spacer" 400.0 barY))
|
|
86
|
+
if (barY > 0.0) {
|
|
87
|
+
page.addChild(spacer)
|
|
88
|
+
}
|
|
89
|
+
def bar:EVGElement (EVGPopoverTest.box("bar" 400.0 40.0))
|
|
90
|
+
bar.setAttribute("flex-direction" "row")
|
|
91
|
+
def trigger:EVGElement (EVGPopoverTest.box("file" 80.0 40.0))
|
|
92
|
+
trigger.setAttribute("anchor-name" "--file")
|
|
93
|
+
bar.addChild(trigger)
|
|
94
|
+
page.addChild(bar)
|
|
95
|
+
; Whatever is left under the bar. The page must not overflow: a column
|
|
96
|
+
; that does shrinks its children, and then the trigger is not where the
|
|
97
|
+
; test says it is.
|
|
98
|
+
def rest:double ((800.0 - barY) - 40.0)
|
|
99
|
+
if (rest > 200.0) {
|
|
100
|
+
rest = 200.0
|
|
101
|
+
}
|
|
102
|
+
page.addChild((EVGPopoverTest.box("body" 400.0 rest)))
|
|
103
|
+
return page
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
sfn menu:EVGElement (w:double h:double) {
|
|
107
|
+
def m (EVGElement.createDiv())
|
|
108
|
+
m.tagName = "popover"
|
|
109
|
+
m.id = "menu"
|
|
110
|
+
m.setAttribute("width" ((to_string (to_int w)) + "px"))
|
|
111
|
+
m.setAttribute("height" ((to_string (to_int h)) + "px"))
|
|
112
|
+
m.setAttribute("position-anchor" "--file")
|
|
113
|
+
return m
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
; ---------------------------------------------------------------------
|
|
117
|
+
|
|
118
|
+
sfn testNamedAnchor:void (c:PopCheck) {
|
|
119
|
+
print ""
|
|
120
|
+
print "--- position-anchor names the box, wherever it is ---"
|
|
121
|
+
def page:EVGElement (EVGPopoverTest.buildPage(0.0))
|
|
122
|
+
def m:EVGElement (EVGPopoverTest.menu(160.0 100.0))
|
|
123
|
+
m.setAttribute("position-area" "bottom start")
|
|
124
|
+
page.addChild(m)
|
|
125
|
+
def lay:EVGLayout (EVGPopoverTest.laidOut(page 400.0 800.0))
|
|
126
|
+
; The trigger is two levels down in another subtree; the surface is a
|
|
127
|
+
; child of the page and has no anchor sibling at all.
|
|
128
|
+
c.eqInt("under the trigger, not under its own parent" (to_int m.calculatedY) 44)
|
|
129
|
+
c.eqInt("aligned to the trigger's left edge" (to_int m.calculatedX) 0)
|
|
130
|
+
c.eqStr("and it says where it went" m.overlayPlacedSide "bottom")
|
|
131
|
+
c.eqStr("…and how it was aligned" m.overlayPlacedAlign "start")
|
|
132
|
+
c.eqStr("…and what it is" m.overlayPlacedPresentation "anchored")
|
|
133
|
+
; Out of flow: the body sits directly under the bar, and the `popover`
|
|
134
|
+
; tag alone was enough to make that true — nothing said `overlay`.
|
|
135
|
+
def body:EVGElement (page.getChild(1))
|
|
136
|
+
c.eqStr("the third child is the body" body.id "body")
|
|
137
|
+
c.eqInt("a popover takes no space in the flow" (to_int body.calculatedY) 40)
|
|
138
|
+
c.ok("it is a surface" (m.isSurface()))
|
|
139
|
+
c.ok("…which position-anchor said on its own" m.isOverlay)
|
|
140
|
+
; And the tag says it too, with no properties at all: `popover` is the
|
|
141
|
+
; web's name for this and is worth having as one.
|
|
142
|
+
def bare (EVGElement.createDiv())
|
|
143
|
+
bare.tagName = "popover"
|
|
144
|
+
c.ok("the popover tag alone is a surface" (bare.isSurface()))
|
|
145
|
+
c.ok("…and it took no property to say so" (bare.isOverlay == false))
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
sfn testPositionArea:void (c:PopCheck) {
|
|
149
|
+
print ""
|
|
150
|
+
print "--- position-area, both spellings of the cross axis ---"
|
|
151
|
+
def page:EVGElement (EVGPopoverTest.buildPage(200.0))
|
|
152
|
+
def m:EVGElement (EVGPopoverTest.menu(160.0 100.0))
|
|
153
|
+
m.setAttribute("position-area" "bottom end")
|
|
154
|
+
page.addChild(m)
|
|
155
|
+
def lay:EVGLayout (EVGPopoverTest.laidOut(page 400.0 800.0))
|
|
156
|
+
; The trigger is 80 wide at x=0, so `end` puts the surface's right edge
|
|
157
|
+
; on the trigger's: 80 - 160. Clamped back onto the page at 0.
|
|
158
|
+
c.eqInt("end aligns the far edges" (to_int m.calculatedX) 0)
|
|
159
|
+
c.eqInt("below the bar at y=200" (to_int m.calculatedY) 244)
|
|
160
|
+
|
|
161
|
+
def page2:EVGElement (EVGPopoverTest.buildPage(200.0))
|
|
162
|
+
def m2:EVGElement (EVGPopoverTest.menu(160.0 100.0))
|
|
163
|
+
m2.setAttribute("position-area" "right top")
|
|
164
|
+
page2.addChild(m2)
|
|
165
|
+
def lay2:EVGLayout (EVGPopoverTest.laidOut(page2 400.0 800.0))
|
|
166
|
+
c.eqStr("a physical cross word is read as start" m2.overlayPlacedAlign "start")
|
|
167
|
+
c.eqInt("beside the trigger" (to_int m2.calculatedX) 84)
|
|
168
|
+
c.eqInt("…and level with its top" (to_int m2.calculatedY) 200)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
sfn testFallbacks:void (c:PopCheck) {
|
|
172
|
+
print ""
|
|
173
|
+
print "--- position-try-fallbacks, in order, first one that fits ---"
|
|
174
|
+
; The bar is at the bottom of the page: 740..780.
|
|
175
|
+
def page:EVGElement (EVGPopoverTest.buildPage(740.0))
|
|
176
|
+
def m:EVGElement (EVGPopoverTest.menu(160.0 100.0))
|
|
177
|
+
m.setAttribute("position-area" "bottom start")
|
|
178
|
+
m.setAttribute("position-try-fallbacks" "right start, top start")
|
|
179
|
+
page.addChild(m)
|
|
180
|
+
def lay:EVGLayout (EVGPopoverTest.laidOut(page 400.0 800.0))
|
|
181
|
+
; bottom: 784 + 100 runs 84 past the page.
|
|
182
|
+
; right: x=84 fits, but `start` puts its top at 740 and its bottom at
|
|
183
|
+
; 840 — off the page by 40, so it is not a fit either.
|
|
184
|
+
; top: 636..736. It fits, and it is the first that does.
|
|
185
|
+
c.eqStr("it landed on the first candidate that is wholly on the page" m.overlayPlacedSide "top")
|
|
186
|
+
c.eqInt("above the trigger, gap included" (to_int m.calculatedY) 636)
|
|
187
|
+
|
|
188
|
+
; A fallback that is not a position-area is reported, not obeyed.
|
|
189
|
+
def page2:EVGElement (EVGPopoverTest.buildPage(740.0))
|
|
190
|
+
def m2:EVGElement (EVGPopoverTest.menu(160.0 100.0))
|
|
191
|
+
m2.setAttribute("position-area" "bottom start")
|
|
192
|
+
m2.setAttribute("position-try-fallbacks" "sideways, top start")
|
|
193
|
+
page2.addChild(m2)
|
|
194
|
+
def lay2:EVGLayout (EVGPopoverTest.laidOut(page2 400.0 800.0))
|
|
195
|
+
c.eqStr("a nonsense fallback is skipped" m2.overlayPlacedSide "top")
|
|
196
|
+
def said:boolean false
|
|
197
|
+
def i:int 0
|
|
198
|
+
while (i < (lay2.warningCount())) {
|
|
199
|
+
def w:string (lay2.warningAt(i))
|
|
200
|
+
if ((indexOf w "sideways") >= 0) {
|
|
201
|
+
said = true
|
|
202
|
+
}
|
|
203
|
+
i = (i + 1)
|
|
204
|
+
}
|
|
205
|
+
c.ok("…and named in the warnings" said)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
sfn testMostSpace:void (c:PopCheck) {
|
|
209
|
+
print ""
|
|
210
|
+
print "--- position-try-order: most-space ---"
|
|
211
|
+
; Trigger at 300..340: 300 above, 460 below. Both fit a 100-tall menu.
|
|
212
|
+
def page:EVGElement (EVGPopoverTest.buildPage(300.0))
|
|
213
|
+
def m:EVGElement (EVGPopoverTest.menu(160.0 100.0))
|
|
214
|
+
m.setAttribute("position-area" "top start")
|
|
215
|
+
m.setAttribute("position-try-fallbacks" "bottom start")
|
|
216
|
+
page.addChild(m)
|
|
217
|
+
def lay:EVGLayout (EVGPopoverTest.laidOut(page 400.0 800.0))
|
|
218
|
+
c.eqStr("in order, the declared side wins because it fits" m.overlayPlacedSide "top")
|
|
219
|
+
|
|
220
|
+
def page2:EVGElement (EVGPopoverTest.buildPage(300.0))
|
|
221
|
+
def m2:EVGElement (EVGPopoverTest.menu(160.0 100.0))
|
|
222
|
+
m2.setAttribute("position-area" "top start")
|
|
223
|
+
m2.setAttribute("position-try-fallbacks" "bottom start")
|
|
224
|
+
m2.setAttribute("position-try-order" "most-space")
|
|
225
|
+
page2.addChild(m2)
|
|
226
|
+
def lay2:EVGLayout (EVGPopoverTest.laidOut(page2 400.0 800.0))
|
|
227
|
+
c.eqStr("most-space takes the roomier one instead" m2.overlayPlacedSide "bottom")
|
|
228
|
+
c.eqInt("below the trigger" (to_int m2.calculatedY) 344)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
sfn testFitViewport:void (c:PopCheck) {
|
|
232
|
+
print ""
|
|
233
|
+
print "--- fit-viewport: take the room there is, and scroll for the rest ---"
|
|
234
|
+
def page:EVGElement (EVGPopoverTest.buildPage(600.0))
|
|
235
|
+
def m:EVGElement (EVGPopoverTest.menu(160.0 700.0))
|
|
236
|
+
m.setAttribute("position-area" "bottom start")
|
|
237
|
+
m.setAttribute("fit-viewport" "true")
|
|
238
|
+
m.setAttribute("overflow" "hidden")
|
|
239
|
+
def r:int 0
|
|
240
|
+
while (r < 10) {
|
|
241
|
+
m.addChild((EVGPopoverTest.box(("row" + (to_string r)) 160.0 70.0)))
|
|
242
|
+
r = (r + 1)
|
|
243
|
+
}
|
|
244
|
+
page.addChild(m)
|
|
245
|
+
def lay:EVGLayout (EVGPopoverTest.laidOut(page 400.0 800.0))
|
|
246
|
+
; Neither side fits 700. Above the trigger hangs off by 104 and below
|
|
247
|
+
; by 544, so it opens upwards and is cut to the 596 there is.
|
|
248
|
+
c.eqStr("it opened on the side it loses least on" m.overlayPlacedSide "top")
|
|
249
|
+
c.ok("and it says it was cut" m.overlayClamped)
|
|
250
|
+
c.eqInt("to the room above the trigger" (to_int m.calculatedHeight) 596)
|
|
251
|
+
c.eqInt("which starts at the top of the page" (to_int m.calculatedY) 0)
|
|
252
|
+
c.eqInt("the rest is there to scroll to" (to_int m.scrollHeight) 700)
|
|
253
|
+
c.eqInt("…which is 104 of scrolling" (to_int (m.maxScrollTop())) 104)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
sfn sheetMenu:EVGElement () {
|
|
257
|
+
def m (EVGElement.createDiv())
|
|
258
|
+
m.tagName = "popover"
|
|
259
|
+
m.id = "menu"
|
|
260
|
+
m.setAttribute("width" "160px")
|
|
261
|
+
m.setAttribute("position-anchor" "--file")
|
|
262
|
+
m.setAttribute("position-area" "bottom start")
|
|
263
|
+
m.setAttribute("flex-direction" "column")
|
|
264
|
+
def r:int 0
|
|
265
|
+
while (r < 3) {
|
|
266
|
+
def row:EVGElement (EVGPopoverTest.box(("row" + (to_string r)) 160.0 60.0))
|
|
267
|
+
row.setAttribute("width" "100%")
|
|
268
|
+
m.addChild(row)
|
|
269
|
+
r = (r + 1)
|
|
270
|
+
}
|
|
271
|
+
return m
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
sfn testSheet:void (c:PopCheck) {
|
|
275
|
+
print ""
|
|
276
|
+
print "--- presentation: a menu is the wrong widget on a phone ---"
|
|
277
|
+
def page:EVGElement (EVGPopoverTest.buildPage(0.0))
|
|
278
|
+
def m:EVGElement (EVGPopoverTest.sheetMenu())
|
|
279
|
+
m.setAttribute("sheet-below" "600px")
|
|
280
|
+
page.addChild(m)
|
|
281
|
+
def lay:EVGLayout (EVGPopoverTest.laidOut(page 400.0 800.0))
|
|
282
|
+
c.eqStr("at 400 wide it is a sheet" m.overlayPlacedPresentation "sheet")
|
|
283
|
+
c.eqInt("as wide as the page" (to_int m.calculatedWidth) 400)
|
|
284
|
+
c.eqInt("against its left edge" (to_int m.calculatedX) 0)
|
|
285
|
+
c.eqInt("as tall as its three rows" (to_int m.calculatedHeight) 180)
|
|
286
|
+
c.eqInt("along the bottom edge" (to_int m.calculatedY) 620)
|
|
287
|
+
; The rows were laid out again at the sheet's width, not the menu's.
|
|
288
|
+
def row0:EVGElement (m.getChild(0))
|
|
289
|
+
c.eqInt("and the rows were laid out again at that width" (to_int row0.calculatedWidth) 400)
|
|
290
|
+
|
|
291
|
+
def wide:EVGElement (EVGPopoverTest.buildPage(0.0))
|
|
292
|
+
def m2:EVGElement (EVGPopoverTest.sheetMenu())
|
|
293
|
+
m2.setAttribute("sheet-below" "600px")
|
|
294
|
+
wide.addChild(m2)
|
|
295
|
+
def lay2:EVGLayout (EVGPopoverTest.laidOut(wide 900.0 800.0))
|
|
296
|
+
c.eqStr("at 900 it is an anchored menu again" m2.overlayPlacedPresentation "anchored")
|
|
297
|
+
c.eqInt("under the trigger" (to_int m2.calculatedY) 44)
|
|
298
|
+
c.eqInt("and back to its own width" (to_int m2.calculatedWidth) 160)
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
sfn testFullscreen:void (c:PopCheck) {
|
|
302
|
+
print ""
|
|
303
|
+
print "--- presentation: fullscreen ---"
|
|
304
|
+
def page:EVGElement (EVGPopoverTest.buildPage(0.0))
|
|
305
|
+
def m:EVGElement (EVGPopoverTest.sheetMenu())
|
|
306
|
+
m.setAttribute("presentation" "fullscreen")
|
|
307
|
+
page.addChild(m)
|
|
308
|
+
def lay:EVGLayout (EVGPopoverTest.laidOut(page 400.0 800.0))
|
|
309
|
+
c.eqStr("it covers the page" m.overlayPlacedPresentation "fullscreen")
|
|
310
|
+
c.eqInt("from the top left" (to_int m.calculatedX) 0)
|
|
311
|
+
c.eqInt("…" (to_int m.calculatedY) 0)
|
|
312
|
+
c.eqInt("as wide as the page" (to_int m.calculatedWidth) 400)
|
|
313
|
+
c.eqInt("and as tall" (to_int m.calculatedHeight) 800)
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
sfn testAnchorInsets:void (c:PopCheck) {
|
|
317
|
+
print ""
|
|
318
|
+
print "--- anchor() places an edge, not a side ---"
|
|
319
|
+
def page:EVGElement (EVGPopoverTest.buildPage(200.0))
|
|
320
|
+
; A badge hanging off the trigger's top-right corner. The trigger is
|
|
321
|
+
; 80x40 at (0,200), so its right edge is x=80 and its top is y=200.
|
|
322
|
+
def badge (EVGElement.createDiv())
|
|
323
|
+
badge.id = "badge"
|
|
324
|
+
badge.setAttribute("width" "20px")
|
|
325
|
+
badge.setAttribute("height" "20px")
|
|
326
|
+
badge.setAttribute("position-anchor" "--file")
|
|
327
|
+
badge.setAttribute("left" "calc(anchor(right) - 8px)")
|
|
328
|
+
badge.setAttribute("top" "calc(anchor(top) - 6px)")
|
|
329
|
+
page.addChild(badge)
|
|
330
|
+
def lay:EVGLayout (EVGPopoverTest.laidOut(page 400.0 800.0))
|
|
331
|
+
c.eqStr("placed by its edges" badge.overlayPlacedSide "anchor")
|
|
332
|
+
c.eqInt("left edge on the anchor's right, less the offset" (to_int badge.calculatedX) 72)
|
|
333
|
+
c.eqInt("top edge above the anchor's top" (to_int badge.calculatedY) 194)
|
|
334
|
+
|
|
335
|
+
; The other two insets: `right`/`bottom` measure the box's far edges.
|
|
336
|
+
def page2:EVGElement (EVGPopoverTest.buildPage(200.0))
|
|
337
|
+
def tag2 (EVGElement.createDiv())
|
|
338
|
+
tag2.id = "tag"
|
|
339
|
+
tag2.setAttribute("width" "30px")
|
|
340
|
+
tag2.setAttribute("height" "10px")
|
|
341
|
+
tag2.setAttribute("position-anchor" "--file")
|
|
342
|
+
tag2.setAttribute("right" "anchor(left)")
|
|
343
|
+
tag2.setAttribute("bottom" "anchor(center)")
|
|
344
|
+
page2.addChild(tag2)
|
|
345
|
+
def lay2:EVGLayout (EVGPopoverTest.laidOut(page2 400.0 800.0))
|
|
346
|
+
c.eqInt("its right edge on the anchor's left" (to_int (tag2.calculatedX + tag2.calculatedWidth)) 0)
|
|
347
|
+
c.eqInt("its bottom on the anchor's middle" (to_int (tag2.calculatedY + tag2.calculatedHeight)) 220)
|
|
348
|
+
|
|
349
|
+
; Both insets on an axis stretch the box between them.
|
|
350
|
+
def page3:EVGElement (EVGPopoverTest.buildPage(200.0))
|
|
351
|
+
def rule (EVGElement.createDiv())
|
|
352
|
+
rule.id = "rule"
|
|
353
|
+
rule.setAttribute("height" "2px")
|
|
354
|
+
rule.setAttribute("position-anchor" "--file")
|
|
355
|
+
rule.setAttribute("left" "anchor(left)")
|
|
356
|
+
rule.setAttribute("right" "calc(anchor(right) + 40px)")
|
|
357
|
+
rule.setAttribute("top" "calc(anchor(bottom) + 4px)")
|
|
358
|
+
page3.addChild(rule)
|
|
359
|
+
def lay3:EVGLayout (EVGPopoverTest.laidOut(page3 400.0 800.0))
|
|
360
|
+
c.eqInt("stretched from one inset to the other" (to_int rule.calculatedWidth) 120)
|
|
361
|
+
c.eqInt("starting at the anchor's left" (to_int rule.calculatedX) 0)
|
|
362
|
+
c.eqInt("just under it" (to_int rule.calculatedY) 244)
|
|
363
|
+
|
|
364
|
+
; An inset on the wrong axis is reported, not guessed at.
|
|
365
|
+
def page4:EVGElement (EVGPopoverTest.buildPage(200.0))
|
|
366
|
+
def bad (EVGElement.createDiv())
|
|
367
|
+
bad.id = "bad"
|
|
368
|
+
bad.setAttribute("width" "10px")
|
|
369
|
+
bad.setAttribute("height" "10px")
|
|
370
|
+
bad.setAttribute("position-anchor" "--file")
|
|
371
|
+
bad.setAttribute("left" "anchor(bottom)")
|
|
372
|
+
page4.addChild(bad)
|
|
373
|
+
c.ok("a cross-axis edge is not taken" ((strlen bad.anchorLeftSide) == 0))
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
sfn main:void () {
|
|
377
|
+
def c (new PopCheck())
|
|
378
|
+
print "=== EVG popovers ==="
|
|
379
|
+
EVGPopoverTest.testNamedAnchor(c)
|
|
380
|
+
EVGPopoverTest.testPositionArea(c)
|
|
381
|
+
EVGPopoverTest.testFallbacks(c)
|
|
382
|
+
EVGPopoverTest.testMostSpace(c)
|
|
383
|
+
EVGPopoverTest.testFitViewport(c)
|
|
384
|
+
EVGPopoverTest.testSheet(c)
|
|
385
|
+
EVGPopoverTest.testFullscreen(c)
|
|
386
|
+
EVGPopoverTest.testAnchorInsets(c)
|
|
387
|
+
print ""
|
|
388
|
+
print ("passed=" + (to_string c.passed) + " failed=" + (to_string c.failed))
|
|
389
|
+
if (c.failed > 0) {
|
|
390
|
+
print "FAILURES"
|
|
391
|
+
} {
|
|
392
|
+
print "ALL PASS"
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
; SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
; ==============================================================================
|
|
4
|
+
; EVGReconcile — keyed child reconciliation, so a rebuilt tree keeps its elements
|
|
5
|
+
; ==============================================================================
|
|
6
|
+
; THE PROBLEM THIS EXISTS FOR, in the words the code already used.
|
|
7
|
+
;
|
|
8
|
+
; Every demo in `gallery/ui/demo` keeps its element tree between frames, and
|
|
9
|
+
; `main.js` says why: "Rebuild only if the data changed." The comment beside it
|
|
10
|
+
; is a measurement, not a worry —
|
|
11
|
+
;
|
|
12
|
+
; every element is new every frame, so every flight establishes at its
|
|
13
|
+
; destination. Measured before it was believed: the rows making room for a
|
|
14
|
+
; dragged item were at their final positions 40ms after the pointer
|
|
15
|
+
; crossed, having travelled through nothing.
|
|
16
|
+
;
|
|
17
|
+
; So the declarative half of this gallery runs ONCE, at init, and everything
|
|
18
|
+
; after that is imperative mutation of the tree it produced. `SortableDemo`'s
|
|
19
|
+
; `applyShift()` is the clearest case: it re-aims an existing list at a new
|
|
20
|
+
; target by hand, and it walks by INDEX — `list.children[i]` is assumed to be
|
|
21
|
+
; the row for `order[i]`, which holds only because `order` does not change
|
|
22
|
+
; during a drag. It is a keyed reconciler for one list, written without keys,
|
|
23
|
+
; correct by luck.
|
|
24
|
+
;
|
|
25
|
+
; The missing piece is identity. A transition, a caret, a scroll offset and a
|
|
26
|
+
; hover are all properties OF AN ELEMENT, and a tree built from scratch has
|
|
27
|
+
; different elements in every position — so they have nowhere to live.
|
|
28
|
+
;
|
|
29
|
+
; --- WHAT THIS DOES -----------------------------------------------------------
|
|
30
|
+
;
|
|
31
|
+
; `reconcile(live, next)` mutates `live` until it describes what `next`
|
|
32
|
+
; describes, keeping every `live` element it can:
|
|
33
|
+
;
|
|
34
|
+
; * children are matched within their parent by (key, tagName)
|
|
35
|
+
; * a matched child KEEPS ITS ELEMENT — `adoptFrom` overwrites everything it
|
|
36
|
+
; describes except `children`, `parent` and `transitions`
|
|
37
|
+
; * an unmatched new child is taken from `next` as-is
|
|
38
|
+
; * an unmatched old child is dropped
|
|
39
|
+
; * the resulting child ORDER is `next`'s, so a reorder MOVES elements
|
|
40
|
+
;
|
|
41
|
+
; `next` is consumed: after this returns it must not be used again. Nothing is
|
|
42
|
+
; cloned, so the two trees would share nodes.
|
|
43
|
+
;
|
|
44
|
+
; --- MATCHING, and the one rule that is not obvious ---------------------------
|
|
45
|
+
;
|
|
46
|
+
; A KEYED child matches the old child with the same key AND the same tag.
|
|
47
|
+
; Same key, different tag is NOT a match: the key says "same logical child",
|
|
48
|
+
; the tag says "same kind of thing", and reusing a text node's element as a
|
|
49
|
+
; button's would carry a text node's leftovers into a button.
|
|
50
|
+
;
|
|
51
|
+
; AN UNKEYED child matches by POSITION AMONG UNKEYED SIBLINGS — not by
|
|
52
|
+
; position among all siblings. Consider `[a(key=1), b, c(key=2)]` becoming
|
|
53
|
+
; `[c(key=2), b, a(key=1)]`: `b` is the first unkeyed child in both, and it
|
|
54
|
+
; should stay itself. Counting all siblings would match `b` against `c`,
|
|
55
|
+
; fail the tag test, and throw away an element for no reason.
|
|
56
|
+
;
|
|
57
|
+
; A key is scoped to ITS PARENT. Two lists on one page may both key a row
|
|
58
|
+
; "video"; that is the whole reason `key` is not `id`.
|
|
59
|
+
;
|
|
60
|
+
; --- WHAT THIS DELIBERATELY DOES NOT DO ---------------------------------------
|
|
61
|
+
;
|
|
62
|
+
; It does not decide WHEN to rebuild, it does not track dependencies, and it
|
|
63
|
+
; does not know what a component is. It is the identity layer those need and
|
|
64
|
+
; nothing above it.
|
|
65
|
+
;
|
|
66
|
+
; It also does not run `EVGTransition`. The order is: reconcile the structure,
|
|
67
|
+
; then let the stylesheet write what it wants, then let `EVGTransition`
|
|
68
|
+
; reconcile that against the flights it kept. Two passes that both decide which
|
|
69
|
+
; element is "the same one" would be two opinions; this one decides, and the
|
|
70
|
+
; transition pass reads the answer.
|
|
71
|
+
; ==============================================================================
|
|
72
|
+
|
|
73
|
+
Import "EVGElement.rgr"
|
|
74
|
+
|
|
75
|
+
class EVGReconcileStats {
|
|
76
|
+
; How many elements survived, and how many had to be made. The ratio is the
|
|
77
|
+
; whole point of this class, so it is reported rather than inferred: a
|
|
78
|
+
; reconciler that silently matches nothing looks exactly like one that
|
|
79
|
+
; works, until something tries to animate.
|
|
80
|
+
def kept:int 0
|
|
81
|
+
def created:int 0
|
|
82
|
+
def dropped:int 0
|
|
83
|
+
def moved:int 0
|
|
84
|
+
|
|
85
|
+
Constructor () {
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
class EVGReconcile {
|
|
90
|
+
|
|
91
|
+
def stats:EVGReconcileStats (new EVGReconcileStats())
|
|
92
|
+
|
|
93
|
+
Constructor () {
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
; True when these two may be the same element.
|
|
97
|
+
sfn matches:boolean (a:EVGElement b:EVGElement) {
|
|
98
|
+
if (a.tagName != b.tagName) {
|
|
99
|
+
return false
|
|
100
|
+
}
|
|
101
|
+
return (a.key == b.key)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
; Bring `live` into line with `next`, keeping `live` itself.
|
|
105
|
+
fn reconcile:void (live:EVGElement next:EVGElement) {
|
|
106
|
+
def kids:[EVGElement] (this.matchChildren(live next))
|
|
107
|
+
; Matching first and adopting second is the order that reads correctly,
|
|
108
|
+
; and swapping the two changes NOTHING — `adoptFrom` skips `children`,
|
|
109
|
+
; so the match still sees the old list either way. Tried as a mutation
|
|
110
|
+
; and it survived, which is the only reason that is stated here rather
|
|
111
|
+
; than as a warning: the safety comes from `adoptFrom`'s skip list, not
|
|
112
|
+
; from these two lines being in this order.
|
|
113
|
+
live.adoptFrom(next)
|
|
114
|
+
def fresh:[EVGElement]
|
|
115
|
+
live.children = fresh
|
|
116
|
+
def i:int 0
|
|
117
|
+
while (i < (array_length kids)) {
|
|
118
|
+
live.addChild((itemAt kids i))
|
|
119
|
+
i = (i + 1)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
; The new child list, with survivors reused in `next`'s order.
|
|
124
|
+
fn matchChildren:[EVGElement] (live:EVGElement next:EVGElement) {
|
|
125
|
+
def out:[EVGElement]
|
|
126
|
+
def old:[EVGElement] live.children
|
|
127
|
+
def n:int (array_length old)
|
|
128
|
+
|
|
129
|
+
; Which old children have been claimed. Ranger has no set, and the
|
|
130
|
+
; lists here are a screenful of rows rather than a document, so this
|
|
131
|
+
; is a flag array and a linear scan.
|
|
132
|
+
def taken:[boolean]
|
|
133
|
+
def t:int 0
|
|
134
|
+
while (t < n) {
|
|
135
|
+
push taken false
|
|
136
|
+
t = (t + 1)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
def m:int (array_length next.children)
|
|
140
|
+
def j:int 0
|
|
141
|
+
; How many unkeyed children have been seen so far on each side. The
|
|
142
|
+
; unkeyed match is "the k-th unkeyed here is the k-th unkeyed there".
|
|
143
|
+
def unkeyedSeen:int 0
|
|
144
|
+
while (j < m) {
|
|
145
|
+
def cand:EVGElement (itemAt next.children j)
|
|
146
|
+
def hit:int (0 - 1)
|
|
147
|
+
if ((strlen cand.key) > 0) {
|
|
148
|
+
hit = (this.findKeyed(old taken cand))
|
|
149
|
+
} {
|
|
150
|
+
hit = (this.findUnkeyed(old taken cand unkeyedSeen))
|
|
151
|
+
unkeyedSeen = (unkeyedSeen + 1)
|
|
152
|
+
}
|
|
153
|
+
if (hit >= 0) {
|
|
154
|
+
def keep:EVGElement (itemAt old hit)
|
|
155
|
+
set_at taken hit true
|
|
156
|
+
if (hit != j) {
|
|
157
|
+
stats.moved = (stats.moved + 1)
|
|
158
|
+
}
|
|
159
|
+
this.reconcile(keep cand)
|
|
160
|
+
stats.kept = (stats.kept + 1)
|
|
161
|
+
push out keep
|
|
162
|
+
} {
|
|
163
|
+
; Nothing to keep. The new subtree comes across whole, and its
|
|
164
|
+
; own children are already in the shape the builder made — no
|
|
165
|
+
; recursion, because there is nothing on this side to match
|
|
166
|
+
; them against.
|
|
167
|
+
stats.created = (stats.created + 1)
|
|
168
|
+
push out cand
|
|
169
|
+
}
|
|
170
|
+
j = (j + 1)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
def k:int 0
|
|
174
|
+
while (k < n) {
|
|
175
|
+
if ((itemAt taken k) == false) {
|
|
176
|
+
stats.dropped = (stats.dropped + 1)
|
|
177
|
+
}
|
|
178
|
+
k = (k + 1)
|
|
179
|
+
}
|
|
180
|
+
return out
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
fn findKeyed:int (old:[EVGElement] taken:[boolean] cand:EVGElement) {
|
|
184
|
+
def i:int 0
|
|
185
|
+
while (i < (array_length old)) {
|
|
186
|
+
if ((itemAt taken i) == false) {
|
|
187
|
+
if (EVGReconcile.matches((itemAt old i) cand)) {
|
|
188
|
+
return i
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
i = (i + 1)
|
|
192
|
+
}
|
|
193
|
+
return (0 - 1)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
; The `want`-th unkeyed old child, if it is still free and the tag agrees.
|
|
197
|
+
;
|
|
198
|
+
; Deliberately NOT "the first free unkeyed child with a matching tag":
|
|
199
|
+
; falling forward past a mismatch would pair the second unkeyed child of
|
|
200
|
+
; one render with the third of another, which is how an unkeyed list ends
|
|
201
|
+
; up shifting its state by one on every insert.
|
|
202
|
+
fn findUnkeyed:int (old:[EVGElement] taken:[boolean] cand:EVGElement want:int) {
|
|
203
|
+
def seen:int 0
|
|
204
|
+
def i:int 0
|
|
205
|
+
while (i < (array_length old)) {
|
|
206
|
+
def o:EVGElement (itemAt old i)
|
|
207
|
+
if ((strlen o.key) == 0) {
|
|
208
|
+
if (seen == want) {
|
|
209
|
+
if ((itemAt taken i) == false) {
|
|
210
|
+
if (o.tagName == cand.tagName) {
|
|
211
|
+
return i
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return (0 - 1)
|
|
215
|
+
}
|
|
216
|
+
seen = (seen + 1)
|
|
217
|
+
}
|
|
218
|
+
i = (i + 1)
|
|
219
|
+
}
|
|
220
|
+
return (0 - 1)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
fn resetStats:void () {
|
|
224
|
+
stats = (new EVGReconcileStats())
|
|
225
|
+
}
|
|
226
|
+
}
|