Index: TODO =================================================================== diff -u -r1c770abc23851d575aa3e1c28ee07d0653147d01 -ra774481bc677369c7b0f7d1fcf3275ee1afd4fba --- TODO (.../TODO) (revision 1c770abc23851d575aa3e1c28ee07d0653147d01) +++ TODO (.../TODO) (revision a774481bc677369c7b0f7d1fcf3275ee1afd4fba) @@ -4485,10 +4485,13 @@ - nsf.c: fix crash when "nsf::my" is called with a single argument outside the object context. +- fixed cases, where valuechangedcmd (and the other traces) did not + work with "configure" method. When slot traces are used, it cleans + other traces for the same operations. +- extended regression test + ======================================================================== TODO: -- valuechangedcmd implemented via initcmd does - not work with "configure" method - fix property inheritance in traits (nx-traits.tcl) - maybe remove unneeded values, align naming in enumeration of first arg of *::info::objectparameter and *::info::method Index: library/nx/nx.tcl =================================================================== diff -u -r9cead8929011bb4dcc6c44630a91bc5d597520b2 -ra774481bc677369c7b0f7d1fcf3275ee1afd4fba --- library/nx/nx.tcl (.../nx.tcl) (revision 9cead8929011bb4dcc6c44630a91bc5d597520b2) +++ library/nx/nx.tcl (.../nx.tcl) (revision a774481bc677369c7b0f7d1fcf3275ee1afd4fba) @@ -1649,7 +1649,7 @@ } ::nx::VariableSlot public method setCheckedInstVar {-nocomplain:switch object value} { - + if {[::nsf::var::exists $object ${:name}] && !$nocomplain} { error "object $object has already an instance variable named '${:name}'" } @@ -1673,12 +1673,7 @@ ::nsf::is -complain [join $options ,] $value } - set traces [::nsf::directdispatch $object -frame object ::trace info variable ${:name}] - foreach trace $traces { - lassign $trace ops cmdPrefix - ::nsf::directdispatch $object -frame object ::trace remove variable ${:name} $ops $cmdPrefix - append restore "[list ::nsf::directdispatch $object -frame object ::trace add variable ${:name} $ops $cmdPrefix]\n" - } + set restore [:removeTraces $object *] ::nsf::var::set $object ${:name} ${:default} if {[info exists restore]} { {*}$restore } } @@ -1864,25 +1859,42 @@ ###################################################################### # Handle variable traces ###################################################################### + ::nx::VariableSlot protected method removeTraces {object matchOps} { + #puts stderr "====removeTraces ${:name} $matchOps" + set restore "" + set traces [::nsf::directdispatch $object -frame object ::trace info variable ${:name}] + foreach trace $traces { + lassign $trace ops cmdPrefix + if {![string match $matchOps $ops]} continue + #puts stderr "====remove trace variable ${:name} $ops $cmdPrefix" + ::nsf::directdispatch $object -frame object ::trace remove variable ${:name} $ops $cmdPrefix + append restore "[list ::nsf::directdispatch $object -frame object ::trace add variable ${:name} $ops $cmdPrefix]\n" + } + return $restore + } ::nx::VariableSlot protected method handleTraces {} { # essentially like before set __initcmd "" set trace {::nsf::directdispatch [::nsf::self] -frame object ::trace} - # There might be already default values registered on the + + # There be already default values registered on the # class. If so, defaultcmd is ignored. if {[info exists :default]} { if {[info exists :defaultcmd]} {error "defaultcmd can't be used together with default value"} if {[info exists :valuecmd]} {error "valuecmd can't be used together with default value"} } elseif [info exists :defaultcmd] { if {[info exists :valuecmd]} {error "valuecmd can't be used together with defaultcmd"} + append __initcmd "::nsf::directdispatch [::nsf::self] -frame object :removeTraces \[::nsf::self\] read\n" append __initcmd "$trace add variable [list ${:name}] read \ \[list [::nsf::self] __default_from_cmd \[::nsf::self\] [list [set :defaultcmd]]\]\n" } elseif [info exists :valuecmd] { + append __initcmd "::nsf::directdispatch [::nsf::self] -frame object :removeTraces \[::nsf::self\] read\n" append __initcmd "$trace add variable [list ${:name}] read \ \[list [::nsf::self] __value_from_cmd \[::nsf::self\] [list [set :valuecmd]]\]" } if {[info exists :valuechangedcmd]} { + append __initcmd "::nsf::directdispatch [::nsf::self] -frame object :removeTraces \[::nsf::self\] write\n" append __initcmd "$trace add variable [list ${:name}] write \ \[list [::nsf::self] __value_changed_cmd \[::nsf::self\] [list [set :valuechangedcmd]]\]" } @@ -1908,7 +1920,6 @@ ::nsf::var::set $obj $var [$obj eval $cmd] } ::nx::VariableSlot method __value_changed_cmd {obj cmd var sub op} { - # puts stderr "**************************" # puts "valuechanged obj=$obj cmd=$cmd, var=$var, op=$op, ...\n$obj exists $var -> [::nsf::var::set $obj $var]" eval $cmd } @@ -2030,7 +2041,12 @@ if {$nocomplain} {$slot eval {set :nocomplain 1}} if {!$config} {$slot eval {set :config false}} - if {[info exists defaultValue]} {$slot setCheckedInstVar -nocomplain=$nocomplain [self] $defaultValue} + if {[info exists defaultValue]} { + # We could consider calling "configure" instead, but that would + # not work for true "variable" handlers + $slot setCheckedInstVar -nocomplain=$nocomplain [self] $defaultValue + #set :__initcmd($name) 1 + } if {[$slot eval {info exists :settername}]} { set name [$slot settername] Index: tests/cget.test =================================================================== diff -u -r2872e1f0a6523c7fb44952492e05414c4f8d9c84 -ra774481bc677369c7b0f7d1fcf3275ee1afd4fba --- tests/cget.test (.../cget.test) (revision 2872e1f0a6523c7fb44952492e05414c4f8d9c84) +++ tests/cget.test (.../cget.test) (revision a774481bc677369c7b0f7d1fcf3275ee1afd4fba) @@ -220,4 +220,81 @@ ? {p1 configure -age 27} "" ? {p1 configure -bar 102} "" +} + +nx::Test parameter count 10 +Test case configure-trace-class { + + # + # class case with no default + # + nx::Class create C + C property p { + set :valuechangedcmd { + #puts stderr "C.p valuechangedcmd $obj $var +1" + ::nsf::var::set $obj $var [expr [list [::nsf::var::set $obj $var] + 1]] + } + } + + C create c1 + + ? {c1 eval {info exists :p}} 0 + ? {c1 cget -p} {can't read "p": no such variable} + ? {c1 configure -p 1} "" + ? {c1 eval {info exists :p}} 1 + ? {c1 cget -p} "2" + + # + # class case with default + # + C property {q 100} { + set :valuechangedcmd { + #puts stderr "C.q valuechangedcmd $obj $var +1" + ::nsf::var::set $obj $var [expr [list [::nsf::var::set $obj $var] + 1]] + } + } + C create c2 + + ? {c2 eval {info exists :q}} 1 + ? {c2 cget -q} 100 + ? {c2 configure -q 101} "" + ? {c2 cget -q} "102" +} + +Test case configure-trace-object { + # + # object case with no default + # + + nx::Object create o + ? {o eval {info exists :A}} 0 + o object property A { + set :valuechangedcmd { + #puts stderr "o.A valuechangedcmd $obj $var +1" + ::nsf::var::set $obj $var [expr [list [::nsf::var::set $obj $var] + 1]] + } + } + # puts [o info object variables A] + # puts [o info variable parameter [o info object variables A]] + # puts [[o info object slots A] getParameterSpec] + + ? {o eval {info exists :A}} 0 + ? {o cget -A} {can't read "A": no such variable} + ? {o configure -A 1} "" + ? {o cget -A} "2" + + # + # object case with default + # + + ? {o eval {info exists :B}} 0 + o object property {B 1000} { + #puts stderr "o.B valuechangedcmd $obj $var +1" + set :valuechangedcmd {::nsf::var::set $obj $var [expr [list [::nsf::var::set $obj $var] + 1]]} + } + + ? {o eval {info exists :B}} 1 + ? {o cget -B} 1000 + ? {o configure -B 1001} "" + ? {o cget -B} 1002 } \ No newline at end of file Index: tests/parameters.test =================================================================== diff -u -r9cead8929011bb4dcc6c44630a91bc5d597520b2 -ra774481bc677369c7b0f7d1fcf3275ee1afd4fba --- tests/parameters.test (.../parameters.test) (revision 9cead8929011bb4dcc6c44630a91bc5d597520b2) +++ tests/parameters.test (.../parameters.test) (revision a774481bc677369c7b0f7d1fcf3275ee1afd4fba) @@ -1394,7 +1394,7 @@ ? {o eval {info exists :A}} 1 ? {o cget -A} 0 ? {o configure -A 1} "" - ? {o cget -A} 3 + ? {o cget -A} 2 # per-class: Class create Klass