@xincli/opentui-core 0.4.6
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 +63 -0
- package/dev/keypress-debug-renderer.ts +148 -0
- package/dev/keypress-debug.ts +43 -0
- package/dev/print-env-vars.ts +32 -0
- package/dev/test-tmux-graphics-334.sh +68 -0
- package/dev/test-tmux-theme-queries.sh +151 -0
- package/dev/thai-debug-test.ts +68 -0
- package/dev/theme-mode.ts +290 -0
- package/docs/development.md +144 -0
- package/package.json +102 -0
- package/prebuilt/aarch64-android/libopentui.so +0 -0
- package/scripts/build-android.ts +226 -0
- package/scripts/build-native-termux.sh +525 -0
- package/scripts/build.ts +530 -0
- package/scripts/dist-test.ts +312 -0
- package/scripts/package-prebuilt.ts +110 -0
- package/scripts/publish.ts +60 -0
- package/scripts/test-node-hook.mjs +16 -0
- package/scripts/test-node.ts +241 -0
- package/scripts/vendor-deps.sh +73 -0
- package/src/NativeSpanFeed.ts +375 -0
- package/src/Renderable.ts +1878 -0
- package/src/__snapshots__/buffer.test.ts.snap +28 -0
- package/src/animation/Timeline.test.ts +2709 -0
- package/src/animation/Timeline.ts +598 -0
- package/src/ansi.ts +22 -0
- package/src/audio.ts +479 -0
- package/src/benchmark/attenuation-benchmark.ts +81 -0
- package/src/benchmark/audio-playback-benchmark.ts +344 -0
- package/src/benchmark/box-draw-benchmark.ts +1042 -0
- package/src/benchmark/colormatrix-benchmark.ts +123 -0
- package/src/benchmark/gain-benchmark.ts +80 -0
- package/src/benchmark/latest-all-bench-run.json +707 -0
- package/src/benchmark/latest-async-bench-run.json +336 -0
- package/src/benchmark/latest-default-bench-run.json +657 -0
- package/src/benchmark/latest-large-bench-run.json +707 -0
- package/src/benchmark/latest-quick-bench-run.json +207 -0
- package/src/benchmark/layout-benchmark.ts +2547 -0
- package/src/benchmark/markdown-benchmark.ts +1796 -0
- package/src/benchmark/native-span-feed-async-benchmark.ts +355 -0
- package/src/benchmark/native-span-feed-benchmark.md +56 -0
- package/src/benchmark/native-span-feed-benchmark.ts +596 -0
- package/src/benchmark/native-span-feed-compare.ts +280 -0
- package/src/benchmark/render-traversal-benchmark.ts +928 -0
- package/src/benchmark/text-buffer-render-benchmark.ts +874 -0
- package/src/benchmark/text-table-benchmark.ts +948 -0
- package/src/buffer.test.ts +292 -0
- package/src/buffer.ts +564 -0
- package/src/console.test.ts +629 -0
- package/src/console.ts +1253 -0
- package/src/edit-buffer.test.ts +1852 -0
- package/src/edit-buffer.ts +417 -0
- package/src/editor-view.test.ts +1056 -0
- package/src/editor-view.ts +290 -0
- package/src/index.ts +25 -0
- package/src/lib/KeyHandler.integration.test.ts +292 -0
- package/src/lib/KeyHandler.stopPropagation.test.ts +289 -0
- package/src/lib/KeyHandler.test.ts +662 -0
- package/src/lib/KeyHandler.ts +222 -0
- package/src/lib/RGBA.test.ts +60 -0
- package/src/lib/RGBA.ts +343 -0
- package/src/lib/ascii.font.ts +330 -0
- package/src/lib/border.test.ts +83 -0
- package/src/lib/border.ts +170 -0
- package/src/lib/bunfs.test.ts +27 -0
- package/src/lib/bunfs.ts +18 -0
- package/src/lib/clipboard.test.ts +63 -0
- package/src/lib/clipboard.ts +41 -0
- package/src/lib/clock.ts +35 -0
- package/src/lib/data-paths.test.ts +133 -0
- package/src/lib/data-paths.ts +109 -0
- package/src/lib/debounce.ts +106 -0
- package/src/lib/detect-links.test.ts +98 -0
- package/src/lib/detect-links.ts +56 -0
- package/src/lib/env.test.ts +228 -0
- package/src/lib/env.ts +209 -0
- package/src/lib/extmarks-history.ts +51 -0
- package/src/lib/extmarks-multiwidth.test.ts +324 -0
- package/src/lib/extmarks.test.ts +3547 -0
- package/src/lib/extmarks.ts +861 -0
- package/src/lib/fonts/block.json +405 -0
- package/src/lib/fonts/grid.json +265 -0
- package/src/lib/fonts/huge.json +741 -0
- package/src/lib/fonts/pallet.json +314 -0
- package/src/lib/fonts/shade.json +591 -0
- package/src/lib/fonts/slick.json +321 -0
- package/src/lib/fonts/tiny.json +69 -0
- package/src/lib/hast-styled-text.ts +59 -0
- package/src/lib/index.ts +22 -0
- package/src/lib/keybinding.internal.test.ts +342 -0
- package/src/lib/keybinding.internal.ts +182 -0
- package/src/lib/objects-in-viewport.test.ts +789 -0
- package/src/lib/objects-in-viewport.ts +153 -0
- package/src/lib/output.capture.ts +58 -0
- package/src/lib/parse.keypress-kitty.protocol.test.ts +347 -0
- package/src/lib/parse.keypress-kitty.test.ts +712 -0
- package/src/lib/parse.keypress-kitty.ts +470 -0
- package/src/lib/parse.keypress.test.ts +2020 -0
- package/src/lib/parse.keypress.ts +480 -0
- package/src/lib/parse.mouse.test.ts +552 -0
- package/src/lib/parse.mouse.ts +232 -0
- package/src/lib/paste.ts +18 -0
- package/src/lib/queue.ts +65 -0
- package/src/lib/render-geometry.ts +36 -0
- package/src/lib/renderable.validations.test.ts +87 -0
- package/src/lib/renderable.validations.ts +83 -0
- package/src/lib/scroll-acceleration.ts +98 -0
- package/src/lib/selection.ts +261 -0
- package/src/lib/singleton.ts +34 -0
- package/src/lib/stdin-parser.test.ts +2439 -0
- package/src/lib/stdin-parser.ts +2007 -0
- package/src/lib/styled-text.ts +178 -0
- package/src/lib/terminal-capability-detection.test.ts +226 -0
- package/src/lib/terminal-capability-detection.ts +98 -0
- package/src/lib/terminal-palette.test.ts +954 -0
- package/src/lib/terminal-palette.ts +441 -0
- package/src/lib/tree-sitter/assets/README.md +119 -0
- package/src/lib/tree-sitter/assets/javascript/highlights.scm +205 -0
- package/src/lib/tree-sitter/assets/javascript/tree-sitter-javascript.wasm +0 -0
- package/src/lib/tree-sitter/assets/markdown/highlights.scm +150 -0
- package/src/lib/tree-sitter/assets/markdown/injections.scm +27 -0
- package/src/lib/tree-sitter/assets/markdown/tree-sitter-markdown.wasm +0 -0
- package/src/lib/tree-sitter/assets/markdown_inline/highlights.scm +115 -0
- package/src/lib/tree-sitter/assets/markdown_inline/tree-sitter-markdown_inline.wasm +0 -0
- package/src/lib/tree-sitter/assets/typescript/highlights.scm +604 -0
- package/src/lib/tree-sitter/assets/typescript/tree-sitter-typescript.wasm +0 -0
- package/src/lib/tree-sitter/assets/update.ts +360 -0
- package/src/lib/tree-sitter/assets/zig/highlights.scm +284 -0
- package/src/lib/tree-sitter/assets/zig/tree-sitter-zig.wasm +0 -0
- package/src/lib/tree-sitter/assets.d.ts +9 -0
- package/src/lib/tree-sitter/cache.test.ts +291 -0
- package/src/lib/tree-sitter/client.test.ts +1434 -0
- package/src/lib/tree-sitter/client.ts +847 -0
- package/src/lib/tree-sitter/default-parsers.ts +133 -0
- package/src/lib/tree-sitter/download-utils.ts +148 -0
- package/src/lib/tree-sitter/index.ts +75 -0
- package/src/lib/tree-sitter/parser.worker.ts +1098 -0
- package/src/lib/tree-sitter/parsers-config.ts +81 -0
- package/src/lib/tree-sitter/resolve-ft.test.ts +55 -0
- package/src/lib/tree-sitter/resolve-ft.ts +189 -0
- package/src/lib/tree-sitter/types.ts +132 -0
- package/src/lib/tree-sitter/update-assets.ts +10 -0
- package/src/lib/tree-sitter-styled-text.test.ts +1253 -0
- package/src/lib/tree-sitter-styled-text.ts +314 -0
- package/src/lib/validate-dir-name.ts +55 -0
- package/src/lib/yoga.options.test.ts +628 -0
- package/src/lib/yoga.options.ts +346 -0
- package/src/native-event-worker-repro.test.ts +48 -0
- package/src/native-event-worker-repro.worker.ts +9 -0
- package/src/native-handle.test.ts +221 -0
- package/src/platform/ffi.test.ts +516 -0
- package/src/platform/ffi.ts +653 -0
- package/src/platform/runtime.test.ts +52 -0
- package/src/platform/runtime.ts +121 -0
- package/src/platform/test.ts +45 -0
- package/src/platform/worker-handler-cleanup.fixture.ts +12 -0
- package/src/platform/worker-onmessage-startup.fixture.ts +10 -0
- package/src/platform/worker-startup.fixture.ts +9 -0
- package/src/platform/worker.node-test.ts +153 -0
- package/src/platform/worker.ts +425 -0
- package/src/plugins/core-slot.ts +579 -0
- package/src/plugins/registry.ts +402 -0
- package/src/plugins/types.ts +46 -0
- package/src/post/effects.ts +948 -0
- package/src/post/filters.ts +512 -0
- package/src/post/matrices.ts +288 -0
- package/src/renderables/ASCIIFont.ts +219 -0
- package/src/renderables/Box.test.ts +474 -0
- package/src/renderables/Box.ts +353 -0
- package/src/renderables/Code.test.ts +2388 -0
- package/src/renderables/Code.ts +555 -0
- package/src/renderables/Diff.regression.test.ts +381 -0
- package/src/renderables/Diff.test.ts +3212 -0
- package/src/renderables/Diff.ts +1278 -0
- package/src/renderables/EditBufferRenderable.test.ts +288 -0
- package/src/renderables/EditBufferRenderable.ts +1152 -0
- package/src/renderables/FrameBuffer.ts +47 -0
- package/src/renderables/Input.test.ts +1310 -0
- package/src/renderables/Input.ts +273 -0
- package/src/renderables/LineNumberRenderable.ts +725 -0
- package/src/renderables/Markdown.ts +2144 -0
- package/src/renderables/ScrollBar.ts +423 -0
- package/src/renderables/ScrollBox.ts +903 -0
- package/src/renderables/Select.test.ts +1134 -0
- package/src/renderables/Select.ts +535 -0
- package/src/renderables/Slider.test.ts +456 -0
- package/src/renderables/Slider.ts +342 -0
- package/src/renderables/TabSelect.test.ts +238 -0
- package/src/renderables/TabSelect.ts +447 -0
- package/src/renderables/Text.selection-buffer.test.ts +118 -0
- package/src/renderables/Text.test.ts +2662 -0
- package/src/renderables/Text.ts +147 -0
- package/src/renderables/TextBufferRenderable.ts +504 -0
- package/src/renderables/TextNode.test.ts +1137 -0
- package/src/renderables/TextNode.ts +379 -0
- package/src/renderables/TextTable.test.ts +1411 -0
- package/src/renderables/TextTable.ts +1434 -0
- package/src/renderables/Textarea.ts +425 -0
- package/src/renderables/TimeToFirstDraw.ts +89 -0
- package/src/renderables/__snapshots__/Code.test.ts.snap +13 -0
- package/src/renderables/__snapshots__/Diff.test.ts.snap +785 -0
- package/src/renderables/__snapshots__/Text.test.ts.snap +421 -0
- package/src/renderables/__snapshots__/TextTable.test.ts.snap +215 -0
- package/src/renderables/__tests__/Code.test.ts +55 -0
- package/src/renderables/__tests__/LineNumberRenderable.scrollbox-simple.test.ts +144 -0
- package/src/renderables/__tests__/LineNumberRenderable.scrollbox.test.ts +827 -0
- package/src/renderables/__tests__/LineNumberRenderable.test.ts +1906 -0
- package/src/renderables/__tests__/LineNumberRenderable.wrapping.test.ts +85 -0
- package/src/renderables/__tests__/Markdown.code-colors.test.ts +288 -0
- package/src/renderables/__tests__/Markdown.test.ts +4175 -0
- package/src/renderables/__tests__/MultiRenderable.selection.test.ts +87 -0
- package/src/renderables/__tests__/Textarea.buffer.test.ts +683 -0
- package/src/renderables/__tests__/Textarea.destroyed-events.test.ts +675 -0
- package/src/renderables/__tests__/Textarea.editing.test.ts +2120 -0
- package/src/renderables/__tests__/Textarea.error-handling.test.ts +35 -0
- package/src/renderables/__tests__/Textarea.events.test.ts +738 -0
- package/src/renderables/__tests__/Textarea.highlights.test.ts +590 -0
- package/src/renderables/__tests__/Textarea.keybinding.test.ts +3275 -0
- package/src/renderables/__tests__/Textarea.paste.test.ts +357 -0
- package/src/renderables/__tests__/Textarea.rendering.test.ts +1927 -0
- package/src/renderables/__tests__/Textarea.scroll.test.ts +733 -0
- package/src/renderables/__tests__/Textarea.selection.test.ts +1589 -0
- package/src/renderables/__tests__/Textarea.stress.test.ts +670 -0
- package/src/renderables/__tests__/Textarea.undo-redo.test.ts +383 -0
- package/src/renderables/__tests__/Textarea.visual-lines.test.ts +310 -0
- package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.code.test.ts.snap +221 -0
- package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.scrollbox-simple.test.ts.snap +89 -0
- package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.scrollbox.test.ts.snap +457 -0
- package/src/renderables/__tests__/__snapshots__/LineNumberRenderable.test.ts.snap +158 -0
- package/src/renderables/__tests__/__snapshots__/Textarea.rendering.test.ts.snap +387 -0
- package/src/renderables/__tests__/markdown-parser.test.ts +241 -0
- package/src/renderables/__tests__/renderable-test-utils.ts +60 -0
- package/src/renderables/composition/README.md +8 -0
- package/src/renderables/composition/VRenderable.ts +32 -0
- package/src/renderables/composition/constructs.ts +127 -0
- package/src/renderables/composition/vnode.ts +277 -0
- package/src/renderables/index.ts +23 -0
- package/src/renderables/markdown-parser.ts +79 -0
- package/src/renderer-theme-mode.ts +199 -0
- package/src/renderer.ts +4975 -0
- package/src/runtime-plugin-support-configure.ts +63 -0
- package/src/runtime-plugin-support.ts +13 -0
- package/src/runtime-plugin.ts +626 -0
- package/src/specs/terminal-startup.md +64 -0
- package/src/syntax-style.test.ts +841 -0
- package/src/syntax-style.ts +274 -0
- package/src/testing/README.md +219 -0
- package/src/testing/bun-test-node.ts +880 -0
- package/src/testing/capture-spans.test.ts +216 -0
- package/src/testing/integration.test.ts +276 -0
- package/src/testing/manual-clock.ts +117 -0
- package/src/testing/mock-keys.test.ts +1378 -0
- package/src/testing/mock-keys.ts +457 -0
- package/src/testing/mock-mouse.test.ts +218 -0
- package/src/testing/mock-mouse.ts +247 -0
- package/src/testing/mock-tree-sitter-client.ts +81 -0
- package/src/testing/spy.ts +13 -0
- package/src/testing/terminal-capabilities.ts +47 -0
- package/src/testing/test-recorder.test.ts +410 -0
- package/src/testing/test-recorder.ts +133 -0
- package/src/testing/test-renderer.ts +375 -0
- package/src/testing/test-renderer.wait.test.ts +278 -0
- package/src/testing/test-streams.ts +31 -0
- package/src/testing.ts +9 -0
- package/src/tests/__snapshots__/absolute-positioning.snapshot.test.ts.snap +481 -0
- package/src/tests/__snapshots__/renderable.snapshot.test.ts.snap +19 -0
- package/src/tests/__snapshots__/scrollbox.test.ts.snap +29 -0
- package/src/tests/absolute-positioning.snapshot.test.ts +638 -0
- package/src/tests/allocator-stats.test.ts +53 -0
- package/src/tests/audio.test.ts +303 -0
- package/src/tests/destroy-during-render.test.ts +200 -0
- package/src/tests/destroy-on-exit.fixture.ts +36 -0
- package/src/tests/destroy-on-exit.test.ts +62 -0
- package/src/tests/ffi-borrowed-pointer-callsites.test.ts +153 -0
- package/src/tests/hover-cursor.test.ts +98 -0
- package/src/tests/native-backed-measurement-lifecycle.test.ts +133 -0
- package/src/tests/native-backed-measurement-parity.test.ts +322 -0
- package/src/tests/native-span-feed-async.test.ts +173 -0
- package/src/tests/native-span-feed-close.test.ts +120 -0
- package/src/tests/native-span-feed-coverage.test.ts +227 -0
- package/src/tests/native-span-feed-edge-cases.test.ts +384 -0
- package/src/tests/native-span-feed-use-after-free.test.ts +45 -0
- package/src/tests/opacity.test.ts +123 -0
- package/src/tests/renderable.snapshot.test.ts +524 -0
- package/src/tests/renderable.test.ts +1851 -0
- package/src/tests/renderer.clock.test.ts +270 -0
- package/src/tests/renderer.console-startup.test.ts +2151 -0
- package/src/tests/renderer.control.test.ts +458 -0
- package/src/tests/renderer.core-slot-binding.test.ts +962 -0
- package/src/tests/renderer.cursor.test.ts +26 -0
- package/src/tests/renderer.custom-stdout.test.ts +1112 -0
- package/src/tests/renderer.destroy-during-render.test.ts +147 -0
- package/src/tests/renderer.focus-restore.test.ts +257 -0
- package/src/tests/renderer.focus.test.ts +329 -0
- package/src/tests/renderer.idle.test.ts +219 -0
- package/src/tests/renderer.input.test.ts +2615 -0
- package/src/tests/renderer.kitty-flags.test.ts +195 -0
- package/src/tests/renderer.lifecycle.test.ts +34 -0
- package/src/tests/renderer.mouse.test.ts +1315 -0
- package/src/tests/renderer.notifications.test.ts +27 -0
- package/src/tests/renderer.palette.test.ts +1199 -0
- package/src/tests/renderer.remote.test.ts +247 -0
- package/src/tests/renderer.render-stats.test.ts +146 -0
- package/src/tests/renderer.scrollback-surface.test.ts +474 -0
- package/src/tests/renderer.selection.test.ts +138 -0
- package/src/tests/renderer.slot-registry.test.ts +684 -0
- package/src/tests/renderer.tracker.test.ts +202 -0
- package/src/tests/renderer.useMouse.test.ts +83 -0
- package/src/tests/runtime-plugin-node-modules-cycle.fixture.ts +76 -0
- package/src/tests/runtime-plugin-node-modules-import-like-string.fixture.ts +48 -0
- package/src/tests/runtime-plugin-node-modules-mjs.fixture.ts +43 -0
- package/src/tests/runtime-plugin-node-modules-no-bare-rewrite.fixture.ts +67 -0
- package/src/tests/runtime-plugin-node-modules-package-type-cache.fixture.ts +72 -0
- package/src/tests/runtime-plugin-node-modules-runtime-specifier.fixture.ts +44 -0
- package/src/tests/runtime-plugin-node-modules-scoped-package-bare-rewrite.fixture.ts +85 -0
- package/src/tests/runtime-plugin-path-alias.fixture.ts +43 -0
- package/src/tests/runtime-plugin-resolve-roots.fixture.ts +65 -0
- package/src/tests/runtime-plugin-support-configure.fixture.ts +31 -0
- package/src/tests/runtime-plugin-support-late-addition.fixture.ts +17 -0
- package/src/tests/runtime-plugin-support.fixture.ts +11 -0
- package/src/tests/runtime-plugin-support.test.ts +54 -0
- package/src/tests/runtime-plugin-windows-file-url.fixture.ts +30 -0
- package/src/tests/runtime-plugin.fixture.ts +40 -0
- package/src/tests/runtime-plugin.test.ts +369 -0
- package/src/tests/scrollbox-culling-bug.test.ts +114 -0
- package/src/tests/scrollbox-hitgrid-resize.test.ts +136 -0
- package/src/tests/scrollbox-hitgrid.test.ts +909 -0
- package/src/tests/scrollbox.test.ts +1895 -0
- package/src/tests/wrap-resize-perf.test.ts +276 -0
- package/src/tests/yoga-api-coverage.test.ts +409 -0
- package/src/tests/yoga-callback-stress.test.ts +122 -0
- package/src/tests/yoga-ffi.test.ts +117 -0
- package/src/tests/yoga-setters.test.ts +921 -0
- package/src/tests/yoga-upstream/YGAlignBaselineTest.test.ts +124 -0
- package/src/tests/yoga-upstream/YGComputedBorderTest.test.ts +27 -0
- package/src/tests/yoga-upstream/YGComputedMarginTest.test.ts +27 -0
- package/src/tests/yoga-upstream/YGComputedPaddingTest.test.ts +27 -0
- package/src/tests/yoga-upstream/YGDirtiedTest.test.ts +157 -0
- package/src/tests/yoga-upstream/YGErrataTest.test.ts +40 -0
- package/src/tests/yoga-upstream/YGFlexBasisAuto.test.ts +23 -0
- package/src/tests/yoga-upstream/YGHasNewLayout.test.ts +81 -0
- package/src/tests/yoga-upstream/YGMeasureCacheTest.test.ts +31 -0
- package/src/tests/yoga-upstream/YGMeasureTest.test.ts +70 -0
- package/src/tests/yoga-upstream/generated/YGAbsolutePositionTest.test.ts +1864 -0
- package/src/tests/yoga-upstream/generated/YGAlignContentTest.test.ts +5829 -0
- package/src/tests/yoga-upstream/generated/YGAlignItemsTest.test.ts +2444 -0
- package/src/tests/yoga-upstream/generated/YGAlignSelfTest.test.ts +293 -0
- package/src/tests/yoga-upstream/generated/YGAndroidNewsFeed.test.ts +315 -0
- package/src/tests/yoga-upstream/generated/YGAspectRatioTest.test.ts +231 -0
- package/src/tests/yoga-upstream/generated/YGAutoTest.test.ts +347 -0
- package/src/tests/yoga-upstream/generated/YGBorderTest.test.ts +256 -0
- package/src/tests/yoga-upstream/generated/YGBoxSizingTest.test.ts +2747 -0
- package/src/tests/yoga-upstream/generated/YGDimensionTest.test.ts +129 -0
- package/src/tests/yoga-upstream/generated/YGDisplayContentsTest.test.ts +109 -0
- package/src/tests/yoga-upstream/generated/YGDisplayTest.test.ts +1097 -0
- package/src/tests/yoga-upstream/generated/YGFlexDirectionTest.test.ts +4562 -0
- package/src/tests/yoga-upstream/generated/YGFlexTest.test.ts +677 -0
- package/src/tests/yoga-upstream/generated/YGFlexWrapTest.test.ts +2071 -0
- package/src/tests/yoga-upstream/generated/YGGapTest.test.ts +3303 -0
- package/src/tests/yoga-upstream/generated/YGIntrinsicSizeTest.test.ts +582 -0
- package/src/tests/yoga-upstream/generated/YGJustifyContentTest.test.ts +2150 -0
- package/src/tests/yoga-upstream/generated/YGMarginTest.test.ts +1963 -0
- package/src/tests/yoga-upstream/generated/YGMinMaxDimensionTest.test.ts +1446 -0
- package/src/tests/yoga-upstream/generated/YGPaddingTest.test.ts +354 -0
- package/src/tests/yoga-upstream/generated/YGPercentageTest.test.ts +1760 -0
- package/src/tests/yoga-upstream/generated/YGRoundingTest.test.ts +1161 -0
- package/src/tests/yoga-upstream/generated/YGSizeOverflowTest.test.ts +207 -0
- package/src/tests/yoga-upstream/generated/YGStaticPositionTest.test.ts +6338 -0
- package/src/tests/yoga-upstream/tools/MeasureCounter.ts +54 -0
- package/src/tests/yoga-upstream/tools/utils.ts +81 -0
- package/src/text-buffer-view.test.ts +722 -0
- package/src/text-buffer-view.ts +193 -0
- package/src/text-buffer.test.ts +512 -0
- package/src/text-buffer.ts +250 -0
- package/src/types.ts +199 -0
- package/src/utils.ts +88 -0
- package/src/yoga.ts +1146 -0
- package/src/zig/ansi.zig +502 -0
- package/src/zig/audio.zig +1206 -0
- package/src/zig/bench/README.md +50 -0
- package/src/zig/bench/buffer-color-blending_bench.zig +350 -0
- package/src/zig/bench/buffer-draw-box_bench.zig +786 -0
- package/src/zig/bench/buffer-draw-text-buffer_bench.zig +894 -0
- package/src/zig/bench/edit-buffer_bench.zig +476 -0
- package/src/zig/bench/editor-view_bench.zig +169 -0
- package/src/zig/bench/native-span-feed_bench.zig +107 -0
- package/src/zig/bench/rope-markers_bench.zig +713 -0
- package/src/zig/bench/rope_bench.zig +514 -0
- package/src/zig/bench/styled-text_bench.zig +476 -0
- package/src/zig/bench/text-buffer-coords_bench.zig +362 -0
- package/src/zig/bench/text-buffer-view_bench.zig +459 -0
- package/src/zig/bench/text-chunk-graphemes_bench.zig +272 -0
- package/src/zig/bench/utf8_bench.zig +799 -0
- package/src/zig/bench-utils.zig +431 -0
- package/src/zig/bench.zig +223 -0
- package/src/zig/buffer-methods.zig +215 -0
- package/src/zig/buffer.zig +2515 -0
- package/src/zig/build.zig +700 -0
- package/src/zig/build.zig.zon +19 -0
- package/src/zig/edit-buffer.zig +900 -0
- package/src/zig/editor-view.zig +812 -0
- package/src/zig/event-bus.zig +28 -0
- package/src/zig/event-emitter.zig +65 -0
- package/src/zig/file-logger.zig +92 -0
- package/src/zig/grapheme.zig +838 -0
- package/src/zig/handles.zig +267 -0
- package/src/zig/lib.zig +2813 -0
- package/src/zig/link.zig +349 -0
- package/src/zig/logger.zig +43 -0
- package/src/zig/mem-registry.zig +126 -0
- package/src/zig/miniaudio_shim.c +21 -0
- package/src/zig/native-renderable.zig +112 -0
- package/src/zig/native-span-feed-bench-lib.zig +7 -0
- package/src/zig/native-span-feed.zig +747 -0
- package/src/zig/renderer-output.zig +677 -0
- package/src/zig/renderer.zig +2106 -0
- package/src/zig/rope.zig +1220 -0
- package/src/zig/split-scrollback.zig +88 -0
- package/src/zig/syntax-style.zig +174 -0
- package/src/zig/terminal.zig +1652 -0
- package/src/zig/test.zig +82 -0
- package/src/zig/tests/README.md +18 -0
- package/src/zig/tests/ansi_test.zig +18 -0
- package/src/zig/tests/audio_test.zig +439 -0
- package/src/zig/tests/buffer-methods_test.zig +1111 -0
- package/src/zig/tests/buffer_test.zig +2926 -0
- package/src/zig/tests/edit-buffer-history_test.zig +299 -0
- package/src/zig/tests/edit-buffer_test.zig +1688 -0
- package/src/zig/tests/editor-view_test.zig +3454 -0
- package/src/zig/tests/event-emitter_test.zig +249 -0
- package/src/zig/tests/grapheme_test.zig +1329 -0
- package/src/zig/tests/handles_test.zig +123 -0
- package/src/zig/tests/link_test.zig +220 -0
- package/src/zig/tests/mem-registry_test.zig +473 -0
- package/src/zig/tests/memory_leak_regression_test.zig +234 -0
- package/src/zig/tests/native-renderable_test.zig +16 -0
- package/src/zig/tests/native-span-feed_test.zig +1266 -0
- package/src/zig/tests/renderer_test.zig +2604 -0
- package/src/zig/tests/rope-nested_test.zig +712 -0
- package/src/zig/tests/rope_fuzz_test.zig +238 -0
- package/src/zig/tests/rope_test.zig +2362 -0
- package/src/zig/tests/segment-merge.test.zig +148 -0
- package/src/zig/tests/split-scrollback_test.zig +70 -0
- package/src/zig/tests/syntax-style_test.zig +558 -0
- package/src/zig/tests/terminal_test.zig +1489 -0
- package/src/zig/tests/test-renderer.zig +126 -0
- package/src/zig/tests/text-buffer-drawing_test.zig +3291 -0
- package/src/zig/tests/text-buffer-highlights_test.zig +706 -0
- package/src/zig/tests/text-buffer-iterators_test.zig +776 -0
- package/src/zig/tests/text-buffer-segment_test.zig +320 -0
- package/src/zig/tests/text-buffer-selection_test.zig +1056 -0
- package/src/zig/tests/text-buffer-selection_viewport_test.zig +358 -0
- package/src/zig/tests/text-buffer-view_test.zig +3672 -0
- package/src/zig/tests/text-buffer_test.zig +2191 -0
- package/src/zig/tests/unicode-width-map.zon +3909 -0
- package/src/zig/tests/utf8_no_zwj_test.zig +260 -0
- package/src/zig/tests/utf8_test.zig +4060 -0
- package/src/zig/tests/utf8_wcwidth_cursor_test.zig +267 -0
- package/src/zig/tests/utf8_wcwidth_test.zig +357 -0
- package/src/zig/tests/word-wrap-editing_test.zig +498 -0
- package/src/zig/tests/wrap-cache-perf_test.zig +113 -0
- package/src/zig/tests/yoga_test.zig +65 -0
- package/src/zig/text-buffer-iterators.zig +496 -0
- package/src/zig/text-buffer-segment.zig +403 -0
- package/src/zig/text-buffer-view.zig +1428 -0
- package/src/zig/text-buffer.zig +1272 -0
- package/src/zig/utf8.zig +1954 -0
- package/src/zig/utils.zig +12 -0
- package/src/zig/uucode-stub.zig +76 -0
- package/src/zig/vendor/miniaudio/miniaudio.h +93468 -0
- package/src/zig/yoga.zig +658 -0
- package/src/zig-structs.ts +381 -0
- package/src/zig.ts +5260 -0
- package/tsconfig.build.json +17 -0
- package/tsconfig.json +27 -0
- package/tsconfig.node-test.json +138 -0
|
@@ -0,0 +1,903 @@
|
|
|
1
|
+
import { type KeyEvent } from "../lib/index.js"
|
|
2
|
+
import { getObjectsInViewport } from "../lib/objects-in-viewport.js"
|
|
3
|
+
import { LinearScrollAccel, MacOSScrollAccel, type ScrollAcceleration } from "../lib/scroll-acceleration.js"
|
|
4
|
+
import type { BaseRenderable, Renderable, RenderableOptions } from "../Renderable.js"
|
|
5
|
+
import type { MouseEvent } from "../renderer.js"
|
|
6
|
+
import type { RenderContext } from "../types.js"
|
|
7
|
+
import { BoxRenderable, type BoxOptions } from "./Box.js"
|
|
8
|
+
import type { VNode } from "./composition/vnode.js"
|
|
9
|
+
import { ScrollBarRenderable, type ScrollBarOptions, type ScrollUnit } from "./ScrollBar.js"
|
|
10
|
+
|
|
11
|
+
class ContentRenderable extends BoxRenderable {
|
|
12
|
+
private viewport: BoxRenderable
|
|
13
|
+
private _viewportCulling: boolean
|
|
14
|
+
|
|
15
|
+
constructor(
|
|
16
|
+
ctx: RenderContext,
|
|
17
|
+
viewport: BoxRenderable,
|
|
18
|
+
viewportCulling: boolean,
|
|
19
|
+
options: RenderableOptions<BoxRenderable>,
|
|
20
|
+
) {
|
|
21
|
+
super(ctx, options)
|
|
22
|
+
this.viewport = viewport
|
|
23
|
+
this._viewportCulling = viewportCulling
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get viewportCulling(): boolean {
|
|
27
|
+
return this._viewportCulling
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
set viewportCulling(value: boolean) {
|
|
31
|
+
this._viewportCulling = value
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
protected _hasVisibleChildFilter(): boolean {
|
|
35
|
+
return this._viewportCulling
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
protected _getVisibleChildren(): number[] {
|
|
39
|
+
if (this._viewportCulling) {
|
|
40
|
+
// The viewport is in terminal coordinates, so culling has to compare it
|
|
41
|
+
// against each child's absolute screen position rather than local x/y.
|
|
42
|
+
return getObjectsInViewport(
|
|
43
|
+
{
|
|
44
|
+
x: this.viewport.screenX,
|
|
45
|
+
y: this.viewport.screenY,
|
|
46
|
+
width: this.viewport.width,
|
|
47
|
+
height: this.viewport.height,
|
|
48
|
+
},
|
|
49
|
+
this.getChildrenSortedByPrimaryAxis(),
|
|
50
|
+
this.primaryAxis,
|
|
51
|
+
0,
|
|
52
|
+
).map((child) => child.num)
|
|
53
|
+
}
|
|
54
|
+
return super._getVisibleChildren()
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface ScrollBoxOptions extends BoxOptions<ScrollBoxRenderable> {
|
|
59
|
+
rootOptions?: BoxOptions
|
|
60
|
+
wrapperOptions?: BoxOptions
|
|
61
|
+
viewportOptions?: BoxOptions
|
|
62
|
+
contentOptions?: BoxOptions
|
|
63
|
+
scrollbarOptions?: Omit<ScrollBarOptions, "orientation">
|
|
64
|
+
verticalScrollbarOptions?: Omit<ScrollBarOptions, "orientation">
|
|
65
|
+
horizontalScrollbarOptions?: Omit<ScrollBarOptions, "orientation">
|
|
66
|
+
stickyScroll?: boolean
|
|
67
|
+
stickyStart?: "bottom" | "top" | "left" | "right"
|
|
68
|
+
scrollX?: boolean
|
|
69
|
+
scrollY?: boolean
|
|
70
|
+
scrollAcceleration?: ScrollAcceleration
|
|
71
|
+
viewportCulling?: boolean
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const SCROLLBOX_PADDING_KEYS = [
|
|
75
|
+
"padding",
|
|
76
|
+
"paddingX",
|
|
77
|
+
"paddingY",
|
|
78
|
+
"paddingTop",
|
|
79
|
+
"paddingRight",
|
|
80
|
+
"paddingBottom",
|
|
81
|
+
"paddingLeft",
|
|
82
|
+
] as const
|
|
83
|
+
|
|
84
|
+
type ScrollBoxPaddingKey = (typeof SCROLLBOX_PADDING_KEYS)[number]
|
|
85
|
+
type ScrollBoxPaddingOptions = Pick<ScrollBoxOptions, ScrollBoxPaddingKey>
|
|
86
|
+
|
|
87
|
+
function pickScrollBoxPadding(options: Partial<ScrollBoxOptions> | undefined): Partial<ScrollBoxPaddingOptions> {
|
|
88
|
+
if (!options) return {}
|
|
89
|
+
|
|
90
|
+
const picked: Partial<ScrollBoxPaddingOptions> = {}
|
|
91
|
+
for (const key of SCROLLBOX_PADDING_KEYS) {
|
|
92
|
+
const value = options[key]
|
|
93
|
+
if (value !== undefined) {
|
|
94
|
+
picked[key] = value
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return picked
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function stripScrollBoxPadding<T extends object>(options: T): Omit<T, ScrollBoxPaddingKey> {
|
|
102
|
+
const sanitized = { ...options }
|
|
103
|
+
for (const key of SCROLLBOX_PADDING_KEYS) {
|
|
104
|
+
delete (sanitized as Partial<Record<ScrollBoxPaddingKey, unknown>>)[key]
|
|
105
|
+
}
|
|
106
|
+
return sanitized as Omit<T, ScrollBoxPaddingKey>
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export class ScrollBoxRenderable extends BoxRenderable {
|
|
110
|
+
static idCounter = 0
|
|
111
|
+
private internalId = 0
|
|
112
|
+
public readonly wrapper: BoxRenderable
|
|
113
|
+
public readonly viewport: BoxRenderable
|
|
114
|
+
public readonly content: ContentRenderable
|
|
115
|
+
public readonly horizontalScrollBar: ScrollBarRenderable
|
|
116
|
+
public readonly verticalScrollBar: ScrollBarRenderable
|
|
117
|
+
|
|
118
|
+
protected _focusable: boolean = true
|
|
119
|
+
private selectionListener?: () => void
|
|
120
|
+
|
|
121
|
+
private autoScrollMouseX: number = 0
|
|
122
|
+
private autoScrollMouseY: number = 0
|
|
123
|
+
private readonly autoScrollThresholdVertical = 3
|
|
124
|
+
private readonly autoScrollThresholdHorizontal = 3
|
|
125
|
+
private readonly autoScrollSpeedSlow = 6
|
|
126
|
+
private readonly autoScrollSpeedMedium = 36
|
|
127
|
+
private readonly autoScrollSpeedFast = 72
|
|
128
|
+
private isAutoScrolling: boolean = false
|
|
129
|
+
private cachedAutoScrollSpeed: number = 3
|
|
130
|
+
private autoScrollAccumulatorX: number = 0
|
|
131
|
+
private autoScrollAccumulatorY: number = 0
|
|
132
|
+
|
|
133
|
+
private scrollAccumulatorX: number = 0
|
|
134
|
+
private scrollAccumulatorY: number = 0
|
|
135
|
+
|
|
136
|
+
private _stickyScroll: boolean
|
|
137
|
+
private _stickyScrollTop: boolean = false
|
|
138
|
+
private _stickyScrollBottom: boolean = false
|
|
139
|
+
private _stickyScrollLeft: boolean = false
|
|
140
|
+
private _stickyScrollRight: boolean = false
|
|
141
|
+
private _stickyStart?: "bottom" | "top" | "left" | "right"
|
|
142
|
+
private _hasManualScroll: boolean = false
|
|
143
|
+
private _isApplyingStickyScroll: boolean = false
|
|
144
|
+
private scrollAccel: ScrollAcceleration
|
|
145
|
+
|
|
146
|
+
get stickyScroll(): boolean {
|
|
147
|
+
return this._stickyScroll
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
set stickyScroll(value: boolean) {
|
|
151
|
+
this._stickyScroll = value
|
|
152
|
+
this.updateStickyState()
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
get stickyStart(): "bottom" | "top" | "left" | "right" | undefined {
|
|
156
|
+
return this._stickyStart
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
set stickyStart(value: "bottom" | "top" | "left" | "right" | undefined) {
|
|
160
|
+
this._stickyStart = value
|
|
161
|
+
this.updateStickyState()
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
get scrollTop(): number {
|
|
165
|
+
return this.verticalScrollBar.scrollPosition
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
set scrollTop(value: number) {
|
|
169
|
+
this.verticalScrollBar.scrollPosition = value
|
|
170
|
+
this.updateStickyState()
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
get scrollLeft(): number {
|
|
174
|
+
return this.horizontalScrollBar.scrollPosition
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
set scrollLeft(value: number) {
|
|
178
|
+
this.horizontalScrollBar.scrollPosition = value
|
|
179
|
+
this.updateStickyState()
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
get scrollWidth(): number {
|
|
183
|
+
return this.horizontalScrollBar.scrollSize
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
get scrollHeight(): number {
|
|
187
|
+
return this.verticalScrollBar.scrollSize
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
private updateStickyState(): void {
|
|
191
|
+
if (!this._stickyScroll) {
|
|
192
|
+
this.syncManualScrollState()
|
|
193
|
+
return
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const maxScrollTop = Math.max(0, this.scrollHeight - this.viewport.height)
|
|
197
|
+
const maxScrollLeft = Math.max(0, this.scrollWidth - this.viewport.width)
|
|
198
|
+
|
|
199
|
+
if (this.scrollTop <= 0) {
|
|
200
|
+
this._stickyScrollTop = true
|
|
201
|
+
this._stickyScrollBottom = false
|
|
202
|
+
} else if (this.scrollTop >= maxScrollTop) {
|
|
203
|
+
this._stickyScrollTop = false
|
|
204
|
+
this._stickyScrollBottom = true
|
|
205
|
+
} else {
|
|
206
|
+
this._stickyScrollTop = false
|
|
207
|
+
this._stickyScrollBottom = false
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (this.scrollLeft <= 0) {
|
|
211
|
+
this._stickyScrollLeft = true
|
|
212
|
+
this._stickyScrollRight = false
|
|
213
|
+
} else if (this.scrollLeft >= maxScrollLeft) {
|
|
214
|
+
this._stickyScrollLeft = false
|
|
215
|
+
this._stickyScrollRight = true
|
|
216
|
+
} else {
|
|
217
|
+
this._stickyScrollLeft = false
|
|
218
|
+
this._stickyScrollRight = false
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
this.syncManualScrollState()
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
private syncManualScrollState(): void {
|
|
225
|
+
if (!this._stickyScroll) {
|
|
226
|
+
this._hasManualScroll = false
|
|
227
|
+
return
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const maxScrollTop = Math.max(0, this.scrollHeight - this.viewport.height)
|
|
231
|
+
const maxScrollLeft = Math.max(0, this.scrollWidth - this.viewport.width)
|
|
232
|
+
const hasScrollableContent = maxScrollTop > 1 || maxScrollLeft > 1
|
|
233
|
+
|
|
234
|
+
if (this._isApplyingStickyScroll) {
|
|
235
|
+
if (this._hasManualScroll && hasScrollableContent && this.isAtStickyPosition()) {
|
|
236
|
+
this._hasManualScroll = false
|
|
237
|
+
}
|
|
238
|
+
return
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
this._hasManualScroll = hasScrollableContent && !this.isAtStickyPosition()
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
private applyStickyStart(stickyStart: "bottom" | "top" | "left" | "right"): void {
|
|
245
|
+
const wasApplyingStickyScroll = this._isApplyingStickyScroll
|
|
246
|
+
this._isApplyingStickyScroll = true
|
|
247
|
+
try {
|
|
248
|
+
switch (stickyStart) {
|
|
249
|
+
case "top":
|
|
250
|
+
this._stickyScrollTop = true
|
|
251
|
+
this._stickyScrollBottom = false
|
|
252
|
+
this.verticalScrollBar.scrollPosition = 0
|
|
253
|
+
break
|
|
254
|
+
case "bottom":
|
|
255
|
+
this._stickyScrollTop = false
|
|
256
|
+
this._stickyScrollBottom = true
|
|
257
|
+
this.verticalScrollBar.scrollPosition = Math.max(0, this.scrollHeight - this.viewport.height)
|
|
258
|
+
break
|
|
259
|
+
case "left":
|
|
260
|
+
this._stickyScrollLeft = true
|
|
261
|
+
this._stickyScrollRight = false
|
|
262
|
+
this.horizontalScrollBar.scrollPosition = 0
|
|
263
|
+
break
|
|
264
|
+
case "right":
|
|
265
|
+
this._stickyScrollLeft = false
|
|
266
|
+
this._stickyScrollRight = true
|
|
267
|
+
this.horizontalScrollBar.scrollPosition = Math.max(0, this.scrollWidth - this.viewport.width)
|
|
268
|
+
break
|
|
269
|
+
}
|
|
270
|
+
} finally {
|
|
271
|
+
this._isApplyingStickyScroll = wasApplyingStickyScroll
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
constructor(ctx: RenderContext, options: ScrollBoxOptions) {
|
|
276
|
+
const {
|
|
277
|
+
wrapperOptions,
|
|
278
|
+
viewportOptions,
|
|
279
|
+
contentOptions,
|
|
280
|
+
rootOptions,
|
|
281
|
+
scrollbarOptions,
|
|
282
|
+
verticalScrollbarOptions,
|
|
283
|
+
horizontalScrollbarOptions,
|
|
284
|
+
stickyScroll = false,
|
|
285
|
+
stickyStart,
|
|
286
|
+
scrollX = false,
|
|
287
|
+
scrollY = true,
|
|
288
|
+
scrollAcceleration,
|
|
289
|
+
viewportCulling = true,
|
|
290
|
+
...rootBoxOptions
|
|
291
|
+
} = options
|
|
292
|
+
|
|
293
|
+
const forwardedContentPadding = {
|
|
294
|
+
...pickScrollBoxPadding(rootBoxOptions),
|
|
295
|
+
...pickScrollBoxPadding(rootOptions),
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const sanitizedRootBoxOptions = stripScrollBoxPadding(rootBoxOptions)
|
|
299
|
+
const sanitizedRootOptions = rootOptions ? stripScrollBoxPadding(rootOptions) : undefined
|
|
300
|
+
const mergedContentOptions = {
|
|
301
|
+
...forwardedContentPadding,
|
|
302
|
+
...contentOptions,
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Root
|
|
306
|
+
super(ctx, {
|
|
307
|
+
flexDirection: "row",
|
|
308
|
+
alignItems: "stretch",
|
|
309
|
+
...(sanitizedRootBoxOptions as BoxOptions),
|
|
310
|
+
...(sanitizedRootOptions as BoxOptions),
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
this.internalId = ScrollBoxRenderable.idCounter++
|
|
314
|
+
this._stickyScroll = stickyScroll
|
|
315
|
+
this._stickyStart = stickyStart
|
|
316
|
+
this.scrollAccel = scrollAcceleration ?? new LinearScrollAccel()
|
|
317
|
+
|
|
318
|
+
this.wrapper = new BoxRenderable(ctx, {
|
|
319
|
+
flexDirection: "column",
|
|
320
|
+
flexGrow: 1,
|
|
321
|
+
...wrapperOptions,
|
|
322
|
+
id: `scroll-box-wrapper-${this.internalId}`,
|
|
323
|
+
})
|
|
324
|
+
super.add(this.wrapper)
|
|
325
|
+
|
|
326
|
+
this.viewport = new BoxRenderable(ctx, {
|
|
327
|
+
flexDirection: "column",
|
|
328
|
+
flexGrow: 1,
|
|
329
|
+
// NOTE: Overflow scroll makes the content size behave weird
|
|
330
|
+
// when the scrollbox is in a container with max-width/height
|
|
331
|
+
overflow: "hidden",
|
|
332
|
+
onSizeChange: () => {
|
|
333
|
+
this.recalculateBarProps()
|
|
334
|
+
},
|
|
335
|
+
...viewportOptions,
|
|
336
|
+
id: `scroll-box-viewport-${this.internalId}`,
|
|
337
|
+
})
|
|
338
|
+
this.wrapper.add(this.viewport)
|
|
339
|
+
|
|
340
|
+
this.content = new ContentRenderable(ctx, this.viewport, viewportCulling, {
|
|
341
|
+
alignSelf: "flex-start",
|
|
342
|
+
flexShrink: 0,
|
|
343
|
+
...(scrollX ? { minWidth: "100%" } : { minWidth: "100%", maxWidth: "100%" }),
|
|
344
|
+
...(scrollY ? { minHeight: "100%" } : { minHeight: "100%", maxHeight: "100%" }),
|
|
345
|
+
onSizeChange: () => {
|
|
346
|
+
this.recalculateBarProps()
|
|
347
|
+
},
|
|
348
|
+
...mergedContentOptions,
|
|
349
|
+
id: `scroll-box-content-${this.internalId}`,
|
|
350
|
+
})
|
|
351
|
+
this.viewport.add(this.content)
|
|
352
|
+
|
|
353
|
+
this.verticalScrollBar = new ScrollBarRenderable(ctx, {
|
|
354
|
+
...scrollbarOptions,
|
|
355
|
+
...verticalScrollbarOptions,
|
|
356
|
+
arrowOptions: {
|
|
357
|
+
...scrollbarOptions?.arrowOptions,
|
|
358
|
+
...verticalScrollbarOptions?.arrowOptions,
|
|
359
|
+
},
|
|
360
|
+
id: `scroll-box-vertical-scrollbar-${this.internalId}`,
|
|
361
|
+
orientation: "vertical",
|
|
362
|
+
onChange: (position) => {
|
|
363
|
+
this.content.translateY = -position
|
|
364
|
+
this.updateStickyState()
|
|
365
|
+
},
|
|
366
|
+
})
|
|
367
|
+
super.add(this.verticalScrollBar)
|
|
368
|
+
|
|
369
|
+
this.horizontalScrollBar = new ScrollBarRenderable(ctx, {
|
|
370
|
+
...scrollbarOptions,
|
|
371
|
+
...horizontalScrollbarOptions,
|
|
372
|
+
arrowOptions: {
|
|
373
|
+
...scrollbarOptions?.arrowOptions,
|
|
374
|
+
...horizontalScrollbarOptions?.arrowOptions,
|
|
375
|
+
},
|
|
376
|
+
id: `scroll-box-horizontal-scrollbar-${this.internalId}`,
|
|
377
|
+
orientation: "horizontal",
|
|
378
|
+
onChange: (position) => {
|
|
379
|
+
this.content.translateX = -position
|
|
380
|
+
this.updateStickyState()
|
|
381
|
+
},
|
|
382
|
+
})
|
|
383
|
+
this.wrapper.add(this.horizontalScrollBar)
|
|
384
|
+
|
|
385
|
+
this.recalculateBarProps()
|
|
386
|
+
|
|
387
|
+
if (stickyStart && stickyScroll) {
|
|
388
|
+
this.applyStickyStart(stickyStart)
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
this.selectionListener = () => {
|
|
392
|
+
const selection = this._ctx.getSelection()
|
|
393
|
+
if (!selection || !selection.isDragging) {
|
|
394
|
+
this.stopAutoScroll()
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
this._ctx.on("selection", this.selectionListener)
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
protected onUpdate(deltaTime: number): void {
|
|
401
|
+
this.handleAutoScroll(deltaTime)
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
public scrollBy(delta: number | { x: number; y: number }, unit: ScrollUnit = "absolute"): void {
|
|
405
|
+
if (typeof delta === "number") {
|
|
406
|
+
this.verticalScrollBar.scrollBy(delta, unit)
|
|
407
|
+
} else {
|
|
408
|
+
this.verticalScrollBar.scrollBy(delta.y, unit)
|
|
409
|
+
this.horizontalScrollBar.scrollBy(delta.x, unit)
|
|
410
|
+
}
|
|
411
|
+
// Note: scrollBy doesn't need to set _hasManualScroll here because the scrollbar
|
|
412
|
+
// change will trigger the scrollTop setter which handles it
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
public scrollChildIntoView(childId: string): void {
|
|
416
|
+
const child = this.content.findDescendantById(childId)
|
|
417
|
+
if (!child) return
|
|
418
|
+
|
|
419
|
+
const getNearestDelta = (
|
|
420
|
+
elementStart: number,
|
|
421
|
+
elementEnd: number,
|
|
422
|
+
viewportStart: number,
|
|
423
|
+
viewportEnd: number,
|
|
424
|
+
): number => {
|
|
425
|
+
const elementSize = elementEnd - elementStart
|
|
426
|
+
const viewportSize = viewportEnd - viewportStart
|
|
427
|
+
const elementStartOutside = elementStart < viewportStart
|
|
428
|
+
const elementEndOutside = elementEnd > viewportEnd
|
|
429
|
+
|
|
430
|
+
if (elementStartOutside && elementEndOutside) {
|
|
431
|
+
return 0
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
if ((elementStartOutside && elementSize < viewportSize) || (elementEndOutside && elementSize > viewportSize)) {
|
|
435
|
+
return elementStart - viewportStart
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
if ((elementStartOutside && elementSize > viewportSize) || (elementEndOutside && elementSize < viewportSize)) {
|
|
439
|
+
return elementEnd - viewportEnd
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
return 0
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const childTop = child.y
|
|
446
|
+
const childBottom = child.y + child.height
|
|
447
|
+
const viewportTop = this.viewport.y
|
|
448
|
+
const viewportBottom = this.viewport.y + this.viewport.height
|
|
449
|
+
|
|
450
|
+
const dy = getNearestDelta(childTop, childBottom, viewportTop, viewportBottom)
|
|
451
|
+
|
|
452
|
+
const childLeft = child.x
|
|
453
|
+
const childRight = child.x + child.width
|
|
454
|
+
const viewportLeft = this.viewport.x
|
|
455
|
+
const viewportRight = this.viewport.x + this.viewport.width
|
|
456
|
+
|
|
457
|
+
const dx = getNearestDelta(childLeft, childRight, viewportLeft, viewportRight)
|
|
458
|
+
|
|
459
|
+
if (dx !== 0 || dy !== 0) {
|
|
460
|
+
this.scrollBy({ x: dx, y: dy })
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
public scrollTo(position: number | { x: number; y: number }): void {
|
|
465
|
+
if (typeof position === "number") {
|
|
466
|
+
this.scrollTop = position
|
|
467
|
+
} else {
|
|
468
|
+
this.scrollTop = position.y
|
|
469
|
+
this.scrollLeft = position.x
|
|
470
|
+
}
|
|
471
|
+
// Note: scrollTo doesn't need to set _hasManualScroll here because
|
|
472
|
+
// the scrollTop/scrollLeft setters handle it
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
private isAtStickyPosition(): boolean {
|
|
476
|
+
if (!this._stickyScroll || !this._stickyStart) {
|
|
477
|
+
return false
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const maxScrollTop = Math.max(0, this.scrollHeight - this.viewport.height)
|
|
481
|
+
const maxScrollLeft = Math.max(0, this.scrollWidth - this.viewport.width)
|
|
482
|
+
|
|
483
|
+
switch (this._stickyStart) {
|
|
484
|
+
case "top":
|
|
485
|
+
return this.scrollTop === 0
|
|
486
|
+
case "bottom":
|
|
487
|
+
return this.scrollTop >= maxScrollTop
|
|
488
|
+
case "left":
|
|
489
|
+
return this.scrollLeft === 0
|
|
490
|
+
case "right":
|
|
491
|
+
return this.scrollLeft >= maxScrollLeft
|
|
492
|
+
default:
|
|
493
|
+
return false
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
private isAtStickyReengagePoint(
|
|
498
|
+
stickyStart: "bottom" | "top" | "left" | "right",
|
|
499
|
+
maxScrollTop: number,
|
|
500
|
+
maxScrollLeft: number,
|
|
501
|
+
): boolean {
|
|
502
|
+
switch (stickyStart) {
|
|
503
|
+
case "top":
|
|
504
|
+
return maxScrollTop > 0 && this.scrollTop <= 0
|
|
505
|
+
case "bottom":
|
|
506
|
+
return maxScrollTop > 0 && this.scrollTop >= maxScrollTop - 1
|
|
507
|
+
case "left":
|
|
508
|
+
return maxScrollLeft > 0 && this.scrollLeft <= 0
|
|
509
|
+
case "right":
|
|
510
|
+
return maxScrollLeft > 0 && this.scrollLeft >= maxScrollLeft - 1
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
public add(obj: Renderable | VNode<any, any[]>, index?: number): number {
|
|
515
|
+
return this.content.add(obj, index)
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
public insertBefore(obj: Renderable | VNode<any, any[]> | unknown, anchor?: Renderable | unknown): number {
|
|
519
|
+
return this.content.insertBefore(obj, anchor)
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
public remove(child: BaseRenderable): void {
|
|
523
|
+
// Internal parts (wrapper, scrollbars) are direct children of the root,
|
|
524
|
+
// not of content. Route by actual parentage so destroy() and explicit
|
|
525
|
+
// removals of internals detach them instead of silently no-oping through
|
|
526
|
+
// the content delegation.
|
|
527
|
+
if (child.parent === this) {
|
|
528
|
+
super.remove(child)
|
|
529
|
+
return
|
|
530
|
+
}
|
|
531
|
+
this.content.remove(child)
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
public getChildren(): Renderable[] {
|
|
535
|
+
return this.content.getChildren()
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
public getRenderable(id: string): Renderable | undefined {
|
|
539
|
+
return this.content.getRenderable(id)
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
protected onMouseEvent(event: MouseEvent): void {
|
|
543
|
+
if (event.type === "scroll") {
|
|
544
|
+
let dir = event.scroll?.direction
|
|
545
|
+
if (event.modifiers.shift)
|
|
546
|
+
dir = dir === "up" ? "left" : dir === "down" ? "right" : dir === "right" ? "down" : "up"
|
|
547
|
+
|
|
548
|
+
const baseDelta = event.scroll?.delta ?? 0
|
|
549
|
+
const now = Date.now()
|
|
550
|
+
const multiplier = this.scrollAccel.tick(now)
|
|
551
|
+
const scrollAmount = baseDelta * multiplier
|
|
552
|
+
|
|
553
|
+
if (dir === "up") {
|
|
554
|
+
this.scrollAccumulatorY -= scrollAmount
|
|
555
|
+
const integerScroll = Math.trunc(this.scrollAccumulatorY)
|
|
556
|
+
if (integerScroll !== 0) {
|
|
557
|
+
this.scrollTop += integerScroll
|
|
558
|
+
this.scrollAccumulatorY -= integerScroll
|
|
559
|
+
}
|
|
560
|
+
} else if (dir === "down") {
|
|
561
|
+
this.scrollAccumulatorY += scrollAmount
|
|
562
|
+
const integerScroll = Math.trunc(this.scrollAccumulatorY)
|
|
563
|
+
if (integerScroll !== 0) {
|
|
564
|
+
this.scrollTop += integerScroll
|
|
565
|
+
this.scrollAccumulatorY -= integerScroll
|
|
566
|
+
}
|
|
567
|
+
} else if (dir === "left") {
|
|
568
|
+
this.scrollAccumulatorX -= scrollAmount
|
|
569
|
+
const integerScroll = Math.trunc(this.scrollAccumulatorX)
|
|
570
|
+
if (integerScroll !== 0) {
|
|
571
|
+
this.scrollLeft += integerScroll
|
|
572
|
+
this.scrollAccumulatorX -= integerScroll
|
|
573
|
+
}
|
|
574
|
+
} else if (dir === "right") {
|
|
575
|
+
this.scrollAccumulatorX += scrollAmount
|
|
576
|
+
const integerScroll = Math.trunc(this.scrollAccumulatorX)
|
|
577
|
+
if (integerScroll !== 0) {
|
|
578
|
+
this.scrollLeft += integerScroll
|
|
579
|
+
this.scrollAccumulatorX -= integerScroll
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
this.syncManualScrollState()
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
if (event.type === "drag" && event.isDragging) {
|
|
587
|
+
this.updateAutoScroll(event.x, event.y)
|
|
588
|
+
} else if (event.type === "up") {
|
|
589
|
+
this.stopAutoScroll()
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
public handleKeyPress(key: KeyEvent): boolean {
|
|
594
|
+
// Let scrollbars handle their own acceleration
|
|
595
|
+
if (this.verticalScrollBar.handleKeyPress(key)) {
|
|
596
|
+
this.scrollAccel.reset()
|
|
597
|
+
this.resetScrollAccumulators()
|
|
598
|
+
this.syncManualScrollState()
|
|
599
|
+
return true
|
|
600
|
+
}
|
|
601
|
+
if (this.horizontalScrollBar.handleKeyPress(key)) {
|
|
602
|
+
this.scrollAccel.reset()
|
|
603
|
+
this.resetScrollAccumulators()
|
|
604
|
+
this.syncManualScrollState()
|
|
605
|
+
return true
|
|
606
|
+
}
|
|
607
|
+
return false
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
private resetScrollAccumulators(): void {
|
|
611
|
+
this.scrollAccumulatorX = 0
|
|
612
|
+
this.scrollAccumulatorY = 0
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
public startAutoScroll(mouseX: number, mouseY: number): void {
|
|
616
|
+
this.stopAutoScroll()
|
|
617
|
+
this.autoScrollMouseX = mouseX
|
|
618
|
+
this.autoScrollMouseY = mouseY
|
|
619
|
+
this.cachedAutoScrollSpeed = this.getAutoScrollSpeed(mouseX, mouseY)
|
|
620
|
+
this.isAutoScrolling = true
|
|
621
|
+
|
|
622
|
+
if (!this.live) {
|
|
623
|
+
this.live = true
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
public updateAutoScroll(mouseX: number, mouseY: number): void {
|
|
628
|
+
this.autoScrollMouseX = mouseX
|
|
629
|
+
this.autoScrollMouseY = mouseY
|
|
630
|
+
|
|
631
|
+
// Cache the speed based on current mouse position
|
|
632
|
+
this.cachedAutoScrollSpeed = this.getAutoScrollSpeed(mouseX, mouseY)
|
|
633
|
+
|
|
634
|
+
const scrollX = this.getAutoScrollDirectionX(mouseX)
|
|
635
|
+
const scrollY = this.getAutoScrollDirectionY(mouseY)
|
|
636
|
+
|
|
637
|
+
if (scrollX === 0 && scrollY === 0) {
|
|
638
|
+
this.stopAutoScroll()
|
|
639
|
+
} else if (!this.isAutoScrolling) {
|
|
640
|
+
this.startAutoScroll(mouseX, mouseY)
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
public stopAutoScroll(): void {
|
|
645
|
+
const wasAutoScrolling = this.isAutoScrolling
|
|
646
|
+
this.isAutoScrolling = false
|
|
647
|
+
this.autoScrollAccumulatorX = 0
|
|
648
|
+
this.autoScrollAccumulatorY = 0
|
|
649
|
+
|
|
650
|
+
// Only turn off live if no other features need it
|
|
651
|
+
// For now, auto-scroll is the only feature using live, but this could be extended
|
|
652
|
+
if (wasAutoScrolling && !this.hasOtherLiveReasons()) {
|
|
653
|
+
this.live = false
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
private hasOtherLiveReasons(): boolean {
|
|
658
|
+
// Placeholder for future features that might need live mode
|
|
659
|
+
// For now, always return false since auto-scroll is the only user
|
|
660
|
+
return false
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
private handleAutoScroll(deltaTime: number): void {
|
|
664
|
+
if (!this.isAutoScrolling) return
|
|
665
|
+
|
|
666
|
+
const scrollX = this.getAutoScrollDirectionX(this.autoScrollMouseX)
|
|
667
|
+
const scrollY = this.getAutoScrollDirectionY(this.autoScrollMouseY)
|
|
668
|
+
const scrollAmount = this.cachedAutoScrollSpeed * (deltaTime / 1000)
|
|
669
|
+
|
|
670
|
+
let scrolled = false
|
|
671
|
+
|
|
672
|
+
if (scrollX !== 0) {
|
|
673
|
+
this.autoScrollAccumulatorX += scrollX * scrollAmount
|
|
674
|
+
const integerScrollX = Math.trunc(this.autoScrollAccumulatorX)
|
|
675
|
+
if (integerScrollX !== 0) {
|
|
676
|
+
this.scrollLeft += integerScrollX
|
|
677
|
+
this.autoScrollAccumulatorX -= integerScrollX
|
|
678
|
+
scrolled = true
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
if (scrollY !== 0) {
|
|
683
|
+
this.autoScrollAccumulatorY += scrollY * scrollAmount
|
|
684
|
+
const integerScrollY = Math.trunc(this.autoScrollAccumulatorY)
|
|
685
|
+
if (integerScrollY !== 0) {
|
|
686
|
+
this.scrollTop += integerScrollY
|
|
687
|
+
this.autoScrollAccumulatorY -= integerScrollY
|
|
688
|
+
scrolled = true
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
if (scrolled) {
|
|
693
|
+
this._ctx.requestSelectionUpdate()
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
if (scrollX === 0 && scrollY === 0) {
|
|
697
|
+
this.stopAutoScroll()
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
private getAutoScrollDirectionX(mouseX: number): number {
|
|
702
|
+
const relativeX = mouseX - this.x
|
|
703
|
+
const distToLeft = relativeX
|
|
704
|
+
const distToRight = this.width - relativeX
|
|
705
|
+
|
|
706
|
+
if (distToLeft <= this.autoScrollThresholdHorizontal) {
|
|
707
|
+
return this.scrollLeft > 0 ? -1 : 0
|
|
708
|
+
} else if (distToRight <= this.autoScrollThresholdHorizontal) {
|
|
709
|
+
const maxScrollLeft = this.scrollWidth - this.viewport.width
|
|
710
|
+
return this.scrollLeft < maxScrollLeft ? 1 : 0
|
|
711
|
+
}
|
|
712
|
+
return 0
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
private getAutoScrollDirectionY(mouseY: number): number {
|
|
716
|
+
const relativeY = mouseY - this.y
|
|
717
|
+
const distToTop = relativeY
|
|
718
|
+
const distToBottom = this.height - relativeY
|
|
719
|
+
|
|
720
|
+
if (distToTop <= this.autoScrollThresholdVertical) {
|
|
721
|
+
return this.scrollTop > 0 ? -1 : 0
|
|
722
|
+
} else if (distToBottom <= this.autoScrollThresholdVertical) {
|
|
723
|
+
const maxScrollTop = this.scrollHeight - this.viewport.height
|
|
724
|
+
return this.scrollTop < maxScrollTop ? 1 : 0
|
|
725
|
+
}
|
|
726
|
+
return 0
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
private getAutoScrollSpeed(mouseX: number, mouseY: number): number {
|
|
730
|
+
const relativeX = mouseX - this.x
|
|
731
|
+
const relativeY = mouseY - this.y
|
|
732
|
+
|
|
733
|
+
const distToLeft = relativeX
|
|
734
|
+
const distToRight = this.width - relativeX
|
|
735
|
+
const distToTop = relativeY
|
|
736
|
+
const distToBottom = this.height - relativeY
|
|
737
|
+
|
|
738
|
+
const minDistance = Math.min(distToLeft, distToRight, distToTop, distToBottom)
|
|
739
|
+
|
|
740
|
+
if (minDistance <= 1) {
|
|
741
|
+
return this.autoScrollSpeedFast
|
|
742
|
+
} else if (minDistance <= 2) {
|
|
743
|
+
return this.autoScrollSpeedMedium
|
|
744
|
+
} else {
|
|
745
|
+
return this.autoScrollSpeedSlow
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
private recalculateBarProps(): void {
|
|
750
|
+
// Wrap entire method to prevent scroll changes from being treated as manual
|
|
751
|
+
const wasApplyingStickyScroll = this._isApplyingStickyScroll
|
|
752
|
+
this._isApplyingStickyScroll = true
|
|
753
|
+
|
|
754
|
+
try {
|
|
755
|
+
this.verticalScrollBar.scrollSize = this.content.height
|
|
756
|
+
this.verticalScrollBar.viewportSize = this.viewport.height
|
|
757
|
+
this.horizontalScrollBar.scrollSize = this.content.width
|
|
758
|
+
this.horizontalScrollBar.viewportSize = this.viewport.width
|
|
759
|
+
|
|
760
|
+
if (this._stickyScroll) {
|
|
761
|
+
const newMaxScrollTop = Math.max(0, this.scrollHeight - this.viewport.height)
|
|
762
|
+
const newMaxScrollLeft = Math.max(0, this.scrollWidth - this.viewport.width)
|
|
763
|
+
const stickyStart = this._stickyStart
|
|
764
|
+
|
|
765
|
+
if (stickyStart && !this._hasManualScroll) {
|
|
766
|
+
this.applyStickyStart(stickyStart)
|
|
767
|
+
} else if (
|
|
768
|
+
stickyStart &&
|
|
769
|
+
this._hasManualScroll &&
|
|
770
|
+
this.isAtStickyReengagePoint(stickyStart, newMaxScrollTop, newMaxScrollLeft)
|
|
771
|
+
) {
|
|
772
|
+
// User scrolled back to the sticky edge during streaming; re-engage sticky.
|
|
773
|
+
this._hasManualScroll = false
|
|
774
|
+
this.applyStickyStart(stickyStart)
|
|
775
|
+
} else if (!this._hasManualScroll) {
|
|
776
|
+
if (this._stickyScrollTop) {
|
|
777
|
+
this.scrollTop = 0
|
|
778
|
+
} else if (this._stickyScrollBottom && newMaxScrollTop > 0) {
|
|
779
|
+
this.scrollTop = newMaxScrollTop
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
if (this._stickyScrollLeft) {
|
|
783
|
+
this.scrollLeft = 0
|
|
784
|
+
} else if (this._stickyScrollRight && newMaxScrollLeft > 0) {
|
|
785
|
+
this.scrollLeft = newMaxScrollLeft
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
} finally {
|
|
790
|
+
this._isApplyingStickyScroll = wasApplyingStickyScroll
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
// NOTE: This is obviously a workaround for something,
|
|
794
|
+
// which is that the bar props are recalculated when the viewport is resized,
|
|
795
|
+
// which intially happens onUpdate but is the viewport does not have the correct dimensions yet,
|
|
796
|
+
// then when it does, no update is triggered and when we do we are in the middle of a render,
|
|
797
|
+
// which just ignores the request. ¯\_(ツ)_/¯
|
|
798
|
+
// TODO: Fix this properly. How? Move yoga to native, get all changes for elements in one go
|
|
799
|
+
// and update all renderables in one go before rendering.
|
|
800
|
+
// OR: Move this logic to the viewport. IMHO the wrapper and viewport are overkill and not necessary.
|
|
801
|
+
// The Scrollbox can be the viewport, we are using translations on the content anyway.
|
|
802
|
+
process.nextTick(() => {
|
|
803
|
+
this.requestRender()
|
|
804
|
+
})
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
// Setters for reactive properties
|
|
808
|
+
public set padding(value: number | `${number}%` | null | undefined) {
|
|
809
|
+
this.content.padding = value
|
|
810
|
+
this.requestRender()
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
public set paddingX(value: number | `${number}%` | null | undefined) {
|
|
814
|
+
this.content.paddingX = value
|
|
815
|
+
this.requestRender()
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
public set paddingY(value: number | `${number}%` | null | undefined) {
|
|
819
|
+
this.content.paddingY = value
|
|
820
|
+
this.requestRender()
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
public set paddingTop(value: number | `${number}%` | null | undefined) {
|
|
824
|
+
this.content.paddingTop = value
|
|
825
|
+
this.requestRender()
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
public set paddingRight(value: number | `${number}%` | null | undefined) {
|
|
829
|
+
this.content.paddingRight = value
|
|
830
|
+
this.requestRender()
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
public set paddingBottom(value: number | `${number}%` | null | undefined) {
|
|
834
|
+
this.content.paddingBottom = value
|
|
835
|
+
this.requestRender()
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
public set paddingLeft(value: number | `${number}%` | null | undefined) {
|
|
839
|
+
this.content.paddingLeft = value
|
|
840
|
+
this.requestRender()
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
public set rootOptions(options: ScrollBoxOptions["rootOptions"]) {
|
|
844
|
+
Object.assign(this, options)
|
|
845
|
+
this.requestRender()
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
public set wrapperOptions(options: ScrollBoxOptions["wrapperOptions"]) {
|
|
849
|
+
Object.assign(this.wrapper, options)
|
|
850
|
+
this.requestRender()
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
public set viewportOptions(options: ScrollBoxOptions["viewportOptions"]) {
|
|
854
|
+
Object.assign(this.viewport, options)
|
|
855
|
+
this.requestRender()
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
public set contentOptions(options: ScrollBoxOptions["contentOptions"]) {
|
|
859
|
+
Object.assign(this.content, options)
|
|
860
|
+
this.requestRender()
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
public set scrollbarOptions(options: ScrollBoxOptions["scrollbarOptions"]) {
|
|
864
|
+
Object.assign(this.verticalScrollBar, options)
|
|
865
|
+
Object.assign(this.horizontalScrollBar, options)
|
|
866
|
+
this.requestRender()
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
public set verticalScrollbarOptions(options: ScrollBoxOptions["verticalScrollbarOptions"]) {
|
|
870
|
+
Object.assign(this.verticalScrollBar, options)
|
|
871
|
+
this.requestRender()
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
public set horizontalScrollbarOptions(options: ScrollBoxOptions["horizontalScrollbarOptions"]) {
|
|
875
|
+
Object.assign(this.horizontalScrollBar, options)
|
|
876
|
+
this.requestRender()
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
public get scrollAcceleration(): ScrollAcceleration {
|
|
880
|
+
return this.scrollAccel
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
public set scrollAcceleration(value: ScrollAcceleration) {
|
|
884
|
+
this.scrollAccel = value
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
get viewportCulling(): boolean {
|
|
888
|
+
return this.content.viewportCulling
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
set viewportCulling(value: boolean) {
|
|
892
|
+
this.content.viewportCulling = value
|
|
893
|
+
this.requestRender()
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
protected destroySelf(): void {
|
|
897
|
+
if (this.selectionListener) {
|
|
898
|
+
this._ctx.off("selection", this.selectionListener)
|
|
899
|
+
this.selectionListener = undefined
|
|
900
|
+
}
|
|
901
|
+
super.destroySelf()
|
|
902
|
+
}
|
|
903
|
+
}
|