@trebco/treb 36.1.4 → 37.0.1
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/api-config.json +1 -1
- package/build/package.json +119 -0
- package/build/treb-base-types/src/api_types.d.ts +11 -0
- package/build/treb-base-types/src/api_types.js +22 -0
- package/build/treb-base-types/src/api_types.js.map +1 -0
- package/build/treb-base-types/src/area-utils.d.ts +9 -0
- package/build/treb-base-types/src/area-utils.js +50 -0
- package/build/treb-base-types/src/area-utils.js.map +1 -0
- package/build/treb-base-types/src/area.d.ts +182 -0
- package/build/treb-base-types/src/area.js +715 -0
- package/build/treb-base-types/src/area.js.map +1 -0
- package/build/treb-base-types/src/basic_types.d.ts +20 -0
- package/build/treb-base-types/src/basic_types.js +22 -0
- package/build/treb-base-types/src/basic_types.js.map +1 -0
- package/build/treb-base-types/src/cell.d.ts +167 -0
- package/build/treb-base-types/src/cell.js +432 -0
- package/build/treb-base-types/src/cell.js.map +1 -0
- package/build/treb-base-types/src/cells.d.ts +251 -0
- package/build/treb-base-types/src/cells.js +1136 -0
- package/build/treb-base-types/src/cells.js.map +1 -0
- package/build/treb-base-types/src/color.d.ts +35 -0
- package/build/treb-base-types/src/color.js +162 -0
- package/build/treb-base-types/src/color.js.map +1 -0
- package/build/treb-base-types/src/dom-utilities.d.ts +70 -0
- package/build/treb-base-types/src/dom-utilities.js +144 -0
- package/build/treb-base-types/src/dom-utilities.js.map +1 -0
- package/build/treb-base-types/src/evaluate-options.d.ts +35 -0
- package/build/treb-base-types/src/evaluate-options.js +22 -0
- package/build/treb-base-types/src/evaluate-options.js.map +1 -0
- package/build/treb-base-types/src/font-stack.d.ts +37 -0
- package/build/treb-base-types/src/font-stack.js +93 -0
- package/build/treb-base-types/src/font-stack.js.map +1 -0
- package/build/treb-base-types/src/gradient.d.ts +18 -0
- package/build/treb-base-types/src/gradient.js +86 -0
- package/build/treb-base-types/src/gradient.js.map +1 -0
- package/build/treb-base-types/src/import.d.ts +48 -0
- package/build/treb-base-types/src/import.js +22 -0
- package/build/treb-base-types/src/import.js.map +1 -0
- package/build/treb-base-types/src/index-standalone.d.ts +6 -0
- package/build/treb-base-types/src/index-standalone.js +27 -0
- package/build/treb-base-types/src/index-standalone.js.map +1 -0
- package/build/treb-base-types/src/index.d.ts +22 -0
- package/build/treb-base-types/src/index.js +45 -0
- package/build/treb-base-types/src/index.js.map +1 -0
- package/build/treb-base-types/src/layout.d.ts +22 -0
- package/build/treb-base-types/src/layout.js +22 -0
- package/build/treb-base-types/src/layout.js.map +1 -0
- package/build/treb-base-types/src/localization.d.ts +37 -0
- package/build/treb-base-types/src/localization.js +157 -0
- package/build/treb-base-types/src/localization.js.map +1 -0
- package/build/treb-base-types/src/rectangle.d.ts +51 -0
- package/build/treb-base-types/src/rectangle.js +123 -0
- package/build/treb-base-types/src/rectangle.js.map +1 -0
- package/build/treb-base-types/src/render_text.d.ts +34 -0
- package/build/treb-base-types/src/render_text.js +22 -0
- package/build/treb-base-types/src/render_text.js.map +1 -0
- package/build/treb-base-types/src/style.d.ts +214 -0
- package/build/treb-base-types/src/style.js +373 -0
- package/build/treb-base-types/src/style.js.map +1 -0
- package/build/treb-base-types/src/table.d.ts +58 -0
- package/build/treb-base-types/src/table.js +27 -0
- package/build/treb-base-types/src/table.js.map +1 -0
- package/build/treb-base-types/src/text_part.d.ts +26 -0
- package/build/treb-base-types/src/text_part.js +47 -0
- package/build/treb-base-types/src/text_part.js.map +1 -0
- package/build/treb-base-types/src/theme.d.ts +120 -0
- package/build/treb-base-types/src/theme.js +460 -0
- package/build/treb-base-types/src/theme.js.map +1 -0
- package/build/treb-base-types/src/union.d.ts +73 -0
- package/build/treb-base-types/src/union.js +61 -0
- package/build/treb-base-types/src/union.js.map +1 -0
- package/build/treb-base-types/src/value-type.d.ts +86 -0
- package/build/treb-base-types/src/value-type.js +168 -0
- package/build/treb-base-types/src/value-type.js.map +1 -0
- package/build/treb-base-types/src/worker-proxy.d.ts +95 -0
- package/build/treb-base-types/src/worker-proxy.js +221 -0
- package/build/treb-base-types/src/worker-proxy.js.map +1 -0
- package/build/treb-calculator/src/calculator.d.ts +249 -0
- package/build/treb-calculator/src/calculator.js +2755 -0
- package/build/treb-calculator/src/calculator.js.map +1 -0
- package/build/treb-calculator/src/complex-math.d.ts +75 -0
- package/build/treb-calculator/src/complex-math.js +559 -0
- package/build/treb-calculator/src/complex-math.js.map +1 -0
- package/build/treb-calculator/src/dag/array-vertex.d.ts +71 -0
- package/build/treb-calculator/src/dag/array-vertex.js +156 -0
- package/build/treb-calculator/src/dag/array-vertex.js.map +1 -0
- package/build/treb-calculator/src/dag/calculation_leaf_vertex.d.ts +48 -0
- package/build/treb-calculator/src/dag/calculation_leaf_vertex.js +84 -0
- package/build/treb-calculator/src/dag/calculation_leaf_vertex.js.map +1 -0
- package/build/treb-calculator/src/dag/graph.d.ts +134 -0
- package/build/treb-calculator/src/dag/graph.js +842 -0
- package/build/treb-calculator/src/dag/graph.js.map +1 -0
- package/build/treb-calculator/src/dag/spreadsheet_vertex.d.ts +58 -0
- package/build/treb-calculator/src/dag/spreadsheet_vertex.js +232 -0
- package/build/treb-calculator/src/dag/spreadsheet_vertex.js.map +1 -0
- package/build/treb-calculator/src/dag/spreadsheet_vertex_base.d.ts +20 -0
- package/build/treb-calculator/src/dag/spreadsheet_vertex_base.js +25 -0
- package/build/treb-calculator/src/dag/spreadsheet_vertex_base.js.map +1 -0
- package/build/treb-calculator/src/dag/state_leaf_vertex.d.ts +43 -0
- package/build/treb-calculator/src/dag/state_leaf_vertex.js +81 -0
- package/build/treb-calculator/src/dag/state_leaf_vertex.js.map +1 -0
- package/build/treb-calculator/src/dag/vertex.d.ts +71 -0
- package/build/treb-calculator/src/dag/vertex.js +274 -0
- package/build/treb-calculator/src/dag/vertex.js.map +1 -0
- package/build/treb-calculator/src/descriptors.d.ts +189 -0
- package/build/treb-calculator/src/descriptors.js +22 -0
- package/build/treb-calculator/src/descriptors.js.map +1 -0
- package/build/treb-calculator/src/expression-calculator.d.ts +127 -0
- package/build/treb-calculator/src/expression-calculator.js +1033 -0
- package/build/treb-calculator/src/expression-calculator.js.map +1 -0
- package/build/treb-calculator/src/function-error.d.ts +35 -0
- package/build/treb-calculator/src/function-error.js +85 -0
- package/build/treb-calculator/src/function-error.js.map +1 -0
- package/build/treb-calculator/src/function-library.d.ts +22 -0
- package/build/treb-calculator/src/function-library.js +96 -0
- package/build/treb-calculator/src/function-library.js.map +1 -0
- package/build/treb-calculator/src/functions/base-functions.d.ts +7 -0
- package/build/treb-calculator/src/functions/base-functions.js +2611 -0
- package/build/treb-calculator/src/functions/base-functions.js.map +1 -0
- package/build/treb-calculator/src/functions/beta.d.ts +17 -0
- package/build/treb-calculator/src/functions/beta.js +201 -0
- package/build/treb-calculator/src/functions/beta.js.map +1 -0
- package/build/treb-calculator/src/functions/checkbox.d.ts +3 -0
- package/build/treb-calculator/src/functions/checkbox.js +128 -0
- package/build/treb-calculator/src/functions/checkbox.js.map +1 -0
- package/build/treb-calculator/src/functions/complex-functions.d.ts +2 -0
- package/build/treb-calculator/src/functions/complex-functions.js +217 -0
- package/build/treb-calculator/src/functions/complex-functions.js.map +1 -0
- package/build/treb-calculator/src/functions/date-utils.d.ts +3 -0
- package/build/treb-calculator/src/functions/date-utils.js +59 -0
- package/build/treb-calculator/src/functions/date-utils.js.map +1 -0
- package/build/treb-calculator/src/functions/finance-functions.d.ts +2 -0
- package/build/treb-calculator/src/functions/finance-functions.js +547 -0
- package/build/treb-calculator/src/functions/finance-functions.js.map +1 -0
- package/build/treb-calculator/src/functions/fp.d.ts +2 -0
- package/build/treb-calculator/src/functions/fp.js +463 -0
- package/build/treb-calculator/src/functions/fp.js.map +1 -0
- package/build/treb-calculator/src/functions/function-utilities.d.ts +2 -0
- package/build/treb-calculator/src/functions/function-utilities.js +36 -0
- package/build/treb-calculator/src/functions/function-utilities.js.map +1 -0
- package/build/treb-calculator/src/functions/gamma.d.ts +20 -0
- package/build/treb-calculator/src/functions/gamma.js +142 -0
- package/build/treb-calculator/src/functions/gamma.js.map +1 -0
- package/build/treb-calculator/src/functions/information-functions.d.ts +2 -0
- package/build/treb-calculator/src/functions/information-functions.js +71 -0
- package/build/treb-calculator/src/functions/information-functions.js.map +1 -0
- package/build/treb-calculator/src/functions/lambda-functions.d.ts +2 -0
- package/build/treb-calculator/src/functions/lambda-functions.js +85 -0
- package/build/treb-calculator/src/functions/lambda-functions.js.map +1 -0
- package/build/treb-calculator/src/functions/matrix-functions.d.ts +2 -0
- package/build/treb-calculator/src/functions/matrix-functions.js +144 -0
- package/build/treb-calculator/src/functions/matrix-functions.js.map +1 -0
- package/build/treb-calculator/src/functions/normal.d.ts +2 -0
- package/build/treb-calculator/src/functions/normal.js +32 -0
- package/build/treb-calculator/src/functions/normal.js.map +1 -0
- package/build/treb-calculator/src/functions/regex-functions.d.ts +2 -0
- package/build/treb-calculator/src/functions/regex-functions.js +188 -0
- package/build/treb-calculator/src/functions/regex-functions.js.map +1 -0
- package/build/treb-calculator/src/functions/sparkline.d.ts +37 -0
- package/build/treb-calculator/src/functions/sparkline.js +264 -0
- package/build/treb-calculator/src/functions/sparkline.js.map +1 -0
- package/build/treb-calculator/src/functions/statistics-functions.d.ts +6 -0
- package/build/treb-calculator/src/functions/statistics-functions.js +989 -0
- package/build/treb-calculator/src/functions/statistics-functions.js.map +1 -0
- package/build/treb-calculator/src/functions/students-t.d.ts +3 -0
- package/build/treb-calculator/src/functions/students-t.js +64 -0
- package/build/treb-calculator/src/functions/students-t.js.map +1 -0
- package/build/treb-calculator/src/functions/text-functions.d.ts +3 -0
- package/build/treb-calculator/src/functions/text-functions.js +320 -0
- package/build/treb-calculator/src/functions/text-functions.js.map +1 -0
- package/build/treb-calculator/src/index.d.ts +2 -0
- package/build/treb-calculator/src/index.js +22 -0
- package/build/treb-calculator/src/index.js.map +1 -0
- package/build/treb-calculator/src/notifier-types.d.ts +26 -0
- package/build/treb-calculator/src/notifier-types.js +22 -0
- package/build/treb-calculator/src/notifier-types.js.map +1 -0
- package/build/treb-calculator/src/primitives.d.ts +15 -0
- package/build/treb-calculator/src/primitives.js +398 -0
- package/build/treb-calculator/src/primitives.js.map +1 -0
- package/build/treb-calculator/src/utilities.d.ts +68 -0
- package/build/treb-calculator/src/utilities.js +324 -0
- package/build/treb-calculator/src/utilities.js.map +1 -0
- package/build/treb-charts/src/chart-functions.d.ts +8 -0
- package/build/treb-charts/src/chart-functions.js +209 -0
- package/build/treb-charts/src/chart-functions.js.map +1 -0
- package/build/treb-charts/src/chart-types.d.ts +233 -0
- package/build/treb-charts/src/chart-types.js +57 -0
- package/build/treb-charts/src/chart-types.js.map +1 -0
- package/build/treb-charts/src/chart-utils.d.ts +106 -0
- package/build/treb-charts/src/chart-utils.js +1060 -0
- package/build/treb-charts/src/chart-utils.js.map +1 -0
- package/build/treb-charts/src/chart.d.ts +23 -0
- package/build/treb-charts/src/chart.js +94 -0
- package/build/treb-charts/src/chart.js.map +1 -0
- package/build/treb-charts/src/default-chart-renderer.d.ts +16 -0
- package/build/treb-charts/src/default-chart-renderer.js +533 -0
- package/build/treb-charts/src/default-chart-renderer.js.map +1 -0
- package/build/treb-charts/src/index.d.ts +5 -0
- package/build/treb-charts/src/index.js +24 -0
- package/build/treb-charts/src/index.js.map +1 -0
- package/build/treb-charts/src/main.d.ts +1 -0
- package/build/treb-charts/src/main.js +34 -0
- package/build/treb-charts/src/main.js.map +1 -0
- package/build/treb-charts/src/quicksort.d.ts +1 -0
- package/build/treb-charts/src/quicksort.js +49 -0
- package/build/treb-charts/src/quicksort.js.map +1 -0
- package/build/treb-charts/src/rectangle.d.ts +18 -0
- package/build/treb-charts/src/rectangle.js +41 -0
- package/build/treb-charts/src/rectangle.js.map +1 -0
- package/build/treb-charts/src/renderer-type.d.ts +24 -0
- package/build/treb-charts/src/renderer-type.js +22 -0
- package/build/treb-charts/src/renderer-type.js.map +1 -0
- package/build/treb-charts/src/renderer.d.ts +127 -0
- package/build/treb-charts/src/renderer.js +1518 -0
- package/build/treb-charts/src/renderer.js.map +1 -0
- package/build/treb-charts/src/util.d.ts +18 -0
- package/build/treb-charts/src/util.js +71 -0
- package/build/treb-charts/src/util.js.map +1 -0
- package/build/treb-data-model/src/annotation.d.ts +167 -0
- package/build/treb-data-model/src/annotation.js +120 -0
- package/build/treb-data-model/src/annotation.js.map +1 -0
- package/build/treb-data-model/src/conditional_format.d.ts +155 -0
- package/build/treb-data-model/src/conditional_format.js +62 -0
- package/build/treb-data-model/src/conditional_format.js.map +1 -0
- package/build/treb-data-model/src/data-validation.d.ts +28 -0
- package/build/treb-data-model/src/data-validation.js +22 -0
- package/build/treb-data-model/src/data-validation.js.map +1 -0
- package/build/treb-data-model/src/data_model.d.ts +173 -0
- package/build/treb-data-model/src/data_model.js +637 -0
- package/build/treb-data-model/src/data_model.js.map +1 -0
- package/build/treb-data-model/src/index.d.ts +13 -0
- package/build/treb-data-model/src/index.js +28 -0
- package/build/treb-data-model/src/index.js.map +1 -0
- package/build/treb-data-model/src/language-model.d.ts +22 -0
- package/build/treb-data-model/src/language-model.js +22 -0
- package/build/treb-data-model/src/language-model.js.map +1 -0
- package/build/treb-data-model/src/named.d.ts +124 -0
- package/build/treb-data-model/src/named.js +372 -0
- package/build/treb-data-model/src/named.js.map +1 -0
- package/build/treb-data-model/src/serialize_options.d.ts +49 -0
- package/build/treb-data-model/src/serialize_options.js +22 -0
- package/build/treb-data-model/src/serialize_options.js.map +1 -0
- package/build/treb-data-model/src/sheet.d.ts +499 -0
- package/build/treb-data-model/src/sheet.js +2904 -0
- package/build/treb-data-model/src/sheet.js.map +1 -0
- package/build/treb-data-model/src/sheet_collection.d.ts +58 -0
- package/build/treb-data-model/src/sheet_collection.js +112 -0
- package/build/treb-data-model/src/sheet_collection.js.map +1 -0
- package/build/treb-data-model/src/sheet_selection.d.ts +42 -0
- package/build/treb-data-model/src/sheet_selection.js +39 -0
- package/build/treb-data-model/src/sheet_selection.js.map +1 -0
- package/build/treb-data-model/src/sheet_types.d.ts +104 -0
- package/build/treb-data-model/src/sheet_types.js +22 -0
- package/build/treb-data-model/src/sheet_types.js.map +1 -0
- package/build/treb-data-model/src/types.d.ts +59 -0
- package/build/treb-data-model/src/types.js +22 -0
- package/build/treb-data-model/src/types.js.map +1 -0
- package/build/treb-embed/src/custom-element/spreadsheet-constructor.d.ts +75 -0
- package/build/treb-embed/src/custom-element/spreadsheet-constructor.js +1144 -0
- package/build/treb-embed/src/custom-element/spreadsheet-constructor.js.map +1 -0
- package/build/treb-embed/src/custom-element/treb-global.d.ts +36 -0
- package/build/treb-embed/src/custom-element/treb-global.js +64 -0
- package/build/treb-embed/src/custom-element/treb-global.js.map +1 -0
- package/build/treb-embed/src/custom-element/treb-spreadsheet-element.d.ts +1 -0
- package/build/treb-embed/src/custom-element/treb-spreadsheet-element.js +61 -0
- package/build/treb-embed/src/custom-element/treb-spreadsheet-element.js.map +1 -0
- package/build/treb-embed/src/embedded-spreadsheet.d.ts +1358 -0
- package/build/treb-embed/src/embedded-spreadsheet.js +5205 -0
- package/build/treb-embed/src/embedded-spreadsheet.js.map +1 -0
- package/build/treb-embed/src/index.d.ts +12 -0
- package/build/treb-embed/src/index.js +34 -0
- package/build/treb-embed/src/index.js.map +1 -0
- package/build/treb-embed/src/options.d.ts +266 -0
- package/build/treb-embed/src/options.js +56 -0
- package/build/treb-embed/src/options.js.map +1 -0
- package/build/treb-embed/src/plugin.d.ts +9 -0
- package/build/treb-embed/src/plugin.js +22 -0
- package/build/treb-embed/src/plugin.js.map +1 -0
- package/build/treb-embed/src/progress-dialog.d.ts +49 -0
- package/build/treb-embed/src/progress-dialog.js +178 -0
- package/build/treb-embed/src/progress-dialog.js.map +1 -0
- package/build/treb-embed/src/selection-state.d.ts +15 -0
- package/build/treb-embed/src/selection-state.js +22 -0
- package/build/treb-embed/src/selection-state.js.map +1 -0
- package/build/treb-embed/src/spinner.d.ts +8 -0
- package/build/treb-embed/src/spinner.js +40 -0
- package/build/treb-embed/src/spinner.js.map +1 -0
- package/build/treb-embed/src/toolbar-message.d.ts +72 -0
- package/build/treb-embed/src/toolbar-message.js +22 -0
- package/build/treb-embed/src/toolbar-message.js.map +1 -0
- package/build/treb-embed/src/types.d.ts +185 -0
- package/build/treb-embed/src/types.js +45 -0
- package/build/treb-embed/src/types.js.map +1 -0
- package/build/treb-embed/tsconfig.tsbuildinfo +1 -0
- package/build/treb-export/src/address-type.d.ts +34 -0
- package/build/treb-export/src/address-type.js +53 -0
- package/build/treb-export/src/address-type.js.map +1 -0
- package/build/treb-export/src/base-template.d.ts +1 -0
- package/build/treb-export/src/base-template.js +22 -0
- package/build/treb-export/src/base-template.js.map +1 -0
- package/build/treb-export/src/column-width.d.ts +2 -0
- package/build/treb-export/src/column-width.js +80 -0
- package/build/treb-export/src/column-width.js.map +1 -0
- package/build/treb-export/src/drawing/bubble-chart-template.d.ts +514 -0
- package/build/treb-export/src/drawing/bubble-chart-template.js +544 -0
- package/build/treb-export/src/drawing/bubble-chart-template.js.map +1 -0
- package/build/treb-export/src/drawing/chart-template-components2.d.ts +365 -0
- package/build/treb-export/src/drawing/chart-template-components2.js +386 -0
- package/build/treb-export/src/drawing/chart-template-components2.js.map +1 -0
- package/build/treb-export/src/drawing/chart.d.ts +26 -0
- package/build/treb-export/src/drawing/chart.js +247 -0
- package/build/treb-export/src/drawing/chart.js.map +1 -0
- package/build/treb-export/src/drawing/column-chart-template2.d.ts +490 -0
- package/build/treb-export/src/drawing/column-chart-template2.js +518 -0
- package/build/treb-export/src/drawing/column-chart-template2.js.map +1 -0
- package/build/treb-export/src/drawing/donut-chart-template2.d.ts +272 -0
- package/build/treb-export/src/drawing/donut-chart-template2.js +293 -0
- package/build/treb-export/src/drawing/donut-chart-template2.js.map +1 -0
- package/build/treb-export/src/drawing/drawing.d.ts +49 -0
- package/build/treb-export/src/drawing/drawing.js +193 -0
- package/build/treb-export/src/drawing/drawing.js.map +1 -0
- package/build/treb-export/src/drawing/embedded-image.d.ts +12 -0
- package/build/treb-export/src/drawing/embedded-image.js +54 -0
- package/build/treb-export/src/drawing/embedded-image.js.map +1 -0
- package/build/treb-export/src/drawing/scatter-chart-template2.d.ts +520 -0
- package/build/treb-export/src/drawing/scatter-chart-template2.js +551 -0
- package/build/treb-export/src/drawing/scatter-chart-template2.js.map +1 -0
- package/build/treb-export/src/export.d.ts +72 -0
- package/build/treb-export/src/export.js +2039 -0
- package/build/treb-export/src/export.js.map +1 -0
- package/build/treb-export/src/import-export-messages.d.ts +31 -0
- package/build/treb-export/src/import-export-messages.js +22 -0
- package/build/treb-export/src/import-export-messages.js.map +1 -0
- package/build/treb-export/src/import.d.ts +33 -0
- package/build/treb-export/src/import.js +1258 -0
- package/build/treb-export/src/import.js.map +1 -0
- package/build/treb-export/src/index.worker.d.ts +1 -0
- package/build/treb-export/src/index.worker.js +93 -0
- package/build/treb-export/src/index.worker.js.map +1 -0
- package/build/treb-export/src/metadata.d.ts +51 -0
- package/build/treb-export/src/metadata.js +153 -0
- package/build/treb-export/src/metadata.js.map +1 -0
- package/build/treb-export/src/ooxml.d.ts +7 -0
- package/build/treb-export/src/ooxml.js +41 -0
- package/build/treb-export/src/ooxml.js.map +1 -0
- package/build/treb-export/src/relationship.d.ts +8 -0
- package/build/treb-export/src/relationship.js +27 -0
- package/build/treb-export/src/relationship.js.map +1 -0
- package/build/treb-export/src/shared-strings.d.ts +11 -0
- package/build/treb-export/src/shared-strings.js +105 -0
- package/build/treb-export/src/shared-strings.js.map +1 -0
- package/build/treb-export/src/template-2.d.ts +1 -0
- package/build/treb-export/src/template-2.js +22 -0
- package/build/treb-export/src/template-2.js.map +1 -0
- package/build/treb-export/src/unescape_xml.d.ts +1 -0
- package/build/treb-export/src/unescape_xml.js +61 -0
- package/build/treb-export/src/unescape_xml.js.map +1 -0
- package/build/treb-export/src/workbook-sheet.d.ts +75 -0
- package/build/treb-export/src/workbook-sheet.js +128 -0
- package/build/treb-export/src/workbook-sheet.js.map +1 -0
- package/build/treb-export/src/workbook-style.d.ts +110 -0
- package/build/treb-export/src/workbook-style.js +1134 -0
- package/build/treb-export/src/workbook-style.js.map +1 -0
- package/build/treb-export/src/workbook-theme.d.ts +13 -0
- package/build/treb-export/src/workbook-theme.js +85 -0
- package/build/treb-export/src/workbook-theme.js.map +1 -0
- package/build/treb-export/src/workbook.d.ts +123 -0
- package/build/treb-export/src/workbook.js +644 -0
- package/build/treb-export/src/workbook.js.map +1 -0
- package/build/treb-export/src/xml-test.d.ts +9 -0
- package/build/treb-export/src/xml-test.js +52 -0
- package/build/treb-export/src/xml-test.js.map +1 -0
- package/build/treb-export/src/xml-utils.d.ts +76 -0
- package/build/treb-export/src/xml-utils.js +223 -0
- package/build/treb-export/src/xml-utils.js.map +1 -0
- package/build/treb-export/src/zip-wrapper.d.ts +22 -0
- package/build/treb-export/src/zip-wrapper.js +93 -0
- package/build/treb-export/src/zip-wrapper.js.map +1 -0
- package/build/treb-format/src/format.d.ts +130 -0
- package/build/treb-format/src/format.js +805 -0
- package/build/treb-format/src/format.js.map +1 -0
- package/build/treb-format/src/format_cache.d.ts +55 -0
- package/build/treb-format/src/format_cache.js +166 -0
- package/build/treb-format/src/format_cache.js.map +1 -0
- package/build/treb-format/src/format_parser.d.ts +70 -0
- package/build/treb-format/src/format_parser.js +618 -0
- package/build/treb-format/src/format_parser.js.map +1 -0
- package/build/treb-format/src/index.d.ts +4 -0
- package/build/treb-format/src/index.js +25 -0
- package/build/treb-format/src/index.js.map +1 -0
- package/build/treb-format/src/number_format_section.d.ts +58 -0
- package/build/treb-format/src/number_format_section.js +78 -0
- package/build/treb-format/src/number_format_section.js.map +1 -0
- package/build/treb-format/src/value_parser.d.ts +48 -0
- package/build/treb-format/src/value_parser.js +244 -0
- package/build/treb-format/src/value_parser.js.map +1 -0
- package/build/treb-grid/src/editors/autocomplete.d.ts +39 -0
- package/build/treb-grid/src/editors/autocomplete.js +316 -0
- package/build/treb-grid/src/editors/autocomplete.js.map +1 -0
- package/build/treb-grid/src/editors/autocomplete_matcher.d.ts +74 -0
- package/build/treb-grid/src/editors/autocomplete_matcher.js +212 -0
- package/build/treb-grid/src/editors/autocomplete_matcher.js.map +1 -0
- package/build/treb-grid/src/editors/editor.d.ts +214 -0
- package/build/treb-grid/src/editors/editor.js +879 -0
- package/build/treb-grid/src/editors/editor.js.map +1 -0
- package/build/treb-grid/src/editors/external_editor.d.ts +11 -0
- package/build/treb-grid/src/editors/external_editor.js +118 -0
- package/build/treb-grid/src/editors/external_editor.js.map +1 -0
- package/build/treb-grid/src/editors/formula_bar.d.ts +85 -0
- package/build/treb-grid/src/editors/formula_bar.js +444 -0
- package/build/treb-grid/src/editors/formula_bar.js.map +1 -0
- package/build/treb-grid/src/editors/overlay_editor.d.ts +85 -0
- package/build/treb-grid/src/editors/overlay_editor.js +353 -0
- package/build/treb-grid/src/editors/overlay_editor.js.map +1 -0
- package/build/treb-grid/src/index.d.ts +12 -0
- package/build/treb-grid/src/index.js +28 -0
- package/build/treb-grid/src/index.js.map +1 -0
- package/build/treb-grid/src/layout/base_layout.d.ts +346 -0
- package/build/treb-grid/src/layout/base_layout.js +2050 -0
- package/build/treb-grid/src/layout/base_layout.js.map +1 -0
- package/build/treb-grid/src/layout/grid_layout.d.ts +19 -0
- package/build/treb-grid/src/layout/grid_layout.js +235 -0
- package/build/treb-grid/src/layout/grid_layout.js.map +1 -0
- package/build/treb-grid/src/layout/mock-layout.d.ts +10 -0
- package/build/treb-grid/src/layout/mock-layout.js +37 -0
- package/build/treb-grid/src/layout/mock-layout.js.map +1 -0
- package/build/treb-grid/src/render/selection-renderer.d.ts +97 -0
- package/build/treb-grid/src/render/selection-renderer.js +315 -0
- package/build/treb-grid/src/render/selection-renderer.js.map +1 -0
- package/build/treb-grid/src/render/svg_header_overlay.d.ts +20 -0
- package/build/treb-grid/src/render/svg_header_overlay.js +76 -0
- package/build/treb-grid/src/render/svg_header_overlay.js.map +1 -0
- package/build/treb-grid/src/render/svg_selection_block.d.ts +27 -0
- package/build/treb-grid/src/render/svg_selection_block.js +106 -0
- package/build/treb-grid/src/render/svg_selection_block.js.map +1 -0
- package/build/treb-grid/src/render/tile_renderer.d.ts +121 -0
- package/build/treb-grid/src/render/tile_renderer.js +1609 -0
- package/build/treb-grid/src/render/tile_renderer.js.map +1 -0
- package/build/treb-grid/src/types/border_constants.d.ts +9 -0
- package/build/treb-grid/src/types/border_constants.js +34 -0
- package/build/treb-grid/src/types/border_constants.js.map +1 -0
- package/build/treb-grid/src/types/clipboard_data.d.ts +11 -0
- package/build/treb-grid/src/types/clipboard_data.js +22 -0
- package/build/treb-grid/src/types/clipboard_data.js.map +1 -0
- package/build/treb-grid/src/types/clipboard_data2.d.ts +46 -0
- package/build/treb-grid/src/types/clipboard_data2.js +22 -0
- package/build/treb-grid/src/types/clipboard_data2.js.map +1 -0
- package/build/treb-grid/src/types/drag_mask.d.ts +10 -0
- package/build/treb-grid/src/types/drag_mask.js +78 -0
- package/build/treb-grid/src/types/drag_mask.js.map +1 -0
- package/build/treb-grid/src/types/external_editor_config.d.ts +33 -0
- package/build/treb-grid/src/types/external_editor_config.js +22 -0
- package/build/treb-grid/src/types/external_editor_config.js.map +1 -0
- package/build/treb-grid/src/types/grid.d.ts +806 -0
- package/build/treb-grid/src/types/grid.js +6410 -0
- package/build/treb-grid/src/types/grid.js.map +1 -0
- package/build/treb-grid/src/types/grid_base.d.ts +442 -0
- package/build/treb-grid/src/types/grid_base.js +3523 -0
- package/build/treb-grid/src/types/grid_base.js.map +1 -0
- package/build/treb-grid/src/types/grid_command.d.ts +408 -0
- package/build/treb-grid/src/types/grid_command.js +75 -0
- package/build/treb-grid/src/types/grid_command.js.map +1 -0
- package/build/treb-grid/src/types/grid_events.d.ts +93 -0
- package/build/treb-grid/src/types/grid_events.js +36 -0
- package/build/treb-grid/src/types/grid_events.js.map +1 -0
- package/build/treb-grid/src/types/grid_options.d.ts +50 -0
- package/build/treb-grid/src/types/grid_options.js +34 -0
- package/build/treb-grid/src/types/grid_options.js.map +1 -0
- package/build/treb-grid/src/types/scale-control.d.ts +21 -0
- package/build/treb-grid/src/types/scale-control.js +148 -0
- package/build/treb-grid/src/types/scale-control.js.map +1 -0
- package/build/treb-grid/src/types/set_range_options.d.ts +24 -0
- package/build/treb-grid/src/types/set_range_options.js +22 -0
- package/build/treb-grid/src/types/set_range_options.js.map +1 -0
- package/build/treb-grid/src/types/tab_bar.d.ts +84 -0
- package/build/treb-grid/src/types/tab_bar.js +426 -0
- package/build/treb-grid/src/types/tab_bar.js.map +1 -0
- package/build/treb-grid/src/types/tile.d.ts +29 -0
- package/build/treb-grid/src/types/tile.js +22 -0
- package/build/treb-grid/src/types/tile.js.map +1 -0
- package/build/treb-grid/src/types/update_flags.d.ts +48 -0
- package/build/treb-grid/src/types/update_flags.js +22 -0
- package/build/treb-grid/src/types/update_flags.js.map +1 -0
- package/build/treb-grid/src/util/fontmetrics.d.ts +21 -0
- package/build/treb-grid/src/util/fontmetrics.js +82 -0
- package/build/treb-grid/src/util/fontmetrics.js.map +1 -0
- package/build/treb-grid/src/util/ua.d.ts +33 -0
- package/build/treb-grid/src/util/ua.js +86 -0
- package/build/treb-grid/src/util/ua.js.map +1 -0
- package/build/treb-parser/src/csv-parser.d.ts +13 -0
- package/build/treb-parser/src/csv-parser.js +107 -0
- package/build/treb-parser/src/csv-parser.js.map +1 -0
- package/build/treb-parser/src/index.d.ts +4 -0
- package/build/treb-parser/src/index.js +25 -0
- package/build/treb-parser/src/index.js.map +1 -0
- package/build/treb-parser/src/md-parser.d.ts +97 -0
- package/build/treb-parser/src/md-parser.js +403 -0
- package/build/treb-parser/src/md-parser.js.map +1 -0
- package/build/treb-parser/src/parser-types.d.ts +345 -0
- package/build/treb-parser/src/parser-types.js +53 -0
- package/build/treb-parser/src/parser-types.js.map +1 -0
- package/build/treb-parser/src/parser.d.ts +422 -0
- package/build/treb-parser/src/parser.js +2418 -0
- package/build/treb-parser/src/parser.js.map +1 -0
- package/build/treb-utils/src/event_source.d.ts +34 -0
- package/build/treb-utils/src/event_source.js +110 -0
- package/build/treb-utils/src/event_source.js.map +1 -0
- package/build/treb-utils/src/ievent_source.d.ts +9 -0
- package/build/treb-utils/src/ievent_source.js +22 -0
- package/build/treb-utils/src/ievent_source.js.map +1 -0
- package/build/treb-utils/src/index.d.ts +6 -0
- package/build/treb-utils/src/index.js +30 -0
- package/build/treb-utils/src/index.js.map +1 -0
- package/build/treb-utils/src/measurement.d.ts +42 -0
- package/build/treb-utils/src/measurement.js +145 -0
- package/build/treb-utils/src/measurement.js.map +1 -0
- package/build/treb-utils/src/scale.d.ts +16 -0
- package/build/treb-utils/src/scale.js +106 -0
- package/build/treb-utils/src/scale.js.map +1 -0
- package/build/treb-utils/src/serialize_html.d.ts +5 -0
- package/build/treb-utils/src/serialize_html.js +128 -0
- package/build/treb-utils/src/serialize_html.js.map +1 -0
- package/build/treb-utils/src/validate_uri.d.ts +20 -0
- package/build/treb-utils/src/validate_uri.js +55 -0
- package/build/treb-utils/src/validate_uri.js.map +1 -0
- package/dist/{chunk-Z4XFMZ2X.mjs → chunk-E35ONJUS.mjs} +1 -1
- package/dist/treb-export-worker.mjs +2 -2
- package/dist/treb-spreadsheet.mjs +4 -4
- package/dist/treb.d.ts +1 -1
- package/esbuild-composite.mjs +5 -1
- package/package.json +67 -3
- package/treb-embed/src/custom-element/spreadsheet-constructor.ts +7 -3
- package/treb-embed/src/embedded-spreadsheet.ts +1 -1
- package/treb-grid/src/types/grid_options.ts +1 -1
- package/tsproject.json +1 -2
- package/dist/chunk-43DLP2OX.mjs +0 -11
- package/dist/chunk-4CKS56PE.mjs +0 -11
- package/dist/chunk-75PARUQE.mjs +0 -11
- package/dist/chunk-7QD63AZS.mjs +0 -24601
- package/dist/chunk-A55ARVRD.mjs +0 -11
- package/dist/chunk-DESAKYW4.mjs +0 -11
- package/dist/chunk-EQ2R5W6P.mjs +0 -24565
- package/dist/chunk-IYJU2J6D.mjs +0 -24601
- package/dist/chunk-KSJFPGXT.mjs +0 -11
- package/dist/chunk-MQK4DNXI.mjs +0 -11
- package/dist/chunk-ORQFKLXM.mjs +0 -24601
- package/dist/chunk-SFDNNDHY.mjs +0 -11
- package/dist/chunk-T47DX5MI.mjs +0 -11
- package/dist/chunk-T6ILBVEX.mjs +0 -11
- package/dist/chunk-TPRCDYYG.mjs +0 -11
- package/dist/chunk-YAHNOOHO.mjs +0 -11
- package/dist/chunk-YLCFKX2G.mjs +0 -24601
|
@@ -0,0 +1,1609 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of TREB.
|
|
3
|
+
*
|
|
4
|
+
* TREB is free software: you can redistribute it and/or modify it under the
|
|
5
|
+
* terms of the GNU General Public License as published by the Free Software
|
|
6
|
+
* Foundation, either version 3 of the License, or (at your option) any
|
|
7
|
+
* later version.
|
|
8
|
+
*
|
|
9
|
+
* TREB is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
10
|
+
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
11
|
+
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
12
|
+
* details.
|
|
13
|
+
*
|
|
14
|
+
* You should have received a copy of the GNU General Public License along
|
|
15
|
+
* with TREB. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
*
|
|
17
|
+
* Copyright 2022-2026 trebco, llc.
|
|
18
|
+
* info@treb.app
|
|
19
|
+
*
|
|
20
|
+
*/
|
|
21
|
+
import { TextPartFlag, Style, ValueType, Area, Rectangle, ResolveThemeColor, IsDefinedColor } from 'treb-base-types';
|
|
22
|
+
import { MDParser } from 'treb-parser';
|
|
23
|
+
import { BaseLayout, TileRange } from '../layout/base_layout';
|
|
24
|
+
import { Get as GetFontMetrics } from '../util/fontmetrics';
|
|
25
|
+
const DEFAULT_INDENT = ' '; // two spaces in the current font
|
|
26
|
+
export class TileRenderer {
|
|
27
|
+
theme;
|
|
28
|
+
layout;
|
|
29
|
+
model;
|
|
30
|
+
view;
|
|
31
|
+
options;
|
|
32
|
+
// removing last_font because we are doing more complex
|
|
33
|
+
// font manipulation for MD text
|
|
34
|
+
// protected last_font?: string;
|
|
35
|
+
cell_edge_buffer = 4;
|
|
36
|
+
/**
|
|
37
|
+
* a record of cell overflows, also used for merges if they cross tile
|
|
38
|
+
* boundaries. on render, we check if an overflow(ed) cell is dirty; if
|
|
39
|
+
* so, this forces update of dependent cells.
|
|
40
|
+
*/
|
|
41
|
+
overflow_areas = [];
|
|
42
|
+
buffer_canvas;
|
|
43
|
+
buffer_context;
|
|
44
|
+
buffer_canvas_size = { width: 256, height: 256 };
|
|
45
|
+
constructor(theme, layout, model, view, options) {
|
|
46
|
+
this.theme = theme;
|
|
47
|
+
this.layout = layout;
|
|
48
|
+
this.model = model;
|
|
49
|
+
this.view = view;
|
|
50
|
+
this.options = options;
|
|
51
|
+
this.buffer_canvas = layout.buffer_canvas;
|
|
52
|
+
this.buffer_canvas.width = this.buffer_canvas_size.width;
|
|
53
|
+
this.buffer_canvas.height = this.buffer_canvas_size.height;
|
|
54
|
+
// we need this attached to the document so it inherits fonts properly.
|
|
55
|
+
// in fact layout should manage it and then hand it to us (or we can grab it)
|
|
56
|
+
// this.buffer_canvas.classList.add('treb-buffer-canvas');
|
|
57
|
+
// document.body.appendChild(this.buffer_canvas);
|
|
58
|
+
const context = this.buffer_canvas.getContext('2d', { alpha: false });
|
|
59
|
+
if (context) {
|
|
60
|
+
const scale = this.layout.dpr;
|
|
61
|
+
this.buffer_context = context;
|
|
62
|
+
this.buffer_context.setTransform(scale, 0, 0, scale, 0, 0);
|
|
63
|
+
this.buffer_context.textAlign = 'left';
|
|
64
|
+
this.buffer_context.textBaseline = // BASELINE; //
|
|
65
|
+
'alphabetic';
|
|
66
|
+
}
|
|
67
|
+
// this.UpdateTheme();
|
|
68
|
+
/*
|
|
69
|
+
if (this.theme.grid_cell?.font_size?.value){
|
|
70
|
+
if (this.theme.grid_cell.font_size.unit === 'px') {
|
|
71
|
+
FontMetricsCache.base_size_px = this.theme.grid_cell.font_size.value;
|
|
72
|
+
}
|
|
73
|
+
else if (this.theme.grid_cell.font_size.unit === 'pt') {
|
|
74
|
+
FontMetricsCache.base_size_px = this.theme.grid_cell.font_size.value * 4 / 3;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
*/
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* we manage overflow blocks to simplify (more or less) rendering,
|
|
81
|
+
* but they break in the event of insert/delete row/column. we need
|
|
82
|
+
* to adjust, or perhaps flush, when we insert/delete columns.
|
|
83
|
+
*
|
|
84
|
+
*/
|
|
85
|
+
FlushOverflows() {
|
|
86
|
+
// flush all, mark dirty and drop areas.
|
|
87
|
+
const cells = this.view.active_sheet.cells;
|
|
88
|
+
for (const cell of cells.Iterate()) {
|
|
89
|
+
if (cell.renderer_data?.overflowed) {
|
|
90
|
+
cell.renderer_data = undefined;
|
|
91
|
+
cell.render_clean[this.view.view_index] = false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/*
|
|
95
|
+
cells.IterateAll(cell => {
|
|
96
|
+
if (cell.renderer_data?.overflowed) {
|
|
97
|
+
cell.renderer_data = undefined;
|
|
98
|
+
cell.render_clean[this.view.view_index] = false;
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
*/
|
|
102
|
+
for (const overflow_area of this.overflow_areas) {
|
|
103
|
+
overflow_area.tile.dirty = true;
|
|
104
|
+
}
|
|
105
|
+
this.overflow_areas = [];
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* new method for measuring text, intended to take into account
|
|
109
|
+
* indentation and wrapping, scale, and all the other stuff.
|
|
110
|
+
*/
|
|
111
|
+
MeasureText(cell, current_width, override_scale) {
|
|
112
|
+
// we need a canvas. I guess we can just randomly use one?
|
|
113
|
+
const tile = this.layout.grid_tiles[0][0];
|
|
114
|
+
// we need fonts, and check if we need variants
|
|
115
|
+
const scale = override_scale ?? this.layout.scale;
|
|
116
|
+
const style = cell.style || {};
|
|
117
|
+
const base_font = Style.CompositeFont(this.theme.grid_cell_font_size, style, scale, this.theme);
|
|
118
|
+
const metrics = GetFontMetrics(base_font.font, base_font.variants);
|
|
119
|
+
const fonts = {
|
|
120
|
+
base: base_font.font,
|
|
121
|
+
strong: Style.CompositeFont(this.theme.grid_cell_font_size, { ...style, bold: true }, scale, this.theme).font,
|
|
122
|
+
emphasis: Style.CompositeFont(this.theme.grid_cell_font_size, { ...style, italic: true }, scale, this.theme).font,
|
|
123
|
+
strong_emphasis: Style.CompositeFont(this.theme.grid_cell_font_size, { ...style, bold: true, italic: true }, scale, this.theme).font,
|
|
124
|
+
};
|
|
125
|
+
if (base_font.variants) {
|
|
126
|
+
tile.style.fontVariant = base_font.variants;
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
tile.style.fontVariant = '';
|
|
130
|
+
}
|
|
131
|
+
const context = tile.getContext('2d', { alpha: false });
|
|
132
|
+
if (!context) {
|
|
133
|
+
throw new Error('context failed');
|
|
134
|
+
}
|
|
135
|
+
const prepped = this.PrepText(context, fonts, cell, current_width);
|
|
136
|
+
const width = prepped.width;
|
|
137
|
+
const height = metrics.height * prepped.strings.length;
|
|
138
|
+
return { width, height };
|
|
139
|
+
}
|
|
140
|
+
/* *
|
|
141
|
+
* use one of the tile contexts to measure text. we are using the tile
|
|
142
|
+
* context because it's attached to the DOM, and style is applied. we need
|
|
143
|
+
* that for the root font size, in case font size in the style is relative
|
|
144
|
+
* (which it should be).
|
|
145
|
+
*
|
|
146
|
+
* we could use the buffer context, if that were attached to the DOM, but
|
|
147
|
+
* at the moment it is not so this is a shortcut. since we're not actually
|
|
148
|
+
* painting, it's not too bad, but we still fetch the context every time.
|
|
149
|
+
* hopefully it's cached.
|
|
150
|
+
*
|
|
151
|
+
* FIXME: if you're doing it this way, maybe pass in an array of strings/
|
|
152
|
+
* fonts, to avoid getting the context every time?
|
|
153
|
+
*
|
|
154
|
+
* @param text
|
|
155
|
+
* @param font
|
|
156
|
+
* /
|
|
157
|
+
public MeasureText(text: string, font?: string): TextMetrics {
|
|
158
|
+
|
|
159
|
+
const context = this.layout.grid_tiles[0][0].getContext('2d', { alpha: false });
|
|
160
|
+
|
|
161
|
+
if (!context) {
|
|
162
|
+
throw new Error('invalid context');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (font) {
|
|
166
|
+
context.font = font;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return context.measureText(text);
|
|
170
|
+
}
|
|
171
|
+
*/
|
|
172
|
+
/**
|
|
173
|
+
* when drawing to the buffered canvas, (1) ensure it's large enough,
|
|
174
|
+
* and (2) set transform as necessary (we may be overflowing to the left).
|
|
175
|
+
*/
|
|
176
|
+
EnsureBuffer(width = 0, height = 0, offset = 0) {
|
|
177
|
+
// console.info('eb', width, height, offset);
|
|
178
|
+
const scale = this.layout.dpr;
|
|
179
|
+
width = width * scale;
|
|
180
|
+
height = height * scale;
|
|
181
|
+
offset = offset * scale;
|
|
182
|
+
if (width > this.buffer_canvas_size.width
|
|
183
|
+
|| height > this.buffer_canvas_size.height) {
|
|
184
|
+
this.buffer_canvas_size.width = Math.max(Math.ceil(width / 256) * 256, this.buffer_canvas_size.width);
|
|
185
|
+
this.buffer_canvas_size.height = Math.max(Math.ceil(height / 256) * 256, this.buffer_canvas_size.height);
|
|
186
|
+
// console.info('size ->', this.buffer_canvas_size);
|
|
187
|
+
this.buffer_canvas.width = this.buffer_canvas_size.width;
|
|
188
|
+
this.buffer_canvas.height = this.buffer_canvas_size.height;
|
|
189
|
+
const context = this.buffer_canvas.getContext('2d', { alpha: false });
|
|
190
|
+
if (context) {
|
|
191
|
+
this.buffer_context = context;
|
|
192
|
+
this.buffer_context.textAlign = 'left';
|
|
193
|
+
this.buffer_context.textBaseline = // BASELINE;
|
|
194
|
+
'alphabetic';
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
this.buffer_context.setTransform(scale, 0, 0, scale, offset, 0);
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* check all overflow areas. if any elements are dirty, mark all elements
|
|
201
|
+
* as dirty (FIXME: and remove the list?)
|
|
202
|
+
*/
|
|
203
|
+
OverflowDirty(full_tile = false) {
|
|
204
|
+
const mutated = [];
|
|
205
|
+
for (const overflow of this.overflow_areas) {
|
|
206
|
+
const row = overflow.area.start.row;
|
|
207
|
+
let dirty = full_tile; // false;
|
|
208
|
+
if (!dirty) {
|
|
209
|
+
for (let column = overflow.area.start.column; !dirty && column <= overflow.area.end.column; column++) {
|
|
210
|
+
const cell = this.view.active_sheet.cells.GetCell({ row, column }, false);
|
|
211
|
+
dirty = !!(cell && !cell.render_clean[this.view.view_index]);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (dirty) {
|
|
215
|
+
for (let column = overflow.area.start.column; column <= overflow.area.end.column; column++) {
|
|
216
|
+
const cell = this.view.active_sheet.cells.GetCell({ row, column }, false);
|
|
217
|
+
if (cell) {
|
|
218
|
+
cell.render_clean[this.view.view_index] = false;
|
|
219
|
+
if (cell.renderer_data && cell.renderer_data.overflowed) {
|
|
220
|
+
cell.renderer_data = undefined;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
overflow.tile.dirty = true;
|
|
225
|
+
}
|
|
226
|
+
else
|
|
227
|
+
mutated.push(overflow);
|
|
228
|
+
}
|
|
229
|
+
this.overflow_areas = mutated;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
*
|
|
233
|
+
*/
|
|
234
|
+
RenderCorner( /* selection: GridSelection */) {
|
|
235
|
+
const corner = this.layout.corner_canvas;
|
|
236
|
+
const context = corner.getContext('2d', { alpha: false });
|
|
237
|
+
if (!context) {
|
|
238
|
+
throw new Error('invalid context');
|
|
239
|
+
}
|
|
240
|
+
const font_info = Style.CompositeFont(this.theme.grid_cell_font_size, this.theme.headers || {}, this.layout.scale, this.theme);
|
|
241
|
+
const m2 = GetFontMetrics(font_info.font, font_info.variants);
|
|
242
|
+
const scale = this.layout.dpr;
|
|
243
|
+
const header_size = this.layout.header_offset;
|
|
244
|
+
let x = header_size.x;
|
|
245
|
+
for (let i = 0; i < this.view.active_sheet.freeze.columns; i++) {
|
|
246
|
+
x += this.layout.ColumnWidth(i);
|
|
247
|
+
}
|
|
248
|
+
let y = header_size.y;
|
|
249
|
+
for (let i = 0; i < this.view.active_sheet.freeze.rows; i++) {
|
|
250
|
+
y += this.layout.RowHeight(i);
|
|
251
|
+
}
|
|
252
|
+
context.setTransform(scale, 0, 0, scale, 0, 0);
|
|
253
|
+
context.fillStyle = this.theme.headers?.fill ? ResolveThemeColor(this.theme, this.theme.headers.fill) : '';
|
|
254
|
+
context.fillRect(0, 0, x, header_size.y);
|
|
255
|
+
context.fillRect(0, 0, header_size.x, y);
|
|
256
|
+
// we have to split this into two parts because of the new
|
|
257
|
+
// header grid color. do the header part first...
|
|
258
|
+
context.strokeStyle = this.theme.headers_grid_color || '';
|
|
259
|
+
context.beginPath();
|
|
260
|
+
context.moveTo(header_size.x - 0.5, 0);
|
|
261
|
+
context.lineTo(header_size.x - 0.5, header_size.y);
|
|
262
|
+
context.moveTo(0, header_size.y - 0.5);
|
|
263
|
+
context.lineTo(header_size.x, header_size.y - 0.5);
|
|
264
|
+
context.stroke();
|
|
265
|
+
// actually we can bail out first
|
|
266
|
+
if (!this.view.active_sheet.freeze.columns && !this.view.active_sheet.freeze.rows)
|
|
267
|
+
return;
|
|
268
|
+
// then do the other part with the regular grid color
|
|
269
|
+
context.strokeStyle = this.theme.grid_color || '';
|
|
270
|
+
context.beginPath();
|
|
271
|
+
if (y !== header_size.y) {
|
|
272
|
+
context.moveTo(header_size.x - 0.5, header_size.y);
|
|
273
|
+
context.lineTo(header_size.x - 0.5, y);
|
|
274
|
+
}
|
|
275
|
+
if (x !== header_size.x) {
|
|
276
|
+
context.moveTo(header_size.x, header_size.y - 0.5);
|
|
277
|
+
context.lineTo(x, header_size.y - 0.5);
|
|
278
|
+
}
|
|
279
|
+
context.stroke();
|
|
280
|
+
// here we go back to the header grid color for the breaks
|
|
281
|
+
context.strokeStyle = this.theme.headers_grid_color || '';
|
|
282
|
+
// NOTE: if headers are hidden (which is done by setting width/height to
|
|
283
|
+
// 0 or 1 pixel) we don't want to render them here.
|
|
284
|
+
context.textAlign = 'center';
|
|
285
|
+
context.textBaseline = 'middle'; // FUXME
|
|
286
|
+
context.font = font_info.font; // Style.Font(this.theme.headers||{}, this.layout.scale);
|
|
287
|
+
context.fillStyle = ResolveThemeColor(this.theme, this.theme.headers?.text);
|
|
288
|
+
if (this.view.active_sheet.freeze.rows && this.layout.header_offset.x > 1) {
|
|
289
|
+
context.setTransform(scale, 0, 0, scale, 0, 0);
|
|
290
|
+
context.translate(0, header_size.y);
|
|
291
|
+
context.beginPath();
|
|
292
|
+
context.moveTo(0, 0 - 0.5);
|
|
293
|
+
context.lineTo(header_size.x, 0 - 0.5);
|
|
294
|
+
context.stroke();
|
|
295
|
+
this.RenderRowLabels(context, 0, this.view.active_sheet.freeze.rows - 1, // m2.block);
|
|
296
|
+
m2.height);
|
|
297
|
+
}
|
|
298
|
+
if (this.view.active_sheet.freeze.columns && this.layout.header_offset.y > 1) {
|
|
299
|
+
context.setTransform(scale, 0, 0, scale, 0, 0);
|
|
300
|
+
context.translate(header_size.x, 0);
|
|
301
|
+
// what is this doing? it's not consistent with the column paint routine...
|
|
302
|
+
// A: it's different. it's drawing the line at the left of the header,
|
|
303
|
+
// which otherwise wouldn't render [really?]. we already have the line
|
|
304
|
+
// at the bottom rendered above.
|
|
305
|
+
context.beginPath();
|
|
306
|
+
context.moveTo(0 - 0.5, 0);
|
|
307
|
+
context.lineTo(0 - 0.5, header_size.y);
|
|
308
|
+
context.stroke();
|
|
309
|
+
this.RenderColumnLabels(context, 0, this.view.active_sheet.freeze.columns - 1);
|
|
310
|
+
}
|
|
311
|
+
/////
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* unifying because headers and corner both render labels.
|
|
315
|
+
*
|
|
316
|
+
* @param context
|
|
317
|
+
* @param column
|
|
318
|
+
* @param end
|
|
319
|
+
*/
|
|
320
|
+
RenderColumnLabels(context, column, end) {
|
|
321
|
+
const header_y = this.layout.header_offset.y;
|
|
322
|
+
if (header_y <= 1) {
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
context.fillStyle = ResolveThemeColor(this.theme, this.theme.headers?.text, 0);
|
|
326
|
+
context.beginPath();
|
|
327
|
+
for (; column <= end; column++) {
|
|
328
|
+
const width = this.layout.ColumnWidth(column);
|
|
329
|
+
const text = Area.ColumnToLabel(column);
|
|
330
|
+
const metrics = context.measureText(text);
|
|
331
|
+
if (width > metrics.width) {
|
|
332
|
+
context.fillText(text, width / 2, header_y / 2 + 1);
|
|
333
|
+
}
|
|
334
|
+
context.moveTo(width - 0.5, 0);
|
|
335
|
+
context.lineTo(width - 0.5, header_y);
|
|
336
|
+
context.translate(width, 0);
|
|
337
|
+
}
|
|
338
|
+
context.stroke();
|
|
339
|
+
}
|
|
340
|
+
RenderRowLabels(context, row, end, block) {
|
|
341
|
+
const header_x = this.layout.header_offset.x;
|
|
342
|
+
if (header_x <= 1) {
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
context.fillStyle = ResolveThemeColor(this.theme, this.theme.headers?.text, 0);
|
|
346
|
+
context.beginPath();
|
|
347
|
+
for (; row <= end; row++) {
|
|
348
|
+
const height = this.layout.RowHeight(row);
|
|
349
|
+
if (height >= block * 1.2) {
|
|
350
|
+
context.fillText(`${row + 1}`, header_x / 2, height / 2 + 1);
|
|
351
|
+
}
|
|
352
|
+
context.moveTo(0, height - 0.5);
|
|
353
|
+
context.lineTo(header_x, height - 0.5);
|
|
354
|
+
context.translate(0, height);
|
|
355
|
+
}
|
|
356
|
+
context.stroke();
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
*/
|
|
360
|
+
RenderHeaders(tiles /*, selection: GridSelection*/, force = false) {
|
|
361
|
+
const scale = this.layout.dpr;
|
|
362
|
+
const header_size = this.layout.header_offset;
|
|
363
|
+
const font_info = Style.CompositeFont(this.theme.grid_cell_font_size, this.theme.headers || {}, this.layout.scale, this.theme);
|
|
364
|
+
const m2 = GetFontMetrics(font_info.font, font_info.variants);
|
|
365
|
+
for (let column = tiles.start.column; column <= tiles.end.column; column++) {
|
|
366
|
+
const tile = this.layout.column_header_tiles[column];
|
|
367
|
+
if (tile.dirty || force) {
|
|
368
|
+
const context = tile.getContext('2d', { alpha: false });
|
|
369
|
+
if (!context)
|
|
370
|
+
continue;
|
|
371
|
+
context.setTransform(scale, 0, 0, scale, 0, 0);
|
|
372
|
+
context.textAlign = 'center';
|
|
373
|
+
context.textBaseline = 'middle'; // FIXME
|
|
374
|
+
context.font = font_info.font; // Style.Font(this.theme.headers||{}, this.layout.scale);
|
|
375
|
+
if (font_info.variants) {
|
|
376
|
+
tile.style.fontVariant = font_info.variants;
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
tile.style.fontVariant = '';
|
|
380
|
+
}
|
|
381
|
+
context.fillStyle = this.theme.headers?.fill ? ResolveThemeColor(this.theme, this.theme.headers.fill) : '';
|
|
382
|
+
context.fillRect(0, 0, tile.logical_size.width, this.layout.header_offset.y);
|
|
383
|
+
// context.strokeStyle = this.theme.grid_color || '';
|
|
384
|
+
context.strokeStyle = this.theme.headers_grid_color || '';
|
|
385
|
+
// this draws a line at the bottom of the header
|
|
386
|
+
// (using regular grid color)
|
|
387
|
+
// looks better using the header grid color
|
|
388
|
+
context.beginPath();
|
|
389
|
+
context.moveTo(0, header_size.y - 0.5);
|
|
390
|
+
context.lineTo(tile.logical_size.width, header_size.y - 0.5);
|
|
391
|
+
context.stroke();
|
|
392
|
+
// then we switch to the header color for the edges
|
|
393
|
+
context.strokeStyle = this.theme.headers_grid_color || '';
|
|
394
|
+
this.RenderColumnLabels(context, tile.first_cell.column, tile.last_cell.column);
|
|
395
|
+
tile.dirty = false;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
for (let row = tiles.start.row; row <= tiles.end.row; row++) {
|
|
399
|
+
const tile = this.layout.row_header_tiles[row];
|
|
400
|
+
if (tile.dirty || force) {
|
|
401
|
+
const context = tile.getContext('2d', { alpha: false });
|
|
402
|
+
if (!context)
|
|
403
|
+
continue;
|
|
404
|
+
context.fillStyle = this.theme.headers?.fill ? ResolveThemeColor(this.theme, this.theme.headers.fill) : '';
|
|
405
|
+
context.setTransform(scale, 0, 0, scale, 0, 0);
|
|
406
|
+
context.textAlign = 'center';
|
|
407
|
+
context.textBaseline = 'middle'; // FIXME
|
|
408
|
+
// context.font = Style.Font(this.theme.headers||{}, this.layout.scale);
|
|
409
|
+
context.font = font_info.font; // Style.Font(this.theme.headers||{}, this.layout.scale);
|
|
410
|
+
if (font_info.variants) {
|
|
411
|
+
tile.style.fontVariant = font_info.variants;
|
|
412
|
+
}
|
|
413
|
+
else {
|
|
414
|
+
tile.style.fontVariant = '';
|
|
415
|
+
}
|
|
416
|
+
context.fillRect(0, 0, this.layout.header_offset.x, tile.logical_size.height);
|
|
417
|
+
// context.strokeStyle = this.theme.grid_color || '';
|
|
418
|
+
context.strokeStyle = this.theme.headers_grid_color || '';
|
|
419
|
+
context.beginPath();
|
|
420
|
+
context.moveTo(header_size.x - 0.5, 0);
|
|
421
|
+
context.lineTo(header_size.x - 0.5, tile.logical_size.height);
|
|
422
|
+
context.stroke();
|
|
423
|
+
context.strokeStyle = this.theme.headers_grid_color || '';
|
|
424
|
+
this.RenderRowLabels(context, tile.first_cell.row, tile.last_cell.row, // m2.block);
|
|
425
|
+
m2.height);
|
|
426
|
+
tile.dirty = false;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
if (this.view.active_sheet.freeze.rows || this.view.active_sheet.freeze.columns) {
|
|
430
|
+
this.RenderCorner();
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
*
|
|
435
|
+
* @param tile starting tile
|
|
436
|
+
* @param scale scale
|
|
437
|
+
* @param dx tile offset, in tiles
|
|
438
|
+
* @param dy tile offset, in tiles
|
|
439
|
+
* @param left (original) translation, in scaled pixels
|
|
440
|
+
* @param top (original) translation, in scaled pixels
|
|
441
|
+
* @param result buffer info
|
|
442
|
+
*/
|
|
443
|
+
CopyToAdjacent(tile, scale, dx, dy, left, top, result) {
|
|
444
|
+
const adjacent = this.layout.AdjacentTile(tile, dy, dx);
|
|
445
|
+
if (!adjacent)
|
|
446
|
+
return; // FIXME: warn?
|
|
447
|
+
let x = left;
|
|
448
|
+
let y = top;
|
|
449
|
+
if (dx > 0) {
|
|
450
|
+
x = left - (tile.pixel_end.x - tile.pixel_start.x) * scale;
|
|
451
|
+
}
|
|
452
|
+
else if (dx < 0) {
|
|
453
|
+
x = left + (adjacent.pixel_end.x - adjacent.pixel_start.x) * scale;
|
|
454
|
+
}
|
|
455
|
+
if (dy > 0) {
|
|
456
|
+
y = top - (tile.pixel_end.y - tile.pixel_start.y) * scale;
|
|
457
|
+
}
|
|
458
|
+
const context = adjacent.getContext('2d', { alpha: false });
|
|
459
|
+
if (context) {
|
|
460
|
+
context.setTransform(scale, 0, 0, scale, x, y);
|
|
461
|
+
context.drawImage(this.buffer_canvas, 0, 0, (result.width || 0) * scale, (result.height || 0) * scale, result.left || 0, 0, result.width || 0, result.height || 0);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
/** render a tile */
|
|
465
|
+
Render(tile) {
|
|
466
|
+
// can we assume this is always set? this feels sloppy, and
|
|
467
|
+
// it feels like something we'd like to turn off... can we
|
|
468
|
+
// fetch the canvas more than once? not sure what the impact of
|
|
469
|
+
// that would be.
|
|
470
|
+
// tile.style.fontVariantNumeric = 'lining-nums tabular-nums';
|
|
471
|
+
const context = tile.getContext('2d', { alpha: false });
|
|
472
|
+
if (!context) {
|
|
473
|
+
return;
|
|
474
|
+
} // should throw
|
|
475
|
+
context.textBaseline = // BASELINE;
|
|
476
|
+
'alphabetic';
|
|
477
|
+
const scale = this.layout.dpr;
|
|
478
|
+
context.setTransform(scale, 0, 0, scale, 0, 0);
|
|
479
|
+
let left = 0;
|
|
480
|
+
let top = 0;
|
|
481
|
+
for (let column = tile.first_cell.column; column <= tile.last_cell.column; column++) {
|
|
482
|
+
const width = this.layout.ColumnWidth(column);
|
|
483
|
+
if (!width)
|
|
484
|
+
continue;
|
|
485
|
+
top = 0;
|
|
486
|
+
for (let row = tile.first_cell.row; row <= tile.last_cell.row; row++) {
|
|
487
|
+
const height = this.layout.RowHeight(row);
|
|
488
|
+
if (height) {
|
|
489
|
+
context.setTransform(scale, 0, 0, scale, left, top);
|
|
490
|
+
const cell = this.view.active_sheet.CellData({ row, column });
|
|
491
|
+
if (tile.needs_full_repaint || !cell.render_clean[this.view.view_index]) {
|
|
492
|
+
const result = this.RenderCell(tile, cell, context, { row, column }, width, height, (tile.pixel_start.x + left), (tile.pixel_start.y + top));
|
|
493
|
+
// render_list.push({row, column, cell});
|
|
494
|
+
if (result.tile_overflow_right) {
|
|
495
|
+
this.CopyToAdjacent(tile, scale, 1, 0, left, top, result);
|
|
496
|
+
}
|
|
497
|
+
if (result.tile_overflow_left) {
|
|
498
|
+
this.CopyToAdjacent(tile, scale, -1, 0, left, top, result);
|
|
499
|
+
}
|
|
500
|
+
if (result.tile_overflow_bottom) {
|
|
501
|
+
this.CopyToAdjacent(tile, scale, 0, 1, left, top, result);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
top += (height * scale);
|
|
506
|
+
}
|
|
507
|
+
left += (width * scale);
|
|
508
|
+
}
|
|
509
|
+
if (!this.view.active_sheet.freeze.rows && !this.view.active_sheet.freeze.columns)
|
|
510
|
+
return; // render_list;
|
|
511
|
+
// paint to headers
|
|
512
|
+
let copy_height = 0;
|
|
513
|
+
let copy_width = 0;
|
|
514
|
+
if (tile.first_cell.row <= this.view.active_sheet.freeze.rows - 1) {
|
|
515
|
+
for (let i = tile.first_cell.row; i < this.view.active_sheet.freeze.rows && i <= tile.last_cell.row; i++) {
|
|
516
|
+
copy_height += this.layout.RowHeight(i);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
if (tile.first_cell.column <= this.view.active_sheet.freeze.columns - 1) {
|
|
520
|
+
for (let i = tile.first_cell.column; i < this.view.active_sheet.freeze.columns && i <= tile.last_cell.column; i++) {
|
|
521
|
+
copy_width += this.layout.ColumnWidth(i);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
if (copy_height) {
|
|
525
|
+
// get tile header
|
|
526
|
+
const header = this.layout.frozen_row_tiles[tile.tile_position.column];
|
|
527
|
+
if (!header)
|
|
528
|
+
throw new Error('can\'t find matching header tile');
|
|
529
|
+
const header_context = header.getContext('2d', { alpha: true });
|
|
530
|
+
if (!header_context)
|
|
531
|
+
throw new Error('header context failed');
|
|
532
|
+
// FIXME: offset for !first tile
|
|
533
|
+
header_context.setTransform(scale, 0, 0, scale, 0, 0); // this.model.sheet.header_offset.y * scale);
|
|
534
|
+
header_context.drawImage(tile, 0, 0, tile.logical_size.width * scale, copy_height * scale, 0, 0, tile.logical_size.width, copy_height);
|
|
535
|
+
}
|
|
536
|
+
if (copy_width) {
|
|
537
|
+
// get tile header
|
|
538
|
+
const header = this.layout.frozen_column_tiles[tile.tile_position.row];
|
|
539
|
+
if (!header)
|
|
540
|
+
throw new Error('can\'t find matching header tile');
|
|
541
|
+
const header_context = header.getContext('2d', { alpha: true });
|
|
542
|
+
if (!header_context)
|
|
543
|
+
throw new Error('header context failed');
|
|
544
|
+
// FIXME: offset for !first tile
|
|
545
|
+
header_context.setTransform(scale, 0, 0, scale, 0, 0);
|
|
546
|
+
header_context.drawImage(tile, 0, 0, copy_width * scale, tile.logical_size.height * scale, 0, 0, copy_width, tile.logical_size.height);
|
|
547
|
+
}
|
|
548
|
+
if (copy_width && copy_height) {
|
|
549
|
+
const corner_context = this.layout.corner_canvas.getContext('2d', { alpha: 'false' });
|
|
550
|
+
if (!corner_context)
|
|
551
|
+
throw new Error('corner context failed');
|
|
552
|
+
// FIXME: offset for !first tile
|
|
553
|
+
corner_context.setTransform(scale, 0, 0, scale, this.layout.header_offset.x * scale, this.layout.header_offset.y * scale);
|
|
554
|
+
corner_context.drawImage(tile, 0, 0, copy_width * scale, copy_height * scale, 0, 0, copy_width, copy_height);
|
|
555
|
+
}
|
|
556
|
+
return; // render_list;
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* split and measure text. can be cached. there are actually two completely
|
|
560
|
+
* separate operations here, which we're consolidating for convenience (and
|
|
561
|
+
* because they never overlap).
|
|
562
|
+
*
|
|
563
|
+
* UPDATED returning a 2d array, where the first dimension represents lines
|
|
564
|
+
* and the second dimension represents components
|
|
565
|
+
*/
|
|
566
|
+
PrepText(context, fonts, cell, cell_width /*, override_text?: string*/) {
|
|
567
|
+
const strings = [];
|
|
568
|
+
const style = cell.style || {};
|
|
569
|
+
let pad_entry;
|
|
570
|
+
let composite_width = 0;
|
|
571
|
+
// -------------------------------------------------------------------------
|
|
572
|
+
// moved translated boolean formatting here. I don't like this. it
|
|
573
|
+
// should be in sheet (at least that's where the rest of formatting
|
|
574
|
+
// is), but sheet doesn't have a reference to data model (and because
|
|
575
|
+
// sheets are owned by data model, I don't want to add a circular ref).
|
|
576
|
+
if (cell.rendered_type === ValueType.boolean) {
|
|
577
|
+
const value = cell.calculated_type ? cell.calculated : cell.value;
|
|
578
|
+
cell.formatted = value ? (this.model.language_model?.boolean_true || 'TRUE') : (this.model.language_model?.boolean_false || 'FALSE');
|
|
579
|
+
}
|
|
580
|
+
// -------------------------------------------------------------------------
|
|
581
|
+
let override_formatting;
|
|
582
|
+
let formatted = cell.editing ? '' : cell.formatted; // <-- empty on editing, to remove overflows
|
|
583
|
+
// remove existing indent, if any
|
|
584
|
+
if (Array.isArray(formatted)) {
|
|
585
|
+
formatted = formatted.filter(test => test.flag !== TextPartFlag.indent);
|
|
586
|
+
}
|
|
587
|
+
// precalculate indent as string so we can use layout
|
|
588
|
+
let indent = '';
|
|
589
|
+
let align;
|
|
590
|
+
if (style.indent) {
|
|
591
|
+
for (let i = 0; i < style.indent; i++) {
|
|
592
|
+
indent += DEFAULT_INDENT;
|
|
593
|
+
}
|
|
594
|
+
align = style.horizontal_align;
|
|
595
|
+
// default might be left or right based on type
|
|
596
|
+
if (!align) {
|
|
597
|
+
align = (cell.type === ValueType.number ||
|
|
598
|
+
cell.calculated_type === ValueType.number ||
|
|
599
|
+
cell.type === ValueType.complex ||
|
|
600
|
+
cell.calculated_type === ValueType.complex) ? 'right' : 'left';
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
if (Array.isArray(formatted)) {
|
|
604
|
+
// type 1 is a multi-part formatted string; used for number formats.
|
|
605
|
+
// we support invisible characters and padded (expanded) characters
|
|
606
|
+
// FIXME: is there any case where this would include md? ...
|
|
607
|
+
// (potentially yes? what happens if you have a string in a number-formatted cell?)
|
|
608
|
+
// this is a single line, with number formatting
|
|
609
|
+
if (indent) {
|
|
610
|
+
if (align === 'right') {
|
|
611
|
+
formatted.push({ text: indent, flag: TextPartFlag.indent });
|
|
612
|
+
}
|
|
613
|
+
else if (align === 'left' || typeof align === 'undefined') {
|
|
614
|
+
formatted.unshift({ text: indent, flag: TextPartFlag.indent });
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
for (const part of formatted) {
|
|
618
|
+
if (part.flag === TextPartFlag.formatting) {
|
|
619
|
+
override_formatting = part.text;
|
|
620
|
+
continue;
|
|
621
|
+
}
|
|
622
|
+
const mt_width = context.measureText(part.text).width;
|
|
623
|
+
const render_part = {
|
|
624
|
+
width: mt_width,
|
|
625
|
+
text: part.text,
|
|
626
|
+
hidden: part.flag === TextPartFlag.hidden,
|
|
627
|
+
indent: part.flag === TextPartFlag.indent,
|
|
628
|
+
};
|
|
629
|
+
strings.push(render_part);
|
|
630
|
+
if (part.flag === TextPartFlag.padded) {
|
|
631
|
+
pad_entry = render_part;
|
|
632
|
+
}
|
|
633
|
+
else {
|
|
634
|
+
composite_width += mt_width;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
if (pad_entry) {
|
|
638
|
+
const text = pad_entry.text;
|
|
639
|
+
const text_width = pad_entry.width;
|
|
640
|
+
const balance = cell_width - composite_width - (2 * this.cell_edge_buffer);
|
|
641
|
+
pad_entry.width = Math.max(0, balance);
|
|
642
|
+
if (balance > 0) {
|
|
643
|
+
const count = Math.floor(balance / text_width);
|
|
644
|
+
for (let i = 1; i < count; i++) {
|
|
645
|
+
pad_entry.text += text;
|
|
646
|
+
}
|
|
647
|
+
composite_width = cell_width - (2 * this.cell_edge_buffer);
|
|
648
|
+
}
|
|
649
|
+
else {
|
|
650
|
+
pad_entry.text = '';
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
return { strings: [strings], format: override_formatting, width: composite_width };
|
|
654
|
+
}
|
|
655
|
+
else if (formatted) {
|
|
656
|
+
// type 2 is a single string, but may be split into newlines either
|
|
657
|
+
// explicitly or implicitly via wrap
|
|
658
|
+
// ALSO we don't show leading apostrophes, as those indicate a string
|
|
659
|
+
if (cell.type === ValueType.string && formatted[0] === '\'') {
|
|
660
|
+
formatted = formatted.slice(1);
|
|
661
|
+
}
|
|
662
|
+
let md;
|
|
663
|
+
if (this.options.markdown) {
|
|
664
|
+
md = MDParser.instance.Parse(formatted);
|
|
665
|
+
}
|
|
666
|
+
else {
|
|
667
|
+
md = MDParser.instance.Dummy(formatted);
|
|
668
|
+
context.font = fonts.base; // never changes
|
|
669
|
+
}
|
|
670
|
+
// if we are not wrapping, we don't have to do any trimming. if we
|
|
671
|
+
// are wrapping, leave whitespace attached to the front; possibly trim
|
|
672
|
+
// whitespace in between tokens (this should be attached to tokens, but
|
|
673
|
+
// possibly not...)
|
|
674
|
+
let max_width = 0;
|
|
675
|
+
// for wrapping
|
|
676
|
+
let bound = cell_width - (2 * this.cell_edge_buffer);
|
|
677
|
+
const strings = [];
|
|
678
|
+
if (style.wrap) {
|
|
679
|
+
const indent_width = (indent && style.horizontal_align !== 'center') ? context.measureText(indent).width : 0;
|
|
680
|
+
bound -= indent_width;
|
|
681
|
+
for (const line of md) {
|
|
682
|
+
// we should probably normalize whitespace -- because formatting
|
|
683
|
+
// may put some whitespace before tokens, other whitespace after
|
|
684
|
+
// tokens, and so on. it's confusing.
|
|
685
|
+
for (let i = 1; i < line.length; i++) {
|
|
686
|
+
const test = line[i].text.match(/^(\s+)/);
|
|
687
|
+
if (test) {
|
|
688
|
+
line[i - 1].text += test[1];
|
|
689
|
+
line[i].text = line[i].text.replace(/^\s+/, '');
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
const words = [];
|
|
693
|
+
for (const element of line) {
|
|
694
|
+
if (this.options.markdown) {
|
|
695
|
+
if (element.strong && element.emphasis) {
|
|
696
|
+
context.font = fonts.strong_emphasis;
|
|
697
|
+
}
|
|
698
|
+
else if (element.strong) {
|
|
699
|
+
context.font = fonts.strong;
|
|
700
|
+
}
|
|
701
|
+
else if (element.emphasis) {
|
|
702
|
+
context.font = fonts.emphasis;
|
|
703
|
+
}
|
|
704
|
+
else {
|
|
705
|
+
context.font = fonts.base;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
const split = element.text.match(/\S+\s*/g); // preserve extra whitespace on the same line...
|
|
709
|
+
if (split && split.length) {
|
|
710
|
+
for (const word of split) {
|
|
711
|
+
// FIXME: maybe overoptimizing, but this is measuring the same
|
|
712
|
+
// text twice; could reduce...
|
|
713
|
+
const trimmed = context.measureText(word.trim()).width;
|
|
714
|
+
const width = context.measureText(word).width; // including trailing whitespace
|
|
715
|
+
words.push({ part: element, text: word, trimmed, width });
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
// now we can construct wrapped lines. we don't split words, so
|
|
720
|
+
// we always have at least one word on a line.
|
|
721
|
+
while (words.length) {
|
|
722
|
+
// add first word. line length is _trimmed_ length.
|
|
723
|
+
let last = words.shift(); // NOT undefined
|
|
724
|
+
const line2 = [last];
|
|
725
|
+
let line_width = last.trimmed;
|
|
726
|
+
// add more words? check bounds first
|
|
727
|
+
while (line_width < bound && words.length) {
|
|
728
|
+
// we're holding the trim width on the last word, but to
|
|
729
|
+
// test we need the untrimmed width
|
|
730
|
+
const word = words[0];
|
|
731
|
+
const test = line_width - last.trimmed + last.width + word.trimmed;
|
|
732
|
+
if (test >= bound) {
|
|
733
|
+
break; // line finished
|
|
734
|
+
}
|
|
735
|
+
// add this word to the line, remove it from the stack
|
|
736
|
+
last = word;
|
|
737
|
+
line2.push(word);
|
|
738
|
+
line_width = test;
|
|
739
|
+
words.shift();
|
|
740
|
+
max_width = Math.max(max_width, line_width);
|
|
741
|
+
}
|
|
742
|
+
// trim the last word, then insert a row (we're relying on the
|
|
743
|
+
// fact that this points at the last entry in the array)
|
|
744
|
+
last.text = last.text.trim();
|
|
745
|
+
last.width = last.trimmed;
|
|
746
|
+
max_width = Math.max(max_width, last.width);
|
|
747
|
+
const line_string = line2.map((metric) => {
|
|
748
|
+
return {
|
|
749
|
+
...metric.part,
|
|
750
|
+
hidden: false,
|
|
751
|
+
width: metric.width,
|
|
752
|
+
text: metric.text,
|
|
753
|
+
};
|
|
754
|
+
});
|
|
755
|
+
if (style.indent) {
|
|
756
|
+
if (align === 'right') {
|
|
757
|
+
line_string.push({ text: indent, hidden: false, width: indent_width, indent: true });
|
|
758
|
+
}
|
|
759
|
+
else if (align === 'left') {
|
|
760
|
+
line_string.unshift({ text: indent, hidden: false, width: indent_width, indent: true });
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
strings.push(line_string);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
else {
|
|
768
|
+
// simple case
|
|
769
|
+
for (const line of md) {
|
|
770
|
+
const parts = [];
|
|
771
|
+
/*
|
|
772
|
+
if (style.indent) {
|
|
773
|
+
if (align === 'right') {
|
|
774
|
+
line.push({ text: indent });
|
|
775
|
+
}
|
|
776
|
+
else if (align === 'left') {
|
|
777
|
+
line.unshift({ text: indent });
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
*/
|
|
781
|
+
let line_width = 0;
|
|
782
|
+
let indent_width = 0;
|
|
783
|
+
if (style.indent) {
|
|
784
|
+
context.font = fonts.base;
|
|
785
|
+
indent_width = context.measureText(indent).width;
|
|
786
|
+
}
|
|
787
|
+
if (style.indent && align === 'left') {
|
|
788
|
+
line_width += indent_width;
|
|
789
|
+
parts.push({
|
|
790
|
+
text: indent,
|
|
791
|
+
hidden: false,
|
|
792
|
+
width: indent_width,
|
|
793
|
+
indent: true,
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
for (const element of line) {
|
|
797
|
+
if (this.options.markdown) {
|
|
798
|
+
if (element.strong && element.emphasis) {
|
|
799
|
+
context.font = fonts.strong_emphasis;
|
|
800
|
+
}
|
|
801
|
+
else if (element.strong) {
|
|
802
|
+
context.font = fonts.strong;
|
|
803
|
+
}
|
|
804
|
+
else if (element.emphasis) {
|
|
805
|
+
context.font = fonts.emphasis;
|
|
806
|
+
}
|
|
807
|
+
else {
|
|
808
|
+
context.font = fonts.base;
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
const width = context.measureText(element.text).width;
|
|
812
|
+
line_width += width;
|
|
813
|
+
parts.push({
|
|
814
|
+
...element,
|
|
815
|
+
hidden: false,
|
|
816
|
+
width,
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
if (style.indent && align === 'right') {
|
|
820
|
+
line_width += indent_width;
|
|
821
|
+
parts.push({
|
|
822
|
+
text: indent,
|
|
823
|
+
hidden: false,
|
|
824
|
+
width: indent_width,
|
|
825
|
+
indent: true,
|
|
826
|
+
});
|
|
827
|
+
}
|
|
828
|
+
max_width = Math.max(max_width, line_width);
|
|
829
|
+
strings.push(parts);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
return { strings, width: max_width };
|
|
833
|
+
}
|
|
834
|
+
return {
|
|
835
|
+
strings: [[{ text: '', hidden: false, width: 0 }]],
|
|
836
|
+
width: 0,
|
|
837
|
+
};
|
|
838
|
+
}
|
|
839
|
+
ResolveColors(style) {
|
|
840
|
+
const resolved = { ...style };
|
|
841
|
+
resolved.text = { text: ResolveThemeColor(this.theme, style.text, 1) };
|
|
842
|
+
// TODO: other colors
|
|
843
|
+
return resolved;
|
|
844
|
+
}
|
|
845
|
+
RenderCellBorders(address, context, style, left = 0, top = 0, width = 0, height = 0) {
|
|
846
|
+
// cell borders is one of those things that seems simple, even trivial,
|
|
847
|
+
// until you actually try to do it. then it turns out to be ridiculously
|
|
848
|
+
// complicated.
|
|
849
|
+
// one complicating factor that we are adding is that we don't necessarily
|
|
850
|
+
// paint in order, because we may update single cells at a time. so we need
|
|
851
|
+
// to account for shared borders in two directions.
|
|
852
|
+
// general rules:
|
|
853
|
+
//
|
|
854
|
+
// (1) borders take priority over fills
|
|
855
|
+
//
|
|
856
|
+
// (2) bottom cell, then right cell, take priority over this cell (except
|
|
857
|
+
// with regards to rule 1, so our border takes precendence over bottom
|
|
858
|
+
// cell fill, but not bottom cell border).
|
|
859
|
+
//
|
|
860
|
+
// some other things to note:
|
|
861
|
+
//
|
|
862
|
+
// - double borders (we only handle double-bottom, atm) flow _into_ the
|
|
863
|
+
// neighboring cell, instead of just using the shared border. in this
|
|
864
|
+
// case the shared edge should be colored wrt to the cell that owns the
|
|
865
|
+
// double border, either that cell's fill or default.
|
|
866
|
+
//
|
|
867
|
+
// - if we have a fill, we are painting the shared border; but in this case
|
|
868
|
+
// you also have to consider the top-left corner, which could be a border
|
|
869
|
+
// owned by a cell offset by (-1, -1) and because of rule 1, above, that
|
|
870
|
+
// pixel needs to stay border.
|
|
871
|
+
//
|
|
872
|
+
// - that theoretically applies to other corners as well, but somehow that
|
|
873
|
+
// hasn't come up? (...)
|
|
874
|
+
//
|
|
875
|
+
// - instead of clipping all the corners, when necessary, why not just paint
|
|
876
|
+
// the diagonals? might save time
|
|
877
|
+
// I think there are some opportunities for caching here (TODO)
|
|
878
|
+
// ---
|
|
879
|
+
// (moved to sheet, using numpad naming)
|
|
880
|
+
const numpad = this.view.active_sheet.SurroundingStyle(address, this.theme.table);
|
|
881
|
+
// --- start with fills ----------------------------------------------------
|
|
882
|
+
// paint top background
|
|
883
|
+
let color = ResolveThemeColor(this.theme, numpad[8].fill);
|
|
884
|
+
if (color) {
|
|
885
|
+
context.fillStyle = color;
|
|
886
|
+
context.fillRect(left + 0, top - 1, width, 1);
|
|
887
|
+
}
|
|
888
|
+
// paint left background
|
|
889
|
+
color = ResolveThemeColor(this.theme, numpad[4].fill);
|
|
890
|
+
if (color) {
|
|
891
|
+
context.fillStyle = color;
|
|
892
|
+
context.fillRect(left - 1, top, 1, height);
|
|
893
|
+
}
|
|
894
|
+
// paint our background. note this one goes up, left
|
|
895
|
+
color = ResolveThemeColor(this.theme, style.fill);
|
|
896
|
+
if (color) {
|
|
897
|
+
context.fillStyle = color;
|
|
898
|
+
context.fillRect(left - 1, top - 1, width + 1, height + 1);
|
|
899
|
+
}
|
|
900
|
+
// fill of cell to the right
|
|
901
|
+
color = ResolveThemeColor(this.theme, numpad[6].fill);
|
|
902
|
+
if (color) {
|
|
903
|
+
context.fillStyle = color;
|
|
904
|
+
context.fillRect(left + width - 1, top - 1, 1, height + 1);
|
|
905
|
+
}
|
|
906
|
+
// fill of cell underneath
|
|
907
|
+
color = ResolveThemeColor(this.theme, numpad[2].fill);
|
|
908
|
+
if (color) {
|
|
909
|
+
context.fillStyle = color;
|
|
910
|
+
context.fillRect(left - 1, top + height - 1, width + 1, 1);
|
|
911
|
+
}
|
|
912
|
+
// --- corner borders ------------------------------------------------------
|
|
913
|
+
if (numpad[6].border_top && !numpad[6].border_left) {
|
|
914
|
+
context.fillStyle = ResolveThemeColor(this.theme, numpad[6].border_top_fill, 1);
|
|
915
|
+
context.fillRect(left + width - 1, top - 2 + numpad[6].border_top, 1, 1);
|
|
916
|
+
}
|
|
917
|
+
if (numpad[9].border_left) {
|
|
918
|
+
context.fillStyle = ResolveThemeColor(this.theme, numpad[9].border_left_fill, 1);
|
|
919
|
+
context.fillRect(left + width - 1, top - 1, 1, 1);
|
|
920
|
+
}
|
|
921
|
+
if (numpad[9].border_bottom) {
|
|
922
|
+
context.fillStyle = ResolveThemeColor(this.theme, numpad[9].border_bottom_fill, 1);
|
|
923
|
+
context.fillRect(left + width - 1, top - 2 + numpad[9].border_bottom, 1, 1);
|
|
924
|
+
}
|
|
925
|
+
if (numpad[4].border_top && !numpad[4].border_right) {
|
|
926
|
+
context.fillStyle = ResolveThemeColor(this.theme, numpad[4].border_right_fill, 1);
|
|
927
|
+
context.fillRect(left - 1, top - 2 + numpad[4].border_top, 1, 1);
|
|
928
|
+
}
|
|
929
|
+
if (numpad[7].border_right) {
|
|
930
|
+
context.fillStyle = ResolveThemeColor(this.theme, numpad[7].border_right_fill, 1);
|
|
931
|
+
context.fillRect(left - 1, top - 1, 1, 1);
|
|
932
|
+
}
|
|
933
|
+
if (numpad[7].border_bottom) {
|
|
934
|
+
context.fillStyle = ResolveThemeColor(this.theme, numpad[7].border_bottom_fill, 1);
|
|
935
|
+
context.fillRect(left - 1, top - 2 + numpad[7].border_bottom, 1, 1);
|
|
936
|
+
}
|
|
937
|
+
if (numpad[6].border_bottom && !numpad[6].border_left) {
|
|
938
|
+
context.fillStyle = ResolveThemeColor(this.theme, numpad[6].border_bottom_fill, 1);
|
|
939
|
+
context.fillRect(left + width - 1, top + height - numpad[6].border_bottom, 1, 1);
|
|
940
|
+
}
|
|
941
|
+
if (numpad[3].border_left) {
|
|
942
|
+
context.fillStyle = ResolveThemeColor(this.theme, numpad[3].border_left_fill, 1);
|
|
943
|
+
context.fillRect(left + width - 1, top + height - 1, 1, 1);
|
|
944
|
+
}
|
|
945
|
+
if (numpad[3].border_top) {
|
|
946
|
+
context.fillStyle = ResolveThemeColor(this.theme, numpad[3].border_top_fill, 1);
|
|
947
|
+
context.fillRect(left + width - 1, top + height - numpad[3].border_top, 1, 1);
|
|
948
|
+
}
|
|
949
|
+
if (numpad[4].border_bottom && !numpad[4].border_right) {
|
|
950
|
+
context.fillStyle = ResolveThemeColor(this.theme, numpad[4].border_bottom_fill, 1);
|
|
951
|
+
context.fillRect(left - 1, top + height - numpad[4].border_bottom, 1, 1);
|
|
952
|
+
}
|
|
953
|
+
if (numpad[1].border_right) {
|
|
954
|
+
context.fillStyle = ResolveThemeColor(this.theme, numpad[1].border_right_fill, 1);
|
|
955
|
+
context.fillRect(left - 1, top + height - 1, 1, 1);
|
|
956
|
+
}
|
|
957
|
+
if (numpad[1].border_top) {
|
|
958
|
+
context.fillStyle = ResolveThemeColor(this.theme, numpad[1].border_top_fill, 1);
|
|
959
|
+
context.fillRect(left - 1, top + height - numpad[1].border_top, 1, 1);
|
|
960
|
+
}
|
|
961
|
+
// --- neighbor borders ----------------------------------------------------
|
|
962
|
+
// paint top border
|
|
963
|
+
if (numpad[8].border_bottom) {
|
|
964
|
+
context.fillStyle = ResolveThemeColor(this.theme, numpad[8].border_bottom_fill, 1);
|
|
965
|
+
if (numpad[8].border_bottom === 2) {
|
|
966
|
+
context.fillRect(left - 1, top - 2, width + 1, 1);
|
|
967
|
+
context.fillRect(left - 1, top - 0, width + 1, 1);
|
|
968
|
+
context.fillStyle = ResolveThemeColor(this.theme, numpad[8].fill)
|
|
969
|
+
|| ResolveThemeColor(this.theme, this.theme.grid_cell?.fill, 0) || '#fff';
|
|
970
|
+
context.fillRect(left - 1, top - 1, width + 1, 1);
|
|
971
|
+
}
|
|
972
|
+
else {
|
|
973
|
+
context.fillRect(left - 1, top - 1, width + 1, 1);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
// paint left border
|
|
977
|
+
if (numpad[4].border_right) {
|
|
978
|
+
context.fillStyle = ResolveThemeColor(this.theme, numpad[4].border_right_fill, 1);
|
|
979
|
+
context.fillRect(left - 1, top - 1, 1, height + 1);
|
|
980
|
+
}
|
|
981
|
+
// paint right border?
|
|
982
|
+
if (numpad[6].border_left) {
|
|
983
|
+
context.fillStyle = ResolveThemeColor(this.theme, numpad[6].border_left_fill, 1);
|
|
984
|
+
context.fillRect(left + width - 1, top - 1, 1, height + 1);
|
|
985
|
+
}
|
|
986
|
+
// bottom? (...)
|
|
987
|
+
if (numpad[2].border_top) {
|
|
988
|
+
context.fillStyle = ResolveThemeColor(this.theme, numpad[2].border_top_fill, 1);
|
|
989
|
+
if (numpad[2].border_top === 2) {
|
|
990
|
+
context.fillRect(left - 1, top + height - 2, width + 1, 1);
|
|
991
|
+
context.fillRect(left - 1, top + height - 0, width + 1, 1);
|
|
992
|
+
context.fillStyle = ResolveThemeColor(this.theme, numpad[2].fill)
|
|
993
|
+
|| ResolveThemeColor(this.theme, this.theme.grid_cell?.fill, 0) || '#fff';
|
|
994
|
+
context.fillRect(left - 1, top + height - 1, width + 1, 1);
|
|
995
|
+
}
|
|
996
|
+
else {
|
|
997
|
+
context.fillRect(left - 1, top + height - 1, width + 1, 1);
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
// -- our borders ----------------------------------------------------------
|
|
1001
|
+
if (style.border_top) {
|
|
1002
|
+
context.fillStyle = ResolveThemeColor(this.theme, style.border_top_fill, 1);
|
|
1003
|
+
if (style.border_top === 2) {
|
|
1004
|
+
context.fillRect(left - 1, top - 2, width + 1, 1);
|
|
1005
|
+
context.fillRect(left - 1, top + 0, width + 1, 1);
|
|
1006
|
+
context.fillStyle = ResolveThemeColor(this.theme, style.fill)
|
|
1007
|
+
|| ResolveThemeColor(this.theme, this.theme.grid_cell?.fill, 0) || '#fff';
|
|
1008
|
+
context.fillRect(left - 1, top - 1, width + 1, 1);
|
|
1009
|
+
}
|
|
1010
|
+
else {
|
|
1011
|
+
context.fillRect(left - 1, top - 1, width + 1, 1);
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
if (style.border_left) {
|
|
1015
|
+
context.fillStyle = ResolveThemeColor(this.theme, style.border_left_fill, 1);
|
|
1016
|
+
context.fillRect(left - 1, top - 1, 1, height + 1);
|
|
1017
|
+
}
|
|
1018
|
+
if (style.border_right) {
|
|
1019
|
+
context.fillStyle = ResolveThemeColor(this.theme, style.border_right_fill, 1);
|
|
1020
|
+
context.fillRect(left + width - 1, top - 1, 1, height + 1);
|
|
1021
|
+
}
|
|
1022
|
+
if (style.border_bottom) {
|
|
1023
|
+
context.fillStyle = ResolveThemeColor(this.theme, style.border_bottom_fill, 1);
|
|
1024
|
+
if (style.border_bottom === 2) {
|
|
1025
|
+
context.fillRect(left - 1, top + height - 2, width + 1, 1);
|
|
1026
|
+
context.fillRect(left - 1, top + height + 0, width + 1, 1);
|
|
1027
|
+
context.fillStyle = ResolveThemeColor(this.theme, style.fill)
|
|
1028
|
+
|| ResolveThemeColor(this.theme, this.theme.grid_cell?.fill, 0) || '#fff';
|
|
1029
|
+
context.fillRect(left - 1, top + height - 1, width + 1, 1);
|
|
1030
|
+
}
|
|
1031
|
+
else {
|
|
1032
|
+
context.fillRect(left - 1, top + height - 1, width + 1, 1);
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
/**
|
|
1037
|
+
* paint background image, offset and tiled.
|
|
1038
|
+
*/
|
|
1039
|
+
PaintBackgroundImage(context, image, left, top, width, height, render_left = 0, render_top = 0, offset = 0) {
|
|
1040
|
+
// there's no explicit broken flag, but we can infer from size
|
|
1041
|
+
if (!image.width || !image.height) {
|
|
1042
|
+
return;
|
|
1043
|
+
}
|
|
1044
|
+
const scale = (this.layout.scale || 1) * this.layout.dpr;
|
|
1045
|
+
const source_left = (left / scale) % image.width;
|
|
1046
|
+
const source_top = (top / scale) % image.height;
|
|
1047
|
+
const source_width = width / scale;
|
|
1048
|
+
const source_height = height / scale;
|
|
1049
|
+
const roll_x = (source_left + source_width) > image.width;
|
|
1050
|
+
const roll_y = (source_top + source_height) > image.height;
|
|
1051
|
+
if (roll_x) {
|
|
1052
|
+
context.drawImage(image, source_left - image.width, source_top, source_width, source_height, render_left, render_top, width - offset, height - offset);
|
|
1053
|
+
}
|
|
1054
|
+
if (roll_y) {
|
|
1055
|
+
context.drawImage(image, source_left, source_top - image.height, source_width, source_height, render_left, render_top, width - offset, height - offset);
|
|
1056
|
+
}
|
|
1057
|
+
if (roll_x && roll_y) {
|
|
1058
|
+
context.drawImage(image, source_left - image.width, source_top - image.height, source_width, source_height, render_left, render_top, width - offset, height - offset);
|
|
1059
|
+
}
|
|
1060
|
+
context.drawImage(image, source_left, source_top, source_width, source_height, render_left, render_top, width - offset, height - offset);
|
|
1061
|
+
}
|
|
1062
|
+
RenderCellBackground(note, address, context, style, width, height, cell_left = 0, cell_top = 0) {
|
|
1063
|
+
// so here we draw the background and the bottom and right grid edges.
|
|
1064
|
+
// fill is enclosed here, the border method has logic for border colors,
|
|
1065
|
+
// because it turns out to be complicated.
|
|
1066
|
+
context.fillStyle = this.theme.grid_color;
|
|
1067
|
+
context.fillRect(0, 0, width, height);
|
|
1068
|
+
if (this.view.active_sheet.image) {
|
|
1069
|
+
this.PaintBackgroundImage(context, this.view.active_sheet.image, cell_left, cell_top, width, height, 0, 0, 1);
|
|
1070
|
+
}
|
|
1071
|
+
else {
|
|
1072
|
+
const fill = ResolveThemeColor(this.theme, style.fill);
|
|
1073
|
+
if (fill) {
|
|
1074
|
+
context.fillStyle = fill;
|
|
1075
|
+
context.fillRect(0, 0, width - 1, height - 1);
|
|
1076
|
+
}
|
|
1077
|
+
else {
|
|
1078
|
+
context.fillStyle = ResolveThemeColor(this.theme, this.theme.grid_cell?.fill, 0) || '#fff';
|
|
1079
|
+
context.fillRect(0, 0, width - 1, height - 1);
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
// the next call actually paints background, if we have a background color
|
|
1083
|
+
this.RenderCellBorders(address, context, style, 0, 0, width, height);
|
|
1084
|
+
// so we need to draw the note icon after that
|
|
1085
|
+
// why is this here? (it's rendered as background, I guess)
|
|
1086
|
+
if (note) {
|
|
1087
|
+
const offset_x = 2;
|
|
1088
|
+
const offset_y = 1;
|
|
1089
|
+
const length = 8;
|
|
1090
|
+
// FIXME: why is the default in here, and not in theme defaults?
|
|
1091
|
+
// actually it is in theme defaults, probably was here first.
|
|
1092
|
+
context.fillStyle = this.theme.note_marker_color;
|
|
1093
|
+
context.beginPath();
|
|
1094
|
+
context.moveTo(width - offset_x, offset_y);
|
|
1095
|
+
context.lineTo(width - offset_x - length, offset_y);
|
|
1096
|
+
context.lineTo(width - offset_x, offset_y + length);
|
|
1097
|
+
context.lineTo(width - offset_x, offset_y);
|
|
1098
|
+
context.fill();
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
/**
|
|
1102
|
+
* refactoring render to allow rendering to buffered canvas, in the
|
|
1103
|
+
* case of tile overflow. this is problematic because as the code stands
|
|
1104
|
+
* now, it paints before determining if there's an overflow. so we need
|
|
1105
|
+
* to move some paint calls around.
|
|
1106
|
+
*/
|
|
1107
|
+
RenderCell(tile, cell, context, address, width, height, cell_left = 0, cell_top = 0) {
|
|
1108
|
+
const result = {};
|
|
1109
|
+
// preserve the flag, then unset so we don't have to track around
|
|
1110
|
+
const dirty = !cell.render_clean[this.view.view_index];
|
|
1111
|
+
cell.render_clean[this.view.view_index] = true;
|
|
1112
|
+
// special case for overflows (this has been set by someone to the left)
|
|
1113
|
+
if (tile.needs_full_repaint &&
|
|
1114
|
+
cell.renderer_data?.overflowed) {
|
|
1115
|
+
return {};
|
|
1116
|
+
}
|
|
1117
|
+
let style = cell.style ? { ...cell.style } : {};
|
|
1118
|
+
if (cell.table) {
|
|
1119
|
+
style = this.view.active_sheet.CellStyleData(address, cell.table.theme || this.theme.table) || {};
|
|
1120
|
+
}
|
|
1121
|
+
if (cell.merge_area) {
|
|
1122
|
+
if ((address.row === cell.merge_area.start.row) &&
|
|
1123
|
+
(address.column === cell.merge_area.start.column)) {
|
|
1124
|
+
for (let column = cell.merge_area.start.column + 1; column <= cell.merge_area.end.column; column++) {
|
|
1125
|
+
width += this.layout.ColumnWidth(column);
|
|
1126
|
+
}
|
|
1127
|
+
for (let row = cell.merge_area.start.row + 1; row <= cell.merge_area.end.row; row++) {
|
|
1128
|
+
height += this.layout.RowHeight(row);
|
|
1129
|
+
}
|
|
1130
|
+
// get last cell for borders
|
|
1131
|
+
if (cell.merge_area.count > 1) {
|
|
1132
|
+
const end_cell_style = this.view.active_sheet.CellStyleData(cell.merge_area.end);
|
|
1133
|
+
if (end_cell_style) {
|
|
1134
|
+
style.border_bottom = end_cell_style.border_bottom;
|
|
1135
|
+
style.border_right = end_cell_style.border_right;
|
|
1136
|
+
style.border_bottom_fill = end_cell_style.border_bottom_fill;
|
|
1137
|
+
style.border_right_fill = end_cell_style.border_right_fill;
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
// check if we are going to overflow into another tile right or down
|
|
1141
|
+
if (cell.merge_area.end.column > tile.last_cell.column) {
|
|
1142
|
+
result.tile_overflow_right = true;
|
|
1143
|
+
}
|
|
1144
|
+
if (cell.merge_area.end.row > tile.last_cell.row) {
|
|
1145
|
+
result.tile_overflow_bottom = true;
|
|
1146
|
+
}
|
|
1147
|
+
// there's an issue with merges that cross tiles and resizing; they
|
|
1148
|
+
// don't get painted properly. we can reuse the overflow record list
|
|
1149
|
+
// to fix this.
|
|
1150
|
+
// NOTE: this refers to _tile_ overflows, not cell overflows. we
|
|
1151
|
+
// should change the name to make this clearer.
|
|
1152
|
+
if (result.tile_overflow_bottom || result.tile_overflow_right) {
|
|
1153
|
+
this.overflow_areas.push({
|
|
1154
|
+
tile,
|
|
1155
|
+
head: { ...address },
|
|
1156
|
+
area: new Area(cell.merge_area.start, cell.merge_area.end),
|
|
1157
|
+
});
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
else {
|
|
1161
|
+
return {};
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
// want to do some surgery here, need to consider any side-effects.
|
|
1165
|
+
// specifically, to support hyperlinks, I want to (1) do the text
|
|
1166
|
+
// calculation before calling the cell's render_function (so we can figure
|
|
1167
|
+
// out layout); and (2) let the render function indicate that it does not
|
|
1168
|
+
// want to exit, i.e. it's only a prerender for calc purposes.
|
|
1169
|
+
// although that layout calc won't be good enough to account for things
|
|
1170
|
+
// like overflow... also here we are just splitting the string, not
|
|
1171
|
+
// generating text boxes (think about justification, wrap)
|
|
1172
|
+
// doing this a little differently... render function can pass but can
|
|
1173
|
+
// also ask us to preserve layout (text rectangles)
|
|
1174
|
+
// let preserve_layout_info = false;
|
|
1175
|
+
// let renderer_title: string|undefined;
|
|
1176
|
+
// let override_text: string|undefined;
|
|
1177
|
+
// ...updating...
|
|
1178
|
+
const preserve_layout_info = !!cell.hyperlink;
|
|
1179
|
+
if (cell.render_function) {
|
|
1180
|
+
this.RenderCellBackground(!!cell.note, address, context, style, width, height);
|
|
1181
|
+
context.strokeStyle = context.fillStyle = ResolveThemeColor(this.theme, style.text, 1);
|
|
1182
|
+
// there's an issue with theme colors, the function may not be able
|
|
1183
|
+
// to translate so we need to update the style (using a copy) to
|
|
1184
|
+
// resolve colors
|
|
1185
|
+
const apply_style = this.ResolveColors(style);
|
|
1186
|
+
const render_result = cell.render_function.call(undefined, {
|
|
1187
|
+
width, height, context, cell, style: apply_style, scale: this.layout.scale || 1,
|
|
1188
|
+
});
|
|
1189
|
+
if (render_result.handled) {
|
|
1190
|
+
return result;
|
|
1191
|
+
}
|
|
1192
|
+
/*
|
|
1193
|
+
if (render_result.metrics) {
|
|
1194
|
+
preserve_layout_info = true;
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
if (render_result.title) {
|
|
1198
|
+
renderer_title = render_result.title;
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
if (typeof render_result.override_text !== 'undefined') {
|
|
1202
|
+
override_text = render_result.override_text;
|
|
1203
|
+
}
|
|
1204
|
+
*/
|
|
1205
|
+
}
|
|
1206
|
+
// if there's no context, we just need to render the background
|
|
1207
|
+
// and border; but it still might be overflowed (via merge)
|
|
1208
|
+
/*
|
|
1209
|
+
|
|
1210
|
+
this is breaking rendering. not sure if it is because of buffering
|
|
1211
|
+
(that doesn't work, below) or because of overflow, but in any event
|
|
1212
|
+
it doesn't work. we should fix, or at least jump over any font stuff
|
|
1213
|
+
below.
|
|
1214
|
+
|
|
1215
|
+
I suspect it was written at an earlier iteration of the overall render
|
|
1216
|
+
routine, and then got out of sync.
|
|
1217
|
+
|
|
1218
|
+
TODO/FIXME
|
|
1219
|
+
|
|
1220
|
+
if (!cell.formatted) {
|
|
1221
|
+
this.RenderCellBackground(
|
|
1222
|
+
!!cell.note,
|
|
1223
|
+
address,
|
|
1224
|
+
(result.tile_overflow_bottom || result.tile_overflow_right) ?
|
|
1225
|
+
this.buffer_context : context, style, width, height);
|
|
1226
|
+
return result;
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
*/
|
|
1230
|
+
// NOTE: this is OK to do in the original context, even if we're
|
|
1231
|
+
// (eventually) painting to the buffer context. just remember to set
|
|
1232
|
+
// font in the buffer context.
|
|
1233
|
+
const base_font = Style.CompositeFont(this.theme.grid_cell_font_size, style, this.layout.scale, this.theme);
|
|
1234
|
+
/*
|
|
1235
|
+
if (cell.value) {
|
|
1236
|
+
console.info(base_font);
|
|
1237
|
+
}
|
|
1238
|
+
*/
|
|
1239
|
+
const fonts = {
|
|
1240
|
+
base: base_font.font,
|
|
1241
|
+
strong: Style.CompositeFont(this.theme.grid_cell_font_size, { ...style, bold: true }, this.layout.scale, this.theme).font,
|
|
1242
|
+
emphasis: Style.CompositeFont(this.theme.grid_cell_font_size, { ...style, italic: true }, this.layout.scale, this.theme).font,
|
|
1243
|
+
strong_emphasis: Style.CompositeFont(this.theme.grid_cell_font_size, { ...style, bold: true, italic: true }, this.layout.scale, this.theme).font,
|
|
1244
|
+
};
|
|
1245
|
+
if (base_font.variants) {
|
|
1246
|
+
tile.style.fontVariant = base_font.variants;
|
|
1247
|
+
}
|
|
1248
|
+
else {
|
|
1249
|
+
tile.style.fontVariant = '';
|
|
1250
|
+
}
|
|
1251
|
+
context.font = fonts.base;
|
|
1252
|
+
//
|
|
1253
|
+
// NOTE: we appear to be updating render data on cell size changes
|
|
1254
|
+
// (width/height) to account for line breaks, but that should only
|
|
1255
|
+
// be necessary if the text is wrapped -- correct? maybe we can skip
|
|
1256
|
+
//
|
|
1257
|
+
// (FIXME/TODO)
|
|
1258
|
+
//
|
|
1259
|
+
if (dirty || !cell.renderer_data || cell.renderer_data.width !== width || cell.renderer_data.height !== height) {
|
|
1260
|
+
const text_data = this.PrepText(context, fonts, cell, width);
|
|
1261
|
+
cell.renderer_data = {
|
|
1262
|
+
text_data,
|
|
1263
|
+
width,
|
|
1264
|
+
height,
|
|
1265
|
+
};
|
|
1266
|
+
}
|
|
1267
|
+
const text_data = cell.renderer_data.text_data;
|
|
1268
|
+
// overflow is always a huge headache. here are the basic rules:
|
|
1269
|
+
// (1) only strings can overflow. numbers get ### treatment.
|
|
1270
|
+
// (2) wrapped and merged cells cannot overflow.
|
|
1271
|
+
// (3) overflow is horizontal only.
|
|
1272
|
+
// (4) overflow can extend indefinitely.
|
|
1273
|
+
// Q: what about DQ?
|
|
1274
|
+
const overflow = text_data.width > (width - 2 * this.cell_edge_buffer);
|
|
1275
|
+
let paint_right = width;
|
|
1276
|
+
let paint_left = 0;
|
|
1277
|
+
let clip = false;
|
|
1278
|
+
const is_number = (cell.type === ValueType.number ||
|
|
1279
|
+
cell.calculated_type === ValueType.number ||
|
|
1280
|
+
cell.type === ValueType.complex ||
|
|
1281
|
+
cell.calculated_type === ValueType.complex ||
|
|
1282
|
+
cell.type === ValueType.dimensioned_quantity ||
|
|
1283
|
+
cell.calculated_type === ValueType.dimensioned_quantity);
|
|
1284
|
+
let horizontal_align = style.horizontal_align;
|
|
1285
|
+
if (!horizontal_align) {
|
|
1286
|
+
horizontal_align = is_number ? 'right' : 'left'; // Style.HorizontalAlign.Right : Style.HorizontalAlign.Left;
|
|
1287
|
+
}
|
|
1288
|
+
// NOTE: text rendering options (align, baseline) are set globally
|
|
1289
|
+
// when the tile is created, so we don't need to set them repeatedly here.
|
|
1290
|
+
// we cache some data for drawing backgrounds under overflows, if necessary,
|
|
1291
|
+
// so we can do draw calls after we figure out if we need to buffer or not
|
|
1292
|
+
// UPDATE: we have a case where there's a super-long string trying to
|
|
1293
|
+
// render/overflow, and it's breaking everything. we need to address some
|
|
1294
|
+
// caps/limits. WIP.
|
|
1295
|
+
const overflow_backgrounds = [];
|
|
1296
|
+
if (overflow) {
|
|
1297
|
+
const can_overflow = (cell.type !== ValueType.number &&
|
|
1298
|
+
cell.calculated_type !== ValueType.number &&
|
|
1299
|
+
!style.wrap &&
|
|
1300
|
+
!cell.merge_area);
|
|
1301
|
+
if (can_overflow) {
|
|
1302
|
+
// check how far we want to overflow left and right (pixels)
|
|
1303
|
+
// FIXME: should be (buffer * 2), no?
|
|
1304
|
+
const delta = text_data.width - width + this.cell_edge_buffer;
|
|
1305
|
+
let overflow_pixels_left = 0;
|
|
1306
|
+
let overflow_pixels_right = 0;
|
|
1307
|
+
if (horizontal_align === 'center' /* Style.HorizontalAlign.Center */) {
|
|
1308
|
+
overflow_pixels_left = overflow_pixels_right = delta / 2;
|
|
1309
|
+
}
|
|
1310
|
+
else if (horizontal_align === 'right' /* Style.HorizontalAlign.Right */) {
|
|
1311
|
+
overflow_pixels_left = delta;
|
|
1312
|
+
}
|
|
1313
|
+
else {
|
|
1314
|
+
overflow_pixels_right = delta;
|
|
1315
|
+
}
|
|
1316
|
+
// calculate overflow into adjacent columns
|
|
1317
|
+
let overflow_right_column = address.column;
|
|
1318
|
+
let overflow_left_column = address.column;
|
|
1319
|
+
// cap at max. use actual max, not sheet max (which reflects the
|
|
1320
|
+
// extent of spreadsheet data, but not visible cells).
|
|
1321
|
+
while (overflow_pixels_right > 0 && overflow_right_column < this.layout.last_column) {
|
|
1322
|
+
overflow_right_column++;
|
|
1323
|
+
const target_address = { row: address.row, column: overflow_right_column };
|
|
1324
|
+
const target_cell = this.view.active_sheet.CellData(target_address);
|
|
1325
|
+
const target_width = this.layout.ColumnWidth(overflow_right_column);
|
|
1326
|
+
overflow_pixels_right -= target_width;
|
|
1327
|
+
if (target_cell && !target_cell.type && !target_cell.calculated_type) {
|
|
1328
|
+
overflow_backgrounds.push({
|
|
1329
|
+
address: target_address,
|
|
1330
|
+
cell: target_cell,
|
|
1331
|
+
grid: new Rectangle(paint_right, 0, target_width, height),
|
|
1332
|
+
background: new Rectangle(paint_right - 1, 0, target_width, height - 1),
|
|
1333
|
+
border: new Rectangle(paint_right, 0, target_width, height),
|
|
1334
|
+
});
|
|
1335
|
+
paint_right += target_width;
|
|
1336
|
+
// set render data for cells we are going to overflow into;
|
|
1337
|
+
// that will keep them from getting painted. we only need to
|
|
1338
|
+
// do that on the right side.
|
|
1339
|
+
target_cell.render_clean[this.view.view_index] = true;
|
|
1340
|
+
target_cell.renderer_data = {
|
|
1341
|
+
overflowed: true,
|
|
1342
|
+
};
|
|
1343
|
+
}
|
|
1344
|
+
else {
|
|
1345
|
+
// we actually don't have to clip to the right, assuming
|
|
1346
|
+
// we're going to paint the cells anyway... right?
|
|
1347
|
+
// A: not necessarily, because we might not be painting the cell _now_.
|
|
1348
|
+
clip = true; // need to clip
|
|
1349
|
+
break;
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
if (overflow_right_column > tile.last_cell.column) {
|
|
1353
|
+
result.tile_overflow_right = true;
|
|
1354
|
+
}
|
|
1355
|
+
while (overflow_pixels_left > 0 && overflow_left_column >= 1) {
|
|
1356
|
+
overflow_left_column--;
|
|
1357
|
+
const target_address = { row: address.row, column: overflow_left_column };
|
|
1358
|
+
const target_cell = this.view.active_sheet.CellData(target_address);
|
|
1359
|
+
const target_width = this.layout.ColumnWidth(overflow_left_column);
|
|
1360
|
+
overflow_pixels_left -= target_width;
|
|
1361
|
+
if (target_cell && !target_cell.type && !target_cell.calculated_type) {
|
|
1362
|
+
paint_left -= target_width;
|
|
1363
|
+
overflow_backgrounds.push({
|
|
1364
|
+
address: target_address,
|
|
1365
|
+
cell: target_cell,
|
|
1366
|
+
grid: new Rectangle(paint_left, 0, target_width, height),
|
|
1367
|
+
background: new Rectangle(paint_left, 0, target_width, height - 1),
|
|
1368
|
+
border: new Rectangle(paint_left, 0, target_width, height),
|
|
1369
|
+
});
|
|
1370
|
+
}
|
|
1371
|
+
else {
|
|
1372
|
+
clip = true; // need to clip
|
|
1373
|
+
break;
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
if (overflow_left_column < tile.first_cell.column) {
|
|
1377
|
+
result.tile_overflow_left = true;
|
|
1378
|
+
}
|
|
1379
|
+
// push overflow onto the list
|
|
1380
|
+
this.overflow_areas.push({
|
|
1381
|
+
head: { ...address }, tile, area: new Area({ row: address.row, column: overflow_left_column }, { row: address.row, column: overflow_right_column })
|
|
1382
|
+
});
|
|
1383
|
+
}
|
|
1384
|
+
else {
|
|
1385
|
+
// don't clip numbers, we are going to ### them
|
|
1386
|
+
clip = !is_number; // (cell.type !== ValueType.number && cell.calculated_type !== ValueType.number);
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
let buffering = false;
|
|
1390
|
+
// now we can render into either the primary context or the buffer
|
|
1391
|
+
// context. note we don't have to clip for buffered contexts, as we're
|
|
1392
|
+
// going to copy.
|
|
1393
|
+
const original_context = context;
|
|
1394
|
+
if (result.tile_overflow_bottom || result.tile_overflow_left || result.tile_overflow_right) {
|
|
1395
|
+
buffering = true;
|
|
1396
|
+
// console.info("buffering", result, {paint_left, paint_right, text_data});
|
|
1397
|
+
result.width = paint_right - paint_left;
|
|
1398
|
+
result.height = height;
|
|
1399
|
+
result.left = paint_left;
|
|
1400
|
+
this.EnsureBuffer(result.width + 1, height + 1, -paint_left);
|
|
1401
|
+
context = this.buffer_context;
|
|
1402
|
+
context.font = fonts.base;
|
|
1403
|
+
}
|
|
1404
|
+
this.RenderCellBackground(!!cell.note, address, context, style, width, height, cell_left, cell_top);
|
|
1405
|
+
// Q: why are we doing this inline instead of using the background method?
|
|
1406
|
+
for (const element of overflow_backgrounds) {
|
|
1407
|
+
if (element.cell.style?.fill &&
|
|
1408
|
+
IsDefinedColor(element.cell.style.fill) &&
|
|
1409
|
+
// (element.cell.style.fill.text || element.cell.style.fill.theme || element.cell.style.fill.theme === 0) &&
|
|
1410
|
+
!this.options.grid_over_background) {
|
|
1411
|
+
context.fillStyle = ResolveThemeColor(this.theme, element.cell.style.fill, 0);
|
|
1412
|
+
context.fillRect(element.grid.left, element.grid.top, element.grid.width, element.grid.height);
|
|
1413
|
+
}
|
|
1414
|
+
else {
|
|
1415
|
+
context.fillStyle = this.theme.grid_color || '';
|
|
1416
|
+
context.fillRect(element.grid.left, element.grid.top, element.grid.width, element.grid.height);
|
|
1417
|
+
if (this.view.active_sheet.image) {
|
|
1418
|
+
this.PaintBackgroundImage(context, this.view.active_sheet.image, cell_left + element.background.left, cell_top + element.background.top, element.background.width, element.background.height, element.background.left, element.background.top, 0);
|
|
1419
|
+
}
|
|
1420
|
+
else {
|
|
1421
|
+
context.fillStyle = this.theme.grid_cell?.fill ? ResolveThemeColor(this.theme, this.theme.grid_cell.fill, 0) : '';
|
|
1422
|
+
context.fillRect(element.background.left, element.background.top, element.background.width, element.background.height);
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
if (element.cell.style) {
|
|
1426
|
+
this.RenderCellBorders(element.address, context, element.cell.style, element.border.left, element.border.top, element.border.width, element.border.height);
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
if (style.databar && height > 7 && width > 9) { // FIXME: buffers
|
|
1430
|
+
const db = style.databar;
|
|
1431
|
+
if (db) {
|
|
1432
|
+
const margin = { x: 4, y: 3 }; // FIXME: parameterize, move out of this function/block
|
|
1433
|
+
context.fillStyle = ResolveThemeColor(this.theme, db.fill);
|
|
1434
|
+
const bar_top = margin.y;
|
|
1435
|
+
const bar_height = height - (margin.y * 2 + 1);
|
|
1436
|
+
let bar_left = margin.x;
|
|
1437
|
+
let bar_width = 0;
|
|
1438
|
+
if (db.zero > 0) {
|
|
1439
|
+
bar_left = bar_left + db.zero * (width - (margin.x * 2 + 1));
|
|
1440
|
+
if (db.value > db.zero) {
|
|
1441
|
+
bar_width = (width - (margin.x * 2 + 1)) * (db.value - db.zero);
|
|
1442
|
+
}
|
|
1443
|
+
else {
|
|
1444
|
+
if (db.negative) {
|
|
1445
|
+
context.fillStyle = ResolveThemeColor(this.theme, db.negative);
|
|
1446
|
+
}
|
|
1447
|
+
bar_width = (width - (margin.x * 2 + 1)) * (db.zero - db.value);
|
|
1448
|
+
bar_left -= bar_width;
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
else {
|
|
1452
|
+
bar_width = (width - (margin.x * 2 + 1)) * db.value;
|
|
1453
|
+
}
|
|
1454
|
+
context.fillRect(bar_left, bar_top, bar_width, bar_height);
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
// NOTE: we are getting fontmetrics based on the base font (so ignoring italic
|
|
1458
|
+
// and bold variants). this should be OK because we use it for height, mostly.
|
|
1459
|
+
// not sure about invisible text (FIXME)
|
|
1460
|
+
// const m2 = FontMetricsCache2.Get(fonts.base, // this.theme.grid_cell?.font_size?.value);
|
|
1461
|
+
// this.theme.grid_cell_font_size);
|
|
1462
|
+
const m2 = GetFontMetrics(fonts.base, base_font.variants);
|
|
1463
|
+
/*
|
|
1464
|
+
if (cell.value) {
|
|
1465
|
+
console.info(fonts.base, {m2});
|
|
1466
|
+
}
|
|
1467
|
+
*/
|
|
1468
|
+
// set stroke for underline
|
|
1469
|
+
// FIXME: color here should default to style, not ''. it's working only
|
|
1470
|
+
// because our default style happens to be the default color. that applies
|
|
1471
|
+
// to text color, background color and border color.
|
|
1472
|
+
context.lineWidth = 1; // 1.5; // FIXME: scale? font scale?
|
|
1473
|
+
context.strokeStyle = context.fillStyle =
|
|
1474
|
+
text_data.format ? text_data.format : ResolveThemeColor(this.theme, style.text, 1);
|
|
1475
|
+
context.beginPath();
|
|
1476
|
+
let left = this.cell_edge_buffer;
|
|
1477
|
+
const line_height = 1; // 1.25;
|
|
1478
|
+
//const line_count = text_data.single ? 1 : text_data.strings.length;
|
|
1479
|
+
const line_count = text_data.strings.length;
|
|
1480
|
+
// const text_height = (line_count * m2.block * line_height);
|
|
1481
|
+
const text_height = (line_count * m2.height * line_height);
|
|
1482
|
+
// we stopped clipping initially because it was expensive -- but then
|
|
1483
|
+
// we were doing it on every cell. it's hard to imagine that clipping
|
|
1484
|
+
// is more expensive than buffering (painting to a second canvas and
|
|
1485
|
+
// copying). let's test clipping just in the case of unpainted overflow.
|
|
1486
|
+
// don't clip if buffering, it's not necessary
|
|
1487
|
+
clip = (clip || (text_height >= height)) && !buffering;
|
|
1488
|
+
if (clip) {
|
|
1489
|
+
context.save();
|
|
1490
|
+
context.beginPath();
|
|
1491
|
+
context.moveTo(paint_left + 1.5, 0);
|
|
1492
|
+
context.lineTo(paint_left + 1.5, height);
|
|
1493
|
+
context.lineTo(paint_right - 1.5, height);
|
|
1494
|
+
context.lineTo(paint_right - 1.5, 0);
|
|
1495
|
+
context.clip();
|
|
1496
|
+
}
|
|
1497
|
+
// path for underline (and strike). if there's no underline (or strike),
|
|
1498
|
+
// it won't do anything.
|
|
1499
|
+
context.beginPath();
|
|
1500
|
+
let original_baseline = Math.round((height - m2.descent - 2) + // baseline for first line of text
|
|
1501
|
+
(-line_height * m2.height * (line_count - 1))); // adjust for multiple lines
|
|
1502
|
+
switch (style.vertical_align) {
|
|
1503
|
+
case 'top':
|
|
1504
|
+
original_baseline = Math.round(2 + m2.ascent);
|
|
1505
|
+
break;
|
|
1506
|
+
case 'middle':
|
|
1507
|
+
original_baseline = Math.round((height - text_height) / 2 + m2.ascent) - 1.5;
|
|
1508
|
+
break;
|
|
1509
|
+
}
|
|
1510
|
+
if (style.databar?.hide_values) {
|
|
1511
|
+
// ...
|
|
1512
|
+
}
|
|
1513
|
+
else if ((cell.type === ValueType.number ||
|
|
1514
|
+
cell.calculated_type === ValueType.number ||
|
|
1515
|
+
cell.type === ValueType.complex ||
|
|
1516
|
+
cell.calculated_type === ValueType.complex) && overflow) {
|
|
1517
|
+
// number overflow is easy
|
|
1518
|
+
const count = Math.floor((width - 2 * this.cell_edge_buffer) / m2.hash);
|
|
1519
|
+
let text = '';
|
|
1520
|
+
for (let i = 0; i < count; i++) {
|
|
1521
|
+
text += '#';
|
|
1522
|
+
}
|
|
1523
|
+
const text_width = context.measureText(text).width;
|
|
1524
|
+
if (horizontal_align === 'center' /* Style.HorizontalAlign.Center */) {
|
|
1525
|
+
left = Math.round((width - text_width) / 2);
|
|
1526
|
+
}
|
|
1527
|
+
else if (horizontal_align === 'right' /* Style.HorizontalAlign.Right */) {
|
|
1528
|
+
left = width - this.cell_edge_buffer - text_width;
|
|
1529
|
+
}
|
|
1530
|
+
context.fillText(text, left, original_baseline);
|
|
1531
|
+
}
|
|
1532
|
+
else {
|
|
1533
|
+
// unifying the old "single" and "!single" branches. now the data is
|
|
1534
|
+
// an array of rows, each of which is an array of elements. elements
|
|
1535
|
+
// may have different formatting.
|
|
1536
|
+
let baseline = original_baseline;
|
|
1537
|
+
// let index = 0;
|
|
1538
|
+
for (const line of text_data.strings) {
|
|
1539
|
+
// FIXME: cache line width
|
|
1540
|
+
let line_width = 0;
|
|
1541
|
+
for (const part of line) {
|
|
1542
|
+
line_width += part.width;
|
|
1543
|
+
}
|
|
1544
|
+
if (horizontal_align === 'center') {
|
|
1545
|
+
left = Math.round((width - line_width) / 2);
|
|
1546
|
+
}
|
|
1547
|
+
else if (horizontal_align === 'right') {
|
|
1548
|
+
left = width - this.cell_edge_buffer - line_width;
|
|
1549
|
+
}
|
|
1550
|
+
// still tinkering with these. surely we need to apply scale? FIXME
|
|
1551
|
+
const underline_y = baseline + 2.5;
|
|
1552
|
+
const strike_y = Math.floor(baseline - m2.ascent * 1 / 3) + .5;
|
|
1553
|
+
let x = left;
|
|
1554
|
+
for (const part of line) {
|
|
1555
|
+
if (part.strong && part.emphasis) {
|
|
1556
|
+
context.font = fonts.strong_emphasis;
|
|
1557
|
+
}
|
|
1558
|
+
else if (part.strong) {
|
|
1559
|
+
context.font = fonts.strong;
|
|
1560
|
+
}
|
|
1561
|
+
else if (part.emphasis) {
|
|
1562
|
+
context.font = fonts.emphasis;
|
|
1563
|
+
}
|
|
1564
|
+
else {
|
|
1565
|
+
context.font = fonts.base;
|
|
1566
|
+
}
|
|
1567
|
+
if (!part.hidden) {
|
|
1568
|
+
if (part.text) {
|
|
1569
|
+
// console.info({text: part.text, x, baseline, clip, buffering, text_data});
|
|
1570
|
+
context.fillText(part.text, x, baseline);
|
|
1571
|
+
}
|
|
1572
|
+
if (style.underline && !part.indent) {
|
|
1573
|
+
context.moveTo(x, underline_y);
|
|
1574
|
+
context.lineTo(x + part.width, underline_y);
|
|
1575
|
+
}
|
|
1576
|
+
if ((style.strike || part.strike) && !part.indent) {
|
|
1577
|
+
context.moveTo(x, strike_y);
|
|
1578
|
+
context.lineTo(x + part.width, strike_y);
|
|
1579
|
+
}
|
|
1580
|
+
// we're putting this inside the test block so
|
|
1581
|
+
// that you can't click on hidden text. not sure
|
|
1582
|
+
// if it works, though.
|
|
1583
|
+
if (preserve_layout_info) {
|
|
1584
|
+
part.left = x;
|
|
1585
|
+
part.top = baseline - // m2.block;
|
|
1586
|
+
m2.ascent;
|
|
1587
|
+
part.height = // m2.block;
|
|
1588
|
+
m2.height;
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
x += part.width;
|
|
1592
|
+
}
|
|
1593
|
+
// height tends to be an integer, or maybe at worst 1/2 integer,
|
|
1594
|
+
// so stepping is probably ok (we used to index and multiply).
|
|
1595
|
+
baseline += line_height * m2.height;
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
context.stroke();
|
|
1599
|
+
if (clip) {
|
|
1600
|
+
context.restore();
|
|
1601
|
+
}
|
|
1602
|
+
else if (buffering) {
|
|
1603
|
+
const scale = this.layout.dpr;
|
|
1604
|
+
original_context.drawImage(this.buffer_canvas, 0, 0, (result.width || 0) * scale, height * scale, paint_left, 0, result.width || 0, height);
|
|
1605
|
+
}
|
|
1606
|
+
return result;
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
//# sourceMappingURL=tile_renderer.js.map
|