You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
202 lines
4.4 KiB
202 lines
4.4 KiB
# -*- tcl -*- |
|
# ### ### ### ######### ######### ######### |
|
## Terminal packages - string -> action mappings |
|
## (menu objects). For use with 'receive listen'. |
|
## In essence a DFA with tree structure. |
|
|
|
# ### ### ### ######### ######### ######### |
|
## Requirements |
|
|
|
package require snit |
|
package require textutil::repeat |
|
package require textutil::tabify |
|
package require term::ansi::send |
|
package require term::receive::bind |
|
package require term::ansi::code::ctrl |
|
|
|
namespace eval ::term::receive::menu {} |
|
|
|
# ### ### ### ######### ######### ######### |
|
|
|
snit::type ::term::interact::menu { |
|
|
|
option -in -default stdin |
|
option -out -default stdout |
|
option -column -default 0 |
|
option -line -default 0 |
|
option -height -default 25 |
|
option -actions -default {} |
|
option -hilitleft -default 0 |
|
option -hilitright -default end |
|
option -framed -default 0 -readonly 1 |
|
|
|
# ### ### ### ######### ######### ######### |
|
## |
|
|
|
constructor {dict args} { |
|
$self configurelist $args |
|
Save $dict |
|
|
|
install bind using ::term::receive::bind \ |
|
${selfns}::bind $options(-actions) |
|
|
|
$bind map [cd::cu] [mymethod Up] |
|
$bind map [cd::cd] [mymethod Down] |
|
$bind map \n [mymethod Select] |
|
#$bind default [mymethod DEF] |
|
|
|
return |
|
} |
|
|
|
# ### ### ### ######### ######### ######### |
|
## |
|
|
|
method interact {} { |
|
Show |
|
$bind listen $options(-in) |
|
vwait [myvar done] |
|
$bind unlisten $options(-in) |
|
return $map($done) |
|
} |
|
|
|
method done {} {set done $at ; return} |
|
method clear {} {Clear ; return} |
|
|
|
# ### ### ### ######### ######### ######### |
|
## |
|
|
|
component bind |
|
|
|
# ### ### ### ######### ######### ######### |
|
## |
|
|
|
variable map -array {} |
|
variable header |
|
variable labels |
|
variable footer |
|
variable empty |
|
|
|
proc Save {dict} { |
|
upvar 1 header header labels labels footer footer |
|
upvar 1 empty empty at at map map top top |
|
upvar 1 options(-height) height |
|
|
|
set max 0 |
|
foreach {l code} $dict { |
|
if {[set len [string length $l]] > $max} {set max $len} |
|
} |
|
|
|
set header [cd::groptim [cd::tlc][textutil::repeat::strRepeat [cd::hl] $max][cd::trc]] |
|
set footer [cd::groptim [cd::blc][textutil::repeat::strRepeat [cd::hl] $max][cd::brc]] |
|
|
|
set labels {} |
|
set at 0 |
|
foreach {l code} $dict { |
|
set map($at) $code |
|
lappend labels ${l}[textutil::repeat::strRepeat " " [expr {$max-[string length $l]}]] |
|
incr at |
|
} |
|
|
|
set h $height |
|
if {$h > [llength $labels]} {set h [llength $labels]} |
|
|
|
set eline " [textutil::repeat::strRepeat { } $max]" |
|
set empty $eline |
|
for {set i 0} {$i <= $h} {incr i} { |
|
append empty \n$eline |
|
} |
|
|
|
set at 0 |
|
set top 0 |
|
return |
|
} |
|
|
|
variable top 0 |
|
variable at 0 |
|
variable done . |
|
|
|
proc Show {} { |
|
upvar 1 header header labels labels footer footer at at |
|
upvar 1 options(-in) in options(-column) col top top |
|
upvar 1 options(-out) out options(-line) row |
|
upvar 1 options(-height) height options(-framed) framed |
|
upvar 1 options(-hilitleft) left |
|
upvar 1 options(-hilitright) right |
|
|
|
set bot [expr {$top + $height - 1}] |
|
set fr [expr {$framed ? [cd::vl] : { }}] |
|
|
|
set text $header\n |
|
set i $top |
|
foreach l [lrange $labels $top $bot] { |
|
append text $fr |
|
if {$i != $at} { |
|
append text $l |
|
} else { |
|
append text [string replace $l $left $right \ |
|
[cd::sda_revers][string range $l $left $right][cd::sda_reset]] |
|
} |
|
append text $fr \n |
|
incr i |
|
} |
|
append text $footer |
|
|
|
vt::wrch $out [cd::showat $row $col $text] |
|
return |
|
} |
|
|
|
proc Clear {} { |
|
upvar 1 empty empty options(-column) col |
|
upvar 1 options(-out) out options(-line) row |
|
|
|
vt::wrch $out [cd::showat $row $col $empty] |
|
return |
|
} |
|
|
|
# ### ### ### ######### ######### ######### |
|
## |
|
|
|
method Up {str} { |
|
if {$at == 0} return |
|
incr at -1 |
|
if {$at < $top} {incr top -1} |
|
Show |
|
return |
|
} |
|
|
|
method Down {str} { |
|
upvar 0 options(-height) height |
|
if {$at == ([llength $labels]-1)} return |
|
incr at |
|
set bot [expr {$top + $height - 1}] |
|
if {$at > $bot} {incr top} |
|
Show |
|
return |
|
} |
|
|
|
method Select {str} { |
|
$self done |
|
return |
|
} |
|
|
|
method DEF {str} { |
|
puts stderr "($str)" |
|
exit |
|
} |
|
|
|
## |
|
# ### ### ### ######### ######### ######### |
|
} |
|
|
|
# ### ### ### ######### ######### ######### |
|
## Ready |
|
|
|
namespace eval ::term::interact::menu { |
|
term::ansi::code::ctrl::import cd |
|
term::ansi::send::import vt |
|
} |
|
|
|
package provide term::interact::menu 0.1 |
|
|
|
## |
|
# ### ### ### ######### ######### #########
|
|
|