Skip to content

Commit 5cedb5d

Browse files
author
Steven Silvester
authored
Merge pull request #105 from nmichaud/feature/text-eliding
Text eliding with ellipsis on datagrid text renderer
2 parents 4439675 + d6e652f commit 5cedb5d

File tree

2 files changed

+63
-2
lines changed

2 files changed

+63
-2
lines changed

examples/example-datagrid/src/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,13 +436,18 @@ function main(): void {
436436
horizontalAlignment: 'right'
437437
});
438438

439+
let elideFloatRenderer = new TextRenderer({
440+
elideDirection: ({ column }) => (column % 2 === 0) ? 'right' : 'left',
441+
});
442+
439443
let grid1 = new DataGrid({ style: blueStripeStyle });
440444
grid1.dataModel = model1;
441445
grid1.keyHandler = new BasicKeyHandler();
442446
grid1.mouseHandler = new BasicMouseHandler();
443447
grid1.selectionModel = new BasicSelectionModel({ dataModel: model1 });
444448

445449
let grid2 = new DataGrid({ style: brownStripeStyle });
450+
grid2.cellRenderers.update({ 'body': elideFloatRenderer });
446451
grid2.dataModel = model2;
447452
grid2.keyHandler = new BasicKeyHandler();
448453
grid2.mouseHandler = new BasicMouseHandler();

packages/datagrid/src/textrenderer.ts

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class TextRenderer extends CellRenderer {
3434
this.verticalAlignment = options.verticalAlignment || 'center';
3535
this.horizontalAlignment = options.horizontalAlignment || 'left';
3636
this.format = options.format || TextRenderer.formatGeneric();
37+
this.elideDirection = options.elideDirection || 'right';
3738
}
3839

3940
/**
@@ -66,6 +67,11 @@ class TextRenderer extends CellRenderer {
6667
*/
6768
readonly format: TextRenderer.FormatFunc;
6869

70+
/**
71+
* Which side to draw the ellipsis.
72+
*/
73+
readonly elideDirection: CellRenderer.ConfigOption<TextRenderer.ElideDirection>;
74+
6975
/**
7076
* Paint the content for a cell.
7177
*
@@ -136,6 +142,9 @@ class TextRenderer extends CellRenderer {
136142
let vAlign = CellRenderer.resolveOption(this.verticalAlignment, config);
137143
let hAlign = CellRenderer.resolveOption(this.horizontalAlignment, config);
138144

145+
// Resolve the elision direction
146+
let elideDirection = CellRenderer.resolveOption(this.elideDirection, config);
147+
139148
// Compute the padded text box height for the specified alignment.
140149
let boxHeight = config.height - (vAlign === 'center' ? 1 : 2);
141150

@@ -150,6 +159,7 @@ class TextRenderer extends CellRenderer {
150159
// Set up the text position variables.
151160
let textX: number;
152161
let textY: number;
162+
let boxWidth: number;
153163

154164
// Compute the Y position for the text.
155165
switch (vAlign) {
@@ -169,13 +179,16 @@ class TextRenderer extends CellRenderer {
169179
// Compute the X position for the text.
170180
switch (hAlign) {
171181
case 'left':
172-
textX = config.x + 2;
182+
textX = config.x + 8;
183+
boxWidth = config.width - 14;
173184
break;
174185
case 'center':
175186
textX = config.x + config.width / 2;
187+
boxWidth = config.width;
176188
break;
177189
case 'right':
178-
textX = config.x + config.width - 3;
190+
textX = config.x + config.width - 8;
191+
boxWidth = config.width - 14;
179192
break;
180193
default:
181194
throw 'unreachable';
@@ -194,6 +207,35 @@ class TextRenderer extends CellRenderer {
194207
gc.textAlign = hAlign;
195208
gc.textBaseline = 'bottom';
196209

210+
// Elide text that is too long
211+
let elide = '\u2026';
212+
let textWidth = gc.measureText(text).width;
213+
214+
// Compute elided text
215+
if (elideDirection === 'right') {
216+
while ((textWidth > boxWidth) && (text.length > 1)) {
217+
if (text.length > 4 && textWidth >= 2 * boxWidth) {
218+
// If text width is substantially bigger, take half the string
219+
text = text.substring(0, (text.length / 2) + 1) + elide;
220+
} else {
221+
// Otherwise incrementally remove the last character
222+
text = text.substring(0, text.length - 2) + elide;
223+
}
224+
textWidth = gc.measureText(text).width;
225+
}
226+
} else {
227+
while ((textWidth > boxWidth) && (text.length > 1)) {
228+
if (text.length > 4 && textWidth >= 2 * boxWidth) {
229+
// If text width is substantially bigger, take half the string
230+
text = elide + text.substring((text.length / 2));
231+
} else {
232+
// Otherwise incrementally remove the last character
233+
text = elide + text.substring(2);
234+
}
235+
textWidth = gc.measureText(text).width;
236+
}
237+
}
238+
197239
// Draw the text for the cell.
198240
gc.fillText(text, textX, textY);
199241
}
@@ -217,6 +259,12 @@ namespace TextRenderer {
217259
export
218260
type HorizontalAlignment = 'left' | 'center' | 'right';
219261

262+
/**
263+
* A type alias for the supported ellipsis sides.
264+
*/
265+
export
266+
type ElideDirection = 'left' | 'right';
267+
220268
/**
221269
* An options object for initializing a text renderer.
222270
*/
@@ -263,6 +311,14 @@ namespace TextRenderer {
263311
* The default is `TextRenderer.formatGeneric()`.
264312
*/
265313
format?: FormatFunc;
314+
315+
/**
316+
* The ellipsis direction for the cell text.
317+
*
318+
* The default is `'right'`.
319+
*/
320+
elideDirection?: CellRenderer.ConfigOption<ElideDirection>;
321+
266322
}
267323

268324
/**

0 commit comments

Comments
 (0)