From af178f0f216c4b9027314c35d71b49bc1fda42c9 Mon Sep 17 00:00:00 2001 From: Julian Noble Date: Sat, 11 May 2024 19:57:29 +1000 Subject: [PATCH] textblock frame/table fixes --- src/bootsupport/modules/textblock-0.1.1.tm | 530 ++++++++++++++++----- src/modules/textblock-999999.0a1.0.tm | 530 ++++++++++++++++----- 2 files changed, 842 insertions(+), 218 deletions(-) diff --git a/src/bootsupport/modules/textblock-0.1.1.tm b/src/bootsupport/modules/textblock-0.1.1.tm index 2a7da301..798d2c8e 100644 --- a/src/bootsupport/modules/textblock-0.1.1.tm +++ b/src/bootsupport/modules/textblock-0.1.1.tm @@ -50,7 +50,12 @@ namespace eval textblock { bottomleft {} bottominner {} bottomright {} bottomsolo {}\ onlyleft {} onlyinner {} onlyright {} onlysolo {}\ ]\ - -framemap_header [list headerleft {} headerinner {} headerright {} headersolo {}]\ + -framemap_header [list\ + topleft {} topinner {} topright {} topsolo {}\ + middleleft {} middleinner {} middleright {} middlesolo {}\ + bottomleft {} bottominner {} bottomright {} bottomsolo {}\ + onlyleft {} onlyinner {} onlyright {} onlysolo {}\ + ]\ -show_edge 1\ -show_seps 1\ -show_hseps ""\ @@ -58,25 +63,64 @@ namespace eval textblock { -show_header ""\ -show_footer ""\ ] - variable table_border_parts - #for 'L' shaped table building pattern - set table_border_parts [dict create\ - topleft [list hlt vll tlc blc]\ - topinner [list hlt tlc]\ - topright [list hlt tlc vlr trc brc]\ - topsolo [list hlt tlc trc blc brc vl]\ - middleleft [list vll blc]\ - midleinner [list]\ - middleright [list vlr brc]\ - middlesolo [list vl blc brc]\ - bottomleft [list vll blc hlb]\ - bottominner [list hlb blc]\ - bottomright [list hlb blc brc vlr]\ - bottomsolo [list hlb blc brc tlc trc vl]\ - onlyleft [list hlt hlb vll tlc blc]\ - onlyinner [list hlt hlb tlc blc]\ - onlyright [list hlt hlb tlc blc brc trc vlr]\ - onlysolo [list hlt hlb vll vlr blc brc trc brc]\ + #for 'L' shaped table building pattern (tables bigger than 4x4 will be mostly 'L' patterns) + #ie only vll,blc,hlb used for cells except top row and right column + #top right cell uses all 'O' shape, other top cells use 'C' shape (hlt,tlc,vll,blc,hlb) + #right cells use 'U' shape (vll,blc,hlb,brc,vlr) + #e.g for 4x4 + # C C C O + # L L L U + # L L L U + #anti-clockwise elements + set C [list hlt tlc vll blc hlb] + set O [list trc hlt tlc vll blc hlb brc vlr] + set L [list vll blc hlb] + set U [list vll blc hlb brc vlr] + set tops [list trc hlt tlc] + set lefts [list tlc vll blc] + set bottoms [list blc hlb brc] + set rights [list trc brc vlr] + + variable table_edge_parts + set table_edge_parts [dict create\ + topleft [struct::set intersect $C [concat $tops $lefts]]\ + topinner [struct::set intersect $C [concat $tops]]\ + topright [struct::set intersect $O [concat $tops $rights]]\ + topsolo [struct::set intersect $O [concat $tops $lefts $rights]]\ + middleleft [struct::set intersect $L $lefts]\ + middleinner [list]\ + middleright [struct::set intersect $U $rights]\ + middlesolo [struct::set intersect $U [concat $lefts $bottoms $rights]]\ + bottomleft [struct::set intersect $L [concat $lefts]]\ + bottominner [list]\ + bottomright [struct::set intersect $U $rights]\ + bottomsolo [struct::set intersect $U [concat $lefts $rights]]\ + onlyleft [struct::set intersect $C [concat $tops $lefts $bottoms]]\ + onlyinner [struct::set intersect $C [concat $tops $bottoms]]\ + onlyright [struct::set intersect $O [concat $tops $bottoms $rights]]\ + onlysolo [struct::set intersect $O [concat $tops $lefts $bottoms $rights]]\ + ] + + #for header rows - we don't consider the bottom border as part of the edge - even if table body has no rows + #The usual-case of a single header line is the 'onlyleft,onlyinner,onlyright,onlysolo' set. + variable header_edge_parts + set header_edge_parts [dict create\ + topleft [struct::set intersect $C [concat $tops $lefts]]\ + topinner [struct::set intersect $C [concat $tops]]\ + topright [struct::set intersect $O [concat $tops $rights]]\ + topsolo [struct::set intersect $O [concat $tops $lefts $rights]]\ + middleleft [struct::set intersect $L $lefts]\ + middleinner [list]\ + middleright [struct::set intersect $U $rights]\ + middlesolo [struct::set intersect $U [concat $lefts $bottoms $rights]]\ + bottomleft [struct::set intersect $L [concat $lefts]]\ + bottominner [list]\ + bottomright [struct::set intersect $U $rights]\ + bottomsolo [struct::set intersect $U [concat $lefts $rights]]\ + onlyleft [struct::set intersect $C [concat $tops $lefts]]\ + onlyinner [struct::set intersect $C $tops]\ + onlyright [struct::set intersect $O [concat $tops $rights]]\ + onlysolo [struct::set intersect $O [concat $tops $lefts $rights]]\ ] variable table_hseps set table_hseps [dict create\ @@ -99,10 +143,6 @@ namespace eval textblock { ] variable table_vseps set table_vseps [dict create\ - headerleft [list]\ - headerinner [list vll tlc blc]\ - headerright [list vll tlc blc]\ - headersolo [list]\ topleft [list]\ topinner [list vll tlc blc]\ topright [list vll tlc blc]\ @@ -122,19 +162,12 @@ namespace eval textblock { ] - variable header_border_parts - set header_border_parts [dict create\ - headerleft [list vll tlc blc hlt]\ - headerinner [list tlc hlt]\ - headerright [list tlc hlt trc vlr brc]\ - headersolo [list tlc vlr blc hlt trc brc]\ - ] - #e.g $t configure -framemap_body [table_border_map " "] - proc table_border_map {char} { - variable table_border_parts + #e.g $t configure -framemap_body [table_edge_map " "] + proc table_edge_map {char} { + variable table_edge_parts set map [list] - dict for {celltype parts} $table_border_parts { + dict for {celltype parts} $table_edge_parts { set tmap [list] foreach p $parts { dict set tmap $p $char @@ -155,10 +188,10 @@ namespace eval textblock { } return $map } - proc header_border_map {char} { - variable header_border_parts + proc header_edge_map {char} { + variable header_edge_parts set map [list] - dict for {celltype parts} $header_border_parts { + dict for {celltype parts} $header_edge_parts { set tmap [list] foreach p $parts { dict set tmap $p $char @@ -181,7 +214,9 @@ namespace eval textblock { #[enum] CLASS [class interface_caphandler.registry] #[list_begin definitions] # [para] [emph METHODS] - variable o_opts_table + variable o_opts_table ;#options as configured by user (with exception of -ansireset) + variable o_opts_table_effective; #options in effect - e.g with defaults merged in. + variable o_columndefs variable o_columndata variable o_rowdefs @@ -207,7 +242,8 @@ namespace eval textblock { } } #set o_opts_table [dict merge $o_opts_table_defaults $args] - set o_opts_table $o_opts_table_defaults + set o_opts_table $o_opts_table_defaults + set o_opts_table_effective $o_opts_table_defaults my configure {*}[dict merge $o_opts_table_defaults $args] set o_columndefs [dict create] set o_columndata [dict create] ;#we store data by column even though it is often added row by row @@ -266,21 +302,92 @@ namespace eval textblock { } return [dict create header $ft_header body $ft_body] } + method Set_effective_framelimits {} { + upvar ::textblock::class::opts_table_defaults tdefaults + set default_blims [dict get $tdefaults -framelimits_body] + set default_hlims [dict get $tdefaults -framelimits_header] + set eff_blims [dict get $o_opts_table_effective -framelimits_body] + set eff_hlims [dict get $o_opts_table_effective -framelimits_header] + + set requested_blims [dict get $o_opts_table -framelimits_body] + set requested_hlims [dict get $o_opts_table -framelimits_header] + set blims $eff_blims + set hlims $eff_hlims + switch -- $requested_blims { + "default" { + set blims $default_blims + } + default { + #set blims $requested_blims + set blims [list] + foreach lim $requested_blims { + switch -- $lim { + hl { + lappend blims hlt hlb + } + vl { + lappend blims vll vlr + } + default { + lappend blims $lim + } + } + } + set blims [lsort -unique $blims] + } + } + dict set o_opts_table_effective -framelimits_body $blims + switch -- $requested_hlims { + "default" { + set hlims $default_hlims + } + default { + #set hlims $requested_hlims + set hlims [list] + foreach lim $requested_hlims { + switch -- $lim { + hl { + lappend hlims hlt hlb + } + vl { + lappend hlims vll vlr + } + default { + lappend hlims $lim + } + } + } + set hlims [lsort -unique $hlims] + } + } + dict set o_opts_table_effective -framelimits_header $hlims + return [dict create body $blims header $hlims] + } method configure args { if {![llength $args]} { return $o_opts_table } - if {[llength $args] == 1 && [lindex $args 0] in [dict keys $o_opts_table_defaults]} { - #query single option - set k [lindex $args 0] - set val [dict get $o_opts_table $k] - set infodict [dict create] - switch -- $k { - -ansibase_header - -ansibase_body - -ansiborder_header - -ansiborder_body - -ansiborder_footer { - dict set infodict debug [ansistring VIEW $val] + if {[llength $args] == 1} { + if {[lindex $args 0] in [dict keys $o_opts_table_defaults]} { + #query single option + set k [lindex $args 0] + set val [dict get $o_opts_table $k] + set returndict [dict create option $k value $val ansireset "\x1b\[m"] + set infodict [dict create] + switch -- $k { + -ansibase_header - -ansibase_body - -ansiborder_header - -ansiborder_body - -ansiborder_footer { + dict set infodict debug [ansistring VIEW $val] + } + -framemap_body - -framemap_header - -framelimits_body - -framelimits_header { + dict set returndict effective [dict get $o_opts_table_effective $k] + } } + dict set returndict info $infodict + return $returndict + #return [dict create option $k value $val ansireset "\x1b\[m" info $infodict] + } else { + error "textblock::table configure - unrecognised option '[lindex $args 0]'. Known values [dict keys $o_opts_table_defaults]" } - return [dict create option $k value $val ansireset "\x1b\[m" info $infodict] } if {[llength $args] %2 != 0} { error "[namespace current]::table configure - unexpected argument count. Require name value pairs" @@ -308,9 +415,72 @@ namespace eval textblock { set ansival [punk::ansi::codetype::sgr_merge $ansi_codes] lappend checked_opts $k $ansival } + -frametype - -frametype_header - -frametype_body { + #frametype will raise an error if v is not a valid custom dict or one of the known predefined types such as light,heavy,double etc + lassign [textblock::frametype $v] _cat category _type ftype + lappend checked_opts $k $v + } + -framemap_body - -framemap_header { + #upvar ::textblock::class::opts_table_defaults tdefaults + #set default_bmap [dict get $tdefaults -framemap_body] + #todo - check keys and map + if {[llength $v] == 1} { + if {$v eq "default"} { + upvar ::textblock::class::opts_table_defaults tdefaults + set default_map [dict get $tdefaults $k] + lappend checked_opts $k $default_map + } else { + error "textblock::table::configure invalid $k value $v. Expected the value 'default' or a dict e.g topleft {hl *}" + } + } else { + dict for {subk subv} $v { + switch -- $subk { + topleft - topinner - topright - topsolo - middleleft - middleinner - middleright - middlesolo - bottomleft - bottominner - bottomright - bottomsolo - onlyleft - onlyinner - onlyright - onlysolo {} + default { + error "textblock::table::configure invalid $subk. Known values {topleft topinner topright topsolo middleleft middleinner middleright middlesolo bottomleft bottominner bottomright bottomsolo onlyleft onlyinner onlyright onlysolo}" + } + } + dict for {seg subst} $subv { + switch -- $seg { + hl - hlt - hlb - vl - vll - vlr - trc - tlc - blc - brc {} + default { + error "textblock::table::configure invalid $subk value $seg. Known values {hl hlt hlb vl vll vlr trc tlc blc brc}" + } + } + } + + } + lappend checked_opts $k $v + } + + } + -framelimits_body - -framelimits_header { + set specific_framelimits [list] + foreach fl $v { + switch -- $fl { + "default" { + lappend specific_framelimits trc hlt tlc vll blc hlb brc vlr + } + hl { + lappend specific_framelimits hlt hlb + } + vl { + lappend specific_framelimits vll vlr + } + hlt - hlb - vll - vlr - trc - tlc - blc - brc { + lappend specific_framelimits $fl + } + default { + error "textblock::table::configure invalid $k '$fl'. Known values {hl hlb hlt vl vll vlr trc tlc blc brc} (or default for all)" + } + } + } + lappend checked_opts $k $specific_framelimits + } -ansireset { if {$v eq "\uFFEF"} { - lappend checked_opts $k "\x1b\[m" ;# [a] + set RST "\x1b\[m" ;#[a] + lappend checked_opts $k $RST } else { error "textblock::table::configure -ansireset is read-only. It is present only to prevent unwanted colourised output in configure commands" } @@ -320,7 +490,51 @@ namespace eval textblock { } } } - set o_opts_table [dict merge $o_opts_table $checked_opts] + #all options checked - ok to update o_opts_table and o_opts_table_effective + + #set o_opts_table [dict merge $o_opts_table $checked_opts] + dict for {k v} $args { + switch -- $k { + -framemap_header - -framemap_body { + #framemaps don't require setting every key to update. + #e.g configure -framemaps {topleft } + #needs to merge with existing unspecified keys such as topright middleleft etc. + if {$v eq "default"} { + dict set o_opts_table $k default + } else { + if {[dict get $o_opts_table $k] eq "default"} { + dict set o_opts_table $k $v + } else { + dict set o_opts_table $k [dict merge [dict get $o_opts_table $k] $v] + } + } + } + default { + dict set o_opts_table $k $v + } + } + } + #use values from checked_opts for the effective opts + dict for {k v} $checked_opts { + switch -- $k { + -framemap_body - -framemap_header { + set existing [dict get $o_opts_table_effective $k] + set updated $existing + dict for {subk subv} $v { + dict set updated $subk $subv + } + dict set o_opts_table_effective $k $updated + } + -framelimits_body - -framelimits_header { + #my Set_effective_framelimits + dict set o_opts_table_effective $k $v + } + default { + dict set o_opts_table_effective $k $v + } + } + } + return $o_opts_table } #integrate with struct::matrix - allows ::m format 2string $table @@ -671,46 +885,66 @@ namespace eval textblock { } switch -- $opt_posn { left { - set header_boxlimits {hl tlc blc vll} + set header_boxlimits {hlb hlt tlc blc vll} set header_joins [list down-$ftype_body] set boxlimits_position {hlb blc vll} - set boxlimits_headerless {hlb hlt blc vll tlc} + set boxlimits_headerless [concat $boxlimits_position {hlt tlc}] set joins {down} } inner { - set header_boxlimits {hl tlc blc vll} + set header_boxlimits {hlb hlt tlc blc vll} set header_joins [list left down-$ftype_body] set boxlimits_position {hlb blc vll} - set boxlimits_headerless {hlb hlt blc vll tlc} + set boxlimits_headerless [concat $boxlimits_position {hlt tlc}] set joins {down left} } right { - set header_boxlimits {hl tlc blc vll vlr trc brc} + set header_boxlimits {hlb hlt tlc blc vll vlr trc brc} set header_joins [list left down-$ftype_body] set boxlimits_position {hlb blc vll vlr brc} - set boxlimits_headerless {hlb hlt blc vll vlr brc tlc trc} + set boxlimits_headerless [concat $boxlimits_position {hlt tlc trc}] set joins {down left} } solo { - set header_boxlimits {hl tlc blc vll vlr trc brc} + set header_boxlimits {hlb hlt tlc blc vll vlr trc brc} set header_joins [list down-$ftype_body] - set boxlimits_position {hlb blc vll vlr brc} - set boxlimits_headerless {hlb hlt blc vll vlr brc tlc trc} + set boxlimits_position {hlb blc vll vlr brc} + set boxlimits_headerless [concat $boxlimits_position {hlt tlc trc}] set joins {down} } } #use struct::set instead of simple for loop - will be faster at least when critcl available - set boxlimits [struct::set intersect [dict get $o_opts_table -framelimits_body] $boxlimits_position] + set boxlimits [struct::set intersect [dict get $o_opts_table_effective -framelimits_body] $boxlimits_position] + set boxlimits_headerless [struct::set intersect [dict get $o_opts_table_effective -framelimits_body] $boxlimits_headerless] + set header_boxlimits [struct::set intersect [dict get $o_opts_table_effective -framelimits_header] $header_boxlimits] + + #upvar ::textblock::class::opts_table_defaults tdefaults + #set default_bmap [dict get $tdefaults -framemap_body] + #set default_hmap [dict get $tdefaults -framemap_header] + #set fmap $default_bmap + #set hmap $default_hmap + #dict for {k v} $fmap { + # if {[dict exists $o_opts_table -framemap_body $k]} { + # dict set fmap $k [dict merge $v [dict get $o_opts_table -framemap_body $k]] + # } + #} + #dict for {k v} $hmap { + # if {[dict exists $o_opts_table -framemap_header $k]} { + # dict set hmap $k [dict merge $v [dict get $o_opts_table -framemap_header $k]] + # } + #} + set fmap [dict get $o_opts_table_effective -framemap_body] + set hmap [dict get $o_opts_table_effective -framemap_header] - upvar ::textblock::class::opts_table_defaults tdefaults - set defaultmap [dict get $tdefaults -framemap_body] - set default_hmap [dict get $tdefaults -framemap_header] if {![dict get $o_opts_table -show_edge]} { - set fmap [dict merge $defaultmap [textblock::class::table_border_map ""]] - set hmap [dict merge $default_hmap [textblock::class::header_border_map ""]] - } else { - set fmap [dict merge $defaultmap [dict get $o_opts_table -framemap_body]] - set hmap [dict merge $default_hmap [dict get $o_opts_table -framemap_header]] + set body_edgemap [textblock::class::table_edge_map ""] + dict for {k v} $fmap { + dict set fmap $k [dict merge $v [dict get $body_edgemap $k]] + } + set header_edgemap [textblock::class::header_edge_map ""] + dict for {k v} $hmap { + dict set hmap $k [dict merge $v [dict get $header_edgemap $k]] + } } set sep_elements_horizontal $::textblock::class::table_hseps set sep_elements_vertical $::textblock::class::table_vseps @@ -719,14 +953,17 @@ namespace eval textblock { set botmap [dict get $fmap bottom$opt_posn] set midmap [dict get $fmap middle$opt_posn] set onlymap [dict get $fmap only$opt_posn] - set hdrmap [dict get $hmap header$opt_posn] + + set hdrmap [dict get $hmap only${opt_posn}] + set topseps_h [dict get $sep_elements_horizontal top$opt_posn] set topseps_v [dict get $sep_elements_vertical top$opt_posn] set midseps_h [dict get $sep_elements_horizontal middle$opt_posn] set midseps_v [dict get $sep_elements_vertical middle$opt_posn] set botseps_v [dict get $sep_elements_vertical bottom$opt_posn] + set onlyseps_v [dict get $sep_elements_vertical only$opt_posn] - set headerseps_v [dict get $sep_elements_vertical header$opt_posn] + set headerseps_v [dict get $sep_elements_vertical top$opt_posn] lassign [my Get_seps] _h show_seps_h _v show_seps_v @@ -758,7 +995,7 @@ namespace eval textblock { if {!$show_seps_v} { set hlims [struct::set difference $header_boxlimits $headerseps_v] } - + #todo - multiline header cells + multiple header lines (will be more useful when colspans implemented) set header_frame [textblock::frame -width [expr {$colwidth+2}] -type [dict get $ftypes header]\ -ansibase $ansibase_header -ansiborder $ansiborder_final\ -boxlimits $hlims -boxmap $hdrmap -joins $header_joins $hval\ @@ -776,6 +1013,8 @@ namespace eval textblock { set blims_top $boxlimits set blims_bot $boxlimits set blims_top_headerless $boxlimits_headerless + set blims_only $boxlimits + set blims_only_headerless $boxlimits_headerless if {!$show_seps_h} { set blims_mid [struct::set difference $blims_mid $midseps_h] set blims_top [struct::set difference $blims_top $topseps_h] @@ -786,6 +1025,8 @@ namespace eval textblock { set blims_top [struct::set difference $blims_top $topseps_v] set blims_top_headerless [struct::set difference $blims_top_headerless $topseps_v] set blims_bot [struct::set difference $blims_bot $botseps_v] + set blims_only [struct::set difference $blims_only $onlyseps_v] + set blims_only_headerless [struct::set difference $blims_only_headerless $onlyseps_v] } set colidx [lindex [dict keys $o_columndefs] $index_expression] ;#convert possible end-1,2+2 etc expression to >= 0 integer in dict range @@ -823,9 +1064,9 @@ namespace eval textblock { set joins [lremove $joins [lsearch $joins down*]] set bmap $onlymap if {$do_show_header} { - set blims $boxlimits + set blims $blims_only } else { - set blims $boxlimits_headerless + set blims $blims_only_headerless } } else { set bmap $topmap @@ -859,9 +1100,9 @@ namespace eval textblock { #note that if show_edge is 0 - then for this empty line - we will not see any vertical bars #This is because the frame with no data is made entirely of corner elements if {$do_show_header} { - append output [textblock::frame -width [expr {$colwidth + 2}] -type [dict get $ftypes body] -boxlimits $boxlimits -boxmap $onlymap -joins $joins]\n + append output [textblock::frame -width [expr {$colwidth + 2}] -type [dict get $ftypes body] -boxlimits $blims_only -boxmap $onlymap -joins $joins]\n } else { - append output [textblock::frame -width [expr {$colwidth + 2}] -type [dict get $ftypes body] -boxlimits $boxlimits_headerless -boxmap $onlymap -joins $joins] \n + append output [textblock::frame -width [expr {$colwidth + 2}] -type [dict get $ftypes body] -boxlimits $blims_only_headerless -boxmap $onlymap -joins $joins] \n } } return [string trimright $output \n] @@ -881,6 +1122,7 @@ namespace eval textblock { #assert cidx is integer >=0 set cdef [dict get $o_columndefs $cidx] set t [dict get $cdef -header] ;#may be empty string + set t_maxdataheight 1 set items [dict get $o_columndata $cidx] set ansibase_body [dict get $o_opts_table -ansibase_body] @@ -1625,8 +1867,45 @@ namespace eval textblock { } } + variable frametypes + set frametypes [list light heavy arc double block block1 ascii altg] + #class::table needs to be able to determine valid frametypes + proc frametypes {} { + variable frametypes + return $frametypes + } + proc frametype {f} { + variable frametypes + set default_custom [dict create hl " " vl " " tlc " " trc " " blc " " brc " "] + set custom_keys [list hl hlt hlb vl vll vlr tlc trc blc brc] + if {$f ni $frametypes} { + set is_custom_dict_ok 1 + if {[llength $f] %2 == 0} { + #custom dict may leave out keys - but cannot have unknown keys + dict for {k v} $f { + switch -- $k { + hl - hlt - hlb - vl - vll - vlr - tlc - trc - blc - brc {} + default { + #k not in custom_keys + set is_custom_dict_ok 0 + break + } + } + } + } else { + set is_custom_dict_ok 0 + } + if {!$is_custom_dict_ok} { + error "frame option -type must be one of known types: $frametypes or a dictionary with any of keys hl,hlt,hlb,vl,vll,vlr,tlc,trc,blc,brc" + } + set custom_frame [dict merge $default_custom $f] + return [dict create category custom type $custom_frame] + } else { + return [dict create category predefined type $f] + } + } proc frame {args} { - + variable frametypes set expect_optval 0 set argposn 0 set pmax [expr {[llength $args]-1}] @@ -1690,34 +1969,20 @@ namespace eval textblock { set opt_boxlimits [dict get $opts -boxlimits] set opt_joins [dict get $opts -joins] set opt_boxmap [dict get $opts -boxmap] - set known_types [list light heavy arc double block block1 ascii altg] - set default_custom [dict create hl " " vl " " tlc " " trc " " blc " " brc " "] set custom_keys [list hl hlt hlb vl vll vlr tlc trc blc brc] - if {$opt_type ni $known_types} { - set is_custom_dict_ok 1 - if {[llength $opt_type] %2 == 0} { - #custom dict may leave out keys - but cannot have unknown keys - dict for {k v} $opt_type { - switch -- $k { - hl - hlt - hlb - vl - vll - vlr - tlc - trc - blc - brc {} - default { - #k not in custom_keys - set is_custom_dict_ok 0 - break - } - } - } - } else { - set is_custom_dict_ok 0 - } - if {!$is_custom_dict_ok} { - error "frame option -type must be one of known types: $known_types or a dictionary with any of keys hl,hlt,hlb,vl,vll,vlr,tlc,trc,blc,brc" - } - set custom_frame [dict merge $default_custom $opt_type] - set frame_type custom + + set known_frametypes $frametypes ;# light, heavey etc as defined in textblock::frametypes variable + set default_custom [dict create hl " " vl " " tlc " " trc " " blc " " brc " "] + + lassign [textblock::frametype $opt_type] _cat category _type ftype + if {$category eq "custom"} { + set custom_frame $ftype + set frameset "custom" } else { - set frame_type $opt_type + #category = predefined + set frameset $ftype ;# light,heavy etc } + set is_boxlimits_ok 1 set exact_boxlimits [list] foreach v $opt_boxlimits { @@ -1877,7 +2142,7 @@ namespace eval textblock { #the double glyphs in box drawing can do a limited set of joins to light lines - but not enough for seamless table layouts. #the arc set can't even join to itself e.g with curved equivalents of T-like shapes #I guess - switch -- $frame_type { + switch -- $frameset { "altg" { #old style ansi escape sequences with alternate graphics page G0 set hl [cd::hl] @@ -2050,7 +2315,7 @@ namespace eval textblock { set brc \u2527 ;#boxd_dhlul down heavy and left up light (rtj) } light { - set blc [punk::char::charshort boxd_lvr] ;#light vertical and right (ltj) + set blc \u251c ;#[punk::char::charshort boxd_lvr] light vertical and right (ltj) set brc [punk::char::charshort boxd_lvl] ;#light vertical and left (rtj) } } @@ -2088,9 +2353,56 @@ namespace eval textblock { } down_left { #5 - set blc [punk::char::charshort boxd_lvhz] ;#light vertical and horizontal (fwj) - set tlc [punk::char::charshort boxd_ldhz] ;#T shape (ttj) - set brc [punk::char::charshort boxd_lvl] ;#light vertical and left (rtj) + switch -- $targetdown-$targetleft { + other-light { + set blc \u2534 ;#(btj) + set tlc \u252c ;#(ttj) + #brc - default corner + } + other-other { + #default corners + } + other-heavy { + set blc \u2535 ;# heavy left (btj) + set tlc \u252d ;#heavy left (ttj) + #brc default corner + } + heavy-light { + set blc \u2541 ;# heavy down (fwj) + set tlc \u252c ;# light (ttj) + set brc \u2527 ;# heavy down (rtj) + } + heavy-other { + set blc \u251f ;#heavy down (ltj) + #tlc - default corner + set brc \u2527 ;#heavy down (rtj) + } + heavy-heavy { + set blc \u2545 ;#heavy down and left (fwj) + set tlc \u252d ;#heavy left (ttj) + set brc \u2527 ;#heavy down (rtj) + } + light-light { + set blc \u253c ;# [punk::char::charshort boxd_lvhz] ;#light vertical and horizontal (fwj) + set tlc \u252c ;# boxd_ldhz (ttj) + set brc \u2524 ;# boxd_lvl light vertical and left(rtj) + } + light-other { + set blc \u251c ;# (ltj) + #tlc - default corner + set brc \u2524 ;# boxd_lvl (rtj) + } + light-heavy { + set blc \u253d ;# heavy left (fwj) + set tlc \u252d ;# heavy left (ttj) + set brc \u2524 ;# light (rtj) + } + default { + set blc \u253c ;# [punk::char::charshort boxd_lvhz] ;#light vertical and horizontal (fwj) + set tlc \u252c ;# [punk::char::charshort boxd_ldhz] ;#T shape (ttj) + set brc \u2524 ;# [punk::char::charshort boxd_lvl] ;#light vertical and left (rtj) + } + } } down_right { #6 @@ -2727,7 +3039,7 @@ namespace eval textblock { } } - switch -- $frame_type { + switch -- $frameset { custom { set vll_width [punk::ansi::printing_length $vll] @@ -2859,7 +3171,7 @@ namespace eval textblock { } #boxlimits used for partial borders in table generation - set all_exact_boxlimits [list vll vlr hlt hlb tlc blc trc blc] + set all_exact_boxlimits [list vll vlr hlt hlb tlc blc trc brc] set unspecified_limits [struct::set difference $all_exact_boxlimits $exact_boxlimits] foreach lim $unspecified_limits { switch -- $lim { diff --git a/src/modules/textblock-999999.0a1.0.tm b/src/modules/textblock-999999.0a1.0.tm index cfdf45cf..51dcc4ef 100644 --- a/src/modules/textblock-999999.0a1.0.tm +++ b/src/modules/textblock-999999.0a1.0.tm @@ -50,7 +50,12 @@ namespace eval textblock { bottomleft {} bottominner {} bottomright {} bottomsolo {}\ onlyleft {} onlyinner {} onlyright {} onlysolo {}\ ]\ - -framemap_header [list headerleft {} headerinner {} headerright {} headersolo {}]\ + -framemap_header [list\ + topleft {} topinner {} topright {} topsolo {}\ + middleleft {} middleinner {} middleright {} middlesolo {}\ + bottomleft {} bottominner {} bottomright {} bottomsolo {}\ + onlyleft {} onlyinner {} onlyright {} onlysolo {}\ + ]\ -show_edge 1\ -show_seps 1\ -show_hseps ""\ @@ -58,25 +63,64 @@ namespace eval textblock { -show_header ""\ -show_footer ""\ ] - variable table_border_parts - #for 'L' shaped table building pattern - set table_border_parts [dict create\ - topleft [list hlt vll tlc blc]\ - topinner [list hlt tlc]\ - topright [list hlt tlc vlr trc brc]\ - topsolo [list hlt tlc trc blc brc vl]\ - middleleft [list vll blc]\ - midleinner [list]\ - middleright [list vlr brc]\ - middlesolo [list vl blc brc]\ - bottomleft [list vll blc hlb]\ - bottominner [list hlb blc]\ - bottomright [list hlb blc brc vlr]\ - bottomsolo [list hlb blc brc tlc trc vl]\ - onlyleft [list hlt hlb vll tlc blc]\ - onlyinner [list hlt hlb tlc blc]\ - onlyright [list hlt hlb tlc blc brc trc vlr]\ - onlysolo [list hlt hlb vll vlr blc brc trc brc]\ + #for 'L' shaped table building pattern (tables bigger than 4x4 will be mostly 'L' patterns) + #ie only vll,blc,hlb used for cells except top row and right column + #top right cell uses all 'O' shape, other top cells use 'C' shape (hlt,tlc,vll,blc,hlb) + #right cells use 'U' shape (vll,blc,hlb,brc,vlr) + #e.g for 4x4 + # C C C O + # L L L U + # L L L U + #anti-clockwise elements + set C [list hlt tlc vll blc hlb] + set O [list trc hlt tlc vll blc hlb brc vlr] + set L [list vll blc hlb] + set U [list vll blc hlb brc vlr] + set tops [list trc hlt tlc] + set lefts [list tlc vll blc] + set bottoms [list blc hlb brc] + set rights [list trc brc vlr] + + variable table_edge_parts + set table_edge_parts [dict create\ + topleft [struct::set intersect $C [concat $tops $lefts]]\ + topinner [struct::set intersect $C [concat $tops]]\ + topright [struct::set intersect $O [concat $tops $rights]]\ + topsolo [struct::set intersect $O [concat $tops $lefts $rights]]\ + middleleft [struct::set intersect $L $lefts]\ + middleinner [list]\ + middleright [struct::set intersect $U $rights]\ + middlesolo [struct::set intersect $U [concat $lefts $bottoms $rights]]\ + bottomleft [struct::set intersect $L [concat $lefts]]\ + bottominner [list]\ + bottomright [struct::set intersect $U $rights]\ + bottomsolo [struct::set intersect $U [concat $lefts $rights]]\ + onlyleft [struct::set intersect $C [concat $tops $lefts $bottoms]]\ + onlyinner [struct::set intersect $C [concat $tops $bottoms]]\ + onlyright [struct::set intersect $O [concat $tops $bottoms $rights]]\ + onlysolo [struct::set intersect $O [concat $tops $lefts $bottoms $rights]]\ + ] + + #for header rows - we don't consider the bottom border as part of the edge - even if table body has no rows + #The usual-case of a single header line is the 'onlyleft,onlyinner,onlyright,onlysolo' set. + variable header_edge_parts + set header_edge_parts [dict create\ + topleft [struct::set intersect $C [concat $tops $lefts]]\ + topinner [struct::set intersect $C [concat $tops]]\ + topright [struct::set intersect $O [concat $tops $rights]]\ + topsolo [struct::set intersect $O [concat $tops $lefts $rights]]\ + middleleft [struct::set intersect $L $lefts]\ + middleinner [list]\ + middleright [struct::set intersect $U $rights]\ + middlesolo [struct::set intersect $U [concat $lefts $bottoms $rights]]\ + bottomleft [struct::set intersect $L [concat $lefts]]\ + bottominner [list]\ + bottomright [struct::set intersect $U $rights]\ + bottomsolo [struct::set intersect $U [concat $lefts $rights]]\ + onlyleft [struct::set intersect $C [concat $tops $lefts]]\ + onlyinner [struct::set intersect $C $tops]\ + onlyright [struct::set intersect $O [concat $tops $rights]]\ + onlysolo [struct::set intersect $O [concat $tops $lefts $rights]]\ ] variable table_hseps set table_hseps [dict create\ @@ -99,10 +143,6 @@ namespace eval textblock { ] variable table_vseps set table_vseps [dict create\ - headerleft [list]\ - headerinner [list vll tlc blc]\ - headerright [list vll tlc blc]\ - headersolo [list]\ topleft [list]\ topinner [list vll tlc blc]\ topright [list vll tlc blc]\ @@ -122,19 +162,12 @@ namespace eval textblock { ] - variable header_border_parts - set header_border_parts [dict create\ - headerleft [list vll tlc blc hlt]\ - headerinner [list tlc hlt]\ - headerright [list tlc hlt trc vlr brc]\ - headersolo [list tlc vlr blc hlt trc brc]\ - ] - #e.g $t configure -framemap_body [table_border_map " "] - proc table_border_map {char} { - variable table_border_parts + #e.g $t configure -framemap_body [table_edge_map " "] + proc table_edge_map {char} { + variable table_edge_parts set map [list] - dict for {celltype parts} $table_border_parts { + dict for {celltype parts} $table_edge_parts { set tmap [list] foreach p $parts { dict set tmap $p $char @@ -155,10 +188,10 @@ namespace eval textblock { } return $map } - proc header_border_map {char} { - variable header_border_parts + proc header_edge_map {char} { + variable header_edge_parts set map [list] - dict for {celltype parts} $header_border_parts { + dict for {celltype parts} $header_edge_parts { set tmap [list] foreach p $parts { dict set tmap $p $char @@ -181,7 +214,9 @@ namespace eval textblock { #[enum] CLASS [class interface_caphandler.registry] #[list_begin definitions] # [para] [emph METHODS] - variable o_opts_table + variable o_opts_table ;#options as configured by user (with exception of -ansireset) + variable o_opts_table_effective; #options in effect - e.g with defaults merged in. + variable o_columndefs variable o_columndata variable o_rowdefs @@ -207,7 +242,8 @@ namespace eval textblock { } } #set o_opts_table [dict merge $o_opts_table_defaults $args] - set o_opts_table $o_opts_table_defaults + set o_opts_table $o_opts_table_defaults + set o_opts_table_effective $o_opts_table_defaults my configure {*}[dict merge $o_opts_table_defaults $args] set o_columndefs [dict create] set o_columndata [dict create] ;#we store data by column even though it is often added row by row @@ -266,21 +302,92 @@ namespace eval textblock { } return [dict create header $ft_header body $ft_body] } + method Set_effective_framelimits {} { + upvar ::textblock::class::opts_table_defaults tdefaults + set default_blims [dict get $tdefaults -framelimits_body] + set default_hlims [dict get $tdefaults -framelimits_header] + set eff_blims [dict get $o_opts_table_effective -framelimits_body] + set eff_hlims [dict get $o_opts_table_effective -framelimits_header] + + set requested_blims [dict get $o_opts_table -framelimits_body] + set requested_hlims [dict get $o_opts_table -framelimits_header] + set blims $eff_blims + set hlims $eff_hlims + switch -- $requested_blims { + "default" { + set blims $default_blims + } + default { + #set blims $requested_blims + set blims [list] + foreach lim $requested_blims { + switch -- $lim { + hl { + lappend blims hlt hlb + } + vl { + lappend blims vll vlr + } + default { + lappend blims $lim + } + } + } + set blims [lsort -unique $blims] + } + } + dict set o_opts_table_effective -framelimits_body $blims + switch -- $requested_hlims { + "default" { + set hlims $default_hlims + } + default { + #set hlims $requested_hlims + set hlims [list] + foreach lim $requested_hlims { + switch -- $lim { + hl { + lappend hlims hlt hlb + } + vl { + lappend hlims vll vlr + } + default { + lappend hlims $lim + } + } + } + set hlims [lsort -unique $hlims] + } + } + dict set o_opts_table_effective -framelimits_header $hlims + return [dict create body $blims header $hlims] + } method configure args { if {![llength $args]} { return $o_opts_table } - if {[llength $args] == 1 && [lindex $args 0] in [dict keys $o_opts_table_defaults]} { - #query single option - set k [lindex $args 0] - set val [dict get $o_opts_table $k] - set infodict [dict create] - switch -- $k { - -ansibase_header - -ansibase_body - -ansiborder_header - -ansiborder_body - -ansiborder_footer { - dict set infodict debug [ansistring VIEW $val] + if {[llength $args] == 1} { + if {[lindex $args 0] in [dict keys $o_opts_table_defaults]} { + #query single option + set k [lindex $args 0] + set val [dict get $o_opts_table $k] + set returndict [dict create option $k value $val ansireset "\x1b\[m"] + set infodict [dict create] + switch -- $k { + -ansibase_header - -ansibase_body - -ansiborder_header - -ansiborder_body - -ansiborder_footer { + dict set infodict debug [ansistring VIEW $val] + } + -framemap_body - -framemap_header - -framelimits_body - -framelimits_header { + dict set returndict effective [dict get $o_opts_table_effective $k] + } } + dict set returndict info $infodict + return $returndict + #return [dict create option $k value $val ansireset "\x1b\[m" info $infodict] + } else { + error "textblock::table configure - unrecognised option '[lindex $args 0]'. Known values [dict keys $o_opts_table_defaults]" } - return [dict create option $k value $val ansireset "\x1b\[m" info $infodict] } if {[llength $args] %2 != 0} { error "[namespace current]::table configure - unexpected argument count. Require name value pairs" @@ -308,9 +415,72 @@ namespace eval textblock { set ansival [punk::ansi::codetype::sgr_merge $ansi_codes] lappend checked_opts $k $ansival } + -frametype - -frametype_header - -frametype_body { + #frametype will raise an error if v is not a valid custom dict or one of the known predefined types such as light,heavy,double etc + lassign [textblock::frametype $v] _cat category _type ftype + lappend checked_opts $k $v + } + -framemap_body - -framemap_header { + #upvar ::textblock::class::opts_table_defaults tdefaults + #set default_bmap [dict get $tdefaults -framemap_body] + #todo - check keys and map + if {[llength $v] == 1} { + if {$v eq "default"} { + upvar ::textblock::class::opts_table_defaults tdefaults + set default_map [dict get $tdefaults $k] + lappend checked_opts $k $default_map + } else { + error "textblock::table::configure invalid $k value $v. Expected the value 'default' or a dict e.g topleft {hl *}" + } + } else { + dict for {subk subv} $v { + switch -- $subk { + topleft - topinner - topright - topsolo - middleleft - middleinner - middleright - middlesolo - bottomleft - bottominner - bottomright - bottomsolo - onlyleft - onlyinner - onlyright - onlysolo {} + default { + error "textblock::table::configure invalid $subk. Known values {topleft topinner topright topsolo middleleft middleinner middleright middlesolo bottomleft bottominner bottomright bottomsolo onlyleft onlyinner onlyright onlysolo}" + } + } + dict for {seg subst} $subv { + switch -- $seg { + hl - hlt - hlb - vl - vll - vlr - trc - tlc - blc - brc {} + default { + error "textblock::table::configure invalid $subk value $seg. Known values {hl hlt hlb vl vll vlr trc tlc blc brc}" + } + } + } + + } + lappend checked_opts $k $v + } + + } + -framelimits_body - -framelimits_header { + set specific_framelimits [list] + foreach fl $v { + switch -- $fl { + "default" { + lappend specific_framelimits trc hlt tlc vll blc hlb brc vlr + } + hl { + lappend specific_framelimits hlt hlb + } + vl { + lappend specific_framelimits vll vlr + } + hlt - hlb - vll - vlr - trc - tlc - blc - brc { + lappend specific_framelimits $fl + } + default { + error "textblock::table::configure invalid $k '$fl'. Known values {hl hlb hlt vl vll vlr trc tlc blc brc} (or default for all)" + } + } + } + lappend checked_opts $k $specific_framelimits + } -ansireset { if {$v eq "\uFFEF"} { - lappend checked_opts $k "\x1b\[m" ;# [a] + set RST "\x1b\[m" ;#[a] + lappend checked_opts $k $RST } else { error "textblock::table::configure -ansireset is read-only. It is present only to prevent unwanted colourised output in configure commands" } @@ -320,7 +490,51 @@ namespace eval textblock { } } } - set o_opts_table [dict merge $o_opts_table $checked_opts] + #all options checked - ok to update o_opts_table and o_opts_table_effective + + #set o_opts_table [dict merge $o_opts_table $checked_opts] + dict for {k v} $args { + switch -- $k { + -framemap_header - -framemap_body { + #framemaps don't require setting every key to update. + #e.g configure -framemaps {topleft } + #needs to merge with existing unspecified keys such as topright middleleft etc. + if {$v eq "default"} { + dict set o_opts_table $k default + } else { + if {[dict get $o_opts_table $k] eq "default"} { + dict set o_opts_table $k $v + } else { + dict set o_opts_table $k [dict merge [dict get $o_opts_table $k] $v] + } + } + } + default { + dict set o_opts_table $k $v + } + } + } + #use values from checked_opts for the effective opts + dict for {k v} $checked_opts { + switch -- $k { + -framemap_body - -framemap_header { + set existing [dict get $o_opts_table_effective $k] + set updated $existing + dict for {subk subv} $v { + dict set updated $subk $subv + } + dict set o_opts_table_effective $k $updated + } + -framelimits_body - -framelimits_header { + #my Set_effective_framelimits + dict set o_opts_table_effective $k $v + } + default { + dict set o_opts_table_effective $k $v + } + } + } + return $o_opts_table } #integrate with struct::matrix - allows ::m format 2string $table @@ -671,46 +885,66 @@ namespace eval textblock { } switch -- $opt_posn { left { - set header_boxlimits {hl tlc blc vll} + set header_boxlimits {hlb hlt tlc blc vll} set header_joins [list down-$ftype_body] set boxlimits_position {hlb blc vll} - set boxlimits_headerless {hlb hlt blc vll tlc} + set boxlimits_headerless [concat $boxlimits_position {hlt tlc}] set joins {down} } inner { - set header_boxlimits {hl tlc blc vll} + set header_boxlimits {hlb hlt tlc blc vll} set header_joins [list left down-$ftype_body] set boxlimits_position {hlb blc vll} - set boxlimits_headerless {hlb hlt blc vll tlc} + set boxlimits_headerless [concat $boxlimits_position {hlt tlc}] set joins {down left} } right { - set header_boxlimits {hl tlc blc vll vlr trc brc} + set header_boxlimits {hlb hlt tlc blc vll vlr trc brc} set header_joins [list left down-$ftype_body] set boxlimits_position {hlb blc vll vlr brc} - set boxlimits_headerless {hlb hlt blc vll vlr brc tlc trc} + set boxlimits_headerless [concat $boxlimits_position {hlt tlc trc}] set joins {down left} } solo { - set header_boxlimits {hl tlc blc vll vlr trc brc} + set header_boxlimits {hlb hlt tlc blc vll vlr trc brc} set header_joins [list down-$ftype_body] - set boxlimits_position {hlb blc vll vlr brc} - set boxlimits_headerless {hlb hlt blc vll vlr brc tlc trc} + set boxlimits_position {hlb blc vll vlr brc} + set boxlimits_headerless [concat $boxlimits_position {hlt tlc trc}] set joins {down} } } #use struct::set instead of simple for loop - will be faster at least when critcl available - set boxlimits [struct::set intersect [dict get $o_opts_table -framelimits_body] $boxlimits_position] + set boxlimits [struct::set intersect [dict get $o_opts_table_effective -framelimits_body] $boxlimits_position] + set boxlimits_headerless [struct::set intersect [dict get $o_opts_table_effective -framelimits_body] $boxlimits_headerless] + set header_boxlimits [struct::set intersect [dict get $o_opts_table_effective -framelimits_header] $header_boxlimits] + + #upvar ::textblock::class::opts_table_defaults tdefaults + #set default_bmap [dict get $tdefaults -framemap_body] + #set default_hmap [dict get $tdefaults -framemap_header] + #set fmap $default_bmap + #set hmap $default_hmap + #dict for {k v} $fmap { + # if {[dict exists $o_opts_table -framemap_body $k]} { + # dict set fmap $k [dict merge $v [dict get $o_opts_table -framemap_body $k]] + # } + #} + #dict for {k v} $hmap { + # if {[dict exists $o_opts_table -framemap_header $k]} { + # dict set hmap $k [dict merge $v [dict get $o_opts_table -framemap_header $k]] + # } + #} + set fmap [dict get $o_opts_table_effective -framemap_body] + set hmap [dict get $o_opts_table_effective -framemap_header] - upvar ::textblock::class::opts_table_defaults tdefaults - set defaultmap [dict get $tdefaults -framemap_body] - set default_hmap [dict get $tdefaults -framemap_header] if {![dict get $o_opts_table -show_edge]} { - set fmap [dict merge $defaultmap [textblock::class::table_border_map ""]] - set hmap [dict merge $default_hmap [textblock::class::header_border_map ""]] - } else { - set fmap [dict merge $defaultmap [dict get $o_opts_table -framemap_body]] - set hmap [dict merge $default_hmap [dict get $o_opts_table -framemap_header]] + set body_edgemap [textblock::class::table_edge_map ""] + dict for {k v} $fmap { + dict set fmap $k [dict merge $v [dict get $body_edgemap $k]] + } + set header_edgemap [textblock::class::header_edge_map ""] + dict for {k v} $hmap { + dict set hmap $k [dict merge $v [dict get $header_edgemap $k]] + } } set sep_elements_horizontal $::textblock::class::table_hseps set sep_elements_vertical $::textblock::class::table_vseps @@ -719,14 +953,17 @@ namespace eval textblock { set botmap [dict get $fmap bottom$opt_posn] set midmap [dict get $fmap middle$opt_posn] set onlymap [dict get $fmap only$opt_posn] - set hdrmap [dict get $hmap header$opt_posn] + + set hdrmap [dict get $hmap only${opt_posn}] + set topseps_h [dict get $sep_elements_horizontal top$opt_posn] set topseps_v [dict get $sep_elements_vertical top$opt_posn] set midseps_h [dict get $sep_elements_horizontal middle$opt_posn] set midseps_v [dict get $sep_elements_vertical middle$opt_posn] set botseps_v [dict get $sep_elements_vertical bottom$opt_posn] + set onlyseps_v [dict get $sep_elements_vertical only$opt_posn] - set headerseps_v [dict get $sep_elements_vertical header$opt_posn] + set headerseps_v [dict get $sep_elements_vertical top$opt_posn] lassign [my Get_seps] _h show_seps_h _v show_seps_v @@ -758,7 +995,7 @@ namespace eval textblock { if {!$show_seps_v} { set hlims [struct::set difference $header_boxlimits $headerseps_v] } - + #todo - multiline header cells + multiple header lines (will be more useful when colspans implemented) set header_frame [textblock::frame -width [expr {$colwidth+2}] -type [dict get $ftypes header]\ -ansibase $ansibase_header -ansiborder $ansiborder_final\ -boxlimits $hlims -boxmap $hdrmap -joins $header_joins $hval\ @@ -776,6 +1013,8 @@ namespace eval textblock { set blims_top $boxlimits set blims_bot $boxlimits set blims_top_headerless $boxlimits_headerless + set blims_only $boxlimits + set blims_only_headerless $boxlimits_headerless if {!$show_seps_h} { set blims_mid [struct::set difference $blims_mid $midseps_h] set blims_top [struct::set difference $blims_top $topseps_h] @@ -786,6 +1025,8 @@ namespace eval textblock { set blims_top [struct::set difference $blims_top $topseps_v] set blims_top_headerless [struct::set difference $blims_top_headerless $topseps_v] set blims_bot [struct::set difference $blims_bot $botseps_v] + set blims_only [struct::set difference $blims_only $onlyseps_v] + set blims_only_headerless [struct::set difference $blims_only_headerless $onlyseps_v] } set colidx [lindex [dict keys $o_columndefs] $index_expression] ;#convert possible end-1,2+2 etc expression to >= 0 integer in dict range @@ -823,9 +1064,9 @@ namespace eval textblock { set joins [lremove $joins [lsearch $joins down*]] set bmap $onlymap if {$do_show_header} { - set blims $boxlimits + set blims $blims_only } else { - set blims $boxlimits_headerless + set blims $blims_only_headerless } } else { set bmap $topmap @@ -859,9 +1100,9 @@ namespace eval textblock { #note that if show_edge is 0 - then for this empty line - we will not see any vertical bars #This is because the frame with no data is made entirely of corner elements if {$do_show_header} { - append output [textblock::frame -width [expr {$colwidth + 2}] -type [dict get $ftypes body] -boxlimits $boxlimits -boxmap $onlymap -joins $joins]\n + append output [textblock::frame -width [expr {$colwidth + 2}] -type [dict get $ftypes body] -boxlimits $blims_only -boxmap $onlymap -joins $joins]\n } else { - append output [textblock::frame -width [expr {$colwidth + 2}] -type [dict get $ftypes body] -boxlimits $boxlimits_headerless -boxmap $onlymap -joins $joins] \n + append output [textblock::frame -width [expr {$colwidth + 2}] -type [dict get $ftypes body] -boxlimits $blims_only_headerless -boxmap $onlymap -joins $joins] \n } } return [string trimright $output \n] @@ -881,6 +1122,7 @@ namespace eval textblock { #assert cidx is integer >=0 set cdef [dict get $o_columndefs $cidx] set t [dict get $cdef -header] ;#may be empty string + set t_maxdataheight 1 set items [dict get $o_columndata $cidx] set ansibase_body [dict get $o_opts_table -ansibase_body] @@ -1625,8 +1867,45 @@ namespace eval textblock { } } + variable frametypes + set frametypes [list light heavy arc double block block1 ascii altg] + #class::table needs to be able to determine valid frametypes + proc frametypes {} { + variable frametypes + return $frametypes + } + proc frametype {f} { + variable frametypes + set default_custom [dict create hl " " vl " " tlc " " trc " " blc " " brc " "] + set custom_keys [list hl hlt hlb vl vll vlr tlc trc blc brc] + if {$f ni $frametypes} { + set is_custom_dict_ok 1 + if {[llength $f] %2 == 0} { + #custom dict may leave out keys - but cannot have unknown keys + dict for {k v} $f { + switch -- $k { + hl - hlt - hlb - vl - vll - vlr - tlc - trc - blc - brc {} + default { + #k not in custom_keys + set is_custom_dict_ok 0 + break + } + } + } + } else { + set is_custom_dict_ok 0 + } + if {!$is_custom_dict_ok} { + error "frame option -type must be one of known types: $frametypes or a dictionary with any of keys hl,hlt,hlb,vl,vll,vlr,tlc,trc,blc,brc" + } + set custom_frame [dict merge $default_custom $f] + return [dict create category custom type $custom_frame] + } else { + return [dict create category predefined type $f] + } + } proc frame {args} { - + variable frametypes set expect_optval 0 set argposn 0 set pmax [expr {[llength $args]-1}] @@ -1690,34 +1969,20 @@ namespace eval textblock { set opt_boxlimits [dict get $opts -boxlimits] set opt_joins [dict get $opts -joins] set opt_boxmap [dict get $opts -boxmap] - set known_types [list light heavy arc double block block1 ascii altg] - set default_custom [dict create hl " " vl " " tlc " " trc " " blc " " brc " "] set custom_keys [list hl hlt hlb vl vll vlr tlc trc blc brc] - if {$opt_type ni $known_types} { - set is_custom_dict_ok 1 - if {[llength $opt_type] %2 == 0} { - #custom dict may leave out keys - but cannot have unknown keys - dict for {k v} $opt_type { - switch -- $k { - hl - hlt - hlb - vl - vll - vlr - tlc - trc - blc - brc {} - default { - #k not in custom_keys - set is_custom_dict_ok 0 - break - } - } - } - } else { - set is_custom_dict_ok 0 - } - if {!$is_custom_dict_ok} { - error "frame option -type must be one of known types: $known_types or a dictionary with any of keys hl,hlt,hlb,vl,vll,vlr,tlc,trc,blc,brc" - } - set custom_frame [dict merge $default_custom $opt_type] - set frame_type custom + + set known_frametypes $frametypes ;# light, heavey etc as defined in textblock::frametypes variable + set default_custom [dict create hl " " vl " " tlc " " trc " " blc " " brc " "] + + lassign [textblock::frametype $opt_type] _cat category _type ftype + if {$category eq "custom"} { + set custom_frame $ftype + set frameset "custom" } else { - set frame_type $opt_type + #category = predefined + set frameset $ftype ;# light,heavy etc } + set is_boxlimits_ok 1 set exact_boxlimits [list] foreach v $opt_boxlimits { @@ -1877,7 +2142,7 @@ namespace eval textblock { #the double glyphs in box drawing can do a limited set of joins to light lines - but not enough for seamless table layouts. #the arc set can't even join to itself e.g with curved equivalents of T-like shapes #I guess - switch -- $frame_type { + switch -- $frameset { "altg" { #old style ansi escape sequences with alternate graphics page G0 set hl [cd::hl] @@ -2050,7 +2315,7 @@ namespace eval textblock { set brc \u2527 ;#boxd_dhlul down heavy and left up light (rtj) } light { - set blc [punk::char::charshort boxd_lvr] ;#light vertical and right (ltj) + set blc \u251c ;#[punk::char::charshort boxd_lvr] light vertical and right (ltj) set brc [punk::char::charshort boxd_lvl] ;#light vertical and left (rtj) } } @@ -2088,9 +2353,56 @@ namespace eval textblock { } down_left { #5 - set blc [punk::char::charshort boxd_lvhz] ;#light vertical and horizontal (fwj) - set tlc [punk::char::charshort boxd_ldhz] ;#T shape (ttj) - set brc [punk::char::charshort boxd_lvl] ;#light vertical and left (rtj) + switch -- $targetdown-$targetleft { + other-light { + set blc \u2534 ;#(btj) + set tlc \u252c ;#(ttj) + #brc - default corner + } + other-other { + #default corners + } + other-heavy { + set blc \u2535 ;# heavy left (btj) + set tlc \u252d ;#heavy left (ttj) + #brc default corner + } + heavy-light { + set blc \u2541 ;# heavy down (fwj) + set tlc \u252c ;# light (ttj) + set brc \u2527 ;# heavy down (rtj) + } + heavy-other { + set blc \u251f ;#heavy down (ltj) + #tlc - default corner + set brc \u2527 ;#heavy down (rtj) + } + heavy-heavy { + set blc \u2545 ;#heavy down and left (fwj) + set tlc \u252d ;#heavy left (ttj) + set brc \u2527 ;#heavy down (rtj) + } + light-light { + set blc \u253c ;# [punk::char::charshort boxd_lvhz] ;#light vertical and horizontal (fwj) + set tlc \u252c ;# boxd_ldhz (ttj) + set brc \u2524 ;# boxd_lvl light vertical and left(rtj) + } + light-other { + set blc \u251c ;# (ltj) + #tlc - default corner + set brc \u2524 ;# boxd_lvl (rtj) + } + light-heavy { + set blc \u253d ;# heavy left (fwj) + set tlc \u252d ;# heavy left (ttj) + set brc \u2524 ;# light (rtj) + } + default { + set blc \u253c ;# [punk::char::charshort boxd_lvhz] ;#light vertical and horizontal (fwj) + set tlc \u252c ;# [punk::char::charshort boxd_ldhz] ;#T shape (ttj) + set brc \u2524 ;# [punk::char::charshort boxd_lvl] ;#light vertical and left (rtj) + } + } } down_right { #6 @@ -2727,7 +3039,7 @@ namespace eval textblock { } } - switch -- $frame_type { + switch -- $frameset { custom { set vll_width [punk::ansi::printing_length $vll] @@ -2859,7 +3171,7 @@ namespace eval textblock { } #boxlimits used for partial borders in table generation - set all_exact_boxlimits [list vll vlr hlt hlb tlc blc trc blc] + set all_exact_boxlimits [list vll vlr hlt hlb tlc blc trc brc] set unspecified_limits [struct::set difference $all_exact_boxlimits $exact_boxlimits] foreach lim $unspecified_limits { switch -- $lim {