@@ -64,6 +64,10 @@ import {
64
64
JSONExt
65
65
} from '@lumino/coreutils' ;
66
66
67
+ import {
68
+ TextRenderer
69
+ } from './textrenderer' ;
70
+
67
71
/**
68
72
* A widget which implements a high-performance tabular data grid.
69
73
*
@@ -1477,6 +1481,75 @@ class DataGrid extends Widget {
1477
1481
this . repaintOverlay ( ) ;
1478
1482
}
1479
1483
1484
+ /**
1485
+ * Auto sizes column widths based on their text content.
1486
+ * @param area
1487
+ */
1488
+ fitColumnNames ( area : DataGrid . ColumnFitType = 'all' , numCols ?: number ) : void {
1489
+ // Attempt resizing only if a data model is present.
1490
+ if ( this . dataModel ) {
1491
+ // Tracking remaining columns to be resized if numCols arg passed.
1492
+ let colsRemaining = ( numCols === undefined ) || ( numCols < 0 )
1493
+ ? undefined
1494
+ : numCols ;
1495
+
1496
+ if ( area === 'row-header' || area === 'all' ) {
1497
+ // Respecting any column resize cap, if one has been passed.
1498
+ if ( colsRemaining !== undefined ) {
1499
+ const rowColumnCount = this . dataModel . columnCount ( 'row-header' ) ;
1500
+ /*
1501
+ If we have more row-header columns than columns available
1502
+ for resize, resize only remaining columns as per allowance
1503
+ and set remaining resize allowance number to 0.
1504
+ */
1505
+ if ( colsRemaining - rowColumnCount < 0 ) {
1506
+ this . _fitRowColumnHeaders ( this . dataModel , colsRemaining )
1507
+ colsRemaining = 0 ;
1508
+ } else {
1509
+ /*
1510
+ Otherwise the entire row-header column count can be resized.
1511
+ Resize all row-header columns and subtract from remaining
1512
+ column resize allowance.
1513
+ */
1514
+ this . _fitRowColumnHeaders ( this . dataModel , rowColumnCount ) ;
1515
+ colsRemaining = colsRemaining - rowColumnCount ;
1516
+ }
1517
+ } else {
1518
+ // No column resize cap passed - resizing all columns.
1519
+ this . _fitRowColumnHeaders ( this . dataModel ) ;
1520
+ }
1521
+
1522
+ }
1523
+
1524
+ if ( area === 'body' || area === 'all' ) {
1525
+ // Respecting any column resize cap, if one has been passed.
1526
+ if ( colsRemaining !== undefined ) {
1527
+ const bodyColumnCount = this . dataModel . columnCount ( 'body' ) ;
1528
+ /*
1529
+ If we have more body columns than columns available
1530
+ for resize, resize only remaining columns as per allowance
1531
+ and set remaining resize allowance number to 0.
1532
+ */
1533
+ if ( colsRemaining - bodyColumnCount < 0 ) {
1534
+ this . _fitBodyColumnHeaders ( this . dataModel , colsRemaining ) ;
1535
+ colsRemaining = 0 ;
1536
+ } else {
1537
+ /*
1538
+ Otherwise the entire body column count can be resized.
1539
+ Resize based on the smallest number between remaining
1540
+ resize allowance and body column count.
1541
+ */
1542
+ this . _fitBodyColumnHeaders ( this . dataModel ,
1543
+ Math . min ( colsRemaining , bodyColumnCount ) ) ;
1544
+ }
1545
+ } else {
1546
+ // No column resize cap passed - resizing all columns.
1547
+ this . _fitBodyColumnHeaders ( this . dataModel ) ;
1548
+ }
1549
+ }
1550
+ }
1551
+ }
1552
+
1480
1553
/**
1481
1554
* Map a client position to local viewport coordinates.
1482
1555
*
@@ -3718,6 +3791,108 @@ class DataGrid extends Widget {
3718
3791
this . drawCornerHeaderRegion ( rx , ry , rw , rh ) ;
3719
3792
}
3720
3793
3794
+ /**
3795
+ * Resizes body column headers so their text fits
3796
+ * without clipping or wrapping.
3797
+ * @param dataModel
3798
+ */
3799
+ private _fitBodyColumnHeaders ( dataModel : DataModel , numCols ?: number ) : void {
3800
+ // Get the body column count
3801
+ const bodyColumnCount = numCols === undefined
3802
+ ? dataModel . columnCount ( 'body' )
3803
+ : numCols ;
3804
+
3805
+ for ( let i = 0 ; i < bodyColumnCount ; i ++ ) {
3806
+ /*
3807
+ if we're working with nested column headers,
3808
+ retrieve the nested levels and iterate on them.
3809
+ */
3810
+ const numRows = dataModel . rowCount ( 'column-header' ) ;
3811
+
3812
+ /*
3813
+ Calculate the maximum text width vertically, across
3814
+ all nested rows under a given column number.
3815
+ */
3816
+ let maxWidth = 0 ;
3817
+ for ( let j = 0 ; j < numRows ; j ++ ) {
3818
+ const cellValue = dataModel . data ( 'column-header' , j , i ) ;
3819
+
3820
+ // Basic CellConfig object to get the renderer for that cell
3821
+ let config = {
3822
+ x : 0 , y : 0 , width : 0 , height : 0 ,
3823
+ region : 'column-header' as DataModel . CellRegion , row : 0 , column : i ,
3824
+ value : ( null as any ) , metadata : DataModel . emptyMetadata
3825
+ } ;
3826
+
3827
+ // Get the renderer for the given cell
3828
+ const renderer = this . cellRenderers . get ( config ) as TextRenderer ;
3829
+
3830
+ // Use the canvas context to measure the cell's text width
3831
+ const gc = this . canvasGC ;
3832
+ gc . font = CellRenderer . resolveOption ( renderer . font , config ) ;
3833
+ const textWidth = gc . measureText ( cellValue ) . width ;
3834
+
3835
+ // Update the maximum width for that column.
3836
+ maxWidth = Math . max ( maxWidth , textWidth ) ;
3837
+ }
3838
+
3839
+ /*
3840
+ Send a resize message with new width for the given column.
3841
+ Using a padding of 15 pixels to leave some room.
3842
+ */
3843
+ this . resizeColumn ( 'body' , i , maxWidth + 15 ) ;
3844
+ }
3845
+ }
3846
+
3847
+ /**
3848
+ * Resizes row header columns so their text fits
3849
+ * without clipping or wrapping.
3850
+ * @param dataModel
3851
+ */
3852
+ private _fitRowColumnHeaders ( dataModel : DataModel , numCols ?: number ) : void {
3853
+ /*
3854
+ if we're working with nested row headers,
3855
+ retrieve the nested levels and iterate on them.
3856
+ */
3857
+ const rowColumnCount = numCols === undefined
3858
+ ? dataModel . columnCount ( 'row-header' )
3859
+ : numCols ;
3860
+
3861
+ for ( let i = 0 ; i < rowColumnCount ; i ++ ) {
3862
+ const numCols = dataModel . rowCount ( 'column-header' ) ;
3863
+ /*
3864
+ Calculate the maximum text width vertically, across
3865
+ all nested columns under a given row index.
3866
+ */
3867
+ let maxWidth = 0 ;
3868
+ for ( let j = 0 ; j < numCols ; j ++ ) {
3869
+ const cellValue = dataModel . data ( 'corner-header' , j , i ) ;
3870
+
3871
+ // Basic CellConfig object to get the renderer for that cell.
3872
+ let config = {
3873
+ x : 0 , y : 0 , width : 0 , height : 0 ,
3874
+ region : 'column-header' as DataModel . CellRegion , row : 0 , column : i ,
3875
+ value : ( null as any ) , metadata : DataModel . emptyMetadata
3876
+ } ;
3877
+
3878
+ // Get the renderer for the given cell.
3879
+ const renderer = this . cellRenderers . get ( config ) as TextRenderer ;
3880
+
3881
+ // Use the canvas context to measure the cell's text width
3882
+ const gc = this . canvasGC ;
3883
+ gc . font = CellRenderer . resolveOption ( renderer . font , config ) ;
3884
+ const textWidth = gc . measureText ( cellValue ) . width ;
3885
+ maxWidth = Math . max ( maxWidth , textWidth ) ;
3886
+ }
3887
+
3888
+ /*
3889
+ Send a resize message with new width for the given column.
3890
+ Using a padding of 15 pixels to leave some room.
3891
+ */
3892
+ this . resizeColumn ( 'row-header' , i , maxWidth + 15 ) ;
3893
+ }
3894
+ }
3895
+
3721
3896
/**
3722
3897
* Paint the overlay content for the entire grid.
3723
3898
*
@@ -5693,6 +5868,12 @@ namespace DataGrid {
5693
5868
export
5694
5869
type HeaderVisibility = 'all' | 'row' | 'column' | 'none' ;
5695
5870
5871
+ /**
5872
+ * A type alias for the supported column auto resize modes.
5873
+ */
5874
+ export
5875
+ type ColumnFitType = 'all' | 'row-header' | 'body' ;
5876
+
5696
5877
/**
5697
5878
* A type alias for the arguments to a copy format function.
5698
5879
*/
0 commit comments