Skip to content

Commit 77c620f

Browse files
authored
fix: Columns not auto sizing correctly (#2359)
- Improved logic for calculating the range to truncate a string, based on the width of the assumed largest character and smallest character for a given font. This fixes the observed issue of wide strings not being truncated. - Changed column width calculation to calculate text width by iterating through the text and adding up individual character widths which are cached. This approach size columns more precisely than estimating based on a single character's width and the length of the string, and does not appear to have a significant performance impact. Test snippet used to see if column width calculation works as expected; resize columns to observe truncation behaviour ``` from deephaven import new_table from deephaven.column import string_col a = new_table([string_col("allllllllllllllllllllllllllllllllllllllllllllllll", ["a"])]) b = new_table([string_col("ammmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm", ["b"])]) c = new_table([string_col("c", ["allllllllllllllllllllllllllllllllllllllllllllllll"])]) d = new_table([string_col("d", ["ammmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm"])]) ``` Closes #2288
1 parent e28a741 commit 77c620f

File tree

246 files changed

+444
-89
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

246 files changed

+444
-89
lines changed

packages/grid/src/CellRenderer.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -91,14 +91,16 @@ abstract class CellRenderer {
9191
context: CanvasRenderingContext2D,
9292
text: string,
9393
width: number,
94-
fontWidth: number,
94+
fontWidthLower?: number,
95+
fontWidthUpper?: number,
9596
truncationChar?: string
9697
): string =>
9798
GridRenderer.truncateToWidth(
9899
context,
99100
text,
100101
width,
101-
fontWidth,
102+
fontWidthLower,
103+
fontWidthUpper,
102104
truncationChar
103105
),
104106
{ max: 10000 }

packages/grid/src/DataBarCellRenderer.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
import GridColorUtils from './GridColorUtils';
1212
import GridUtils from './GridUtils';
1313
import memoizeClear from './memoizeClear';
14-
import { DEFAULT_FONT_WIDTH, type GridRenderState } from './GridRendererTypes';
14+
import { type GridRenderState } from './GridRendererTypes';
1515
import type GridModel from './GridModel';
1616

1717
interface DataBarRenderMetrics {
@@ -87,7 +87,8 @@ class DataBarCellRenderer extends CellRenderer {
8787
allRowHeights,
8888
allRowYs,
8989
firstColumn,
90-
fontWidths,
90+
fontWidthsLower,
91+
fontWidthsUpper,
9192
} = metrics;
9293

9394
const isFirstColumn = column === firstColumn;
@@ -103,13 +104,15 @@ class DataBarCellRenderer extends CellRenderer {
103104
width: textWidth,
104105
} = GridUtils.getTextRenderMetrics(state, column, row);
105106

106-
const fontWidth = fontWidths?.get(context.font) ?? DEFAULT_FONT_WIDTH;
107+
const fontWidthLower = fontWidthsLower.get(context.font);
108+
const fontWidthUpper = fontWidthsUpper.get(context.font);
107109
const truncationChar = model.truncationCharForCell(modelColumn, modelRow);
108110
const truncatedText = this.getCachedTruncatedString(
109111
context,
110112
text,
111113
textWidth,
112-
fontWidth,
114+
fontWidthLower,
115+
fontWidthUpper,
113116
truncationChar
114117
);
115118

packages/grid/src/GridMetricCalculator.test.ts

+89-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { trimMap } from './GridMetricCalculator';
1+
import GridMetricCalculator, { trimMap } from './GridMetricCalculator';
22

33
describe('trimMap', () => {
44
function makeMap(low = 0, high = 10): Map<number, number> {
@@ -41,3 +41,91 @@ describe('trimMap', () => {
4141
expectResult(makeMap(0, 100), makeMap(51, 100), 100, 50);
4242
});
4343
});
44+
45+
describe('getTextWidth', () => {
46+
const font = 'mock font';
47+
const mockCharWidths = new Map([
48+
['a', 10],
49+
['b', 20],
50+
['c', 30],
51+
['d', 40],
52+
['e', 50],
53+
]);
54+
let gridMetricCalculator;
55+
let allCharWidths;
56+
57+
beforeEach(() => {
58+
gridMetricCalculator = new GridMetricCalculator();
59+
allCharWidths = new Map();
60+
Object.assign(gridMetricCalculator, {
61+
allCharWidths,
62+
});
63+
});
64+
65+
it('should calculate text width and cache char widths if not in cache', () => {
66+
const input = 'abc';
67+
const mockContext = {
68+
measureText: jest.fn().mockImplementation(char => ({
69+
width: mockCharWidths.get(char),
70+
})),
71+
};
72+
73+
const textWidth = gridMetricCalculator.getTextWidth(
74+
mockContext,
75+
font,
76+
input
77+
);
78+
79+
const charWidths = allCharWidths.get(font);
80+
expect(charWidths.get('a')).toEqual(10);
81+
expect(charWidths.get('b')).toEqual(20);
82+
expect(charWidths.get('c')).toEqual(30);
83+
expect(textWidth).toEqual(60);
84+
});
85+
86+
it('should calculate text width using cached char widths if in cache', () => {
87+
const input = 'abcd';
88+
const mockContext = {
89+
measureText: jest.fn().mockImplementation(char => ({
90+
width: mockCharWidths.get(char),
91+
})),
92+
};
93+
const dummyMockContext = {
94+
measureText: jest.fn(),
95+
};
96+
97+
const firstRun = gridMetricCalculator.getTextWidth(
98+
mockContext,
99+
font,
100+
input
101+
);
102+
const secondRun = gridMetricCalculator.getTextWidth(
103+
dummyMockContext,
104+
font,
105+
input
106+
);
107+
108+
expect(mockContext.measureText).toHaveBeenCalledTimes(input.length);
109+
expect(dummyMockContext.measureText).toHaveBeenCalledTimes(0);
110+
expect(firstRun).toEqual(100);
111+
expect(secondRun).toEqual(100);
112+
});
113+
114+
it('should terminate early if max width is exceeded', () => {
115+
const input = 'abcde';
116+
const mockContext = {
117+
measureText: jest.fn().mockImplementation(char => ({
118+
width: mockCharWidths.get(char),
119+
})),
120+
};
121+
122+
const textWidth = gridMetricCalculator.getTextWidth(
123+
mockContext,
124+
font,
125+
input,
126+
50
127+
);
128+
129+
expect(textWidth).toEqual(50);
130+
});
131+
});

0 commit comments

Comments
 (0)