@trebco/treb 30.16.0 → 31.1.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-generator/api-generator.ts +3 -1
- package/api-generator/package.json +2 -1
- package/dist/languages/treb-i18n-da.mjs +1 -1
- package/dist/languages/treb-i18n-de.mjs +1 -1
- package/dist/languages/treb-i18n-es.mjs +1 -1
- package/dist/languages/treb-i18n-fr.mjs +1 -1
- package/dist/languages/treb-i18n-it.mjs +1 -1
- package/dist/languages/treb-i18n-nl.mjs +1 -1
- package/dist/languages/treb-i18n-no.mjs +1 -1
- package/dist/languages/treb-i18n-pl.mjs +1 -1
- package/dist/languages/treb-i18n-pt.mjs +1 -1
- package/dist/languages/treb-i18n-sv.mjs +1 -1
- package/dist/treb-export-worker.mjs +2 -2
- package/dist/treb-spreadsheet.mjs +13 -13
- package/dist/treb.d.ts +19 -2
- package/package.json +8 -7
- package/treb-base-types/src/font-stack.ts +144 -0
- package/treb-base-types/src/style.ts +121 -11
- package/treb-base-types/src/theme.ts +53 -8
- package/treb-calculator/src/calculator.ts +13 -13
- package/treb-calculator/src/descriptors.ts +12 -4
- package/treb-calculator/src/expression-calculator.ts +17 -4
- package/treb-calculator/src/functions/base-functions.ts +57 -4
- package/treb-calculator/src/functions/statistics-functions.ts +9 -6
- package/treb-calculator/tsconfig.json +11 -0
- package/treb-charts/style/charts.scss +7 -1
- package/treb-data-model/src/annotation.ts +6 -0
- package/treb-data-model/src/data_model.ts +14 -3
- package/treb-data-model/src/language-model.ts +11 -2
- package/treb-data-model/src/sheet.ts +57 -56
- package/treb-embed/markup/toolbar.html +15 -1
- package/treb-embed/src/custom-element/spreadsheet-constructor.ts +38 -0
- package/treb-embed/src/embedded-spreadsheet.ts +139 -32
- package/treb-embed/src/options.ts +3 -0
- package/treb-embed/src/selection-state.ts +1 -0
- package/treb-embed/src/toolbar-message.ts +6 -0
- package/treb-embed/src/types.ts +9 -0
- package/treb-embed/style/defaults.scss +12 -1
- package/treb-embed/style/font-stacks.scss +105 -0
- package/treb-embed/style/layout.scss +1 -0
- package/treb-embed/style/theme-defaults.scss +12 -2
- package/treb-embed/style/toolbar.scss +16 -0
- package/treb-grid/src/editors/overlay_editor.ts +36 -3
- package/treb-grid/src/layout/base_layout.ts +52 -37
- package/treb-grid/src/layout/grid_layout.ts +7 -0
- package/treb-grid/src/render/tile_renderer.ts +156 -148
- package/treb-grid/src/types/grid.ts +188 -54
- package/treb-grid/src/types/grid_events.ts +1 -1
- package/treb-grid/src/types/grid_options.ts +3 -0
- package/treb-grid/src/util/fontmetrics.ts +134 -0
- package/treb-parser/src/parser.ts +12 -3
- package/treb-utils/src/measurement.ts +2 -3
- package/tsproject.json +1 -1
- package/treb-calculator/modern.tsconfig.json +0 -11
- package/treb-grid/src/util/fontmetrics2.ts +0 -182
- package/treb-parser/src/parser.test.ts +0 -298
- /package/treb-embed/{modern.tsconfig.json → tsconfig.json} +0 -0
- /package/treb-export/{modern.tsconfig.json → tsconfig.json} +0 -0
|
@@ -377,6 +377,11 @@ export abstract class BaseLayout {
|
|
|
377
377
|
|
|
378
378
|
}
|
|
379
379
|
|
|
380
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
381
|
+
public FocusInLayout(target?: EventTarget): boolean {
|
|
382
|
+
return false;
|
|
383
|
+
}
|
|
384
|
+
|
|
380
385
|
/**
|
|
381
386
|
* if the DPR has changed, update it and return true. otherwise return
|
|
382
387
|
* false. this is used on resize events: if the scale has changed, we
|
|
@@ -672,7 +677,7 @@ export abstract class BaseLayout {
|
|
|
672
677
|
|
|
673
678
|
}
|
|
674
679
|
|
|
675
|
-
public UpdateAnnotation(elements: Annotation | Annotation[]): void {
|
|
680
|
+
public UpdateAnnotation(elements: Annotation | Annotation[], theme: Theme): void {
|
|
676
681
|
if (!Array.isArray(elements)) elements = [elements];
|
|
677
682
|
for (const annotation of elements) {
|
|
678
683
|
const view = annotation.view[this.view.view_index] || {};
|
|
@@ -684,8 +689,48 @@ export abstract class BaseLayout {
|
|
|
684
689
|
}
|
|
685
690
|
*/
|
|
686
691
|
|
|
692
|
+
let font_size_pt = 10 * this.scale;
|
|
693
|
+
|
|
694
|
+
// console.info(annotation.data.style);
|
|
695
|
+
|
|
696
|
+
if (annotation.data.style?.font_size?.unit === 'em') {
|
|
697
|
+
|
|
698
|
+
font_size_pt *= annotation.data.style.font_size.value;
|
|
699
|
+
|
|
700
|
+
// console.info("scaling up...", font_size_pt)
|
|
701
|
+
|
|
702
|
+
}
|
|
703
|
+
|
|
687
704
|
view.node.dataset.scale = this.scale.toString();
|
|
688
|
-
|
|
705
|
+
|
|
706
|
+
// maybe we should just reset and then apply?
|
|
707
|
+
|
|
708
|
+
if (annotation.data.style?.font_face && annotation.data.style.font_face.startsWith('stack:')) {
|
|
709
|
+
const name = annotation.data.style.font_face.substring(6);
|
|
710
|
+
const stack = theme.font_stacks[name];
|
|
711
|
+
if(stack) {
|
|
712
|
+
view.node.classList.add('treb-inherit-font');
|
|
713
|
+
view.node.style.fontFamily = stack.font || '';
|
|
714
|
+
|
|
715
|
+
// font_size_pt *= stack.scale;
|
|
716
|
+
|
|
717
|
+
if (stack.variants) {
|
|
718
|
+
view.node.style.fontVariant = stack.variants;
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
else {
|
|
722
|
+
view.node.classList.remove('treb-inherit-font');
|
|
723
|
+
view.node.style.fontFamily = '';
|
|
724
|
+
view.node.style.fontVariant = '';
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
else {
|
|
728
|
+
view.node.classList.remove('treb-inherit-font');
|
|
729
|
+
view.node.style.fontFamily = '';
|
|
730
|
+
view.node.style.fontVariant = '';
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
view.node.style.fontSize = `${font_size_pt}pt`;
|
|
689
734
|
|
|
690
735
|
// update the layout here if necessary. after that it should
|
|
691
736
|
// be persisted (assuming it's saved). eventually this should
|
|
@@ -848,13 +893,13 @@ export abstract class BaseLayout {
|
|
|
848
893
|
|
|
849
894
|
}
|
|
850
895
|
|
|
851
|
-
public AddAnnotation(annotation: Annotation): void {
|
|
896
|
+
public AddAnnotation(annotation: Annotation, theme: Theme): void {
|
|
852
897
|
const view = annotation.view[this.view.view_index] || {};
|
|
853
898
|
if (!view.node) {
|
|
854
899
|
throw new Error('annotation view/node missing');
|
|
855
900
|
}
|
|
856
901
|
this.annotation_container.appendChild(view.node);
|
|
857
|
-
this.UpdateAnnotation(annotation);
|
|
902
|
+
this.UpdateAnnotation(annotation, theme);
|
|
858
903
|
}
|
|
859
904
|
|
|
860
905
|
// testing moving this here...
|
|
@@ -1246,38 +1291,6 @@ export abstract class BaseLayout {
|
|
|
1246
1291
|
|
|
1247
1292
|
parent.appendChild(tile);
|
|
1248
1293
|
|
|
1249
|
-
// NOTE re: text rendering. you can't use baseline = top, because that's
|
|
1250
|
-
// inconsistent among browsers. in fact of all baselines, the only ones that
|
|
1251
|
-
// are even close are alphabetic and bottom -- bottom is slightly different
|
|
1252
|
-
// in ffx compared to chrome and edge, but that could be because of different
|
|
1253
|
-
// font rendering schemes. alphabetic is the closest, but requires offset for
|
|
1254
|
-
// ascender (or descender).
|
|
1255
|
-
|
|
1256
|
-
// actually it looks like there's a 1px difference in bottom baseline...
|
|
1257
|
-
// alphabetic is the only one that's consistent.
|
|
1258
|
-
|
|
1259
|
-
// FIXME: why not just offset on a per-browser basis? it might be ugly
|
|
1260
|
-
// but it's simpler.
|
|
1261
|
-
|
|
1262
|
-
// for the time being we will use bottom.
|
|
1263
|
-
|
|
1264
|
-
// why were we prepainting (because firefox, below?) and why was this so
|
|
1265
|
-
// slow? do we need to preset the context text parameters?
|
|
1266
|
-
|
|
1267
|
-
/*
|
|
1268
|
-
const context = tile.getContext('2d', {alpha: false});
|
|
1269
|
-
|
|
1270
|
-
if (context) {
|
|
1271
|
-
context.textAlign = 'left';
|
|
1272
|
-
context.textBaseline = 'alphabetic';
|
|
1273
|
-
|
|
1274
|
-
// prepaint -- firefox is a little slow so flashes empty tiles sometimes
|
|
1275
|
-
|
|
1276
|
-
// context.fillStyle = '#fff'; // FIXME: use theme color
|
|
1277
|
-
// context.fillRect(0, 0, tile.width, tile.height);
|
|
1278
|
-
}
|
|
1279
|
-
*/
|
|
1280
|
-
|
|
1281
1294
|
return tile;
|
|
1282
1295
|
|
|
1283
1296
|
}
|
|
@@ -1319,7 +1332,9 @@ export abstract class BaseLayout {
|
|
|
1319
1332
|
|
|
1320
1333
|
// TODO: dropdown caret
|
|
1321
1334
|
|
|
1322
|
-
this.dropdown_list.style.font = Style.
|
|
1335
|
+
this.dropdown_list.style.font = Style.CompositeFont(theme.grid_cell_font_size, {
|
|
1336
|
+
font_face: 'stack:default',
|
|
1337
|
+
}, this.scale, theme).font;
|
|
1323
1338
|
|
|
1324
1339
|
// testing
|
|
1325
1340
|
|
|
@@ -99,6 +99,13 @@ export class GridLayout extends BaseLayout {
|
|
|
99
99
|
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
public FocusInLayout(target?: EventTarget): boolean {
|
|
103
|
+
if (target && target instanceof Element && this.container?.contains(target)) {
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
|
|
102
109
|
public ResizeCursor(resize?: 'row'|'column'): void {
|
|
103
110
|
switch (resize) {
|
|
104
111
|
case 'row':
|
|
@@ -28,16 +28,18 @@ import type { ICellAddress,
|
|
|
28
28
|
import { TextPartFlag, Style, ValueType, Area, Rectangle, ResolveThemeColor, IsDefinedColor } from 'treb-base-types';
|
|
29
29
|
|
|
30
30
|
import type { Tile } from '../types/tile';
|
|
31
|
-
import { FontMetricsCache as FontMetricsCache2 } from '../util/fontmetrics2';
|
|
31
|
+
// import { FontMetricsCache as FontMetricsCache2 } from '../util/fontmetrics2';
|
|
32
32
|
import type { FormattedString} from 'treb-parser';
|
|
33
33
|
import { MDParser } from 'treb-parser';
|
|
34
|
-
import
|
|
34
|
+
import { BaseLayout, TileRange } from '../layout/base_layout';
|
|
35
35
|
import type { DataModel, ViewModel } from 'treb-data-model';
|
|
36
36
|
import type { GridOptions } from '../types/grid_options';
|
|
37
37
|
|
|
38
|
+
import { Get as GetFontMetrics } from '../util/fontmetrics';
|
|
39
|
+
|
|
38
40
|
const DEFAULT_INDENT = ' '; // two spaces in the current font
|
|
39
|
-
const BASELINE = 'bottom';
|
|
40
|
-
const WK = /webkit/i.test(typeof navigator === 'undefined' ? '' : navigator?.userAgent || '') ? 1 : 0;
|
|
41
|
+
// const BASELINE = 'bottom';
|
|
42
|
+
// const WK = /webkit/i.test(typeof navigator === 'undefined' ? '' : navigator?.userAgent || '') ? 1 : 0;
|
|
41
43
|
|
|
42
44
|
interface FontSet {
|
|
43
45
|
base: string,
|
|
@@ -118,7 +120,8 @@ export class TileRenderer {
|
|
|
118
120
|
this.buffer_context = context;
|
|
119
121
|
this.buffer_context.setTransform(scale, 0, 0, scale, 0, 0);
|
|
120
122
|
this.buffer_context.textAlign = 'left';
|
|
121
|
-
this.buffer_context.textBaseline = BASELINE; //
|
|
123
|
+
this.buffer_context.textBaseline = // BASELINE; //
|
|
124
|
+
'alphabetic';
|
|
122
125
|
}
|
|
123
126
|
|
|
124
127
|
// this.UpdateTheme();
|
|
@@ -173,6 +176,52 @@ export class TileRenderer {
|
|
|
173
176
|
}
|
|
174
177
|
|
|
175
178
|
/**
|
|
179
|
+
* new method for measuring text, intended to take into account
|
|
180
|
+
* indentation and wrapping, scale, and all the other stuff.
|
|
181
|
+
*/
|
|
182
|
+
public MeasureText(cell: Cell, current_width: number, override_scale?: number) {
|
|
183
|
+
|
|
184
|
+
// we need a canvas. I guess we can just randomly use one?
|
|
185
|
+
|
|
186
|
+
const tile = this.layout.grid_tiles[0][0];
|
|
187
|
+
|
|
188
|
+
// we need fonts, and check if we need variants
|
|
189
|
+
|
|
190
|
+
const scale = override_scale ?? this.layout.scale;
|
|
191
|
+
const style: CellStyle = cell.style || {};
|
|
192
|
+
const base_font = Style.CompositeFont(this.theme.grid_cell_font_size, style, scale, this.theme);
|
|
193
|
+
const metrics = GetFontMetrics(base_font.font, base_font.variants);
|
|
194
|
+
|
|
195
|
+
const fonts: FontSet = {
|
|
196
|
+
base: base_font.font,
|
|
197
|
+
strong: Style.CompositeFont(this.theme.grid_cell_font_size, {...style, bold: true}, scale, this.theme).font,
|
|
198
|
+
emphasis: Style.CompositeFont(this.theme.grid_cell_font_size, {...style, italic: true}, scale, this.theme).font,
|
|
199
|
+
strong_emphasis: Style.CompositeFont(this.theme.grid_cell_font_size, {...style, bold: true, italic: true }, scale, this.theme).font,
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
if (base_font.variants) {
|
|
203
|
+
tile.style.fontVariant = base_font.variants;
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
tile.style.fontVariant = '';
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const context = tile.getContext('2d', { alpha: false });
|
|
210
|
+
|
|
211
|
+
if (!context) {
|
|
212
|
+
throw new Error('context failed');
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const prepped = this.PrepText(context, fonts, cell, current_width);
|
|
216
|
+
|
|
217
|
+
const width = prepped.width;
|
|
218
|
+
const height = metrics.height * prepped.strings.length;
|
|
219
|
+
|
|
220
|
+
return { width, height };
|
|
221
|
+
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/* *
|
|
176
225
|
* use one of the tile contexts to measure text. we are using the tile
|
|
177
226
|
* context because it's attached to the DOM, and style is applied. we need
|
|
178
227
|
* that for the root font size, in case font size in the style is relative
|
|
@@ -188,7 +237,7 @@ export class TileRenderer {
|
|
|
188
237
|
*
|
|
189
238
|
* @param text
|
|
190
239
|
* @param font
|
|
191
|
-
|
|
240
|
+
* /
|
|
192
241
|
public MeasureText(text: string, font?: string): TextMetrics {
|
|
193
242
|
|
|
194
243
|
const context = this.layout.grid_tiles[0][0].getContext('2d', { alpha: false });
|
|
@@ -203,6 +252,7 @@ export class TileRenderer {
|
|
|
203
252
|
|
|
204
253
|
return context.measureText(text);
|
|
205
254
|
}
|
|
255
|
+
*/
|
|
206
256
|
|
|
207
257
|
/**
|
|
208
258
|
* when drawing to the buffered canvas, (1) ensure it's large enough,
|
|
@@ -233,7 +283,8 @@ export class TileRenderer {
|
|
|
233
283
|
if (context) {
|
|
234
284
|
this.buffer_context = context;
|
|
235
285
|
this.buffer_context.textAlign = 'left';
|
|
236
|
-
this.buffer_context.textBaseline = BASELINE;
|
|
286
|
+
this.buffer_context.textBaseline = // BASELINE;
|
|
287
|
+
'alphabetic';
|
|
237
288
|
}
|
|
238
289
|
|
|
239
290
|
}
|
|
@@ -291,7 +342,8 @@ export class TileRenderer {
|
|
|
291
342
|
throw new Error('invalid context');
|
|
292
343
|
}
|
|
293
344
|
|
|
294
|
-
const
|
|
345
|
+
const font_info = Style.CompositeFont(this.theme.grid_cell_font_size, this.theme.headers||{}, this.layout.scale, this.theme);
|
|
346
|
+
const m2 = GetFontMetrics(font_info.font, font_info.variants);
|
|
295
347
|
|
|
296
348
|
const scale = this.layout.dpr;
|
|
297
349
|
const header_size = this.layout.header_offset;
|
|
@@ -351,8 +403,8 @@ export class TileRenderer {
|
|
|
351
403
|
// 0 or 1 pixel) we don't want to render them here.
|
|
352
404
|
|
|
353
405
|
context.textAlign = 'center';
|
|
354
|
-
context.textBaseline = 'middle';
|
|
355
|
-
context.font = Style.Font(this.theme.headers||{}, this.layout.scale);
|
|
406
|
+
context.textBaseline = 'middle'; // FUXME
|
|
407
|
+
context.font = font_info.font; // Style.Font(this.theme.headers||{}, this.layout.scale);
|
|
356
408
|
|
|
357
409
|
context.fillStyle = ResolveThemeColor(this.theme, this.theme.headers?.text);
|
|
358
410
|
|
|
@@ -365,7 +417,8 @@ export class TileRenderer {
|
|
|
365
417
|
context.lineTo(header_size.x, 0 - 0.5);
|
|
366
418
|
context.stroke();
|
|
367
419
|
|
|
368
|
-
this.RenderRowLabels(context, 0, this.view.active_sheet.freeze.rows - 1, m2.block);
|
|
420
|
+
this.RenderRowLabels(context, 0, this.view.active_sheet.freeze.rows - 1, // m2.block);
|
|
421
|
+
m2.height);
|
|
369
422
|
|
|
370
423
|
}
|
|
371
424
|
|
|
@@ -458,7 +511,10 @@ export class TileRenderer {
|
|
|
458
511
|
|
|
459
512
|
const scale = this.layout.dpr;
|
|
460
513
|
const header_size = this.layout.header_offset;
|
|
461
|
-
|
|
514
|
+
|
|
515
|
+
const font_info = Style.CompositeFont(this.theme.grid_cell_font_size, this.theme.headers||{}, this.layout.scale, this.theme);
|
|
516
|
+
const m2 = GetFontMetrics(font_info.font, font_info.variants);
|
|
517
|
+
|
|
462
518
|
|
|
463
519
|
for (let column = tiles.start.column; column <= tiles.end.column; column++) {
|
|
464
520
|
|
|
@@ -470,8 +526,15 @@ export class TileRenderer {
|
|
|
470
526
|
context.setTransform(scale, 0, 0, scale, 0, 0);
|
|
471
527
|
|
|
472
528
|
context.textAlign = 'center';
|
|
473
|
-
context.textBaseline = 'middle';
|
|
474
|
-
|
|
529
|
+
context.textBaseline = 'middle'; // FIXME
|
|
530
|
+
|
|
531
|
+
context.font = font_info.font; // Style.Font(this.theme.headers||{}, this.layout.scale);
|
|
532
|
+
if (font_info.variants) {
|
|
533
|
+
tile.style.fontVariant = font_info.variants;
|
|
534
|
+
}
|
|
535
|
+
else {
|
|
536
|
+
tile.style.fontVariant = '';
|
|
537
|
+
}
|
|
475
538
|
|
|
476
539
|
context.fillStyle = this.theme.headers?.fill ? ResolveThemeColor(this.theme, this.theme.headers.fill) : '';
|
|
477
540
|
context.fillRect(0, 0, tile.logical_size.width, this.layout.header_offset.y);
|
|
@@ -512,8 +575,16 @@ export class TileRenderer {
|
|
|
512
575
|
context.setTransform(scale, 0, 0, scale, 0, 0);
|
|
513
576
|
|
|
514
577
|
context.textAlign = 'center';
|
|
515
|
-
context.textBaseline = 'middle';
|
|
516
|
-
context.font = Style.Font(this.theme.headers||{}, this.layout.scale);
|
|
578
|
+
context.textBaseline = 'middle'; // FIXME
|
|
579
|
+
// context.font = Style.Font(this.theme.headers||{}, this.layout.scale);
|
|
580
|
+
|
|
581
|
+
context.font = font_info.font; // Style.Font(this.theme.headers||{}, this.layout.scale);
|
|
582
|
+
if (font_info.variants) {
|
|
583
|
+
tile.style.fontVariant = font_info.variants;
|
|
584
|
+
}
|
|
585
|
+
else {
|
|
586
|
+
tile.style.fontVariant = '';
|
|
587
|
+
}
|
|
517
588
|
|
|
518
589
|
context.fillRect(0, 0, this.layout.header_offset.x, tile.logical_size.height);
|
|
519
590
|
|
|
@@ -527,7 +598,8 @@ export class TileRenderer {
|
|
|
527
598
|
|
|
528
599
|
context.strokeStyle = this.theme.headers_grid_color || '';
|
|
529
600
|
|
|
530
|
-
this.RenderRowLabels(context, tile.first_cell.row, tile.last_cell.row, m2.block);
|
|
601
|
+
this.RenderRowLabels(context, tile.first_cell.row, tile.last_cell.row, // m2.block);
|
|
602
|
+
m2.height);
|
|
531
603
|
|
|
532
604
|
tile.dirty = false;
|
|
533
605
|
}
|
|
@@ -587,10 +659,18 @@ export class TileRenderer {
|
|
|
587
659
|
/** render a tile */
|
|
588
660
|
public Render(tile: Tile): void {
|
|
589
661
|
|
|
662
|
+
// can we assume this is always set? this feels sloppy, and
|
|
663
|
+
// it feels like something we'd like to turn off... can we
|
|
664
|
+
// fetch the canvas more than once? not sure what the impact of
|
|
665
|
+
// that would be.
|
|
666
|
+
|
|
667
|
+
// tile.style.fontVariantNumeric = 'lining-nums tabular-nums';
|
|
668
|
+
|
|
590
669
|
const context = tile.getContext('2d', { alpha: false });
|
|
591
670
|
if (!context) { return; } // should throw
|
|
592
671
|
|
|
593
|
-
context.textBaseline = BASELINE;
|
|
672
|
+
context.textBaseline = // BASELINE;
|
|
673
|
+
'alphabetic';
|
|
594
674
|
|
|
595
675
|
const scale = this.layout.dpr;
|
|
596
676
|
|
|
@@ -1654,13 +1734,28 @@ export class TileRenderer {
|
|
|
1654
1734
|
// (eventually) painting to the buffer context. just remember to set
|
|
1655
1735
|
// font in the buffer context.
|
|
1656
1736
|
|
|
1737
|
+
const base_font = Style.CompositeFont(this.theme.grid_cell_font_size, style, this.layout.scale, this.theme);
|
|
1738
|
+
|
|
1739
|
+
/*
|
|
1740
|
+
if (cell.value) {
|
|
1741
|
+
console.info(base_font);
|
|
1742
|
+
}
|
|
1743
|
+
*/
|
|
1744
|
+
|
|
1657
1745
|
const fonts: FontSet = {
|
|
1658
|
-
base:
|
|
1659
|
-
strong: Style.
|
|
1660
|
-
emphasis: Style.
|
|
1661
|
-
strong_emphasis: Style.
|
|
1746
|
+
base: base_font.font,
|
|
1747
|
+
strong: Style.CompositeFont(this.theme.grid_cell_font_size, {...style, bold: true}, this.layout.scale, this.theme).font,
|
|
1748
|
+
emphasis: Style.CompositeFont(this.theme.grid_cell_font_size, {...style, italic: true}, this.layout.scale, this.theme).font,
|
|
1749
|
+
strong_emphasis: Style.CompositeFont(this.theme.grid_cell_font_size, {...style, bold: true, italic: true }, this.layout.scale, this.theme).font,
|
|
1662
1750
|
};
|
|
1663
1751
|
|
|
1752
|
+
if (base_font.variants) {
|
|
1753
|
+
tile.style.fontVariant = base_font.variants;
|
|
1754
|
+
}
|
|
1755
|
+
else {
|
|
1756
|
+
tile.style.fontVariant = '';
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1664
1759
|
context.font = fonts.base;
|
|
1665
1760
|
|
|
1666
1761
|
//
|
|
@@ -1927,8 +2022,17 @@ export class TileRenderer {
|
|
|
1927
2022
|
// and bold variants). this should be OK because we use it for height, mostly.
|
|
1928
2023
|
// not sure about invisible text (FIXME)
|
|
1929
2024
|
|
|
1930
|
-
const m2 = FontMetricsCache2.Get(fonts.base, this.theme.grid_cell?.font_size?.value);
|
|
1931
|
-
//
|
|
2025
|
+
// const m2 = FontMetricsCache2.Get(fonts.base, // this.theme.grid_cell?.font_size?.value);
|
|
2026
|
+
// this.theme.grid_cell_font_size);
|
|
2027
|
+
|
|
2028
|
+
|
|
2029
|
+
const m2 = GetFontMetrics(fonts.base, base_font.variants);
|
|
2030
|
+
|
|
2031
|
+
/*
|
|
2032
|
+
if (cell.value) {
|
|
2033
|
+
console.info(fonts.base, {m2});
|
|
2034
|
+
}
|
|
2035
|
+
*/
|
|
1932
2036
|
|
|
1933
2037
|
// set stroke for underline
|
|
1934
2038
|
|
|
@@ -1936,7 +2040,7 @@ export class TileRenderer {
|
|
|
1936
2040
|
// because our default style happens to be the default color. that applies
|
|
1937
2041
|
// to text color, background color and border color.
|
|
1938
2042
|
|
|
1939
|
-
context.lineWidth = 1;
|
|
2043
|
+
context.lineWidth = 1; // 1.5; // FIXME: scale? font scale?
|
|
1940
2044
|
|
|
1941
2045
|
context.strokeStyle = context.fillStyle =
|
|
1942
2046
|
text_data.format ? text_data.format : ResolveThemeColor(this.theme, style.text, 1);
|
|
@@ -1945,11 +2049,12 @@ export class TileRenderer {
|
|
|
1945
2049
|
|
|
1946
2050
|
let left = this.cell_edge_buffer;
|
|
1947
2051
|
|
|
1948
|
-
const line_height = 1.25;
|
|
2052
|
+
const line_height = 1; // 1.25;
|
|
1949
2053
|
|
|
1950
2054
|
//const line_count = text_data.single ? 1 : text_data.strings.length;
|
|
1951
2055
|
const line_count = text_data.strings.length;
|
|
1952
|
-
const text_height = (line_count * m2.block * line_height);
|
|
2056
|
+
// const text_height = (line_count * m2.block * line_height);
|
|
2057
|
+
const text_height = (line_count * m2.height * line_height);
|
|
1953
2058
|
|
|
1954
2059
|
// we stopped clipping initially because it was expensive -- but then
|
|
1955
2060
|
// we were doing it on every cell. it's hard to imagine that clipping
|
|
@@ -1970,20 +2075,23 @@ export class TileRenderer {
|
|
|
1970
2075
|
context.clip();
|
|
1971
2076
|
}
|
|
1972
2077
|
|
|
1973
|
-
// path for underline. if there's no underline
|
|
2078
|
+
// path for underline (and strike). if there's no underline (or strike),
|
|
2079
|
+
// it won't do anything.
|
|
1974
2080
|
|
|
1975
2081
|
context.beginPath();
|
|
1976
2082
|
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
2083
|
+
let original_baseline = Math.round(
|
|
2084
|
+
(height - m2.descent - 2) + // baseline for first line of text
|
|
2085
|
+
(-line_height * m2.height * (line_count - 1))); // adjust for multiple lines
|
|
1980
2086
|
|
|
1981
2087
|
switch (style.vertical_align) {
|
|
1982
|
-
case 'top':
|
|
1983
|
-
original_baseline = Math.round(m2.
|
|
2088
|
+
case 'top':
|
|
2089
|
+
original_baseline = Math.round(2 + m2.ascent);
|
|
1984
2090
|
break;
|
|
1985
|
-
|
|
1986
|
-
|
|
2091
|
+
|
|
2092
|
+
case 'middle':
|
|
2093
|
+
original_baseline = Math.round((height - text_height) / 2 + m2.ascent) - 1.5;
|
|
2094
|
+
|
|
1987
2095
|
break;
|
|
1988
2096
|
}
|
|
1989
2097
|
|
|
@@ -2017,7 +2125,7 @@ export class TileRenderer {
|
|
|
2017
2125
|
// may have different formatting.
|
|
2018
2126
|
|
|
2019
2127
|
let baseline = original_baseline;
|
|
2020
|
-
let index = 0;
|
|
2128
|
+
// let index = 0;
|
|
2021
2129
|
|
|
2022
2130
|
for (const line of text_data.strings) {
|
|
2023
2131
|
|
|
@@ -2026,29 +2134,17 @@ export class TileRenderer {
|
|
|
2026
2134
|
let line_width = 0;
|
|
2027
2135
|
for (const part of line) { line_width += part.width; }
|
|
2028
2136
|
|
|
2029
|
-
if (horizontal_align === 'center'
|
|
2137
|
+
if (horizontal_align === 'center') {
|
|
2030
2138
|
left = Math.round((width - line_width) / 2);
|
|
2031
2139
|
}
|
|
2032
|
-
else if (horizontal_align === 'right'
|
|
2140
|
+
else if (horizontal_align === 'right') {
|
|
2033
2141
|
left = width - this.cell_edge_buffer - line_width;
|
|
2034
2142
|
}
|
|
2035
2143
|
|
|
2036
|
-
|
|
2037
|
-
if (style.font_underline) {
|
|
2038
|
-
const underline_y = Math.floor(baseline + 1.5 - m2.descender - WK) + .5; // metrics.block - 3.5 - metrics.ascent - 3;
|
|
2039
|
-
context.moveTo(left, underline_y);
|
|
2040
|
-
context.lineTo(left + line_width, underline_y);
|
|
2041
|
-
}
|
|
2042
|
-
|
|
2043
|
-
if (style.font_strike) {
|
|
2044
|
-
const strike_y = Math.floor(baseline - m2.descender - m2.ascender / 2) + .5;
|
|
2045
|
-
context.moveTo(left, strike_y);
|
|
2046
|
-
context.lineTo(left + line_width, strike_y);
|
|
2047
|
-
}
|
|
2048
|
-
*/
|
|
2144
|
+
// still tinkering with these. surely we need to apply scale? FIXME
|
|
2049
2145
|
|
|
2050
|
-
const underline_y =
|
|
2051
|
-
const strike_y = Math.floor(baseline - m2.
|
|
2146
|
+
const underline_y = baseline + 2.5;
|
|
2147
|
+
const strike_y = Math.floor(baseline - m2.ascent * 1 / 3) + .5;
|
|
2052
2148
|
|
|
2053
2149
|
let x = left;
|
|
2054
2150
|
for (const part of line) {
|
|
@@ -2089,8 +2185,10 @@ export class TileRenderer {
|
|
|
2089
2185
|
|
|
2090
2186
|
if (preserve_layout_info) {
|
|
2091
2187
|
part.left = x;
|
|
2092
|
-
part.top = baseline - m2.block;
|
|
2093
|
-
|
|
2188
|
+
part.top = baseline - // m2.block;
|
|
2189
|
+
m2.ascent;
|
|
2190
|
+
part.height = // m2.block;
|
|
2191
|
+
m2.height;
|
|
2094
2192
|
}
|
|
2095
2193
|
|
|
2096
2194
|
}
|
|
@@ -2099,106 +2197,16 @@ export class TileRenderer {
|
|
|
2099
2197
|
|
|
2100
2198
|
}
|
|
2101
2199
|
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
}
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
}
|
|
2109
|
-
|
|
2110
|
-
/*
|
|
2111
|
-
else if (text_data.single) {
|
|
2112
|
-
|
|
2113
|
-
// const cached_font = context.font;
|
|
2114
|
-
// const italic_font = /italic/i.test(cached_font) ? cached_font : 'italic ' + cached_font;
|
|
2115
|
-
|
|
2116
|
-
// single refers to single-line text that has multiple components,
|
|
2117
|
-
// including spacing or hidden text. single line text (not formatted)
|
|
2118
|
-
// probably doesn't have this flag set, it will use the next block.
|
|
2119
|
-
// these could (should?) be consolidated
|
|
2120
|
-
|
|
2121
|
-
if (horizontal_align === Style.HorizontalAlign.Center) {
|
|
2122
|
-
left = Math.round((width - text_data.width) / 2);
|
|
2123
|
-
}
|
|
2124
|
-
else if (horizontal_align === Style.HorizontalAlign.Right) {
|
|
2125
|
-
left = width - this.cell_edge_buffer - text_data.width;
|
|
2126
|
-
}
|
|
2127
|
-
|
|
2128
|
-
const underline_y = Math.floor(original_baseline + 1.5 - m2.descender - WK) + .5; // metrics.block - 3.5 - metrics.ascent - 3;
|
|
2129
|
-
const strike_y = Math.floor(original_baseline - m2.descender - m2.ascender / 2) + .5;
|
|
2130
|
-
|
|
2131
|
-
// we want a single underline, possibly spanning hidden elements,
|
|
2132
|
-
// but not starting or stopping on a hidden element (usually invisible
|
|
2133
|
-
// parentheses).
|
|
2200
|
+
// height tends to be an integer, or maybe at worst 1/2 integer,
|
|
2201
|
+
// so stepping is probably ok (we used to index and multiply).
|
|
2134
2202
|
|
|
2135
|
-
|
|
2136
|
-
if (!part.hidden) {
|
|
2203
|
+
baseline += line_height * m2.height;
|
|
2137
2204
|
|
|
2138
|
-
context.fillText(part.text, left, original_baseline);
|
|
2139
2205
|
|
|
2140
|
-
if (style.font_underline) {
|
|
2141
|
-
context.moveTo(left, underline_y);
|
|
2142
|
-
context.lineTo(left + part.width, underline_y);
|
|
2143
|
-
}
|
|
2144
|
-
if (style.font_strike) {
|
|
2145
|
-
context.moveTo(left, strike_y);
|
|
2146
|
-
context.lineTo(left + part.width, strike_y);
|
|
2147
|
-
}
|
|
2148
|
-
}
|
|
2149
|
-
|
|
2150
|
-
if (preserve_layout_info) {
|
|
2151
|
-
part.left = left;
|
|
2152
|
-
part.top = original_baseline - m2.block;
|
|
2153
|
-
part.height = m2.block;
|
|
2154
|
-
}
|
|
2155
|
-
|
|
2156
|
-
left += part.width;
|
|
2157
2206
|
}
|
|
2158
2207
|
|
|
2159
|
-
}
|
|
2160
|
-
else {
|
|
2161
|
-
|
|
2162
|
-
let baseline = original_baseline;
|
|
2163
|
-
let index = 0;
|
|
2164
|
-
|
|
2165
|
-
for (const part of text_data.strings) {
|
|
2166
|
-
|
|
2167
|
-
// here we justify based on part, each line might have different width
|
|
2168
|
-
|
|
2169
|
-
if (horizontal_align === Style.HorizontalAlign.Center) {
|
|
2170
|
-
left = Math.round((width - part.width) / 2);
|
|
2171
|
-
}
|
|
2172
|
-
else if (horizontal_align === Style.HorizontalAlign.Right) {
|
|
2173
|
-
left = width - this.cell_edge_buffer - part.width;
|
|
2174
|
-
}
|
|
2175
|
-
|
|
2176
|
-
if (style.font_underline) {
|
|
2177
|
-
const underline_y = Math.floor(baseline + 1.5 - m2.descender - WK) + .5; // metrics.block - 3.5 - metrics.ascent - 3;
|
|
2178
|
-
context.moveTo(left, underline_y);
|
|
2179
|
-
context.lineTo(left + part.width, underline_y);
|
|
2180
|
-
}
|
|
2181
|
-
|
|
2182
|
-
if (style.font_strike) {
|
|
2183
|
-
const strike_y = Math.floor(baseline - m2.descender - m2.ascender / 2) + .5;
|
|
2184
|
-
context.moveTo(left, strike_y);
|
|
2185
|
-
context.lineTo(left + part.width, strike_y);
|
|
2186
|
-
}
|
|
2187
|
-
|
|
2188
|
-
context.fillText(part.text, left, baseline);
|
|
2189
|
-
|
|
2190
|
-
if (preserve_layout_info) {
|
|
2191
|
-
part.left = left;
|
|
2192
|
-
part.top = baseline - m2.block;
|
|
2193
|
-
part.height = m2.block;
|
|
2194
|
-
}
|
|
2195
|
-
|
|
2196
|
-
index++;
|
|
2197
|
-
baseline = Math.round(original_baseline + index * m2.block * line_height);
|
|
2198
|
-
}
|
|
2199
2208
|
|
|
2200
2209
|
}
|
|
2201
|
-
*/
|
|
2202
2210
|
|
|
2203
2211
|
context.stroke();
|
|
2204
2212
|
|