From b38ff367dda48aaed6be9c3838dd23020eafbfee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Ronvel?= Date: Fri, 30 Apr 2021 17:49:58 +0200 Subject: [PATCH] New TextTable methods: .setCellAttr() and variations (methods named: set/reset + Cell/Row/Column/Table + Attr()) allowing to modify text attribute of cells (a first step for #166) --- .eslintrc.js | 6 +- CHANGELOG | 6 ++ doc/TextTable.md | 97 +++++++++++++++++++- lib/document/TextBox.js | 30 ++++--- lib/document/TextTable.js | 137 ++++++++++++++++++++++++----- package.json | 2 +- sample/document/text-table-test.js | 21 ++++- 7 files changed, 259 insertions(+), 40 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index c6491687..48adf730 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -25,7 +25,7 @@ module.exports = { 'valid-typeof': 'error' , 'no-unneeded-ternary': 'error' , 'no-unused-vars': 'warn' , // During development phase, it's boring to clean unused var since they can be used later - 'no-lonely-if': 'error' , + 'no-lonely-if': 'off' , // Can hurt semantic programming 'no-nested-ternary': 'off' , // Now I use the streamlined ternary operator a lot 'no-shadow': 'warn' , 'no-shadow-restricted-names': 'error' , @@ -62,9 +62,7 @@ module.exports = { 'MemberExpression': 1 , 'flatTernaryExpressions': true } ] , - 'newline-per-chained-call': [ 'error', { - 'ignoreChainWithDepth': 2 - } ] , + 'newline-per-chained-call': 'off', 'no-multi-spaces': 'off' , 'block-spacing': 'error' , 'comma-spacing': [ 'error' , { diff --git a/CHANGELOG b/CHANGELOG index 4384d26f..95b7cbc8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,10 @@ +v2.1.0 +------ + +New TextTable methods: .setCellAttr() and variations (methods named: set/reset + Cell/Row/Column/Table + Attr()) allowing to modify text attribute of cells (a first step for #166) + + v2.0.7 ------ diff --git a/doc/TextTable.md b/doc/TextTable.md index f28c7e32..13901055 100644 --- a/doc/TextTable.md +++ b/doc/TextTable.md @@ -33,6 +33,14 @@ TextTable features: * Methods: * [.setCellContent()](#ref.TextTable.setCellContent) + * [.setCellAttr()](#ref.TextTable.setCellAttr) + * [.resetCellAttr()](#ref.TextTable.resetCellAttr) + * [.setRowAttr()](#ref.TextTable.setRowAttr) + * [.resetRowAttr()](#ref.TextTable.resetRowAttr) + * [.setColumnAttr()](#ref.TextTable.setColumnAttr) + * [.resetColumnAttr()](#ref.TextTable.resetColumnAttr) + * [.setTableAttr()](#ref.TextTable.setTableAttr) + * [.resetTableAttr()](#ref.TextTable.resetTableAttr) * Inherit methods and properties from [Element](Element.md#ref.Element.toc) @@ -103,10 +111,97 @@ This creates a *TextTable element*. * x,y `number` the cell coordinate to modify * content `string` the new content for this table cell -* dontDraw `boolean` when set, the cell content's update does not trigger the *redraw* of the *textTable* +* dontDraw `boolean` when set, the cell content's update does not trigger the *redraw* of the *textTable* (or of the cell's *textBox* + if *dontUpdateLayout* is set) * dontUpdateLayout `boolean` when set, the table layout is not updated This update an existing cell content. The table layout will be updated if needed, except if *dontUpdateLayout* is set. The content may contain *markup*, but it should have been enabled on the *textTable* creation for this to work. + + + +### .setCellAttr( x , y , textAttr , [voidAttr] , [dontDraw] ) + +* x,y `number` the cell coordinate to modify +* textAttr `object` generic/default attributes for the cell's content (*textBox*) +* voidAttr `object` attributes for the area of the cell (*textBox*) without any text content, default to the *textAttr* argument +* dontDraw `boolean` when set, the cell attr's update does not trigger the *redraw* of the cell's *textBox* + +This update an existing cell text attribute. + + + + +### .resetCellAttr( x , y , [dontDraw] ) + +* x,y `number` the cell coordinate to reset +* dontDraw `boolean` when set, the cell attr's update does not trigger the *redraw* of the cell's *textBox* + +This reset an existing cell text attribute to what it should be, based upon the constructor's parameters. + + + + +### .setRowAttr( y , textAttr , [voidAttr] , [dontDraw] ) + +* y `number` the row's index to modify +* textAttr `object` generic/default attributes for the row's cell's content (*textBox*) +* voidAttr `object` attributes for the area of the cell (*textBox*) without any text content, default to the *textAttr* argument +* dontDraw `boolean` when set, the cell attr's update does not trigger the *redraw* of the *textTable* + +This update all cells' text attribute of a row. + + + + +### .resetRowAttr( y , [dontDraw] ) + +* y `number` the row's index to reset +* dontDraw `boolean` when set, the cell attr's update does not trigger the *redraw* of the *textTable* + +This reset all cells' text attribute of a row to what it should be, based upon the constructor's parameters. + + + + +### .setColumnAttr( x , textAttr , voidAttr , [dontDraw] ) + +* x `number` the column's index to modify +* textAttr `object` generic/default attributes for the column's cell's content (*textBox*) +* voidAttr `object` attributes for the area of the cell (*textBox*) without any text content, default to the *textAttr* argument +* dontDraw `boolean` when set, the cell attr's update does not trigger the *redraw* of the *textTable* + +This update all cells's text attribute of a column. + + + + +### .resetColumnAttr( x , [dontDraw] ) + +* x `number` the column's index to reset +* dontDraw `boolean` when set, the cell attr's update does not trigger the *redraw* of the *textTable* + +This reset all cells' text attribute of a column to what it should be, based upon the constructor's parameters. + + + + +### .setTableAttr( textAttr , voidAttr , [dontDraw] ) + +* textAttr `object` generic/default attributes for the table's cell's content (*textBox*) +* voidAttr `object` attributes for the area of the cell (*textBox*) without any text content, default to the *textAttr* argument +* dontDraw `boolean` when set, the cell attr's update does not trigger the *redraw* of the *textTable* + +This update all cells's text attribute. + + + + +### .resetTableAttr( [dontDraw] ) + +* dontDraw `boolean` when set, the cell attr's update does not trigger the *redraw* of the *textTable* + +This reset all cells's text attribute to what it should be, based upon the constructor's parameters. + diff --git a/lib/document/TextBox.js b/lib/document/TextBox.js index 789ee046..957bffb3 100644 --- a/lib/document/TextBox.js +++ b/lib/document/TextBox.js @@ -166,8 +166,7 @@ TextBox.prototype.initChildren = function() { stateMachine: this.stateMachine } ) ; - this.textBuffer.setDefaultAttr( this.textAttr ) ; - this.textBuffer.setVoidAttr( this.voidAttr ) ; + this.setAttr( undefined , undefined , true ) ; if ( this.useAltTextBuffer ) { @@ -184,8 +183,7 @@ TextBox.prototype.initChildren = function() { //, stateMachine: this.stateMachine } ) ; - this.altTextBuffer.setDefaultAttr( this.altTextAttr ) ; - this.altTextBuffer.setVoidAttr( this.voidAttr ) ; + this.setAltAttr() ; this.textBuffer.setVoidTextBuffer( this.altTextBuffer ) ; } @@ -384,24 +382,34 @@ TextBox.prototype.autoScrollAndDraw = function( onlyDrawCursor = false ) { -TextBox.prototype.autoScrollAndDrawCursor = function() { - return this.autoScrollAndDraw( true ) ; -} ; +TextBox.prototype.autoScrollAndDrawCursor = function() { return this.autoScrollAndDraw( true ) ; } ; -TextBox.prototype.getContentSize = function() { - return this.textBuffer.getContentSize() ; +TextBox.prototype.setAttr = function( textAttr = this.textAttr , voidAttr = this.voidAttr , dontDraw = false , dontSetContent = false ) { + this.textAttr = textAttr ; + this.voidAttr = voidAttr ; + this.textBuffer.setDefaultAttr( this.textAttr ) ; + this.textBuffer.setVoidAttr( this.voidAttr ) ; + + if ( ! dontSetContent ) { this.setContent( this.content , this.contentHasMarkup , dontDraw ) ; } } ; -TextBox.prototype.getContent = function() { - return this.textBuffer.getText() ; +TextBox.prototype.setAltAttr = function( altTextAttr = this.altTextAttr ) { + this.altTextAttr = altTextAttr ; + this.altTextBuffer.setDefaultAttr( this.altTextAttr ) ; + this.altTextBuffer.setVoidAttr( this.voidAttr ) ; } ; +TextBox.prototype.getContentSize = function() { return this.textBuffer.getContentSize() ; } ; +TextBox.prototype.getContent = function() { return this.textBuffer.getText() ; } ; + + + TextBox.prototype.setContent = function( content , hasMarkup , dontDraw ) { var contentSize ; diff --git a/lib/document/TextTable.js b/lib/document/TextTable.js index f653a40f..d3758e90 100644 --- a/lib/document/TextTable.js +++ b/lib/document/TextTable.js @@ -75,6 +75,16 @@ function TextTable( options ) { this.checkerEvenCellTextAttr = options.checkerEvenCellTextAttr || null ; this.checkerEvenCellVoidAttr = options.checkerEvenCellVoidAttr || null ; + /* + // Select attr + // /!\ NOT IMPLEMENTED YET /!\ + // Would allow one to navigate the table (it could be useful for making editable cells) + this.selectedTextAttr = options.selectedTextAttr || null ; + this.selectedVoidAttr = options.selectedVoidAttr || null ; + this.selectable = options.selectable || null ; // Can be 'row', 'column' or 'cell' + this.selectedX = this.selectedY = 0 ; + */ + this.expandToWidth = options.expandToWidth !== undefined ? !! options.expandToWidth : !! options.fit ; this.shrinkToWidth = options.shrinkToWidth !== undefined ? !! options.shrinkToWidth : !! options.fit ; this.expandToHeight = @@ -136,16 +146,118 @@ TextTable.prototype.setCellContent = function( x , y , content , dontDraw = fals // We don't add cell, it should already exists if ( ! textBox ) { return ; } - // For instance, .cellContents is rather useless, but we still update it + // Save cell content this.cellContents[ y ][ x ] = content ; textBox.setContent( content , this.contentHasMarkup , true ) ; - if ( ! dontUpdateLayout ) { this.computeCells() ; } + if ( ! dontUpdateLayout ) { + this.computeCells() ; + if ( ! dontDraw ) { this.draw() ; } + } + else { + if ( ! dontDraw ) { textBox.draw() ; } + } +} ; + + + +TextTable.prototype.setCellAttr = function( x , y , textAttr , voidAttr , dontDraw = false ) { + var textBox = this.textBoxes[ y ] && this.textBoxes[ y ][ x ] ; + if ( ! textBox ) { return ; } + + if ( voidAttr === undefined ) { voidAttr = textAttr ; } + + textBox.setAttr( textAttr , voidAttr , dontDraw ) ; +} ; + + + +TextTable.prototype.resetCellAttr = function( x , y , dontDraw = false ) { + var textBox = this.textBoxes[ y ] && this.textBoxes[ y ][ x ] ; + if ( ! textBox ) { return ; } + + var textAttr = this.getTextAttrForCell( x , y ) , + voidAttr = this.getVoidAttrForCell( x , y , textAttr ) ; + + textBox.setAttr( textAttr , voidAttr , dontDraw ) ; +} ; + + + +TextTable.prototype.setRowAttr = function( y , textAttr , voidAttr , dontDraw = false ) { + for ( let x = 0 ; x < this.columnCount ; x ++ ) { this.setCellAttr( x , y , textAttr , voidAttr , true ) ; } + if ( ! dontDraw ) { this.draw() ; } +} ; + + + +TextTable.prototype.resetRowAttr = function( y , dontDraw = false ) { + for ( let x = 0 ; x < this.columnCount ; x ++ ) { this.resetCellAttr( x , y , true ) ; } + if ( ! dontDraw ) { this.draw() ; } +} ; + + + +TextTable.prototype.setColumnAttr = function( x , textAttr , voidAttr , dontDraw = false ) { + for ( let y = 0 ; y < this.rowCount ; y ++ ) { this.setCellAttr( x , y , textAttr , voidAttr , true ) ; } + if ( ! dontDraw ) { this.draw() ; } +} ; + + + +TextTable.prototype.resetColumnAttr = function( x , dontDraw = false ) { + for ( let y = 0 ; y < this.rowCount ; y ++ ) { this.resetCellAttr( x , y , true ) ; } + if ( ! dontDraw ) { this.draw() ; } +} ; + + + +TextTable.prototype.setTableAttr = function( textAttr , voidAttr , dontDraw = false ) { + for ( let y = 0 ; y < this.rowCount ; y ++ ) { + for ( let x = 0 ; x < this.columnCount ; x ++ ) { this.setCellAttr( x , y , textAttr , voidAttr , true ) ; } + } + if ( ! dontDraw ) { this.draw() ; } } ; +TextTable.prototype.resetTableAttr = function( dontDraw = false ) { + for ( let y = 0 ; y < this.rowCount ; y ++ ) { + for ( let x = 0 ; x < this.columnCount ; x ++ ) { this.resetCellAttr( x , y , true ) ; } + } + + if ( ! dontDraw ) { this.draw() ; } +} ; + + + +TextTable.prototype.getTextAttrForCell = function( x , y ) { + return this.firstCellTextAttr && ! x && ! y ? this.firstCellTextAttr : + this.firstRowTextAttr && ! y ? this.firstRowTextAttr : + this.firstColumnTextAttr && ! x ? this.firstColumnTextAttr : + this.evenCellTextAttr && ! ( x % 2 ) && ! ( y % 2 ) ? this.evenCellTextAttr : + this.checkerEvenCellTextAttr && ! ( ( x + y ) % 2 ) ? this.checkerEvenCellTextAttr : + this.evenRowTextAttr && ! ( y % 2 ) ? this.evenRowTextAttr : + this.evenColumnTextAttr && ! ( y % 2 ) ? this.evenColumnTextAttr : + this.textAttr ; +} ; + + + +TextTable.prototype.getVoidAttrForCell = function( x , y , textAttr ) { + return this.firstCellVoidAttr && ! x && ! y ? this.firstCellVoidAttr : + this.firstRowVoidAttr && ! y ? this.firstRowVoidAttr : + this.firstColumnVoidAttr && ! x ? this.firstColumnVoidAttr : + this.evenCellVoidAttr && ! ( x % 2 ) && ! ( y % 2 ) ? this.evenCellVoidAttr : + this.checkerEvenCellVoidAttr && ! ( ( x + y ) % 2 ) ? this.checkerEvenCellVoidAttr : + this.evenRowVoidAttr && ! ( y % 2 ) ? this.evenRowVoidAttr : + this.evenColumnVoidAttr && ! ( y % 2 ) ? this.evenColumnVoidAttr : + this.voidAttr || textAttr ; +} ; + + + TextTable.prototype.initChildren = function() { var row , cellContent , textAttr , voidAttr ; @@ -162,25 +274,8 @@ TextTable.prototype.initChildren = function() { for ( cellContent of row ) { if ( x >= this.columnCount ) { this.columnCount = x + 1 ; } - textAttr = - this.firstCellTextAttr && ! x && ! y ? this.firstCellTextAttr : - this.firstRowTextAttr && ! y ? this.firstRowTextAttr : - this.firstColumnTextAttr && ! x ? this.firstColumnTextAttr : - this.evenCellTextAttr && ! ( x % 2 ) && ! ( y % 2 ) ? this.evenCellTextAttr : - this.checkerEvenCellTextAttr && ! ( ( x + y ) % 2 ) ? this.checkerEvenCellTextAttr : - this.evenRowTextAttr && ! ( y % 2 ) ? this.evenRowTextAttr : - this.evenColumnTextAttr && ! ( y % 2 ) ? this.evenColumnTextAttr : - this.textAttr ; - - voidAttr = - this.firstCellVoidAttr && ! x && ! y ? this.firstCellVoidAttr : - this.firstRowVoidAttr && ! y ? this.firstRowVoidAttr : - this.firstColumnVoidAttr && ! x ? this.firstColumnVoidAttr : - this.evenCellVoidAttr && ! ( x % 2 ) && ! ( y % 2 ) ? this.evenCellVoidAttr : - this.checkerEvenCellVoidAttr && ! ( ( x + y ) % 2 ) ? this.checkerEvenCellVoidAttr : - this.evenRowVoidAttr && ! ( y % 2 ) ? this.evenRowVoidAttr : - this.evenColumnVoidAttr && ! ( y % 2 ) ? this.evenColumnVoidAttr : - this.voidAttr || textAttr ; + textAttr = this.getTextAttrForCell( x , y ) ; + voidAttr = this.getVoidAttrForCell( x , y , textAttr ) ; this.textBoxes[ y ][ x ] = new TextBox( { internal: true , diff --git a/package.json b/package.json index 66412677..d5fc7aa2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "terminal-kit", - "version": "2.0.7", + "version": "2.1.0", "description": "256 colors, keys and mouse, input field, progress bars, screen buffer (including 32-bit composition and image loading), text buffer, and many more... Whether you just need colors and styles, build a simple interactive command line tool or a complexe terminal app: this is the absolute terminal lib for Node.js!", "main": "lib/termkit.js", "directories": { diff --git a/sample/document/text-table-test.js b/sample/document/text-table-test.js index 914489dd..8c705cd8 100755 --- a/sample/document/text-table-test.js +++ b/sample/document/text-table-test.js @@ -71,6 +71,7 @@ var textTable = new termkit.TextTable( { //evenCellTextAttr: { bgColor: 'gray' } , //evenRowTextAttr: { bgColor: 'gray' } , //evenColumnTextAttr: { bgColor: 'gray' } , + selectedTextAttr: { bgColor: 'blue' } , selectable: 'cell' , width: 50 , //width: term.width , height: 20 , @@ -79,9 +80,25 @@ var textTable = new termkit.TextTable( { //lineWrap: true , } ) ; +/* +setTimeout( () => { + textTable.setCellContent( 2 , 3 , "New ^R^+content^:! And BTW... We have to force some line break and so on..." ) ; +} , 1000 ) ; +//*/ + +setTimeout( () => { + //textTable.setCellAttr( 1 , 2 , { bgColor: 'cyan' } , { bgColor: 'cyan' } ) ; + textTable.setRowAttr( 2 , { bgColor: 'cyan' } , { bgColor: 'cyan' } ) ; + //textTable.setColumnAttr( 1 , { bgColor: 'cyan' } , { bgColor: 'cyan' } ) ; + //textTable.setTableAttr( { bgColor: 'cyan' } , { bgColor: 'cyan' } ) ; +} , 500 ) ; + setTimeout( () => { - textTable.setCellContent( 2 , 3 , "New ^R^+content^:! And BTW... We have to force some line break and so on..." ) ; -} , 1200 ) ; + //textTable.resetCellAttr( 1 , 2 ) ; + textTable.resetRowAttr( 2 ) ; + //textTable.resetColumnAttr( 1 ) ; + //textTable.resetTableAttr() ; +} , 1500 ) ; term.on( 'key' , function( key ) {