@react-x11/components 0.8.0 → 0.10.0
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/README.md +105 -38
- package/dist/charts/node.d.ts +12 -2
- package/dist/charts/node.d.ts.map +1 -1
- package/dist/charts/node.js +50 -16
- package/dist/charts/node.js.map +1 -1
- package/dist/charts/render.d.ts +12 -3
- package/dist/charts/render.d.ts.map +1 -1
- package/dist/charts/render.js +94 -25
- package/dist/charts/render.js.map +1 -1
- package/dist/code/index.d.ts.map +1 -1
- package/dist/code/index.js +3 -1
- package/dist/code/index.js.map +1 -1
- package/dist/code-editor/completion.d.ts.map +1 -1
- package/dist/code-editor/completion.js +3 -2
- package/dist/code-editor/completion.js.map +1 -1
- package/dist/code-editor/index.d.ts.map +1 -1
- package/dist/code-editor/index.js +19 -0
- package/dist/code-editor/index.js.map +1 -1
- package/dist/code-editor/node.d.ts +177 -7
- package/dist/code-editor/node.d.ts.map +1 -1
- package/dist/code-editor/node.js +1329 -164
- package/dist/code-editor/node.js.map +1 -1
- package/dist/code-language/languages/glsl.d.ts.map +1 -1
- package/dist/code-language/languages/glsl.js +10 -2
- package/dist/code-language/languages/glsl.js.map +1 -1
- package/dist/code-language/languages/javascript.d.ts.map +1 -1
- package/dist/code-language/languages/javascript.js +18 -3
- package/dist/code-language/languages/javascript.js.map +1 -1
- package/dist/code-language/languages/json.d.ts.map +1 -1
- package/dist/code-language/languages/json.js +10 -2
- package/dist/code-language/languages/json.js.map +1 -1
- package/dist/code-language/languages/shell.d.ts.map +1 -1
- package/dist/code-language/languages/shell.js +10 -2
- package/dist/code-language/languages/shell.js.map +1 -1
- package/dist/code-language/languages/sql.d.ts.map +1 -1
- package/dist/code-language/languages/sql.js +20 -2
- package/dist/code-language/languages/sql.js.map +1 -1
- package/dist/code-language/stream.d.ts +22 -0
- package/dist/code-language/stream.d.ts.map +1 -1
- package/dist/code-language/stream.js +259 -9
- package/dist/code-language/stream.js.map +1 -1
- package/dist/code-language/types.d.ts +5 -4
- package/dist/code-language/types.d.ts.map +1 -1
- package/dist/codeblock/index.d.ts +13 -2
- package/dist/codeblock/index.d.ts.map +1 -1
- package/dist/codeblock/index.js +20 -3
- package/dist/codeblock/index.js.map +1 -1
- package/dist/embed/client.d.ts +48 -3
- package/dist/embed/client.d.ts.map +1 -1
- package/dist/embed/client.js +79 -5
- package/dist/embed/client.js.map +1 -1
- package/dist/embed/index.d.ts +1 -1
- package/dist/embed/index.d.ts.map +1 -1
- package/dist/embed/index.js +1 -1
- package/dist/embed/index.js.map +1 -1
- package/dist/flow/draw.d.ts +50 -0
- package/dist/flow/draw.d.ts.map +1 -1
- package/dist/flow/draw.js +132 -33
- package/dist/flow/draw.js.map +1 -1
- package/dist/flow/gl/color.d.ts +13 -0
- package/dist/flow/gl/color.d.ts.map +1 -0
- package/dist/flow/gl/color.js +82 -0
- package/dist/flow/gl/color.js.map +1 -0
- package/dist/flow/gl/index.d.ts +60 -0
- package/dist/flow/gl/index.d.ts.map +1 -0
- package/dist/flow/gl/index.js +283 -0
- package/dist/flow/gl/index.js.map +1 -0
- package/dist/flow/gl/pack.d.ts +133 -0
- package/dist/flow/gl/pack.d.ts.map +1 -0
- package/dist/flow/gl/pack.js +408 -0
- package/dist/flow/gl/pack.js.map +1 -0
- package/dist/flow/gl/renderer.d.ts +95 -0
- package/dist/flow/gl/renderer.d.ts.map +1 -0
- package/dist/flow/gl/renderer.js +459 -0
- package/dist/flow/gl/renderer.js.map +1 -0
- package/dist/flow/gl/shaders.d.ts +79 -0
- package/dist/flow/gl/shaders.d.ts.map +1 -0
- package/dist/flow/gl/shaders.js +346 -0
- package/dist/flow/gl/shaders.js.map +1 -0
- package/dist/flow/gl/text.d.ts +318 -0
- package/dist/flow/gl/text.d.ts.map +1 -0
- package/dist/flow/gl/text.js +1103 -0
- package/dist/flow/gl/text.js.map +1 -0
- package/dist/flow/index.d.ts +1 -1
- package/dist/flow/index.d.ts.map +1 -1
- package/dist/flow/index.js +619 -64
- package/dist/flow/index.js.map +1 -1
- package/dist/flow/node.d.ts +621 -72
- package/dist/flow/node.d.ts.map +1 -1
- package/dist/flow/node.js +2253 -1018
- package/dist/flow/node.js.map +1 -1
- package/dist/flow/paint.d.ts +35 -0
- package/dist/flow/paint.d.ts.map +1 -0
- package/dist/flow/paint.js +393 -0
- package/dist/flow/paint.js.map +1 -0
- package/dist/flow/paths.d.ts +14 -0
- package/dist/flow/paths.d.ts.map +1 -1
- package/dist/flow/paths.js +45 -19
- package/dist/flow/paths.js.map +1 -1
- package/dist/flow/scene.d.ts +584 -0
- package/dist/flow/scene.d.ts.map +1 -0
- package/dist/flow/scene.js +1453 -0
- package/dist/flow/scene.js.map +1 -0
- package/dist/flow/types.d.ts +111 -1
- package/dist/flow/types.d.ts.map +1 -1
- package/dist/html/controls.d.ts +28 -0
- package/dist/html/controls.d.ts.map +1 -1
- package/dist/html/controls.js +52 -2
- package/dist/html/controls.js.map +1 -1
- package/dist/html/css/calc.d.ts +14 -0
- package/dist/html/css/calc.d.ts.map +1 -0
- package/dist/html/css/calc.js +344 -0
- package/dist/html/css/calc.js.map +1 -0
- package/dist/html/css/cascade.d.ts +240 -6
- package/dist/html/css/cascade.d.ts.map +1 -1
- package/dist/html/css/cascade.js +1327 -66
- package/dist/html/css/cascade.js.map +1 -1
- package/dist/html/css/color.d.ts +23 -0
- package/dist/html/css/color.d.ts.map +1 -0
- package/dist/html/css/color.js +740 -0
- package/dist/html/css/color.js.map +1 -0
- package/dist/html/css/content.d.ts +92 -0
- package/dist/html/css/content.d.ts.map +1 -0
- package/dist/html/css/content.js +388 -0
- package/dist/html/css/content.js.map +1 -0
- package/dist/html/css/counter-styles.d.ts +76 -0
- package/dist/html/css/counter-styles.d.ts.map +1 -0
- package/dist/html/css/counter-styles.js +893 -0
- package/dist/html/css/counter-styles.js.map +1 -0
- package/dist/html/css/decode.d.ts +12 -0
- package/dist/html/css/decode.d.ts.map +1 -0
- package/dist/html/css/decode.js +116 -0
- package/dist/html/css/decode.js.map +1 -0
- package/dist/html/css/parse.d.ts +110 -10
- package/dist/html/css/parse.d.ts.map +1 -1
- package/dist/html/css/parse.js +1473 -180
- package/dist/html/css/parse.js.map +1 -1
- package/dist/html/css/style.d.ts +498 -15
- package/dist/html/css/style.d.ts.map +1 -1
- package/dist/html/css/style.js +3698 -206
- package/dist/html/css/style.js.map +1 -1
- package/dist/html/css/ua.d.ts.map +1 -1
- package/dist/html/css/ua.js +57 -11
- package/dist/html/css/ua.js.map +1 -1
- package/dist/html/css/values.d.ts +37 -3
- package/dist/html/css/values.d.ts.map +1 -1
- package/dist/html/css/values.js +122 -48
- package/dist/html/css/values.js.map +1 -1
- package/dist/html/css/vars.d.ts +52 -0
- package/dist/html/css/vars.d.ts.map +1 -0
- package/dist/html/css/vars.js +358 -0
- package/dist/html/css/vars.js.map +1 -0
- package/dist/html/dom.d.ts +30 -4
- package/dist/html/dom.d.ts.map +1 -1
- package/dist/html/dom.js +278 -17
- package/dist/html/dom.js.map +1 -1
- package/dist/html/fonts.d.ts +106 -0
- package/dist/html/fonts.d.ts.map +1 -0
- package/dist/html/fonts.js +565 -0
- package/dist/html/fonts.js.map +1 -0
- package/dist/html/gif.d.ts +15 -0
- package/dist/html/gif.d.ts.map +1 -0
- package/dist/html/gif.js +229 -0
- package/dist/html/gif.js.map +1 -0
- package/dist/html/index.d.ts +50 -11
- package/dist/html/index.d.ts.map +1 -1
- package/dist/html/index.js +115 -9
- package/dist/html/index.js.map +1 -1
- package/dist/html/layout/block.d.ts +226 -3
- package/dist/html/layout/block.d.ts.map +1 -1
- package/dist/html/layout/block.js +2937 -197
- package/dist/html/layout/block.js.map +1 -1
- package/dist/html/layout/boxes.d.ts +340 -17
- package/dist/html/layout/boxes.d.ts.map +1 -1
- package/dist/html/layout/boxes.js +1714 -182
- package/dist/html/layout/boxes.js.map +1 -1
- package/dist/html/layout/cache.d.ts +58 -0
- package/dist/html/layout/cache.d.ts.map +1 -0
- package/dist/html/layout/cache.js +224 -0
- package/dist/html/layout/cache.js.map +1 -0
- package/dist/html/layout/collapse.d.ts +45 -0
- package/dist/html/layout/collapse.d.ts.map +1 -0
- package/dist/html/layout/collapse.js +246 -0
- package/dist/html/layout/collapse.js.map +1 -0
- package/dist/html/layout/css-grid.d.ts +7 -0
- package/dist/html/layout/css-grid.d.ts.map +1 -0
- package/dist/html/layout/css-grid.js +900 -0
- package/dist/html/layout/css-grid.js.map +1 -0
- package/dist/html/layout/flex.d.ts.map +1 -1
- package/dist/html/layout/flex.js +1025 -89
- package/dist/html/layout/flex.js.map +1 -1
- package/dist/html/layout/floats.d.ts +45 -6
- package/dist/html/layout/floats.d.ts.map +1 -1
- package/dist/html/layout/floats.js +113 -14
- package/dist/html/layout/floats.js.map +1 -1
- package/dist/html/layout/grid-lines.d.ts +39 -0
- package/dist/html/layout/grid-lines.d.ts.map +1 -0
- package/dist/html/layout/grid-lines.js +119 -0
- package/dist/html/layout/grid-lines.js.map +1 -0
- package/dist/html/layout/grid.d.ts +53 -0
- package/dist/html/layout/grid.d.ts.map +1 -0
- package/dist/html/layout/grid.js +153 -0
- package/dist/html/layout/grid.js.map +1 -0
- package/dist/html/layout/inline.d.ts +151 -1
- package/dist/html/layout/inline.d.ts.map +1 -1
- package/dist/html/layout/inline.js +3375 -174
- package/dist/html/layout/inline.js.map +1 -1
- package/dist/html/layout/shaping.d.ts +6 -0
- package/dist/html/layout/shaping.d.ts.map +1 -0
- package/dist/html/layout/shaping.js +100 -0
- package/dist/html/layout/shaping.js.map +1 -0
- package/dist/html/layout/table.d.ts +3 -0
- package/dist/html/layout/table.d.ts.map +1 -1
- package/dist/html/layout/table.js +681 -175
- package/dist/html/layout/table.js.map +1 -1
- package/dist/html/layout/tracks.d.ts +44 -0
- package/dist/html/layout/tracks.d.ts.map +1 -0
- package/dist/html/layout/tracks.js +349 -0
- package/dist/html/layout/tracks.js.map +1 -0
- package/dist/html/node.d.ts +172 -5
- package/dist/html/node.d.ts.map +1 -1
- package/dist/html/node.js +1336 -103
- package/dist/html/node.js.map +1 -1
- package/dist/html/paint.d.ts +92 -3
- package/dist/html/paint.d.ts.map +1 -1
- package/dist/html/paint.js +3503 -141
- package/dist/html/paint.js.map +1 -1
- package/dist/html/resources.d.ts +60 -14
- package/dist/html/resources.d.ts.map +1 -1
- package/dist/html/resources.js +122 -36
- package/dist/html/resources.js.map +1 -1
- package/dist/html/surfaces.d.ts +23 -0
- package/dist/html/surfaces.d.ts.map +1 -0
- package/dist/html/surfaces.js +68 -0
- package/dist/html/surfaces.js.map +1 -0
- package/dist/html/svg.d.ts +80 -0
- package/dist/html/svg.d.ts.map +1 -0
- package/dist/html/svg.js +435 -0
- package/dist/html/svg.js.map +1 -0
- package/dist/html/url.d.ts +23 -0
- package/dist/html/url.d.ts.map +1 -0
- package/dist/html/url.js +80 -0
- package/dist/html/url.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/internal/collate.d.ts +3 -0
- package/dist/internal/collate.d.ts.map +1 -0
- package/dist/internal/collate.js +11 -0
- package/dist/internal/collate.js.map +1 -0
- package/dist/{markdown → internal/markdown}/ast.d.ts +23 -0
- package/dist/internal/markdown/ast.d.ts.map +1 -0
- package/dist/internal/markdown/ast.js +22 -0
- package/dist/internal/markdown/ast.js.map +1 -0
- package/dist/internal/markdown/parse.d.ts.map +1 -0
- package/dist/{markdown → internal/markdown}/parse.js +221 -30
- package/dist/internal/markdown/parse.js.map +1 -0
- package/dist/internal/markdown/stringify.d.ts +4 -0
- package/dist/internal/markdown/stringify.d.ts.map +1 -0
- package/dist/internal/markdown/stringify.js +684 -0
- package/dist/internal/markdown/stringify.js.map +1 -0
- package/dist/internal/markdown/tags.d.ts.map +1 -0
- package/dist/internal/markdown/tags.js.map +1 -0
- package/dist/internal/prewarm.d.ts +18 -0
- package/dist/internal/prewarm.d.ts.map +1 -0
- package/dist/internal/prewarm.js +42 -0
- package/dist/internal/prewarm.js.map +1 -0
- package/dist/internal/scroll.d.ts +3 -2
- package/dist/internal/scroll.d.ts.map +1 -1
- package/dist/internal/scroll.js +1 -0
- package/dist/internal/scroll.js.map +1 -1
- package/dist/internal/sdf.d.ts +20 -0
- package/dist/internal/sdf.d.ts.map +1 -0
- package/dist/internal/sdf.js +216 -0
- package/dist/internal/sdf.js.map +1 -0
- package/dist/internal/splice.d.ts +12 -0
- package/dist/internal/splice.d.ts.map +1 -0
- package/dist/internal/splice.js +23 -0
- package/dist/internal/splice.js.map +1 -0
- package/dist/internal/window.d.ts +12 -0
- package/dist/internal/window.d.ts.map +1 -1
- package/dist/internal/window.js +23 -6
- package/dist/internal/window.js.map +1 -1
- package/dist/maps/anchors.d.ts +103 -0
- package/dist/maps/anchors.d.ts.map +1 -0
- package/dist/maps/anchors.js +790 -0
- package/dist/maps/anchors.js.map +1 -0
- package/dist/maps/controller.d.ts +340 -0
- package/dist/maps/controller.d.ts.map +1 -0
- package/dist/maps/controller.js +822 -0
- package/dist/maps/controller.js.map +1 -0
- package/dist/maps/gl/buckets.d.ts +99 -0
- package/dist/maps/gl/buckets.d.ts.map +1 -0
- package/dist/maps/gl/buckets.js +365 -0
- package/dist/maps/gl/buckets.js.map +1 -0
- package/dist/maps/gl/build-worker.d.ts +15 -0
- package/dist/maps/gl/build-worker.d.ts.map +1 -0
- package/dist/maps/gl/build-worker.js +44 -0
- package/dist/maps/gl/build-worker.js.map +1 -0
- package/dist/maps/gl/color.d.ts +8 -0
- package/dist/maps/gl/color.d.ts.map +1 -0
- package/dist/maps/gl/color.js +57 -0
- package/dist/maps/gl/color.js.map +1 -0
- package/dist/maps/gl/cover.d.ts +70 -0
- package/dist/maps/gl/cover.d.ts.map +1 -0
- package/dist/maps/gl/cover.js +137 -0
- package/dist/maps/gl/cover.js.map +1 -0
- package/dist/maps/gl/index.d.ts +3 -0
- package/dist/maps/gl/index.d.ts.map +1 -0
- package/dist/maps/gl/index.js +20 -0
- package/dist/maps/gl/index.js.map +1 -0
- package/dist/maps/gl/markers.d.ts +28 -0
- package/dist/maps/gl/markers.d.ts.map +1 -0
- package/dist/maps/gl/markers.js +60 -0
- package/dist/maps/gl/markers.js.map +1 -0
- package/dist/maps/gl/overlays.d.ts +68 -0
- package/dist/maps/gl/overlays.d.ts.map +1 -0
- package/dist/maps/gl/overlays.js +300 -0
- package/dist/maps/gl/overlays.js.map +1 -0
- package/dist/maps/gl/pane.d.ts +12 -0
- package/dist/maps/gl/pane.d.ts.map +1 -0
- package/dist/maps/gl/pane.js +37 -0
- package/dist/maps/gl/pane.js.map +1 -0
- package/dist/maps/gl/placement.d.ts +124 -0
- package/dist/maps/gl/placement.d.ts.map +1 -0
- package/dist/maps/gl/placement.js +642 -0
- package/dist/maps/gl/placement.js.map +1 -0
- package/dist/maps/gl/renderer.d.ts +384 -0
- package/dist/maps/gl/renderer.d.ts.map +1 -0
- package/dist/maps/gl/renderer.js +1184 -0
- package/dist/maps/gl/renderer.js.map +1 -0
- package/dist/maps/gl/shaders.d.ts +121 -0
- package/dist/maps/gl/shaders.d.ts.map +1 -0
- package/dist/maps/gl/shaders.js +453 -0
- package/dist/maps/gl/shaders.js.map +1 -0
- package/dist/maps/gl/store.d.ts +125 -0
- package/dist/maps/gl/store.d.ts.map +1 -0
- package/dist/maps/gl/store.js +512 -0
- package/dist/maps/gl/store.js.map +1 -0
- package/dist/maps/gl/text.d.ts +283 -0
- package/dist/maps/gl/text.d.ts.map +1 -0
- package/dist/maps/gl/text.js +698 -0
- package/dist/maps/gl/text.js.map +1 -0
- package/dist/maps/gl/view.d.ts +345 -0
- package/dist/maps/gl/view.d.ts.map +1 -0
- package/dist/maps/gl/view.js +1209 -0
- package/dist/maps/gl/view.js.map +1 -0
- package/dist/maps/icons.d.ts +32 -0
- package/dist/maps/icons.d.ts.map +1 -0
- package/dist/maps/icons.js +151 -0
- package/dist/maps/icons.js.map +1 -0
- package/dist/maps/index.d.ts +3 -1
- package/dist/maps/index.d.ts.map +1 -1
- package/dist/maps/index.js +218 -52
- package/dist/maps/index.js.map +1 -1
- package/dist/maps/labels.d.ts +91 -18
- package/dist/maps/labels.d.ts.map +1 -1
- package/dist/maps/labels.js +376 -140
- package/dist/maps/labels.js.map +1 -1
- package/dist/maps/node.d.ts +179 -56
- package/dist/maps/node.d.ts.map +1 -1
- package/dist/maps/node.js +663 -537
- package/dist/maps/node.js.map +1 -1
- package/dist/maps/overlay.d.ts +72 -9
- package/dist/maps/overlay.d.ts.map +1 -1
- package/dist/maps/overlay.js +108 -28
- package/dist/maps/overlay.js.map +1 -1
- package/dist/maps/paint.d.ts +3 -0
- package/dist/maps/paint.d.ts.map +1 -1
- package/dist/maps/paint.js.map +1 -1
- package/dist/maps/proj.d.ts +62 -0
- package/dist/maps/proj.d.ts.map +1 -1
- package/dist/maps/proj.js +101 -0
- package/dist/maps/proj.js.map +1 -1
- package/dist/maps/renderer.d.ts +69 -0
- package/dist/maps/renderer.d.ts.map +1 -0
- package/dist/maps/renderer.js +66 -0
- package/dist/maps/renderer.js.map +1 -0
- package/dist/maps/sources.d.ts +30 -6
- package/dist/maps/sources.d.ts.map +1 -1
- package/dist/maps/sources.js +22 -3
- package/dist/maps/sources.js.map +1 -1
- package/dist/maps/style.d.ts +35 -0
- package/dist/maps/style.d.ts.map +1 -1
- package/dist/maps/style.js.map +1 -1
- package/dist/maps/styles.d.ts +12 -0
- package/dist/maps/styles.d.ts.map +1 -1
- package/dist/maps/styles.js +120 -6
- package/dist/maps/styles.js.map +1 -1
- package/dist/maps/theme.d.ts +19 -0
- package/dist/maps/theme.d.ts.map +1 -0
- package/dist/maps/theme.js +44 -0
- package/dist/maps/theme.js.map +1 -0
- package/dist/maps/tiles.d.ts +147 -27
- package/dist/maps/tiles.d.ts.map +1 -1
- package/dist/maps/tiles.js +221 -54
- package/dist/maps/tiles.js.map +1 -1
- package/dist/maps/types.d.ts +213 -21
- package/dist/maps/types.d.ts.map +1 -1
- package/dist/markdown/expressions.d.ts +1 -1
- package/dist/markdown/expressions.d.ts.map +1 -1
- package/dist/markdown/expressions.js.map +1 -1
- package/dist/markdown/index.d.ts +7 -6
- package/dist/markdown/index.d.ts.map +1 -1
- package/dist/markdown/index.js +121 -50
- package/dist/markdown/index.js.map +1 -1
- package/dist/markdown/spans.d.ts +1 -1
- package/dist/markdown/spans.d.ts.map +1 -1
- package/dist/markdown/spans.js +6 -0
- package/dist/markdown/spans.js.map +1 -1
- package/dist/media-player/index.d.ts +11 -3
- package/dist/media-player/index.d.ts.map +1 -1
- package/dist/media-player/index.js +13 -2
- package/dist/media-player/index.js.map +1 -1
- package/dist/qml/containers.d.ts.map +1 -1
- package/dist/qml/containers.js +6 -1
- package/dist/qml/containers.js.map +1 -1
- package/dist/qml/interaction.d.ts.map +1 -1
- package/dist/qml/interaction.js +38 -5
- package/dist/qml/interaction.js.map +1 -1
- package/dist/qml/layouts.d.ts.map +1 -1
- package/dist/qml/layouts.js +21 -6
- package/dist/qml/layouts.js.map +1 -1
- package/dist/qml/qtquick.d.ts.map +1 -1
- package/dist/qml/qtquick.js +9 -2
- package/dist/qml/qtquick.js.map +1 -1
- package/dist/qml/react.d.ts.map +1 -1
- package/dist/qml/react.js +9 -7
- package/dist/qml/react.js.map +1 -1
- package/dist/reorder/clock.d.ts +7 -0
- package/dist/reorder/clock.d.ts.map +1 -0
- package/dist/reorder/clock.js +26 -0
- package/dist/reorder/clock.js.map +1 -0
- package/dist/reorder/index.d.ts +25 -3
- package/dist/reorder/index.d.ts.map +1 -1
- package/dist/reorder/index.js +64 -19
- package/dist/reorder/index.js.map +1 -1
- package/dist/rich-text-editor/clipboard.d.ts +23 -0
- package/dist/rich-text-editor/clipboard.d.ts.map +1 -0
- package/dist/rich-text-editor/clipboard.js +307 -0
- package/dist/rich-text-editor/clipboard.js.map +1 -0
- package/dist/rich-text-editor/collab.d.ts +32 -0
- package/dist/rich-text-editor/collab.d.ts.map +1 -0
- package/dist/rich-text-editor/collab.js +55 -0
- package/dist/rich-text-editor/collab.js.map +1 -0
- package/dist/rich-text-editor/commands.d.ts +50 -0
- package/dist/rich-text-editor/commands.d.ts.map +1 -0
- package/dist/rich-text-editor/commands.js +376 -0
- package/dist/rich-text-editor/commands.js.map +1 -0
- package/dist/rich-text-editor/drag.d.ts +68 -0
- package/dist/rich-text-editor/drag.d.ts.map +1 -0
- package/dist/rich-text-editor/drag.js +148 -0
- package/dist/rich-text-editor/drag.js.map +1 -0
- package/dist/rich-text-editor/html.d.ts +145 -0
- package/dist/rich-text-editor/html.d.ts.map +1 -0
- package/dist/rich-text-editor/html.js +516 -0
- package/dist/rich-text-editor/html.js.map +1 -0
- package/dist/rich-text-editor/index.d.ts +252 -0
- package/dist/rich-text-editor/index.d.ts.map +1 -0
- package/dist/rich-text-editor/index.js +856 -0
- package/dist/rich-text-editor/index.js.map +1 -0
- package/dist/rich-text-editor/inline.d.ts +94 -0
- package/dist/rich-text-editor/inline.d.ts.map +1 -0
- package/dist/rich-text-editor/inline.js +252 -0
- package/dist/rich-text-editor/inline.js.map +1 -0
- package/dist/rich-text-editor/keymap.d.ts +71 -0
- package/dist/rich-text-editor/keymap.d.ts.map +1 -0
- package/dist/rich-text-editor/keymap.js +394 -0
- package/dist/rich-text-editor/keymap.js.map +1 -0
- package/dist/rich-text-editor/keys.d.ts +40 -0
- package/dist/rich-text-editor/keys.d.ts.map +1 -0
- package/dist/rich-text-editor/keys.js +202 -0
- package/dist/rich-text-editor/keys.js.map +1 -0
- package/dist/rich-text-editor/look.d.ts +71 -0
- package/dist/rich-text-editor/look.d.ts.map +1 -0
- package/dist/rich-text-editor/look.js +342 -0
- package/dist/rich-text-editor/look.js.map +1 -0
- package/dist/rich-text-editor/markdown.d.ts +18 -0
- package/dist/rich-text-editor/markdown.d.ts.map +1 -0
- package/dist/rich-text-editor/markdown.js +496 -0
- package/dist/rich-text-editor/markdown.js.map +1 -0
- package/dist/rich-text-editor/marks.d.ts +9 -0
- package/dist/rich-text-editor/marks.d.ts.map +1 -0
- package/dist/rich-text-editor/marks.js +83 -0
- package/dist/rich-text-editor/marks.js.map +1 -0
- package/dist/rich-text-editor/nodes.d.ts +135 -0
- package/dist/rich-text-editor/nodes.d.ts.map +1 -0
- package/dist/rich-text-editor/nodes.js +397 -0
- package/dist/rich-text-editor/nodes.js.map +1 -0
- package/dist/rich-text-editor/render.d.ts +68 -0
- package/dist/rich-text-editor/render.d.ts.map +1 -0
- package/dist/rich-text-editor/render.js +518 -0
- package/dist/rich-text-editor/render.js.map +1 -0
- package/dist/rich-text-editor/schema.d.ts +16 -0
- package/dist/rich-text-editor/schema.d.ts.map +1 -0
- package/dist/rich-text-editor/schema.js +386 -0
- package/dist/rich-text-editor/schema.js.map +1 -0
- package/dist/rich-text-editor/store.d.ts +11 -0
- package/dist/rich-text-editor/store.d.ts.map +1 -0
- package/dist/rich-text-editor/store.js +43 -0
- package/dist/rich-text-editor/store.js.map +1 -0
- package/dist/rich-text-editor/suggest.d.ts +127 -0
- package/dist/rich-text-editor/suggest.d.ts.map +1 -0
- package/dist/rich-text-editor/suggest.js +0 -0
- package/dist/rich-text-editor/suggest.js.map +1 -0
- package/dist/rich-text-editor/tables.d.ts +41 -0
- package/dist/rich-text-editor/tables.d.ts.map +1 -0
- package/dist/rich-text-editor/tables.js +204 -0
- package/dist/rich-text-editor/tables.js.map +1 -0
- package/dist/rich-text-editor/toolbar.d.ts +58 -0
- package/dist/rich-text-editor/toolbar.d.ts.map +1 -0
- package/dist/rich-text-editor/toolbar.js +548 -0
- package/dist/rich-text-editor/toolbar.js.map +1 -0
- package/dist/rich-text-editor/view.d.ts +368 -0
- package/dist/rich-text-editor/view.d.ts.map +1 -0
- package/dist/rich-text-editor/view.js +1968 -0
- package/dist/rich-text-editor/view.js.map +1 -0
- package/dist/rich-text-editor/virtual.d.ts +54 -0
- package/dist/rich-text-editor/virtual.d.ts.map +1 -0
- package/dist/rich-text-editor/virtual.js +279 -0
- package/dist/rich-text-editor/virtual.js.map +1 -0
- package/dist/richtext/node.d.ts +71 -4
- package/dist/richtext/node.d.ts.map +1 -1
- package/dist/richtext/node.js +233 -28
- package/dist/richtext/node.js.map +1 -1
- package/dist/richtext/runs.d.ts +13 -8
- package/dist/richtext/runs.d.ts.map +1 -1
- package/dist/richtext/runs.js +18 -8
- package/dist/richtext/runs.js.map +1 -1
- package/dist/table/index.d.ts.map +1 -1
- package/dist/table/index.js +23 -0
- package/dist/table/index.js.map +1 -1
- package/dist/table/rows.d.ts +3 -2
- package/dist/table/rows.d.ts.map +1 -1
- package/dist/table/rows.js +20 -6
- package/dist/table/rows.js.map +1 -1
- package/dist/terminal/backends.d.ts +4 -1
- package/dist/terminal/backends.d.ts.map +1 -1
- package/dist/terminal/backends.js.map +1 -1
- package/dist/terminal/index.d.ts +18 -7
- package/dist/terminal/index.d.ts.map +1 -1
- package/dist/terminal/index.js +32 -11
- package/dist/terminal/index.js.map +1 -1
- package/dist/terminal/vt/pty.d.ts +13 -2
- package/dist/terminal/vt/pty.d.ts.map +1 -1
- package/dist/terminal/vt/pty.js +18 -3
- package/dist/terminal/vt/pty.js.map +1 -1
- package/dist/terminal-output/index.d.ts.map +1 -1
- package/dist/terminal-output/index.js +6 -1
- package/dist/terminal-output/index.js.map +1 -1
- package/dist/three/canvas.d.ts.map +1 -1
- package/dist/three/canvas.js +85 -7
- package/dist/three/canvas.js.map +1 -1
- package/dist/three/events.d.ts +22 -4
- package/dist/three/events.d.ts.map +1 -1
- package/dist/three/events.js +34 -4
- package/dist/three/events.js.map +1 -1
- package/dist/tree/index.d.ts.map +1 -1
- package/dist/tree/index.js +17 -0
- package/dist/tree/index.js.map +1 -1
- package/package.json +59 -31
- package/src/charts/node.ts +68 -28
- package/src/charts/render.ts +120 -30
- package/src/code/index.ts +3 -1
- package/src/code-editor/completion.ts +3 -2
- package/src/code-editor/index.ts +19 -0
- package/src/code-editor/node.ts +1574 -184
- package/src/code-language/languages/glsl.ts +12 -2
- package/src/code-language/languages/javascript.ts +21 -4
- package/src/code-language/languages/json.ts +12 -2
- package/src/code-language/languages/shell.ts +12 -2
- package/src/code-language/languages/sql.ts +22 -2
- package/src/code-language/stream.ts +257 -17
- package/src/code-language/types.ts +5 -4
- package/src/codeblock/index.ts +20 -3
- package/src/embed/client.ts +102 -8
- package/src/embed/index.ts +2 -0
- package/src/flow/draw.ts +167 -41
- package/src/flow/gl/color.ts +85 -0
- package/src/flow/gl/index.ts +342 -0
- package/src/flow/gl/pack.ts +585 -0
- package/src/flow/gl/renderer.ts +679 -0
- package/src/flow/gl/shaders.ts +371 -0
- package/src/flow/gl/text.ts +1295 -0
- package/src/flow/index.ts +794 -67
- package/src/flow/node.ts +2496 -1256
- package/src/flow/paint.ts +511 -0
- package/src/flow/paths.ts +80 -15
- package/src/flow/scene.ts +2199 -0
- package/src/flow/types.ts +113 -1
- package/src/html/controls.ts +73 -2
- package/src/html/css/calc.ts +385 -0
- package/src/html/css/cascade.ts +1403 -62
- package/src/html/css/color.ts +755 -0
- package/src/html/css/content.ts +417 -0
- package/src/html/css/counter-styles.ts +973 -0
- package/src/html/css/decode.ts +130 -0
- package/src/html/css/parse.ts +1514 -160
- package/src/html/css/style.ts +4102 -222
- package/src/html/css/ua.ts +58 -12
- package/src/html/css/values.ts +150 -50
- package/src/html/css/vars.ts +348 -0
- package/src/html/dom.ts +284 -17
- package/src/html/fonts.ts +641 -0
- package/src/html/gif.ts +233 -0
- package/src/html/index.ts +180 -20
- package/src/html/layout/block.ts +3456 -230
- package/src/html/layout/boxes.ts +2100 -190
- package/src/html/layout/cache.ts +289 -0
- package/src/html/layout/collapse.ts +317 -0
- package/src/html/layout/css-grid.ts +1063 -0
- package/src/html/layout/flex.ts +1115 -106
- package/src/html/layout/floats.ts +128 -14
- package/src/html/layout/grid-lines.ts +141 -0
- package/src/html/layout/grid.ts +181 -0
- package/src/html/layout/inline.ts +3943 -192
- package/src/html/layout/shaping.ts +140 -0
- package/src/html/layout/table.ts +696 -186
- package/src/html/layout/tracks.ts +437 -0
- package/src/html/node.ts +1486 -121
- package/src/html/paint.ts +4293 -169
- package/src/html/resources.ts +161 -50
- package/src/html/surfaces.ts +81 -0
- package/src/html/svg.ts +493 -0
- package/src/html/url.ts +86 -0
- package/src/index.ts +4 -0
- package/src/internal/collate.ts +12 -0
- package/src/{markdown → internal/markdown}/ast.ts +35 -7
- package/src/{markdown → internal/markdown}/parse.ts +288 -35
- package/src/internal/markdown/stringify.ts +772 -0
- package/src/internal/prewarm.ts +45 -0
- package/src/internal/scroll.ts +5 -3
- package/src/internal/sdf.ts +227 -0
- package/src/internal/splice.ts +25 -0
- package/src/internal/window.ts +36 -5
- package/src/maps/anchors.ts +913 -0
- package/src/maps/controller.ts +1084 -0
- package/src/maps/gl/buckets.ts +461 -0
- package/src/maps/gl/build-worker.ts +66 -0
- package/src/maps/gl/color.ts +62 -0
- package/src/maps/gl/cover.ts +205 -0
- package/src/maps/gl/index.ts +23 -0
- package/src/maps/gl/markers.ts +83 -0
- package/src/maps/gl/overlays.ts +379 -0
- package/src/maps/gl/pane.ts +42 -0
- package/src/maps/gl/placement.ts +968 -0
- package/src/maps/gl/renderer.ts +1660 -0
- package/src/maps/gl/shaders.ts +489 -0
- package/src/maps/gl/store.ts +615 -0
- package/src/maps/gl/text.ts +868 -0
- package/src/maps/gl/view.ts +1489 -0
- package/src/maps/icons.ts +183 -0
- package/src/maps/index.ts +274 -81
- package/src/maps/labels.ts +457 -144
- package/src/maps/node.ts +832 -665
- package/src/maps/overlay.ts +148 -43
- package/src/maps/paint.ts +3 -0
- package/src/maps/proj.ts +118 -0
- package/src/maps/renderer.ts +155 -0
- package/src/maps/sources.ts +43 -6
- package/src/maps/style.ts +32 -0
- package/src/maps/styles.ts +215 -4
- package/src/maps/theme.ts +51 -0
- package/src/maps/tiles.ts +253 -62
- package/src/maps/types.ts +213 -21
- package/src/markdown/expressions.ts +5 -1
- package/src/markdown/index.ts +163 -78
- package/src/markdown/spans.ts +6 -1
- package/src/media-player/index.ts +18 -4
- package/src/qml/containers.tsx +6 -1
- package/src/qml/interaction.tsx +42 -5
- package/src/qml/layouts.tsx +21 -6
- package/src/qml/qtquick.tsx +9 -2
- package/src/qml/react.tsx +7 -5
- package/src/reorder/clock.ts +27 -0
- package/src/reorder/index.ts +100 -27
- package/src/rich-text-editor/clipboard.ts +439 -0
- package/src/rich-text-editor/collab.ts +91 -0
- package/src/rich-text-editor/commands.ts +448 -0
- package/src/rich-text-editor/dom-globals.d.ts +23 -0
- package/src/rich-text-editor/drag.ts +211 -0
- package/src/rich-text-editor/html.ts +619 -0
- package/src/rich-text-editor/index.ts +1364 -0
- package/src/rich-text-editor/inline.ts +345 -0
- package/src/rich-text-editor/keymap.ts +510 -0
- package/src/rich-text-editor/keys.ts +228 -0
- package/src/rich-text-editor/look.ts +427 -0
- package/src/rich-text-editor/markdown.ts +653 -0
- package/src/rich-text-editor/marks.ts +101 -0
- package/src/rich-text-editor/nodes.ts +497 -0
- package/src/rich-text-editor/render.ts +837 -0
- package/src/rich-text-editor/schema.ts +431 -0
- package/src/rich-text-editor/store.ts +39 -0
- package/src/rich-text-editor/suggest.ts +597 -0
- package/src/rich-text-editor/tables.ts +272 -0
- package/src/rich-text-editor/toolbar.ts +724 -0
- package/src/rich-text-editor/view.ts +2287 -0
- package/src/rich-text-editor/virtual.ts +372 -0
- package/src/richtext/node.ts +294 -44
- package/src/richtext/runs.ts +23 -12
- package/src/table/index.ts +29 -0
- package/src/table/rows.ts +18 -8
- package/src/terminal/backends.ts +4 -1
- package/src/terminal/index.ts +47 -15
- package/src/terminal/vt/pty.ts +16 -2
- package/src/terminal-output/index.ts +6 -1
- package/src/three/canvas.ts +102 -8
- package/src/three/events.ts +49 -9
- package/src/tree/index.ts +19 -0
- package/dist/markdown/ast.d.ts.map +0 -1
- package/dist/markdown/ast.js +0 -17
- package/dist/markdown/ast.js.map +0 -1
- package/dist/markdown/parse.d.ts.map +0 -1
- package/dist/markdown/parse.js.map +0 -1
- package/dist/markdown/tags.d.ts.map +0 -1
- package/dist/markdown/tags.js.map +0 -1
- /package/dist/{markdown → internal/markdown}/parse.d.ts +0 -0
- /package/dist/{markdown → internal/markdown}/tags.d.ts +0 -0
- /package/dist/{markdown → internal/markdown}/tags.js +0 -0
- /package/src/{markdown → internal/markdown}/tags.ts +0 -0
package/src/maps/node.ts
CHANGED
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
// Three caches, and the map's whole performance argument is the way they
|
|
12
12
|
// are layered:
|
|
13
13
|
//
|
|
14
|
-
// 1. **Tile data**, keyed on `
|
|
14
|
+
// 1. **Tile data**, keyed on the source object and `z/x/y`, and valid
|
|
15
|
+
// forever. The object, not its `id` — see `./tiles.ts`.
|
|
15
16
|
// 2. **A rendered `Surface` per tile**, valid for a zoom *level* and a
|
|
16
17
|
// style — not for a camera position. So a **pan** composites the same
|
|
17
18
|
// surfaces at new offsets (and blits, so most of them are not even
|
|
@@ -32,7 +33,12 @@ import type { A11ySceneItem, Context2D } from 'react-x11/node';
|
|
|
32
33
|
import type { KeyboardEvent, MouseEvent, WheelEvent } from 'react-x11';
|
|
33
34
|
import { Surface } from 'react-x11/ntk';
|
|
34
35
|
|
|
35
|
-
import {
|
|
36
|
+
import { MapController, quantizeZoom } from './controller.js';
|
|
37
|
+
import type {
|
|
38
|
+
MapControllerProps,
|
|
39
|
+
MapPointerInput,
|
|
40
|
+
MapView,
|
|
41
|
+
} from './controller.js';
|
|
36
42
|
import {
|
|
37
43
|
BATCH_VERTICES,
|
|
38
44
|
DrawScratch,
|
|
@@ -44,16 +50,13 @@ import {
|
|
|
44
50
|
import type { MapCanvas, PreparedStyle } from './paint.js';
|
|
45
51
|
import {
|
|
46
52
|
DEFAULT_TILE_SIZE,
|
|
47
|
-
|
|
48
|
-
boundsOf,
|
|
53
|
+
dataSquareOf,
|
|
49
54
|
dataTileFor,
|
|
50
55
|
subTileOf,
|
|
51
|
-
projectLngLat,
|
|
52
56
|
rasterFor,
|
|
53
57
|
tileCover,
|
|
58
|
+
tileKey,
|
|
54
59
|
transformFor,
|
|
55
|
-
unprojectPoint,
|
|
56
|
-
visibleBounds,
|
|
57
60
|
} from './proj.js';
|
|
58
61
|
import type {
|
|
59
62
|
LngLat,
|
|
@@ -64,11 +67,13 @@ import type {
|
|
|
64
67
|
TileId,
|
|
65
68
|
Transform,
|
|
66
69
|
} from './proj.js';
|
|
67
|
-
import { TileCache, pyramid } from './tiles.js';
|
|
70
|
+
import { TileCache, drawnFor, pyramid } from './tiles.js';
|
|
68
71
|
import type { CachedTile, SurfaceLike, TileRender } from './tiles.js';
|
|
72
|
+
import { attributionOf } from './sources.js';
|
|
69
73
|
import type { MapSource } from './sources.js';
|
|
70
74
|
import { shortbreadStyle } from './styles.js';
|
|
71
75
|
import type { MapStyle } from './style.js';
|
|
76
|
+
import { isDarkTheme, overlayPalette } from './theme.js';
|
|
72
77
|
import {
|
|
73
78
|
LabelShaper,
|
|
74
79
|
collectLabels,
|
|
@@ -76,9 +81,16 @@ import {
|
|
|
76
81
|
placeLabels,
|
|
77
82
|
} from './labels.js';
|
|
78
83
|
import type { FontsLike, LabelCandidate, PlacedLabel } from './labels.js';
|
|
79
|
-
import {
|
|
84
|
+
import {
|
|
85
|
+
ATTRIBUTION_OPACITY,
|
|
86
|
+
ATTRIBUTION_SIZE,
|
|
87
|
+
attributionLayout,
|
|
88
|
+
drawMarkers,
|
|
89
|
+
drawOverlays,
|
|
90
|
+
markerRect,
|
|
91
|
+
} from './overlay.js';
|
|
80
92
|
import type { MapMarker, MapOverlay, OverlayPalette } from './overlay.js';
|
|
81
|
-
import type {
|
|
93
|
+
import type { FitBoundsOptions, MapFrameStats } from './types.js';
|
|
82
94
|
|
|
83
95
|
/** Registration key, `kind` and JSX tag, one string — react-x11 rejects a
|
|
84
96
|
* node whose `kind` is not the name it was registered under, because
|
|
@@ -103,24 +115,6 @@ export const SELF_DAMAGED_PROPS = [
|
|
|
103
115
|
'mapStyle',
|
|
104
116
|
] as const;
|
|
105
117
|
|
|
106
|
-
/** Screen pixels the pointer may travel before a press becomes a drag. */
|
|
107
|
-
const DRAG_THRESHOLD = 3;
|
|
108
|
-
|
|
109
|
-
/** How long after the last gesture step the map goes back to rasterizing.
|
|
110
|
-
* Long enough that a wheel-zoom's many steps count as one gesture, short
|
|
111
|
-
* enough that the map sharpens before the user has finished looking. */
|
|
112
|
-
const SETTLE_MS = 140;
|
|
113
|
-
|
|
114
|
-
/** Zoom is quantized to this, for the reason react-x11's docs/scale.md
|
|
115
|
-
* gives about a gesture-driven `scale`: every distinct value is a distinct
|
|
116
|
-
* set of font sizes to shape, and a wheel feeding a raw accumulator makes
|
|
117
|
-
* a new set per frame. A sixteenth of a level is finer than the eye reads
|
|
118
|
-
* as stepping. */
|
|
119
|
-
const ZOOM_STEP = 1 / 16;
|
|
120
|
-
|
|
121
|
-
/** A wheel notch is this much zoom. */
|
|
122
|
-
const WHEEL_ZOOM = 1 / 2.5;
|
|
123
|
-
|
|
124
118
|
/** The largest tile surface, per edge, in device pixels. An argb32 surface
|
|
125
119
|
* is `4 × size²` bytes, so 2048 is 16 MB and is already more than any
|
|
126
120
|
* pyramid justifies. */
|
|
@@ -154,33 +148,6 @@ function intersectRects(a: ScreenRect, b: ScreenRect): ScreenRect | null {
|
|
|
154
148
|
return { x, y, width: right - x, height: bottom - y };
|
|
155
149
|
}
|
|
156
150
|
|
|
157
|
-
const timers = globalThis as {
|
|
158
|
-
setTimeout?(fn: () => void, ms: number): unknown;
|
|
159
|
-
clearTimeout?(id: unknown): void;
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* The settle timer, unref'd where the runtime allows it.
|
|
164
|
-
*
|
|
165
|
-
* A map that has just been panned holds a 140 ms timer, and an unref'd one
|
|
166
|
-
* does not keep a process alive on its own — which matters for a script or
|
|
167
|
-
* a test that renders a map and expects to exit, and is the call core's
|
|
168
|
-
* caret blink makes for the same reason.
|
|
169
|
-
*/
|
|
170
|
-
function arm(tick: () => void): unknown {
|
|
171
|
-
const handle = timers.setTimeout?.(tick, SETTLE_MS) ?? null;
|
|
172
|
-
(handle as { unref?(): void } | null)?.unref?.();
|
|
173
|
-
return handle;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
function clamp(value: number, low: number, high: number): number {
|
|
177
|
-
return value < low ? low : value > high ? high : value;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
function quantize(zoom: number): number {
|
|
181
|
-
return Math.round(zoom / ZOOM_STEP) * ZOOM_STEP;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
151
|
/** A logical value put on the device grid — the same helper `src/flow/`
|
|
185
152
|
* keeps, and for the same reason: ntk's fast paths for a blit and a
|
|
186
153
|
* rounded box are gated on integral geometry, and `x * 1.5` is not always
|
|
@@ -200,34 +167,127 @@ function rectsOverlap(a: ScreenRect, b: ScreenRect): boolean {
|
|
|
200
167
|
);
|
|
201
168
|
}
|
|
202
169
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
170
|
+
/** Whether `outer` covers all of `inner`. */
|
|
171
|
+
function containsRect(outer: ScreenRect, inner: ScreenRect): boolean {
|
|
172
|
+
return (
|
|
173
|
+
outer.x <= inner.x &&
|
|
174
|
+
outer.y <= inner.y &&
|
|
175
|
+
outer.x + outer.width >= inner.x + inner.width &&
|
|
176
|
+
outer.y + outer.height >= inner.y + inner.height
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function sameSources(
|
|
181
|
+
a: readonly MapSource[],
|
|
182
|
+
b: readonly MapSource[],
|
|
183
|
+
): boolean {
|
|
184
|
+
return a.length === b.length && a.every((source, i) => source === b[i]);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** A tile of the cover that is in view, as a frame's work found it. */
|
|
188
|
+
interface VisibleTile {
|
|
189
|
+
entry: TileCoverEntry;
|
|
190
|
+
/** Where it lands, in pane-local logical pixels. */
|
|
191
|
+
box: ScreenRect;
|
|
192
|
+
cached: CachedTile;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* How long a restyle may keep the previous style up once the view moves,
|
|
197
|
+
* in milliseconds the map was free to draw in.
|
|
198
|
+
*
|
|
199
|
+
* A still view is never cut short: its redraw is a fixed amount of work,
|
|
200
|
+
* and the swap waits for all of it. A moving one can bring tiles into view
|
|
201
|
+
* as fast as the old ones are finished — a pan with pauses in it, a camera
|
|
202
|
+
* an application animates — and would never swap. So once the camera has
|
|
203
|
+
* moved the wait is bounded, and a tile not yet redrawn when it runs out
|
|
204
|
+
* shows the background until it is. Frames a gesture holds rasterization
|
|
205
|
+
* off in do not count, since nothing is drawn in them.
|
|
206
|
+
*/
|
|
207
|
+
const RESTYLE_WAIT_MS = 1500;
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* The clock {@link RESTYLE_WAIT_MS} is measured on: the raster budget's,
|
|
211
|
+
* read at the same moment, as a frame begins.
|
|
212
|
+
*
|
|
213
|
+
* One object, exported from this module though not from the package, so a
|
|
214
|
+
* test can stand in for it and move the bound's time on a step at a time,
|
|
215
|
+
* as `glideClock` in `./controller.ts` does for a glide. On the real clock a
|
|
216
|
+
* test finds the swap only once the step that made it is over, and on a
|
|
217
|
+
* loaded runner one step took long enough to put that two and a half
|
|
218
|
+
* seconds past the bound. It is not the budget's own `now`, because a test
|
|
219
|
+
* that held that one would hold the raster deadline with it: a frame would
|
|
220
|
+
* draw every tile to the end, and there would be no redraw left for the
|
|
221
|
+
* bound to cut short.
|
|
222
|
+
*/
|
|
223
|
+
export const restyleClock = {
|
|
224
|
+
now(): number {
|
|
225
|
+
return now();
|
|
226
|
+
},
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* The previous style, held on screen while a restyle draws the new one.
|
|
231
|
+
*
|
|
232
|
+
* A `mapStyle` change or `refresh()` retires every tile surface at once,
|
|
233
|
+
* and the redraw is budgeted — a dense tile is 50-140 ms against 8 ms a
|
|
234
|
+
* frame — so it takes a second or more. A tile's own double buffer keeps
|
|
235
|
+
* its old picture until its new one is done, which is right for a zoom and
|
|
236
|
+
* wrong here: the map was a patchwork of both styles for that second, under
|
|
237
|
+
* a background and labels that had already switched. So the whole previous
|
|
238
|
+
* picture — the tiles of its generation, its background, its labels —
|
|
239
|
+
* stays up until the view is redrawn, and is replaced in one frame.
|
|
240
|
+
*/
|
|
241
|
+
interface Outgoing {
|
|
242
|
+
/** The cache generation on screen. Only its pictures are composited. */
|
|
243
|
+
generation: number;
|
|
244
|
+
/** The style background the last frame before the switch painted. */
|
|
245
|
+
background: string | undefined;
|
|
246
|
+
/**
|
|
247
|
+
* The labels on screen, and what they were placed for and from — the
|
|
248
|
+
* quantized zoom, the sources, their candidates and the face — kept
|
|
249
|
+
* because the style that produced them is gone, or, after `refresh()`,
|
|
250
|
+
* was edited in place. A zoom re-places them from the candidates; a
|
|
251
|
+
* source taken off the map takes its names with it.
|
|
252
|
+
*/
|
|
253
|
+
labels: PlacedLabel[];
|
|
254
|
+
labelZoom: number;
|
|
255
|
+
labelSources: readonly MapSource[];
|
|
256
|
+
candidates: Map<MapSource, LabelCandidate[]>;
|
|
257
|
+
family: string | undefined;
|
|
258
|
+
/** The camera and the pane at the switch, and whether either has moved
|
|
259
|
+
* since — which is what makes {@link RESTYLE_WAIT_MS} apply. */
|
|
260
|
+
view: string;
|
|
261
|
+
moved: boolean;
|
|
262
|
+
/** Milliseconds the map has been free to draw since the switch, and when
|
|
263
|
+
* the last frame that was began on {@link restyleClock} — `null` when
|
|
264
|
+
* the last frame was not. */
|
|
265
|
+
waited: number;
|
|
266
|
+
lastDrawn: number | null;
|
|
267
|
+
}
|
|
213
268
|
|
|
214
269
|
export class MapViewNode extends Node {
|
|
215
270
|
private readonly _cache: TileCache;
|
|
216
271
|
private readonly _scratch = new DrawScratch();
|
|
217
|
-
private readonly _geometry = new GeometryBuffer();
|
|
218
272
|
private _shaper: LabelShaper | null = null;
|
|
219
273
|
|
|
220
274
|
/**
|
|
221
|
-
* The camera
|
|
275
|
+
* The camera and the gestures: the controller `<Map>` hands down, or this
|
|
276
|
+
* element's own when it is used bare. Both of `<Map>`'s renderers answer
|
|
277
|
+
* to one (`./controller.ts`), which is how a fallback from one to the
|
|
278
|
+
* other keeps the camera and the handle.
|
|
222
279
|
*
|
|
223
|
-
* The
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
*
|
|
280
|
+
* The camera being an object's rather than React state's is what makes a
|
|
281
|
+
* pan cost nothing but a blit: a drag step moves two numbers and claims a
|
|
282
|
+
* strip, and React is not involved at all. Routed through state instead,
|
|
283
|
+
* every pointer step would be a render, a commit and a full-pane claim,
|
|
284
|
+
* which is the shape `<Flow>` documents as "the content lags and catches
|
|
285
|
+
* up".
|
|
229
286
|
*/
|
|
230
|
-
private
|
|
287
|
+
private readonly _controller: MapController;
|
|
288
|
+
private readonly _ownsController: boolean;
|
|
289
|
+
/** This element, as its controller sees it. */
|
|
290
|
+
private readonly _view: MapView;
|
|
231
291
|
|
|
232
292
|
private _prepared: PreparedStyle | null = null;
|
|
233
293
|
private _preparedFrom: MapStyle | null = null;
|
|
@@ -236,17 +296,28 @@ export class MapViewNode extends Node {
|
|
|
236
296
|
/** The placement, and what it was computed for. */
|
|
237
297
|
private _labels: PlacedLabel[] = [];
|
|
238
298
|
private _labelKey = '';
|
|
299
|
+
/** …and what it was placed from, all of which a restyle holds on to (see
|
|
300
|
+
* {@link Outgoing}): the quantized zoom, the sources on the map, their
|
|
301
|
+
* candidates per source, and the face they were shaped in. */
|
|
302
|
+
private _labelZoom = 0;
|
|
303
|
+
private _labelSources: readonly MapSource[] = [];
|
|
304
|
+
private _labelCandidates = new Map<MapSource, LabelCandidate[]>();
|
|
305
|
+
private _shapedFamily: string | undefined;
|
|
239
306
|
/** Candidates per tile, so a pan that brings a tile back does not redo
|
|
240
307
|
* the walk over its symbol layers. */
|
|
241
308
|
private readonly _candidates = new Map<string, LabelCandidate[]>();
|
|
242
309
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
private
|
|
310
|
+
/** The previous style while a restyle draws the new one behind it, or
|
|
311
|
+
* null when what is on screen is the current generation. */
|
|
312
|
+
private _outgoing: Outgoing | null = null;
|
|
313
|
+
/** The style background the last frame painted — what a restyle keeps
|
|
314
|
+
* painting. Remembered rather than recomputed, because `refresh()`
|
|
315
|
+
* follows an edit made to the very style object it would be read from. */
|
|
316
|
+
private _paintedBackground: string | undefined;
|
|
317
|
+
/** Whether a frame has been painted: before one, a restyle has no
|
|
318
|
+
* previous picture to hold. */
|
|
319
|
+
private _painted = false;
|
|
320
|
+
|
|
250
321
|
/** Whether any tile has been rasterized in the frame being painted — the
|
|
251
322
|
* forward-progress guarantee below. */
|
|
252
323
|
private _rastered = false;
|
|
@@ -256,10 +327,36 @@ export class MapViewNode extends Node {
|
|
|
256
327
|
|
|
257
328
|
constructor(props: Record<string, unknown>, app: unknown) {
|
|
258
329
|
super(ELEMENT, props, app as ConstructorParameters<typeof Node>[2]);
|
|
259
|
-
|
|
260
|
-
//
|
|
330
|
+
const given = props.mapController as MapController | undefined;
|
|
331
|
+
// A bare element's own controller is seeded once: `defaultCamera` is
|
|
332
|
+
// read here and never again, which is what makes it a *default* rather
|
|
333
|
+
// than a second controlled prop. `<Map>` seeds the one it hands down.
|
|
261
334
|
const seed = (props.camera ?? props.defaultCamera) as MapCamera | undefined;
|
|
262
|
-
|
|
335
|
+
this._controller = given ?? new MapController(seed);
|
|
336
|
+
this._ownsController = given === undefined;
|
|
337
|
+
if (this._ownsController) {
|
|
338
|
+
this._controller.setProps(props as MapControllerProps);
|
|
339
|
+
}
|
|
340
|
+
this._view = {
|
|
341
|
+
pane: () => this._pane(),
|
|
342
|
+
scale: () => this._scale,
|
|
343
|
+
moved: (previous, next, blit) => {
|
|
344
|
+
// A new pyramid level: different tiles, different labels.
|
|
345
|
+
if (Math.floor(next.zoom) !== Math.floor(previous.zoom)) {
|
|
346
|
+
this._labelKey = '';
|
|
347
|
+
}
|
|
348
|
+
if (!blit || !this._blitPan(previous, next)) this._repaint('scroll');
|
|
349
|
+
},
|
|
350
|
+
// The gesture is over: sharpen. A wake-up, not a repaint — nothing has
|
|
351
|
+
// moved since the last frame, so what is on screen is still right;
|
|
352
|
+
// what is needed is a frame to start rasterizing in, and each tile
|
|
353
|
+
// claims its own box as it lands.
|
|
354
|
+
settled: () => this._wake('content'),
|
|
355
|
+
refresh: () => this._restyle(),
|
|
356
|
+
stats: () => this._stats,
|
|
357
|
+
focus: () => this.focus(),
|
|
358
|
+
};
|
|
359
|
+
this._controller.attach(this._view);
|
|
263
360
|
// A map is a thing you drive with the keyboard as well as the mouse:
|
|
264
361
|
// arrows pan, +/- zoom. Without this it is never focused and no key
|
|
265
362
|
// arrives.
|
|
@@ -272,9 +369,7 @@ export class MapViewNode extends Node {
|
|
|
272
369
|
(error: unknown, tile: TileId & { sourceId: string }) => void
|
|
273
370
|
>('onTileError')?.(entry.error, {
|
|
274
371
|
...entry.tile,
|
|
275
|
-
|
|
276
|
-
// from the key it was built with.
|
|
277
|
-
sourceId: entry.key.slice(0, entry.key.lastIndexOf(':')),
|
|
372
|
+
sourceId: entry.sourceId,
|
|
278
373
|
});
|
|
279
374
|
},
|
|
280
375
|
onChange: () => {
|
|
@@ -324,26 +419,11 @@ export class MapViewNode extends Node {
|
|
|
324
419
|
}
|
|
325
420
|
|
|
326
421
|
private _isDark(): boolean {
|
|
327
|
-
|
|
328
|
-
const background = theme?.background;
|
|
329
|
-
if (typeof background !== 'string') return false;
|
|
330
|
-
// The same reading `src/code-editor/`'s token themes make: luminance of
|
|
331
|
-
// the surface the widget sits on, not a flag nobody sets.
|
|
332
|
-
const hex = background.trim();
|
|
333
|
-
if (!hex.startsWith('#') || hex.length < 7) return false;
|
|
334
|
-
const r = parseInt(hex.slice(1, 3), 16);
|
|
335
|
-
const g = parseInt(hex.slice(3, 5), 16);
|
|
336
|
-
const b = parseInt(hex.slice(5, 7), 16);
|
|
337
|
-
return 0.2126 * r + 0.7152 * g + 0.0722 * b < 128;
|
|
422
|
+
return isDarkTheme(this.theme);
|
|
338
423
|
}
|
|
339
424
|
|
|
340
425
|
private _palette(): OverlayPalette {
|
|
341
|
-
|
|
342
|
-
return {
|
|
343
|
-
accent: (theme?.accent as string) ?? '#2d6cdf',
|
|
344
|
-
background: (theme?.background as string) ?? '#ffffff',
|
|
345
|
-
text: (theme?.text as string) ?? '#111111',
|
|
346
|
-
};
|
|
426
|
+
return overlayPalette(this.theme);
|
|
347
427
|
}
|
|
348
428
|
|
|
349
429
|
private _sources(): MapSource[] {
|
|
@@ -351,22 +431,12 @@ export class MapViewNode extends Node {
|
|
|
351
431
|
return given ? [...given] : [];
|
|
352
432
|
}
|
|
353
433
|
|
|
434
|
+
/** What a source is called in its requests and its errors. Not what its
|
|
435
|
+
* tiles are cached under — the cache files them under the object. */
|
|
354
436
|
private _sourceId(source: MapSource, index: number): string {
|
|
355
437
|
return source.id ?? `source-${index}`;
|
|
356
438
|
}
|
|
357
439
|
|
|
358
|
-
private _minZoom(): number {
|
|
359
|
-
return this._prop<number>('minZoom') ?? 0;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
private _maxZoom(): number {
|
|
363
|
-
return this._prop<number>('maxZoom') ?? 22;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
private _interactive(): boolean {
|
|
367
|
-
return this._prop<boolean>('interactive') !== false;
|
|
368
|
-
}
|
|
369
|
-
|
|
370
440
|
private _markers(): readonly MapMarker[] {
|
|
371
441
|
return this._prop<readonly MapMarker[]>('markers') ?? [];
|
|
372
442
|
}
|
|
@@ -396,8 +466,7 @@ export class MapViewNode extends Node {
|
|
|
396
466
|
}
|
|
397
467
|
|
|
398
468
|
camera(): MapCamera {
|
|
399
|
-
|
|
400
|
-
return given ?? this._camera;
|
|
469
|
+
return this._controller.camera();
|
|
401
470
|
}
|
|
402
471
|
|
|
403
472
|
/** The camera resolved against the pane. */
|
|
@@ -453,46 +522,6 @@ export class MapViewNode extends Node {
|
|
|
453
522
|
|
|
454
523
|
// --- camera --------------------------------------------------------------
|
|
455
524
|
|
|
456
|
-
/**
|
|
457
|
-
* Move the camera.
|
|
458
|
-
*
|
|
459
|
-
* The one place the camera changes, so the controlled/uncontrolled fork,
|
|
460
|
-
* the clamping, the notification and — the interesting part — the
|
|
461
|
-
* decision between a blit and a repaint all live together.
|
|
462
|
-
*/
|
|
463
|
-
private _applyCamera(next: MapCamera, blit = true): void {
|
|
464
|
-
const previous = this.camera();
|
|
465
|
-
const zoom = clamp(next.zoom, this._minZoom(), this._maxZoom());
|
|
466
|
-
// Latitude is clamped to what Web Mercator can represent; longitude is
|
|
467
|
-
// not, because the map wraps and a camera just past the antimeridian is
|
|
468
|
-
// a camera in the next copy of the world.
|
|
469
|
-
const camera: MapCamera = {
|
|
470
|
-
center: { lon: next.center.lon, lat: clamp(next.center.lat, -85, 85) },
|
|
471
|
-
zoom,
|
|
472
|
-
};
|
|
473
|
-
if (
|
|
474
|
-
camera.zoom === previous.zoom &&
|
|
475
|
-
camera.center.lon === previous.center.lon &&
|
|
476
|
-
camera.center.lat === previous.center.lat
|
|
477
|
-
) {
|
|
478
|
-
return;
|
|
479
|
-
}
|
|
480
|
-
if (this.props.camera === undefined) this._camera = camera;
|
|
481
|
-
this._prop<(camera: MapCamera) => void>('onCameraChange')?.(camera);
|
|
482
|
-
if (this._painting) return;
|
|
483
|
-
// Every camera move defers rasterization, not just a pointer gesture.
|
|
484
|
-
// An application animating a camera with `panBy` in a loop wants
|
|
485
|
-
// exactly what a drag wants — composites while it moves, a sharpen when
|
|
486
|
-
// it stops — and a single programmatic move only pays the settle delay,
|
|
487
|
-
// which is a seventh of a second.
|
|
488
|
-
this._touchGesture();
|
|
489
|
-
if (Math.floor(camera.zoom) !== Math.floor(previous.zoom)) {
|
|
490
|
-
// A new pyramid level: different tiles, different labels.
|
|
491
|
-
this._labelKey = '';
|
|
492
|
-
}
|
|
493
|
-
if (!blit || !this._blitPan(previous, camera)) this._repaint('scroll');
|
|
494
|
-
}
|
|
495
|
-
|
|
496
525
|
/**
|
|
497
526
|
* A pan is a scroll in every way but the bookkeeping, and react-x11#303
|
|
498
527
|
* made the bookkeeping public: `scrollContents` claims the pane, arms the
|
|
@@ -548,183 +577,128 @@ export class MapViewNode extends Node {
|
|
|
548
577
|
return true;
|
|
549
578
|
}
|
|
550
579
|
|
|
551
|
-
/**
|
|
552
|
-
*
|
|
553
|
-
|
|
554
|
-
const camera = this.camera();
|
|
555
|
-
const zoom = clamp(
|
|
556
|
-
quantize(camera.zoom + delta),
|
|
557
|
-
this._minZoom(),
|
|
558
|
-
this._maxZoom(),
|
|
559
|
-
);
|
|
560
|
-
if (zoom === camera.zoom) return;
|
|
561
|
-
const before = this._transform(camera);
|
|
562
|
-
const anchor = unprojectPoint(before, screenX, screenY);
|
|
563
|
-
const after = this._transform({ center: camera.center, zoom });
|
|
564
|
-
// Where the anchor would land at the new zoom, and how far the centre
|
|
565
|
-
// has to move so it lands where it already is.
|
|
566
|
-
const moved = projectLngLat(after, anchor);
|
|
567
|
-
const dx = (moved.x - screenX) / after.world;
|
|
568
|
-
const dy = (moved.y - screenY) / after.world;
|
|
569
|
-
this._applyCamera(
|
|
570
|
-
{
|
|
571
|
-
zoom,
|
|
572
|
-
center: unprojectPoint(
|
|
573
|
-
{
|
|
574
|
-
...after,
|
|
575
|
-
centerX: after.centerX + dx,
|
|
576
|
-
centerY: after.centerY + dy,
|
|
577
|
-
},
|
|
578
|
-
after.paneX,
|
|
579
|
-
after.paneY,
|
|
580
|
-
),
|
|
581
|
-
},
|
|
582
|
-
false,
|
|
583
|
-
);
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
/** Suspend rasterization for the length of a gesture, and arrange for it
|
|
587
|
-
* to resume. */
|
|
588
|
-
private _touchGesture(): void {
|
|
589
|
-
// Wall-clock here, not the budget clock: this is a 140 ms window, and
|
|
590
|
-
// it is compared inside a timer callback.
|
|
591
|
-
this._settleAt = Date.now() + SETTLE_MS;
|
|
592
|
-
if (this._settleTimer !== null) return;
|
|
593
|
-
const tick = (): void => {
|
|
594
|
-
this._settleTimer = null;
|
|
595
|
-
if (Date.now() < this._settleAt) {
|
|
596
|
-
this._settleTimer = arm(tick);
|
|
597
|
-
return;
|
|
598
|
-
}
|
|
599
|
-
// The gesture is over: sharpen. A wake-up, not a repaint — nothing
|
|
600
|
-
// has moved since the last frame, so what is on screen is still
|
|
601
|
-
// right; what is needed is a frame to start rasterizing in, and each
|
|
602
|
-
// tile claims its own box as it lands.
|
|
603
|
-
this._prop<(camera: MapCamera) => void>('onMoveEnd')?.(this.camera());
|
|
604
|
-
this._wake('content');
|
|
605
|
-
};
|
|
606
|
-
this._settleTimer = arm(tick);
|
|
607
|
-
}
|
|
608
|
-
|
|
580
|
+
/** A gesture in flight, or the settle window after a camera move: while
|
|
581
|
+
* it lasts nothing is rasterized, so a drag or a wheel is composites
|
|
582
|
+
* only. The window is the controller's, and one for both renderers. */
|
|
609
583
|
private get _gesturing(): boolean {
|
|
610
|
-
return this.
|
|
584
|
+
return this._controller.gesturing;
|
|
611
585
|
}
|
|
612
586
|
|
|
613
587
|
// --- the imperative surface ----------------------------------------------
|
|
588
|
+
//
|
|
589
|
+
// The controller's, for an application holding the element itself rather
|
|
590
|
+
// than `<Map>`'s handle.
|
|
614
591
|
|
|
615
592
|
getCamera(): MapCamera {
|
|
616
|
-
|
|
617
|
-
return { center: { ...camera.center }, zoom: camera.zoom };
|
|
593
|
+
return this._controller.getCamera();
|
|
618
594
|
}
|
|
619
595
|
|
|
620
596
|
setCamera(camera: Partial<MapCamera>): void {
|
|
621
|
-
|
|
622
|
-
this._applyCamera(
|
|
623
|
-
{
|
|
624
|
-
center: camera.center ?? current.center,
|
|
625
|
-
zoom: camera.zoom ?? current.zoom,
|
|
626
|
-
},
|
|
627
|
-
false,
|
|
628
|
-
);
|
|
597
|
+
this._controller.setCamera(camera);
|
|
629
598
|
}
|
|
630
599
|
|
|
631
600
|
/** Move by a distance in pane-local logical pixels. */
|
|
632
601
|
panBy(dx: number, dy: number): void {
|
|
633
|
-
|
|
634
|
-
this._applyCamera({
|
|
635
|
-
zoom: transform.zoom,
|
|
636
|
-
center: unprojectPoint(
|
|
637
|
-
transform,
|
|
638
|
-
transform.paneX + dx,
|
|
639
|
-
transform.paneY + dy,
|
|
640
|
-
),
|
|
641
|
-
});
|
|
602
|
+
this._controller.panBy(dx, dy);
|
|
642
603
|
}
|
|
643
604
|
|
|
644
605
|
zoomIn(step = 1): void {
|
|
645
|
-
|
|
646
|
-
this._zoomAbout(step, pane.width / 2, pane.height / 2);
|
|
606
|
+
this._controller.zoomIn(step);
|
|
647
607
|
}
|
|
648
608
|
|
|
649
609
|
zoomOut(step = 1): void {
|
|
650
|
-
this.
|
|
610
|
+
this._controller.zoomOut(step);
|
|
651
611
|
}
|
|
652
612
|
|
|
653
613
|
zoomTo(zoom: number): void {
|
|
654
|
-
this.
|
|
614
|
+
this._controller.zoomTo(zoom);
|
|
655
615
|
}
|
|
656
616
|
|
|
657
|
-
fitBounds(
|
|
658
|
-
bounds
|
|
659
|
-
options?: { padding?: number; maxZoom?: number },
|
|
660
|
-
): void {
|
|
661
|
-
const pane = this._pane();
|
|
662
|
-
if (pane.width <= 0 || pane.height <= 0) {
|
|
663
|
-
// Asked before layout has run — which `fitBounds` in an effect always
|
|
664
|
-
// is. Remembered and applied at the first paint that has a size.
|
|
665
|
-
this._pendingFit = { bounds, options };
|
|
666
|
-
return;
|
|
667
|
-
}
|
|
668
|
-
this._applyCamera(
|
|
669
|
-
cameraForBounds(
|
|
670
|
-
bounds,
|
|
671
|
-
{ width: pane.width, height: pane.height },
|
|
672
|
-
{
|
|
673
|
-
padding: options?.padding ?? 24,
|
|
674
|
-
tileSize: DEFAULT_TILE_SIZE,
|
|
675
|
-
minZoom: this._minZoom(),
|
|
676
|
-
maxZoom: options?.maxZoom ?? this._maxZoom(),
|
|
677
|
-
},
|
|
678
|
-
),
|
|
679
|
-
false,
|
|
680
|
-
);
|
|
617
|
+
fitBounds(bounds: LngLatBounds, options?: FitBoundsOptions): void {
|
|
618
|
+
this._controller.fitBounds(bounds, options);
|
|
681
619
|
}
|
|
682
620
|
|
|
683
|
-
fitMarkers(
|
|
684
|
-
ids
|
|
685
|
-
options?: { padding?: number; maxZoom?: number },
|
|
686
|
-
): void {
|
|
687
|
-
const wanted = ids ? new Set(ids) : null;
|
|
688
|
-
const positions: LngLat[] = [];
|
|
689
|
-
for (const marker of this._markers()) {
|
|
690
|
-
if (wanted && !wanted.has(marker.id)) continue;
|
|
691
|
-
positions.push(marker.position);
|
|
692
|
-
}
|
|
693
|
-
const bounds = boundsOf(positions);
|
|
694
|
-
if (bounds) this.fitBounds(bounds, options);
|
|
621
|
+
fitMarkers(ids?: readonly string[], options?: FitBoundsOptions): void {
|
|
622
|
+
this._controller.fitMarkers(ids, options);
|
|
695
623
|
}
|
|
696
624
|
|
|
697
625
|
getBounds(): LngLatBounds {
|
|
698
|
-
return
|
|
626
|
+
return this._controller.getBounds();
|
|
699
627
|
}
|
|
700
628
|
|
|
701
629
|
project(position: LngLat): { x: number; y: number } {
|
|
702
|
-
return
|
|
630
|
+
return this._controller.project(position);
|
|
703
631
|
}
|
|
704
632
|
|
|
705
633
|
unproject(x: number, y: number): LngLat {
|
|
706
|
-
return
|
|
634
|
+
return this._controller.unproject(x, y);
|
|
707
635
|
}
|
|
708
636
|
|
|
709
637
|
markerAt(x: number, y: number): MapMarker | null {
|
|
710
|
-
return
|
|
638
|
+
return this._controller.markerAt(x, y);
|
|
711
639
|
}
|
|
712
640
|
|
|
713
641
|
refresh(): void {
|
|
642
|
+
this._restyle();
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/**
|
|
646
|
+
* Retire every rendered tile — for a new `mapStyle`, or `refresh()` after
|
|
647
|
+
* an edit made to the style in place. One path for both, and for anything
|
|
648
|
+
* else that ever needs the map redrawn in a new look.
|
|
649
|
+
*
|
|
650
|
+
* The compiled style and the label candidates go with the tiles: both are
|
|
651
|
+
* readings of the style, and `refresh()` exists because the object was
|
|
652
|
+
* edited under them. What does not go is the picture on screen. Unless
|
|
653
|
+
* there is none yet, or `progressive` asked to watch the repaint, it is
|
|
654
|
+
* held as {@link Outgoing} until the new one is ready to replace it.
|
|
655
|
+
*
|
|
656
|
+
* True when it is held. Then nothing on screen changes yet, so the only
|
|
657
|
+
* claim is the pixel that asks for a frame to start drawing in — and
|
|
658
|
+
* anything else a commit changed has to claim its own damage.
|
|
659
|
+
*/
|
|
660
|
+
private _restyle(): boolean {
|
|
661
|
+
this._prepared = null;
|
|
662
|
+
this._preparedFrom = null;
|
|
663
|
+
this._candidates.clear();
|
|
664
|
+
if (
|
|
665
|
+
this._outgoing === null &&
|
|
666
|
+
this._painted &&
|
|
667
|
+
this._prop<boolean>('progressive') !== true
|
|
668
|
+
) {
|
|
669
|
+
this._outgoing = {
|
|
670
|
+
generation: this._cache.generation,
|
|
671
|
+
background: this._paintedBackground,
|
|
672
|
+
labels: this._labels,
|
|
673
|
+
labelZoom: this._labelZoom,
|
|
674
|
+
labelSources: this._labelSources,
|
|
675
|
+
candidates: this._labelCandidates,
|
|
676
|
+
family: this._shapedFamily,
|
|
677
|
+
view: this._viewKey(this._transform(), this._pane()),
|
|
678
|
+
moved: false,
|
|
679
|
+
waited: 0,
|
|
680
|
+
lastDrawn: null,
|
|
681
|
+
};
|
|
682
|
+
}
|
|
714
683
|
this._cache.invalidateStyle();
|
|
715
684
|
this._labelKey = '';
|
|
716
|
-
this.
|
|
685
|
+
if (this._outgoing === null) {
|
|
686
|
+
this._repaint('props');
|
|
687
|
+
return false;
|
|
688
|
+
}
|
|
689
|
+
this._wake('props');
|
|
690
|
+
return true;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/** The camera and the pane, as a string that changes when either does. */
|
|
694
|
+
private _viewKey(transform: Transform, pane: ScreenRect): string {
|
|
695
|
+
return `${transform.centerX},${transform.centerY},${transform.zoom},${pane.width}x${pane.height}`;
|
|
717
696
|
}
|
|
718
697
|
|
|
719
698
|
stats(): MapFrameStats | null {
|
|
720
699
|
return this._stats;
|
|
721
700
|
}
|
|
722
701
|
|
|
723
|
-
private _pendingFit: {
|
|
724
|
-
bounds: LngLatBounds;
|
|
725
|
-
options?: { padding?: number; maxZoom?: number };
|
|
726
|
-
} | null = null;
|
|
727
|
-
|
|
728
702
|
// --- painting ------------------------------------------------------------
|
|
729
703
|
|
|
730
704
|
/** How tall the attribution strip is, in logical pixels — 0 when there is
|
|
@@ -735,15 +709,7 @@ export class MapViewNode extends Node {
|
|
|
735
709
|
}
|
|
736
710
|
|
|
737
711
|
private _attributionText(): string {
|
|
738
|
-
|
|
739
|
-
if (given !== undefined) return given;
|
|
740
|
-
const parts: string[] = [];
|
|
741
|
-
for (const source of this._sources()) {
|
|
742
|
-
if (source.attribution && !parts.includes(source.attribution)) {
|
|
743
|
-
parts.push(source.attribution);
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
return parts.join(' · ');
|
|
712
|
+
return attributionOf(this._prop<string>('attribution'), this._sources());
|
|
747
713
|
}
|
|
748
714
|
|
|
749
715
|
/** How large a tile is rasterized, and how many surface pixels one
|
|
@@ -789,7 +755,7 @@ export class MapViewNode extends Node {
|
|
|
789
755
|
if (!this._visible() || !isMapCanvas(ctx)) return;
|
|
790
756
|
|
|
791
757
|
const started = now();
|
|
792
|
-
|
|
758
|
+
const restyleAt = restyleClock.now();
|
|
793
759
|
const scale = this._scale;
|
|
794
760
|
this._frameClip = damage
|
|
795
761
|
? {
|
|
@@ -800,13 +766,10 @@ export class MapViewNode extends Node {
|
|
|
800
766
|
}
|
|
801
767
|
: null;
|
|
802
768
|
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
this.fitBounds(bounds, options);
|
|
808
|
-
this._painting = true;
|
|
809
|
-
}
|
|
769
|
+
// A fit asked for before layout had a size — `fitBounds` in an effect —
|
|
770
|
+
// lands in the first frame that has one, and this frame draws it.
|
|
771
|
+
this._controller.beginFrame();
|
|
772
|
+
this._controller.painting = true;
|
|
810
773
|
|
|
811
774
|
const pane = this._pane();
|
|
812
775
|
const camera = this.camera();
|
|
@@ -814,6 +777,7 @@ export class MapViewNode extends Node {
|
|
|
814
777
|
const style = this._style();
|
|
815
778
|
const frame = this._cache.beginFrame();
|
|
816
779
|
const stats: MapFrameStats = {
|
|
780
|
+
renderer: 'retained',
|
|
817
781
|
rasterMs: 0,
|
|
818
782
|
drawMs: 0,
|
|
819
783
|
tiles: 0,
|
|
@@ -821,6 +785,7 @@ export class MapViewNode extends Node {
|
|
|
821
785
|
fromAncestor: 0,
|
|
822
786
|
fromDescendant: 0,
|
|
823
787
|
pending: 0,
|
|
788
|
+
restyling: false,
|
|
824
789
|
labels: 0,
|
|
825
790
|
errors: 0,
|
|
826
791
|
surfaceBytes: 0,
|
|
@@ -850,19 +815,68 @@ export class MapViewNode extends Node {
|
|
|
850
815
|
? intersectRects(box, this._deviceRect(this._frameClip))
|
|
851
816
|
: box;
|
|
852
817
|
if (!clip) {
|
|
853
|
-
this.
|
|
818
|
+
this._controller.painting = false;
|
|
819
|
+
this._controller.paintEnded();
|
|
854
820
|
this._frameClip = null;
|
|
855
821
|
return;
|
|
856
822
|
}
|
|
857
823
|
ctx.rect(clip.x, clip.y, clip.width, clip.height);
|
|
858
824
|
ctx.clip();
|
|
859
825
|
|
|
826
|
+
// Rasterization is suspended for the length of a gesture, so a drag or
|
|
827
|
+
// a wheel is composites only. `rasterBudgetMs` bounds the rest.
|
|
828
|
+
const budget = this._gesturing
|
|
829
|
+
? 0
|
|
830
|
+
: (this._prop<number>('rasterBudgetMs') ?? 8);
|
|
831
|
+
const deadline = started + budget;
|
|
832
|
+
this._rastered = false;
|
|
833
|
+
const progressive = this._prop<boolean>('progressive') === true;
|
|
834
|
+
|
|
835
|
+
// **The work first, then the picture.** Every tile the cover wants is
|
|
836
|
+
// asked for, and the ones in view are drawn as far as the budget goes —
|
|
837
|
+
// into their own surfaces, so none of it is on screen yet. Which style
|
|
838
|
+
// this frame shows is decided only after that, because it depends on
|
|
839
|
+
// what the work just finished: a restyle swaps in the frame that finds
|
|
840
|
+
// the view redrawn, and has to know before the first pixel goes down.
|
|
841
|
+
const sources = this._sources();
|
|
842
|
+
const holding = this._outgoing !== null && !progressive;
|
|
843
|
+
const views: VisibleTile[][] = [];
|
|
844
|
+
for (let i = 0; i < sources.length; i++) {
|
|
845
|
+
views.push(
|
|
846
|
+
this._workSource(
|
|
847
|
+
sources[i],
|
|
848
|
+
this._sourceId(sources[i], i),
|
|
849
|
+
transform,
|
|
850
|
+
pane,
|
|
851
|
+
style,
|
|
852
|
+
stats,
|
|
853
|
+
budget > 0,
|
|
854
|
+
deadline,
|
|
855
|
+
holding,
|
|
856
|
+
),
|
|
857
|
+
);
|
|
858
|
+
}
|
|
859
|
+
this._settleRestyle(
|
|
860
|
+
transform,
|
|
861
|
+
pane,
|
|
862
|
+
stats,
|
|
863
|
+
budget,
|
|
864
|
+
restyleAt,
|
|
865
|
+
!damage || containsRect(damage, box),
|
|
866
|
+
progressive,
|
|
867
|
+
);
|
|
868
|
+
const outgoing = this._outgoing;
|
|
869
|
+
stats.restyling = outgoing !== null;
|
|
870
|
+
|
|
860
871
|
// The style's background under everything: it is what the parts of the
|
|
861
872
|
// world with no tile yet look like, so it is most of what a map looks
|
|
862
|
-
// like while it loads.
|
|
873
|
+
// like while it loads. The previous style's while a restyle holds it.
|
|
874
|
+
const styleBackground = outgoing
|
|
875
|
+
? outgoing.background
|
|
876
|
+
: this._preparedBackground();
|
|
877
|
+
if (!outgoing) this._paintedBackground = styleBackground;
|
|
863
878
|
const background =
|
|
864
|
-
(this.style.backgroundColor as string | undefined) ??
|
|
865
|
-
this._preparedBackground();
|
|
879
|
+
(this.style.backgroundColor as string | undefined) ?? styleBackground;
|
|
866
880
|
if (background) {
|
|
867
881
|
ctx.fillStyle = background;
|
|
868
882
|
const region = this._frameClip
|
|
@@ -871,26 +885,22 @@ export class MapViewNode extends Node {
|
|
|
871
885
|
ctx.fillRect(region.x, region.y, region.width, region.height);
|
|
872
886
|
}
|
|
873
887
|
|
|
874
|
-
//
|
|
875
|
-
//
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
const
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
const sources = this._sources();
|
|
888
|
+
// Only pictures of the generation on screen are composited: the
|
|
889
|
+
// previous style's while a restyle holds it up, the current one's
|
|
890
|
+
// otherwise — so a tile last drawn before a switch is never shown in
|
|
891
|
+
// the style the map was switched away from. `progressive` shows
|
|
892
|
+
// whatever there is, which is its point.
|
|
893
|
+
const generation = progressive
|
|
894
|
+
? undefined
|
|
895
|
+
: (outgoing?.generation ?? this._cache.generation);
|
|
883
896
|
for (let i = 0; i < sources.length; i++) {
|
|
884
|
-
this.
|
|
897
|
+
this._drawSource(
|
|
885
898
|
ctx,
|
|
886
899
|
sources[i],
|
|
887
|
-
|
|
888
|
-
transform,
|
|
889
|
-
pane,
|
|
890
|
-
style,
|
|
900
|
+
views[i],
|
|
891
901
|
stats,
|
|
892
|
-
|
|
893
|
-
|
|
902
|
+
progressive,
|
|
903
|
+
generation,
|
|
894
904
|
);
|
|
895
905
|
}
|
|
896
906
|
|
|
@@ -906,7 +916,9 @@ export class MapViewNode extends Node {
|
|
|
906
916
|
stats.surfaceBytes = this._cache.surfaceBytes;
|
|
907
917
|
stats.drawMs = now() - started - stats.rasterMs;
|
|
908
918
|
this._stats = stats;
|
|
909
|
-
this.
|
|
919
|
+
this._painted = true;
|
|
920
|
+
this._controller.painting = false;
|
|
921
|
+
this._controller.paintEnded();
|
|
910
922
|
this._frameClip = null;
|
|
911
923
|
void frame;
|
|
912
924
|
this._prop<(stats: MapFrameStats) => void>('onFrame')?.(stats);
|
|
@@ -955,8 +967,82 @@ export class MapViewNode extends Node {
|
|
|
955
967
|
};
|
|
956
968
|
}
|
|
957
969
|
|
|
958
|
-
|
|
959
|
-
|
|
970
|
+
/**
|
|
971
|
+
* Whether this frame keeps showing the previous style or swaps in the new
|
|
972
|
+
* one — see {@link Outgoing}.
|
|
973
|
+
*
|
|
974
|
+
* The swap waits for every tile in view that has data to have a finished
|
|
975
|
+
* rendering in the new style, which is `pending` reaching zero: a tile
|
|
976
|
+
* still loading has nothing to draw and does not hold it, and a tile that
|
|
977
|
+
* is done waits for the rest rather than going up alone. It is made only
|
|
978
|
+
* in a frame that repaints the whole pane, because a swap inside a partial
|
|
979
|
+
* frame would leave the rest of the pane in the old style — so a frame
|
|
980
|
+
* that finds the view redrawn but was clipped to its one pixel claims the
|
|
981
|
+
* pane, and the next frame swaps. That is the one full-pane claim a
|
|
982
|
+
* restyle makes.
|
|
983
|
+
*
|
|
984
|
+
* Or, once the view has moved, when {@link RESTYLE_WAIT_MS} of drawing
|
|
985
|
+
* time has gone by, with whatever is left undrawn.
|
|
986
|
+
*/
|
|
987
|
+
private _settleRestyle(
|
|
988
|
+
transform: Transform,
|
|
989
|
+
pane: ScreenRect,
|
|
990
|
+
stats: MapFrameStats,
|
|
991
|
+
budget: number,
|
|
992
|
+
/** When this frame began, on {@link restyleClock}. */
|
|
993
|
+
began: number,
|
|
994
|
+
whole: boolean,
|
|
995
|
+
progressive: boolean,
|
|
996
|
+
): void {
|
|
997
|
+
const outgoing = this._outgoing;
|
|
998
|
+
if (!outgoing) return;
|
|
999
|
+
if (progressive) {
|
|
1000
|
+
// Turned on while a restyle was held: the opt-in holds nothing.
|
|
1001
|
+
this._swapStyle();
|
|
1002
|
+
if (!whole) this._repaint('content');
|
|
1003
|
+
return;
|
|
1004
|
+
}
|
|
1005
|
+
if (!outgoing.moved && this._viewKey(transform, pane) !== outgoing.view) {
|
|
1006
|
+
outgoing.moved = true;
|
|
1007
|
+
}
|
|
1008
|
+
// The clock runs across frames that could draw, two in a row, so the
|
|
1009
|
+
// gap a gesture made — which drew nothing — is not counted when the
|
|
1010
|
+
// next frame after it begins.
|
|
1011
|
+
if (budget > 0) {
|
|
1012
|
+
if (outgoing.lastDrawn !== null) {
|
|
1013
|
+
outgoing.waited += began - outgoing.lastDrawn;
|
|
1014
|
+
}
|
|
1015
|
+
outgoing.lastDrawn = began;
|
|
1016
|
+
} else {
|
|
1017
|
+
outgoing.lastDrawn = null;
|
|
1018
|
+
}
|
|
1019
|
+
const redrawn = stats.pending === 0;
|
|
1020
|
+
// Never by the clock in the middle of a gesture: nothing is drawn
|
|
1021
|
+
// during one, so the view would swap to holes it cannot fill until the
|
|
1022
|
+
// gesture ends.
|
|
1023
|
+
const overdue =
|
|
1024
|
+
budget > 0 && outgoing.moved && outgoing.waited >= RESTYLE_WAIT_MS;
|
|
1025
|
+
if (!redrawn && !overdue) return;
|
|
1026
|
+
if (whole) this._swapStyle();
|
|
1027
|
+
else this._repaint('content');
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
/** The new style goes on screen: every finished tile, its background and
|
|
1031
|
+
* a label placement of its own, all in the frame being painted. */
|
|
1032
|
+
private _swapStyle(): void {
|
|
1033
|
+
this._outgoing = null;
|
|
1034
|
+
this._cache.swap();
|
|
1035
|
+
this._labelKey = '';
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
/**
|
|
1039
|
+
* A frame's work, for one source: want every tile of its cover, so that
|
|
1040
|
+
* each loads and stays cached, and draw the ones in view into their own
|
|
1041
|
+
* surfaces as far as the budget goes. Returns the ones in view, for
|
|
1042
|
+
* {@link _drawSource} to composite once the frame knows which style it is
|
|
1043
|
+
* showing.
|
|
1044
|
+
*/
|
|
1045
|
+
private _workSource(
|
|
960
1046
|
source: MapSource,
|
|
961
1047
|
sourceId: string,
|
|
962
1048
|
transform: Transform,
|
|
@@ -966,8 +1052,10 @@ export class MapViewNode extends Node {
|
|
|
966
1052
|
/** False for the length of a gesture, when nothing is rasterized. */
|
|
967
1053
|
mayRaster: boolean,
|
|
968
1054
|
deadline: number,
|
|
969
|
-
|
|
970
|
-
|
|
1055
|
+
/** A restyle is holding the previous style up, so a tile that finishes
|
|
1056
|
+
* waits for the swap rather than going on screen alone. */
|
|
1057
|
+
holding: boolean,
|
|
1058
|
+
): VisibleTile[] {
|
|
971
1059
|
const p = pyramid(source);
|
|
972
1060
|
// The cover goes **deeper than the source cuts**, up to
|
|
973
1061
|
// `MAX_OVERZOOM` levels past it, and the data for those tiles comes
|
|
@@ -975,16 +1063,27 @@ export class MapViewNode extends Node {
|
|
|
975
1063
|
// overzoomed map sharp: instead of one tile rasterized onto a surface
|
|
976
1064
|
// and stretched sixty-four times, there are two hundred and fifty-six
|
|
977
1065
|
// tiles sharing one fetch, each drawn at its own natural size, with
|
|
978
|
-
// detail limited by the data rather than by a bitmap.
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
1066
|
+
// detail limited by the data rather than by a bitmap. That is vector
|
|
1067
|
+
// data. An image has no more detail than its pixels, so a raster tile
|
|
1068
|
+
// past the cut is drawn as the whole of its data tile instead — see
|
|
1069
|
+
// `_rasterSquares`.
|
|
1070
|
+
const cover = this._rasterSquares(
|
|
1071
|
+
source,
|
|
1072
|
+
sourceId,
|
|
1073
|
+
tileCover(
|
|
1074
|
+
{ ...transform, zoom: transform.zoom },
|
|
1075
|
+
{ ...p, maxZoom: p.maxZoom + MAX_OVERZOOM },
|
|
1076
|
+
COVER_PADDING,
|
|
1077
|
+
),
|
|
1078
|
+
p.maxZoom,
|
|
983
1079
|
);
|
|
984
1080
|
const zoom = transform.zoom;
|
|
985
1081
|
const styleZoom = Math.floor(zoom);
|
|
986
|
-
const
|
|
1082
|
+
const visible: VisibleTile[] = [];
|
|
987
1083
|
for (const entry of cover) {
|
|
1084
|
+
// In the window's logical pixels, the space `pane` is in: what the
|
|
1085
|
+
// overlap tests below, the claim when the tile lands and `_composite`
|
|
1086
|
+
// all take. The pane's origin goes in here and nowhere after.
|
|
988
1087
|
const box = {
|
|
989
1088
|
x: pane.x + entry.x,
|
|
990
1089
|
y: pane.y + entry.y,
|
|
@@ -994,13 +1093,14 @@ export class MapViewNode extends Node {
|
|
|
994
1093
|
// Two different questions, and conflating them was a bug worth
|
|
995
1094
|
// spelling out. **Whether to work on a tile** is about the pane: the
|
|
996
1095
|
// cover is padded, so some of it is off screen and those tiles are
|
|
997
|
-
// wanted (so they load) but never drawn.
|
|
1096
|
+
// wanted (so they load) but never drawn. They are wanted on *every*
|
|
1097
|
+
// frame, however small its damage rect, because the cache's `sweep`
|
|
1098
|
+
// cancels any load a frame did not want. **Whether to composite it**
|
|
998
1099
|
// is about this pass's damage rect, which may be far smaller —
|
|
999
1100
|
// including the deliberately tiny claim a rasterization continuation
|
|
1000
|
-
// makes, which must still let the rasterizer run.
|
|
1101
|
+
// makes, which must still let the rasterizer run. The first is asked
|
|
1102
|
+
// here, and the second in `_drawSource`.
|
|
1001
1103
|
const onScreen = rectsOverlap(box, pane);
|
|
1002
|
-
const inPass =
|
|
1003
|
-
this._frameClip === null || rectsOverlap(box, this._frameClip);
|
|
1004
1104
|
const cached = this._cache.want(
|
|
1005
1105
|
source,
|
|
1006
1106
|
sourceId,
|
|
@@ -1009,6 +1109,7 @@ export class MapViewNode extends Node {
|
|
|
1009
1109
|
subTileOf(entry.tile, p.maxZoom),
|
|
1010
1110
|
);
|
|
1011
1111
|
if (!onScreen) continue;
|
|
1112
|
+
visible.push({ entry, box, cached });
|
|
1012
1113
|
stats.tiles++;
|
|
1013
1114
|
if (cached.status === 'error') stats.errors++;
|
|
1014
1115
|
|
|
@@ -1020,6 +1121,8 @@ export class MapViewNode extends Node {
|
|
|
1020
1121
|
size,
|
|
1021
1122
|
cached.raster ? 0 : styleZoom,
|
|
1022
1123
|
(edge: number) => this._makeSurface(edge),
|
|
1124
|
+
// A raster tile is the provider's image, the same in every style.
|
|
1125
|
+
!cached.raster,
|
|
1023
1126
|
);
|
|
1024
1127
|
if (drawing && drawing.progress !== -1) {
|
|
1025
1128
|
if (cached.raster) {
|
|
@@ -1048,17 +1151,105 @@ export class MapViewNode extends Node {
|
|
|
1048
1151
|
// goes blank between them. Claim the box it occupies, because
|
|
1049
1152
|
// *that* is the pixel change this whole sequence of frames was
|
|
1050
1153
|
// for; the frames before it claimed almost nothing.
|
|
1051
|
-
|
|
1154
|
+
//
|
|
1155
|
+
// Not while a restyle holds the previous style up, though: one
|
|
1156
|
+
// tile in the new style among the rest in the old is the
|
|
1157
|
+
// patchwork the hold is there to prevent. It waits, finished, for
|
|
1158
|
+
// `_swapStyle` to put the whole view up at once. A raster tile
|
|
1159
|
+
// does not wait — it is in no style, so it cannot make one.
|
|
1160
|
+
if ((!holding || !drawing.styled) && this._cache.promote(cached)) {
|
|
1161
|
+
this._claim(box, 'content');
|
|
1162
|
+
}
|
|
1052
1163
|
}
|
|
1053
1164
|
// "Pending" means *there is work left that this map could still
|
|
1054
1165
|
// do*, and nothing weaker — because `paint` asks for another frame
|
|
1055
|
-
// while it is non-zero
|
|
1056
|
-
//
|
|
1057
|
-
//
|
|
1058
|
-
// repainting a map that cannot
|
|
1059
|
-
|
|
1166
|
+
// while it is non-zero, and a restyle swaps when it reaches zero. A
|
|
1167
|
+
// tile whose surface could not be made (a backend that has none)
|
|
1168
|
+
// never becomes drawable, and counting it would spin the frame
|
|
1169
|
+
// clock at the refresh rate forever, repainting a map that cannot
|
|
1170
|
+
// change. A finished tile waiting for a restyle's swap has nothing
|
|
1171
|
+
// left to do either.
|
|
1172
|
+
if (cached.drawing && cached.drawing.progress !== -1) stats.pending++;
|
|
1060
1173
|
}
|
|
1174
|
+
}
|
|
1175
|
+
return visible;
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
/**
|
|
1179
|
+
* The cover, with every **raster** tile past the source's depth drawn as
|
|
1180
|
+
* the whole of its data tile — once, however many of its cells there are.
|
|
1181
|
+
*
|
|
1182
|
+
* The cover goes deeper than the source cuts because that is what makes
|
|
1183
|
+
* vector data sharp there: each cell is rasterized from its data tile's
|
|
1184
|
+
* features, through the cell (`sub`, in `_rasterize`). An image has no
|
|
1185
|
+
* detail finer than its pixels and no drawing it through a cell: a raster
|
|
1186
|
+
* cell was drawn by uploading the image, all of it, so each cell showed
|
|
1187
|
+
* its data tile whole, shrunk into its own square. Past a raster source's
|
|
1188
|
+
* depth the map was a grid of miniatures of each tile — 2×2 one level
|
|
1189
|
+
* past, 4×4 two — and a 256px source is read a level deeper than the
|
|
1190
|
+
* view, so for `osmRasterSource`, which cuts at 19, that was every zoom
|
|
1191
|
+
* from 19 up.
|
|
1192
|
+
*
|
|
1193
|
+
* A raster map past its provider's depth draws the deepest images larger,
|
|
1194
|
+
* and so does this. The square is where the cover at the source's own
|
|
1195
|
+
* depth puts the data tile, and it is drawn from that tile's own surface —
|
|
1196
|
+
* the one the view at that depth draws, so crossing into overzoom uploads
|
|
1197
|
+
* nothing.
|
|
1198
|
+
*
|
|
1199
|
+
* Which kind a tile is, is known once its data is in. Until then a cell
|
|
1200
|
+
* stays a cell, and is covered from its neighbours in the pyramid like
|
|
1201
|
+
* any other hole. Asking is a `want`, which the frame's own pass repeats
|
|
1202
|
+
* for a cell kept here, and which is idempotent.
|
|
1203
|
+
*/
|
|
1204
|
+
private _rasterSquares(
|
|
1205
|
+
source: MapSource,
|
|
1206
|
+
sourceId: string,
|
|
1207
|
+
cover: TileCoverEntry[],
|
|
1208
|
+
maxZoom: number,
|
|
1209
|
+
): TileCoverEntry[] {
|
|
1210
|
+
// A cover is one level, so every entry in it is past the cut or none is.
|
|
1211
|
+
if (cover.length === 0 || cover[0].tile.z <= maxZoom) return cover;
|
|
1212
|
+
const out: TileCoverEntry[] = [];
|
|
1213
|
+
const squares = new Set<string>();
|
|
1214
|
+
for (const cell of cover) {
|
|
1215
|
+
const cached = this._cache.want(
|
|
1216
|
+
source,
|
|
1217
|
+
sourceId,
|
|
1218
|
+
cell.tile,
|
|
1219
|
+
dataTileFor(cell.tile, maxZoom),
|
|
1220
|
+
subTileOf(cell.tile, maxZoom),
|
|
1221
|
+
);
|
|
1222
|
+
if (!cached.raster) {
|
|
1223
|
+
out.push(cell);
|
|
1224
|
+
continue;
|
|
1225
|
+
}
|
|
1226
|
+
const square = dataSquareOf(cell, maxZoom);
|
|
1227
|
+
const key = `${square.worldCopy}:${tileKey(square.tile)}`;
|
|
1228
|
+
if (squares.has(key)) continue;
|
|
1229
|
+
squares.add(key);
|
|
1230
|
+
out.push(square);
|
|
1231
|
+
}
|
|
1232
|
+
return out;
|
|
1233
|
+
}
|
|
1061
1234
|
|
|
1235
|
+
/**
|
|
1236
|
+
* A frame's picture, for one source: composite each tile in view from its
|
|
1237
|
+
* own finished rendering or, for a tile with none, from a finished
|
|
1238
|
+
* ancestor or descendants. Only renderings of `generation` count, and
|
|
1239
|
+
* `undefined` counts any — which is `progressive`.
|
|
1240
|
+
*/
|
|
1241
|
+
private _drawSource(
|
|
1242
|
+
ctx: MapCanvas,
|
|
1243
|
+
source: MapSource,
|
|
1244
|
+
visible: readonly VisibleTile[],
|
|
1245
|
+
stats: MapFrameStats,
|
|
1246
|
+
progressive: boolean,
|
|
1247
|
+
generation: number | undefined,
|
|
1248
|
+
): void {
|
|
1249
|
+
const scale = this._scale;
|
|
1250
|
+
for (const { entry, box, cached } of visible) {
|
|
1251
|
+
const inPass =
|
|
1252
|
+
this._frameClip === null || rectsOverlap(box, this._frameClip);
|
|
1062
1253
|
// What is composited is `shown`, which is **finished by
|
|
1063
1254
|
// construction** — a rendering only becomes `shown` when its last
|
|
1064
1255
|
// style run is done. So a tile appears whole rather than as water,
|
|
@@ -1070,14 +1261,13 @@ export class MapViewNode extends Node {
|
|
|
1070
1261
|
// behaviour and is honest about what the renderer is doing.
|
|
1071
1262
|
const showing =
|
|
1072
1263
|
progressive && cached.drawing ? cached.drawing : cached.shown;
|
|
1073
|
-
if (showing) {
|
|
1264
|
+
if (showing && drawnFor(showing, generation)) {
|
|
1074
1265
|
if (!inPass) continue;
|
|
1075
1266
|
this._composite(
|
|
1076
1267
|
ctx,
|
|
1077
1268
|
showing.surface,
|
|
1078
1269
|
showing.size,
|
|
1079
1270
|
box,
|
|
1080
|
-
pane,
|
|
1081
1271
|
scale,
|
|
1082
1272
|
0,
|
|
1083
1273
|
0,
|
|
@@ -1086,15 +1276,10 @@ export class MapViewNode extends Node {
|
|
|
1086
1276
|
stats.ready++;
|
|
1087
1277
|
continue;
|
|
1088
1278
|
}
|
|
1089
|
-
// Nothing of this tile yet — a first load, which no buffering can
|
|
1090
|
-
// help. Borrow the ancestor that is already drawn, scaled up: that is
|
|
1091
|
-
// the difference between a map that fills in and one that flashes
|
|
1092
|
-
// empty on every zoom.
|
|
1093
|
-
if (!inPass) continue;
|
|
1094
1279
|
|
|
1095
|
-
// Nothing of this tile
|
|
1096
|
-
// help
|
|
1097
|
-
// the camera moved.
|
|
1280
|
+
// Nothing of this tile to show — a first load, which no buffering can
|
|
1281
|
+
// help, or a picture in a style the map is not showing. Two ways to
|
|
1282
|
+
// cover it, and which is available says which way the camera moved.
|
|
1098
1283
|
//
|
|
1099
1284
|
// **Zooming in**, the tile already in hand is this one's *ancestor*:
|
|
1100
1285
|
// one composite, scaled up, blurry but complete. **Zooming out**, the
|
|
@@ -1104,55 +1289,79 @@ export class MapViewNode extends Node {
|
|
|
1104
1289
|
// background — with the labels and the markers still drawn over it —
|
|
1105
1290
|
// until the coarser tile had been fetched, rasterized and composited.
|
|
1106
1291
|
//
|
|
1107
|
-
//
|
|
1108
|
-
// sharper and they are the level the user is coming *from
|
|
1109
|
-
// ancestor
|
|
1110
|
-
//
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1292
|
+
// Both, in one square: the descendants wherever there are any, because
|
|
1293
|
+
// they are sharper and they are the level the user is coming *from*,
|
|
1294
|
+
// and the ancestor in the gaps between them. The first cut chose one
|
|
1295
|
+
// for the whole square — descendants only when they tiled it, and
|
|
1296
|
+
// only when no ancestor was in hand — and a zoom out lands at the
|
|
1297
|
+
// edges of the level it left, where they never tile it: that was the
|
|
1298
|
+
// background there, sharp tiles in hand or not.
|
|
1299
|
+
//
|
|
1300
|
+
// Looked up whether or not this pass draws the tile, because the
|
|
1301
|
+
// lookup is what stamps a piece as in use: a piece covering a hole is
|
|
1302
|
+
// on screen, and an unstamped entry is the first thing eviction takes
|
|
1303
|
+
// — which, in the one-pixel frames a redraw runs in, was every piece
|
|
1304
|
+
// but the one under that pixel. A restyle keeps such pieces on screen
|
|
1305
|
+
// for as long as its redraw takes.
|
|
1306
|
+
const below = this._cache.descendantsWithSurface(
|
|
1307
|
+
source,
|
|
1308
|
+
entry.tile,
|
|
1309
|
+
generation,
|
|
1310
|
+
);
|
|
1311
|
+
const ancestor =
|
|
1312
|
+
below.gaps.length > 0
|
|
1313
|
+
? this._cache.ancestorWithSurface(
|
|
1314
|
+
source,
|
|
1315
|
+
entry.tile,
|
|
1316
|
+
undefined,
|
|
1317
|
+
generation,
|
|
1318
|
+
)
|
|
1319
|
+
: null;
|
|
1320
|
+
if (!inPass) continue;
|
|
1321
|
+
for (const piece of below.pieces) {
|
|
1322
|
+
const edge = entry.size / piece.span;
|
|
1323
|
+
this._composite(
|
|
1324
|
+
ctx,
|
|
1325
|
+
piece.value.shown!.surface,
|
|
1326
|
+
piece.value.shown!.size,
|
|
1327
|
+
{
|
|
1328
|
+
x: box.x + piece.x * edge,
|
|
1329
|
+
y: box.y + piece.y * edge,
|
|
1330
|
+
width: edge,
|
|
1331
|
+
height: edge,
|
|
1332
|
+
},
|
|
1333
|
+
scale,
|
|
1334
|
+
0,
|
|
1335
|
+
0,
|
|
1336
|
+
1,
|
|
1337
|
+
);
|
|
1338
|
+
}
|
|
1339
|
+
if (below.pieces.length > 0) stats.fromDescendant++;
|
|
1340
|
+
if (!ancestor?.shown) continue;
|
|
1341
|
+
for (const gap of below.gaps) {
|
|
1342
|
+
// The gap is a tile `log2(span)` levels under this one, and the
|
|
1343
|
+
// ancestor is `up` levels over that: the same sub-square arithmetic
|
|
1344
|
+
// as a whole tile, one level set deeper.
|
|
1345
|
+
const edge = entry.size / gap.span;
|
|
1346
|
+
const up = entry.tile.z + Math.log2(gap.span) - ancestor.tile.z;
|
|
1119
1347
|
const span = 1 << up;
|
|
1120
|
-
const fx = entry.tile.x - (ancestor.tile.x << up);
|
|
1121
|
-
const fy = entry.tile.y - (ancestor.tile.y << up);
|
|
1122
1348
|
this._composite(
|
|
1123
1349
|
ctx,
|
|
1124
1350
|
ancestor.shown.surface,
|
|
1125
1351
|
ancestor.shown.size,
|
|
1126
|
-
|
|
1127
|
-
|
|
1352
|
+
{
|
|
1353
|
+
x: box.x + gap.x * edge,
|
|
1354
|
+
y: box.y + gap.y * edge,
|
|
1355
|
+
width: edge,
|
|
1356
|
+
height: edge,
|
|
1357
|
+
},
|
|
1128
1358
|
scale,
|
|
1129
|
-
|
|
1130
|
-
|
|
1359
|
+
entry.tile.x * gap.span + gap.x - (ancestor.tile.x << up),
|
|
1360
|
+
entry.tile.y * gap.span + gap.y - (ancestor.tile.y << up),
|
|
1131
1361
|
span,
|
|
1132
1362
|
);
|
|
1133
|
-
stats.fromAncestor++;
|
|
1134
|
-
} else if (kids.length > 0) {
|
|
1135
|
-
for (const kid of kids) {
|
|
1136
|
-
const piece = entry.size / kid.span;
|
|
1137
|
-
this._composite(
|
|
1138
|
-
ctx,
|
|
1139
|
-
kid.entry.shown!.surface,
|
|
1140
|
-
kid.entry.shown!.size,
|
|
1141
|
-
{
|
|
1142
|
-
x: box.x + kid.x * piece,
|
|
1143
|
-
y: box.y + kid.y * piece,
|
|
1144
|
-
width: piece,
|
|
1145
|
-
height: piece,
|
|
1146
|
-
},
|
|
1147
|
-
pane,
|
|
1148
|
-
scale,
|
|
1149
|
-
0,
|
|
1150
|
-
0,
|
|
1151
|
-
1,
|
|
1152
|
-
);
|
|
1153
|
-
}
|
|
1154
|
-
stats.fromDescendant++;
|
|
1155
1363
|
}
|
|
1364
|
+
stats.fromAncestor++;
|
|
1156
1365
|
}
|
|
1157
1366
|
}
|
|
1158
1367
|
|
|
@@ -1303,14 +1512,23 @@ export class MapViewNode extends Node {
|
|
|
1303
1512
|
* point. Clipping the destination and moving the source rectangle to
|
|
1304
1513
|
* match keeps the scale factor `sw/dw` exactly what it was, so nothing
|
|
1305
1514
|
* about the picture changes.
|
|
1515
|
+
*
|
|
1516
|
+
* **`dest` is already in the window's space**, the one `pane` is in — a
|
|
1517
|
+
* tile's `box` is `pane.x + entry.x` — so nothing is added to it here.
|
|
1518
|
+
* The pane's origin used to be added a second time, which drew every
|
|
1519
|
+
* tile that far right of and below where the markers, labels and
|
|
1520
|
+
* overlays put the same place. A constant offset on screen is a
|
|
1521
|
+
* different distance on the ground at every zoom, so it looked like a
|
|
1522
|
+
* marker sliding across the map as it zoomed, and like nothing at all as
|
|
1523
|
+
* it panned, where the blit moves both together. Every test mounted the
|
|
1524
|
+
* map at the window's origin, where the offset is zero.
|
|
1306
1525
|
*/
|
|
1307
1526
|
private _composite(
|
|
1308
1527
|
ctx: MapCanvas,
|
|
1309
1528
|
surface: SurfaceLike,
|
|
1310
1529
|
size: number,
|
|
1311
|
-
/** Where it lands, in
|
|
1530
|
+
/** Where it lands, in the window's logical pixels. */
|
|
1312
1531
|
dest: ScreenRect,
|
|
1313
|
-
pane: ScreenRect,
|
|
1314
1532
|
scale: number,
|
|
1315
1533
|
/** Which sub-square of the surface to take, in `subSpan`ths. */
|
|
1316
1534
|
subX: number,
|
|
@@ -1318,10 +1536,10 @@ export class MapViewNode extends Node {
|
|
|
1318
1536
|
subSpan: number,
|
|
1319
1537
|
): void {
|
|
1320
1538
|
if (!ctx.drawImage || size <= 0) return;
|
|
1321
|
-
const x0 = Math.round(
|
|
1322
|
-
const y0 = Math.round(
|
|
1323
|
-
const x1 = Math.round((
|
|
1324
|
-
const y1 = Math.round((
|
|
1539
|
+
const x0 = Math.round(dest.x * scale);
|
|
1540
|
+
const y0 = Math.round(dest.y * scale);
|
|
1541
|
+
const x1 = Math.round((dest.x + dest.width) * scale);
|
|
1542
|
+
const y1 = Math.round((dest.y + dest.height) * scale);
|
|
1325
1543
|
if (x1 <= x0 || y1 <= y0) return;
|
|
1326
1544
|
|
|
1327
1545
|
// The clip is the content box — device pixels, like everything core
|
|
@@ -1368,7 +1586,11 @@ export class MapViewNode extends Node {
|
|
|
1368
1586
|
const fonts = (this.app as { fonts?: FontsLike } | undefined)?.fonts;
|
|
1369
1587
|
if (!fonts) return; // headless: nothing to shape with
|
|
1370
1588
|
const text = this.resolvedTextStyle();
|
|
1589
|
+
const outgoing = this._outgoing;
|
|
1590
|
+
// While a restyle holds the previous style up, its labels are set in
|
|
1591
|
+
// its face.
|
|
1371
1592
|
const family =
|
|
1593
|
+
outgoing?.family ??
|
|
1372
1594
|
this._prop<MapStyle>('mapStyle')?.fontFamily ??
|
|
1373
1595
|
this._defaultStyle?.fontFamily ??
|
|
1374
1596
|
text.family;
|
|
@@ -1377,41 +1599,92 @@ export class MapViewNode extends Node {
|
|
|
1377
1599
|
} else {
|
|
1378
1600
|
this._shaper.reconfigure(fonts, family, this._scale);
|
|
1379
1601
|
}
|
|
1380
|
-
const
|
|
1602
|
+
const clip = this._frameClip ? { ...this._frameClip } : null;
|
|
1603
|
+
if (outgoing) {
|
|
1604
|
+
// The labels that were on screen when the restyle began, and not the
|
|
1605
|
+
// new style's, which would go up over a map still drawn in the old
|
|
1606
|
+
// one. Re-placed only when they have to be — a placement is for one
|
|
1607
|
+
// zoom, and a source taken off the map takes its names with it — and
|
|
1608
|
+
// from what they were placed from, since the style that produced
|
|
1609
|
+
// that is being replaced, or was edited in place.
|
|
1610
|
+
const zoom = quantizeZoom(transform.zoom);
|
|
1611
|
+
const sources = this._sources();
|
|
1612
|
+
if (
|
|
1613
|
+
zoom !== outgoing.labelZoom ||
|
|
1614
|
+
!sameSources(sources, outgoing.labelSources)
|
|
1615
|
+
) {
|
|
1616
|
+
const candidates: LabelCandidate[] = [];
|
|
1617
|
+
for (const source of sources) {
|
|
1618
|
+
for (const candidate of outgoing.candidates.get(source) ?? []) {
|
|
1619
|
+
candidates.push(candidate);
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
outgoing.labels = placeLabels(
|
|
1623
|
+
candidates,
|
|
1624
|
+
transform.world,
|
|
1625
|
+
this._shaper,
|
|
1626
|
+
);
|
|
1627
|
+
outgoing.labelZoom = zoom;
|
|
1628
|
+
outgoing.labelSources = sources;
|
|
1629
|
+
}
|
|
1630
|
+
stats.labels = drawLabels(
|
|
1631
|
+
ctx,
|
|
1632
|
+
outgoing.labels,
|
|
1633
|
+
transform,
|
|
1634
|
+
pane,
|
|
1635
|
+
this._scale,
|
|
1636
|
+
clip,
|
|
1637
|
+
this._shaper,
|
|
1638
|
+
);
|
|
1639
|
+
return;
|
|
1640
|
+
}
|
|
1641
|
+
this._shapedFamily = family;
|
|
1642
|
+
const key = `${quantizeZoom(transform.zoom)}|${this._cache.generation}`;
|
|
1381
1643
|
if (key !== this._labelKey) {
|
|
1382
1644
|
this._labelKey = key;
|
|
1383
1645
|
const styleZoom = Math.floor(transform.zoom);
|
|
1384
1646
|
// Labels come from the tile the **data** came from, at the depth that
|
|
1385
1647
|
// source actually cuts — past which many renderings share one tile
|
|
1386
1648
|
// and collecting per rendering would place every label `span²` times.
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1649
|
+
//
|
|
1650
|
+
// And only from the sources on the map now. The cache keeps a
|
|
1651
|
+
// provider's tiles after it is switched away, so that switching back
|
|
1652
|
+
// is free; collecting from all of them drew the old provider's place
|
|
1653
|
+
// names over the new one's map until eviction reached them.
|
|
1654
|
+
const sources = this._sources();
|
|
1655
|
+
const wanted = new Map<MapSource, number>();
|
|
1656
|
+
for (const source of sources) {
|
|
1657
|
+
wanted.set(source, Math.min(styleZoom, pyramid(source).maxZoom));
|
|
1658
|
+
}
|
|
1392
1659
|
const candidates: LabelCandidate[] = [];
|
|
1660
|
+
const bySource = new Map<MapSource, LabelCandidate[]>();
|
|
1393
1661
|
for (const cached of this._cache.dataEntries()) {
|
|
1394
1662
|
if (cached.status !== 'ready' || !cached.vector) continue;
|
|
1395
|
-
if (
|
|
1663
|
+
if (wanted.get(cached.source) !== cached.tile.z) continue;
|
|
1396
1664
|
const at = `${cached.key}|${styleZoom}`;
|
|
1397
1665
|
let found = this._candidates.get(at);
|
|
1398
1666
|
if (!found) {
|
|
1399
|
-
found = collectLabels(
|
|
1400
|
-
cached.vector,
|
|
1401
|
-
cached.tile,
|
|
1402
|
-
style,
|
|
1403
|
-
styleZoom,
|
|
1404
|
-
this._geometry,
|
|
1405
|
-
);
|
|
1667
|
+
found = collectLabels(cached.vector, cached.tile, style, styleZoom);
|
|
1406
1668
|
// Bounded the way the shaper's cache is: a map panned across a
|
|
1407
1669
|
// continent must not turn this into a leak, and rebuilding a
|
|
1408
1670
|
// tile's candidates is one walk over its symbol layers.
|
|
1409
1671
|
if (this._candidates.size > 512) this._candidates.clear();
|
|
1410
1672
|
this._candidates.set(at, found);
|
|
1411
1673
|
}
|
|
1412
|
-
|
|
1674
|
+
let own = bySource.get(cached.source);
|
|
1675
|
+
if (!own) {
|
|
1676
|
+
own = [];
|
|
1677
|
+
bySource.set(cached.source, own);
|
|
1678
|
+
}
|
|
1679
|
+
for (const candidate of found) {
|
|
1680
|
+
candidates.push(candidate);
|
|
1681
|
+
own.push(candidate);
|
|
1682
|
+
}
|
|
1413
1683
|
}
|
|
1414
1684
|
this._labels = placeLabels(candidates, transform.world, this._shaper);
|
|
1685
|
+
this._labelZoom = quantizeZoom(transform.zoom);
|
|
1686
|
+
this._labelSources = sources;
|
|
1687
|
+
this._labelCandidates = bySource;
|
|
1415
1688
|
}
|
|
1416
1689
|
stats.labels = drawLabels(
|
|
1417
1690
|
ctx,
|
|
@@ -1419,7 +1692,7 @@ export class MapViewNode extends Node {
|
|
|
1419
1692
|
transform,
|
|
1420
1693
|
pane,
|
|
1421
1694
|
this._scale,
|
|
1422
|
-
|
|
1695
|
+
clip,
|
|
1423
1696
|
this._shaper,
|
|
1424
1697
|
);
|
|
1425
1698
|
}
|
|
@@ -1440,185 +1713,63 @@ export class MapViewNode extends Node {
|
|
|
1440
1713
|
): void {
|
|
1441
1714
|
const text = this._attributionText();
|
|
1442
1715
|
if (!text || !this._shaper) return;
|
|
1443
|
-
const shaped = this._shaper.shape(text,
|
|
1716
|
+
const shaped = this._shaper.shape(text, ATTRIBUTION_SIZE, palette.text);
|
|
1444
1717
|
if (!shaped) return;
|
|
1445
|
-
const
|
|
1446
|
-
const padding = 4;
|
|
1447
|
-
const width = shaped.width + padding * 2;
|
|
1448
|
-
const height = shaped.height + padding;
|
|
1449
|
-
const x = pane.x + pane.width - width;
|
|
1450
|
-
const y = pane.y + pane.height - height;
|
|
1718
|
+
const at = attributionLayout(shaped, pane, this._scale);
|
|
1451
1719
|
ctx.save();
|
|
1452
|
-
if (ctx.globalAlpha !== undefined) ctx.globalAlpha =
|
|
1720
|
+
if (ctx.globalAlpha !== undefined) ctx.globalAlpha = ATTRIBUTION_OPACITY;
|
|
1453
1721
|
ctx.fillStyle = palette.background;
|
|
1454
|
-
ctx.fillRect(
|
|
1455
|
-
Math.round(x * scale),
|
|
1456
|
-
Math.round(y * scale),
|
|
1457
|
-
Math.ceil(width * scale),
|
|
1458
|
-
Math.ceil(height * scale),
|
|
1459
|
-
);
|
|
1722
|
+
ctx.fillRect(at.box.x, at.box.y, at.box.width, at.box.height);
|
|
1460
1723
|
if (ctx.globalAlpha !== undefined) ctx.globalAlpha = 1;
|
|
1461
|
-
shaped.layout.draw(
|
|
1462
|
-
ctx,
|
|
1463
|
-
Math.round((x + padding) * scale),
|
|
1464
|
-
Math.round((y + padding / 2) * scale),
|
|
1465
|
-
);
|
|
1724
|
+
shaped.layout.draw(ctx, at.x, at.y);
|
|
1466
1725
|
ctx.restore();
|
|
1467
1726
|
}
|
|
1468
1727
|
|
|
1469
1728
|
// --- behaviour -----------------------------------------------------------
|
|
1470
1729
|
|
|
1471
|
-
/**
|
|
1472
|
-
*
|
|
1473
|
-
*
|
|
1474
|
-
|
|
1730
|
+
/**
|
|
1731
|
+
* An event as the controller reads it. A synthetic event's `x`/`y` are
|
|
1732
|
+
* logical and relative to the window, so only the pane's own origin has
|
|
1733
|
+
* to come off.
|
|
1734
|
+
*/
|
|
1735
|
+
private _input(ev: MouseEvent): MapPointerInput {
|
|
1475
1736
|
const pane = this._pane();
|
|
1476
|
-
return { x: ev.x - pane.x, y: ev.y - pane.y };
|
|
1477
|
-
}
|
|
1478
|
-
|
|
1479
|
-
private _pointerEvent(
|
|
1480
|
-
ev: MouseEvent,
|
|
1481
|
-
marker: MapMarker | null,
|
|
1482
|
-
): MapPointerEvent {
|
|
1483
|
-
const point = this._point(ev);
|
|
1484
1737
|
return {
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1738
|
+
x: ev.x - pane.x,
|
|
1739
|
+
y: ev.y - pane.y,
|
|
1740
|
+
button: ev.button,
|
|
1741
|
+
// The click count core puts on a press and on its release, which the
|
|
1742
|
+
// declarations do not name.
|
|
1743
|
+
detail: (ev as { detail?: number }).detail,
|
|
1744
|
+
shiftKey: ev.shiftKey,
|
|
1745
|
+
ctrlKey: ev.ctrlKey,
|
|
1746
|
+
altKey: ev.altKey,
|
|
1747
|
+
metaKey: ev.metaKey,
|
|
1494
1748
|
};
|
|
1495
1749
|
}
|
|
1496
1750
|
|
|
1497
1751
|
override defaultMouseDown(ev: MouseEvent): void {
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
point.x,
|
|
1503
|
-
point.y,
|
|
1504
|
-
);
|
|
1505
|
-
if (marker) {
|
|
1506
|
-
this._gesture = {
|
|
1507
|
-
kind: 'marker',
|
|
1508
|
-
id: marker.id,
|
|
1509
|
-
startX: point.x,
|
|
1510
|
-
startY: point.y,
|
|
1511
|
-
};
|
|
1512
|
-
ev.capturePointer?.();
|
|
1513
|
-
return;
|
|
1514
|
-
}
|
|
1515
|
-
if (!this._interactive()) {
|
|
1516
|
-
// Not a pan, but still a press: the release is what makes a click,
|
|
1517
|
-
// and an application listening for one on a frozen map should get it.
|
|
1518
|
-
this._gesture = {
|
|
1519
|
-
kind: 'pan',
|
|
1520
|
-
startX: point.x,
|
|
1521
|
-
startY: point.y,
|
|
1522
|
-
lastX: point.x,
|
|
1523
|
-
lastY: point.y,
|
|
1524
|
-
moved: false,
|
|
1525
|
-
};
|
|
1526
|
-
ev.capturePointer?.();
|
|
1527
|
-
return;
|
|
1528
|
-
}
|
|
1529
|
-
this._gesture = {
|
|
1530
|
-
kind: 'pan',
|
|
1531
|
-
startX: point.x,
|
|
1532
|
-
startY: point.y,
|
|
1533
|
-
lastX: point.x,
|
|
1534
|
-
lastY: point.y,
|
|
1535
|
-
moved: false,
|
|
1536
|
-
};
|
|
1537
|
-
this.focus();
|
|
1752
|
+
// A pan takes the focus, so the arrows work next. A press on a marker,
|
|
1753
|
+
// or on a map that does not move, is captured all the same: its release
|
|
1754
|
+
// is a click, and an application listening for one should get it.
|
|
1755
|
+
if (this._controller.pointerDown(this._input(ev)) === 'pan') this.focus();
|
|
1538
1756
|
ev.capturePointer?.();
|
|
1539
1757
|
}
|
|
1540
1758
|
|
|
1541
1759
|
override defaultMouseDrag(ev: MouseEvent): void {
|
|
1542
|
-
|
|
1543
|
-
if (!gesture || gesture.kind !== 'pan') return;
|
|
1544
|
-
if (!this._interactive()) return;
|
|
1545
|
-
const point = this._point(ev);
|
|
1546
|
-
if (
|
|
1547
|
-
!gesture.moved &&
|
|
1548
|
-
Math.abs(point.x - gesture.startX) < DRAG_THRESHOLD &&
|
|
1549
|
-
Math.abs(point.y - gesture.startY) < DRAG_THRESHOLD
|
|
1550
|
-
) {
|
|
1551
|
-
return;
|
|
1552
|
-
}
|
|
1553
|
-
gesture.moved = true;
|
|
1554
|
-
// Whole device pixels, because that is what the blit can shift — a
|
|
1555
|
-
// fractional pan would decline it every frame and repaint the pane.
|
|
1556
|
-
const scale = this._scale;
|
|
1557
|
-
const dx = Math.round((point.x - gesture.lastX) * scale) / scale;
|
|
1558
|
-
const dy = Math.round((point.y - gesture.lastY) * scale) / scale;
|
|
1559
|
-
if (dx === 0 && dy === 0) return;
|
|
1560
|
-
gesture.lastX += dx;
|
|
1561
|
-
gesture.lastY += dy;
|
|
1562
|
-
this._touchGesture();
|
|
1563
|
-
this.panBy(-dx, -dy);
|
|
1760
|
+
this._controller.pointerDrag(this._input(ev));
|
|
1564
1761
|
}
|
|
1565
1762
|
|
|
1566
1763
|
override defaultMouseUp(ev: MouseEvent): void {
|
|
1567
|
-
|
|
1568
|
-
this._gesture = null;
|
|
1569
|
-
if (!gesture) return;
|
|
1570
|
-
const point = this._point(ev);
|
|
1571
|
-
if (gesture.kind === 'marker') {
|
|
1572
|
-
const marker = this._markers().find((m) => m.id === gesture.id);
|
|
1573
|
-
if (
|
|
1574
|
-
marker &&
|
|
1575
|
-
Math.abs(point.x - gesture.startX) < DRAG_THRESHOLD &&
|
|
1576
|
-
Math.abs(point.y - gesture.startY) < DRAG_THRESHOLD
|
|
1577
|
-
) {
|
|
1578
|
-
const event = this._pointerEvent(ev, marker);
|
|
1579
|
-
this._prop<(m: MapMarker, e: MapPointerEvent) => void>(
|
|
1580
|
-
'onMarkerClick',
|
|
1581
|
-
)?.(marker, event);
|
|
1582
|
-
this._prop<(e: MapPointerEvent) => void>('onMapClick')?.(event);
|
|
1583
|
-
}
|
|
1584
|
-
return;
|
|
1585
|
-
}
|
|
1586
|
-
if (gesture.moved) {
|
|
1587
|
-
this._touchGesture();
|
|
1588
|
-
return;
|
|
1589
|
-
}
|
|
1590
|
-
this._prop<(e: MapPointerEvent) => void>('onMapClick')?.(
|
|
1591
|
-
this._pointerEvent(ev, null),
|
|
1592
|
-
);
|
|
1764
|
+
this._controller.pointerUp(this._input(ev));
|
|
1593
1765
|
}
|
|
1594
1766
|
|
|
1595
1767
|
override defaultMouseMove(ev: MouseEvent): void {
|
|
1596
|
-
|
|
1597
|
-
this._prop<(m: MapMarker | null, e: MapPointerEvent | null) => void>(
|
|
1598
|
-
'onMarkerHover',
|
|
1599
|
-
);
|
|
1600
|
-
if (!notify) return;
|
|
1601
|
-
const point = this._point(ev);
|
|
1602
|
-
const marker = markerAt(
|
|
1603
|
-
this._markers(),
|
|
1604
|
-
this._transform(),
|
|
1605
|
-
point.x,
|
|
1606
|
-
point.y,
|
|
1607
|
-
);
|
|
1608
|
-
const id = marker?.id ?? null;
|
|
1609
|
-
if (id === this._hover) return;
|
|
1610
|
-
this._hover = id;
|
|
1611
|
-
notify(marker, this._pointerEvent(ev, marker));
|
|
1768
|
+
this._controller.pointerMove(this._input(ev));
|
|
1612
1769
|
}
|
|
1613
1770
|
|
|
1614
1771
|
override defaultMouseLeave(): void {
|
|
1615
|
-
|
|
1616
|
-
this._hover = null;
|
|
1617
|
-
// No event: the pointer has left the map, so there is no position on it
|
|
1618
|
-
// to report and inventing one would be worse than saying so.
|
|
1619
|
-
this._prop<(m: MapMarker | null, e: MapPointerEvent | null) => void>(
|
|
1620
|
-
'onMarkerHover',
|
|
1621
|
-
)?.(null, null);
|
|
1772
|
+
this._controller.pointerLeave();
|
|
1622
1773
|
}
|
|
1623
1774
|
|
|
1624
1775
|
/**
|
|
@@ -1628,103 +1779,106 @@ export class MapViewNode extends Node {
|
|
|
1628
1779
|
* scroll chain never hands over.
|
|
1629
1780
|
*/
|
|
1630
1781
|
override defaultWheel(ev: WheelEvent): void {
|
|
1631
|
-
if (!this._interactive()) return;
|
|
1632
1782
|
const pane = this._pane();
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
)
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1783
|
+
const point = { x: ev.x - pane.x, y: ev.y - pane.y };
|
|
1784
|
+
// Consumed whether or not the zoom moved: a wheel over a map that moves
|
|
1785
|
+
// is never meant for whatever is behind it.
|
|
1786
|
+
// `ev.smooth` is the device: a wheel that clicks whole notches, or a
|
|
1787
|
+
// touchpad whose valuators measured the scroll. The controller eases
|
|
1788
|
+
// the one and applies the other (`MapController.wheel`).
|
|
1789
|
+
if (this._controller.wheel(point, ev.deltaY ?? 0, ev.smooth === true)) {
|
|
1790
|
+
ev.preventDefault();
|
|
1791
|
+
}
|
|
1642
1792
|
}
|
|
1643
1793
|
|
|
1644
1794
|
override defaultKeyDown(ev: KeyboardEvent): void {
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
// optional call rather than a plain one.
|
|
1648
|
-
if (!this._interactive()) {
|
|
1649
|
-
super.defaultKeyDown?.(ev);
|
|
1795
|
+
if (this._controller.keyDown(ev.keysym ?? 0, ev.shiftKey ?? false)) {
|
|
1796
|
+
ev.preventDefault();
|
|
1650
1797
|
return;
|
|
1651
1798
|
}
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
break;
|
|
1658
|
-
case 0xff53: // XK_Right
|
|
1659
|
-
this.panBy(step, 0);
|
|
1660
|
-
break;
|
|
1661
|
-
case 0xff52: // XK_Up
|
|
1662
|
-
this.panBy(0, -step);
|
|
1663
|
-
break;
|
|
1664
|
-
case 0xff54: // XK_Down
|
|
1665
|
-
this.panBy(0, step);
|
|
1666
|
-
break;
|
|
1667
|
-
case 0x002b: // XK_plus
|
|
1668
|
-
case 0x003d: // XK_equal
|
|
1669
|
-
case 0xffab: // XK_KP_Add
|
|
1670
|
-
this._zoomAbout(1, pane.width / 2, pane.height / 2);
|
|
1671
|
-
break;
|
|
1672
|
-
case 0x002d: // XK_minus
|
|
1673
|
-
case 0xffad: // XK_KP_Subtract
|
|
1674
|
-
this._zoomAbout(-1, pane.width / 2, pane.height / 2);
|
|
1675
|
-
break;
|
|
1676
|
-
default:
|
|
1677
|
-
// Everything else goes to the base class, which is what keeps the
|
|
1678
|
-
// selection keys and Space/Enter-as-a-click working.
|
|
1679
|
-
super.defaultKeyDown?.(ev);
|
|
1680
|
-
return;
|
|
1681
|
-
}
|
|
1682
|
-
ev.preventDefault();
|
|
1799
|
+
// Everything else goes to the base class, which is what keeps the
|
|
1800
|
+
// selection keys and Space/Enter-as-a-click working. `Node` declares the
|
|
1801
|
+
// default actions optional — an element that has no behaviour of its
|
|
1802
|
+
// own simply has none — so calling up is an optional call.
|
|
1803
|
+
super.defaultKeyDown?.(ev);
|
|
1683
1804
|
}
|
|
1684
1805
|
|
|
1806
|
+
/** What a screen reader meets: the markers in view, as buttons — the
|
|
1807
|
+
* controller's answer, which both renderers give. */
|
|
1808
|
+
override a11yScene(): A11ySceneItem[] {
|
|
1809
|
+
if (!this._visible()) return [];
|
|
1810
|
+
return this._controller.markerScene();
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
/** Every source this element has been handed, and how many `sources`
|
|
1814
|
+
* changes in a row have made a slot anew — see `_noticeRemade`. */
|
|
1815
|
+
private readonly _seenSources = new WeakSet<MapSource>();
|
|
1816
|
+
private _remade = 0;
|
|
1817
|
+
private _warnedRemade = false;
|
|
1818
|
+
|
|
1685
1819
|
/**
|
|
1686
|
-
*
|
|
1820
|
+
* Say so, once, when the application makes a source anew on every
|
|
1821
|
+
* render.
|
|
1687
1822
|
*
|
|
1688
|
-
*
|
|
1689
|
-
*
|
|
1690
|
-
*
|
|
1691
|
-
* camera
|
|
1692
|
-
*
|
|
1823
|
+
* Tiles are cached per source object (see `./tiles.ts`), so a source
|
|
1824
|
+
* rebuilt on each render starts from an empty cache on each render: every
|
|
1825
|
+
* tile in view is fetched again, and the map is its background until they
|
|
1826
|
+
* land. Under a controlled camera that is a render per pan step — a drag
|
|
1827
|
+
* across a blank map that fetches the whole view on every step.
|
|
1828
|
+
*
|
|
1829
|
+
* What gives it away is a slot handed a source this element has never
|
|
1830
|
+
* had, looking like the one it replaces — the same id, pyramid and
|
|
1831
|
+
* attribution — on three `sources` changes in a row. A provider switch
|
|
1832
|
+
* does that once, and a switch back to a source already shown does not
|
|
1833
|
+
* count at all. Looking alike is only ever the basis of a warning, never
|
|
1834
|
+
* of a cache key: two providers can look exactly alike.
|
|
1835
|
+
*
|
|
1836
|
+
* `process` and `console` come off `globalThis` because `src/` compiles
|
|
1837
|
+
* with `types: []`, as for the terminal's warning.
|
|
1693
1838
|
*/
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
const
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1839
|
+
private _noticeRemade(
|
|
1840
|
+
before: readonly MapSource[] | undefined,
|
|
1841
|
+
next: readonly MapSource[] | undefined,
|
|
1842
|
+
): void {
|
|
1843
|
+
if (this._warnedRemade) return;
|
|
1844
|
+
let slot = -1;
|
|
1845
|
+
if (before && next) {
|
|
1846
|
+
const length = Math.min(before.length, next.length);
|
|
1847
|
+
for (let i = 0; i < length && slot < 0; i++) {
|
|
1848
|
+
const was = before[i];
|
|
1849
|
+
const now = next[i];
|
|
1850
|
+
if (
|
|
1851
|
+
was &&
|
|
1852
|
+
now &&
|
|
1853
|
+
now !== was &&
|
|
1854
|
+
!this._seenSources.has(now) &&
|
|
1855
|
+
now.id === was.id &&
|
|
1856
|
+
now.tileSize === was.tileSize &&
|
|
1857
|
+
now.minZoom === was.minZoom &&
|
|
1858
|
+
now.maxZoom === was.maxZoom &&
|
|
1859
|
+
now.attribution === was.attribution
|
|
1860
|
+
) {
|
|
1861
|
+
slot = i;
|
|
1862
|
+
}
|
|
1709
1863
|
}
|
|
1710
|
-
items.push({
|
|
1711
|
-
id: `marker:${marker.id}`,
|
|
1712
|
-
// Device pixels in the owning window's coordinates, which is what
|
|
1713
|
-
// an a11y scene rect is — the same space as `abs`.
|
|
1714
|
-
rect: {
|
|
1715
|
-
x: (pane.x + rect.x) * scale,
|
|
1716
|
-
y: (pane.y + rect.y) * scale,
|
|
1717
|
-
width: rect.width * scale,
|
|
1718
|
-
height: rect.height * scale,
|
|
1719
|
-
},
|
|
1720
|
-
role: 'button',
|
|
1721
|
-
name:
|
|
1722
|
-
marker.title ??
|
|
1723
|
-
`${marker.position.lat.toFixed(4)}, ${marker.position.lon.toFixed(4)}`,
|
|
1724
|
-
states: { selected: marker.selected ?? false },
|
|
1725
|
-
});
|
|
1726
1864
|
}
|
|
1727
|
-
|
|
1865
|
+
for (const source of before ?? []) this._seenSources.add(source);
|
|
1866
|
+
for (const source of next ?? []) this._seenSources.add(source);
|
|
1867
|
+
this._remade = slot < 0 ? 0 : this._remade + 1;
|
|
1868
|
+
if (this._remade < 3) return;
|
|
1869
|
+
this._warnedRemade = true;
|
|
1870
|
+
const g = globalThis as {
|
|
1871
|
+
process?: { env?: Record<string, string | undefined> };
|
|
1872
|
+
console?: { warn(message: string): void };
|
|
1873
|
+
};
|
|
1874
|
+
if (g.process?.env?.NODE_ENV === 'production') return;
|
|
1875
|
+
g.console?.warn(
|
|
1876
|
+
`@react-x11/components: <Map> was handed a new sources[${slot}] on ` +
|
|
1877
|
+
'three renders in a row. Tiles are cached per source object, so ' +
|
|
1878
|
+
'each one started from an empty cache and fetched every tile in ' +
|
|
1879
|
+
'view again. If it is the same provider each time, make it once — ' +
|
|
1880
|
+
'at module scope, or with useMemo.',
|
|
1881
|
+
);
|
|
1728
1882
|
}
|
|
1729
1883
|
|
|
1730
1884
|
override applyProps(
|
|
@@ -1733,25 +1887,38 @@ export class MapViewNode extends Node {
|
|
|
1733
1887
|
): void {
|
|
1734
1888
|
const before = prev ?? this.props;
|
|
1735
1889
|
super.applyProps(next, prev);
|
|
1890
|
+
if (this._ownsController) {
|
|
1891
|
+
this._controller.setProps(this.props as MapControllerProps);
|
|
1892
|
+
}
|
|
1893
|
+
if (next.sources !== before.sources) {
|
|
1894
|
+
this._noticeRemade(
|
|
1895
|
+
before.sources as readonly MapSource[] | undefined,
|
|
1896
|
+
next.sources as readonly MapSource[] | undefined,
|
|
1897
|
+
);
|
|
1898
|
+
}
|
|
1736
1899
|
// Every one of these is in `selfDamagedProps`, so the commit claimed
|
|
1737
1900
|
// nothing for them and this is the only claim there will be.
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
this._repaint('props');
|
|
1745
|
-
return;
|
|
1746
|
-
}
|
|
1901
|
+
//
|
|
1902
|
+
// A restyle that repainted the pane covers everything else the commit
|
|
1903
|
+
// changed. One that is held claims a single pixel — nothing on screen
|
|
1904
|
+
// changes until the swap — so a camera or the markers moved in the same
|
|
1905
|
+
// commit still have to claim their own damage below.
|
|
1906
|
+
if (next.mapStyle !== before.mapStyle && !this._restyle()) return;
|
|
1747
1907
|
if (next.sources !== before.sources) {
|
|
1908
|
+
// Nothing to throw away here: tiles are filed under the source
|
|
1909
|
+
// object, so a new provider starts empty, and one switched away from
|
|
1910
|
+
// keeps the tiles it has for when it comes back — its loads still in
|
|
1911
|
+
// flight are cancelled by the next frame's `sweep`, since nothing asks
|
|
1912
|
+
// for them. Only the label placement is stale.
|
|
1748
1913
|
this._labelKey = '';
|
|
1749
1914
|
this._repaint('props');
|
|
1750
1915
|
return;
|
|
1751
1916
|
}
|
|
1752
1917
|
if (next.camera !== before.camera && next.camera !== undefined) {
|
|
1753
1918
|
const camera = next.camera as MapCamera;
|
|
1754
|
-
const previous =
|
|
1919
|
+
const previous =
|
|
1920
|
+
(before.camera as MapCamera | undefined) ??
|
|
1921
|
+
this._controller.ownCamera();
|
|
1755
1922
|
if (
|
|
1756
1923
|
camera.zoom !== previous.zoom ||
|
|
1757
1924
|
camera.center.lon !== previous.center.lon ||
|
|
@@ -1807,13 +1974,13 @@ export class MapViewNode extends Node {
|
|
|
1807
1974
|
}
|
|
1808
1975
|
|
|
1809
1976
|
override destroySubtree(): void {
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
this._settleTimer = null;
|
|
1813
|
-
}
|
|
1977
|
+
this._controller.detach(this._view);
|
|
1978
|
+
if (this._ownsController) this._controller.dispose();
|
|
1814
1979
|
this._cache.destroy();
|
|
1815
1980
|
this._candidates.clear();
|
|
1816
1981
|
this._labels = [];
|
|
1982
|
+
this._labelCandidates = new Map();
|
|
1983
|
+
this._outgoing = null;
|
|
1817
1984
|
super.destroySubtree();
|
|
1818
1985
|
}
|
|
1819
1986
|
}
|