package provide MegaWidget 1.0
proc MegaWidget { hWnd } {
variable widgetClasses
# Get the namespace for the mega-widget from the caller
set NS [uplevel namespace current]
# If the widget has already been turned into a mega-widget, just insert
# the new namespace into the top of the search list and return.
if {[info exist widgetClasses($hWnd)]} {
set widgetClasses($hWnd) [linsert $widgetClasses($hWnd) 0 $NS]
return
}
# The widget has yet been turned into a mega-widget. Store the
# caller's namespace as the first in the search list.
set widgetClasses($hWnd) $NS
# Rename the widget command to something in this procedure's namespace
# so that calls to the widget command are not sent to the widget directly.
rename ::$hWnd [namespace current]::mega$hWnd
# Set up binding to clear the search list for the widget and delete
# the replacement procedure for the widget command. Make sure that
# the widget generating the event is the same as the widget that was
# turned into a mega-widget: this allows a toplevel to be turned
# into mega-widget too (otherwise, it will get <Destroy> events from
# child windows).
set template {
if {[string match %W @HWND@]} {
namespace eval @MYNS@ array unset widgetClasses %W
rename %W {}
}
}
regsub -all {@HWND@} $template $hWnd template
regsub -all {@MYNS@} $template [namespace current] template
bind $hWnd <Destroy> $template
# Create a new top-level procedure with the same name as the widget.
# This procedure will scan through the search list for a namespace
# containing a procedure by the same name as the first argument passed
# to this new procedure.
set template {
global errorInfo errorCode
variable widgetClasses
set hWnd @HWND@
foreach NS $@MYNS@::widgetClasses($hWnd) {
if {[namespace inscope $NS info proc $command] == $command} {
set rc [catch { uplevel [set NS]::$command $hWnd $args } result]
set ei $errorInfo
set ec $errorCode
break
}
}
if {![info exist rc]} {
set rc [catch { uplevel @MYNS@::mega$hWnd $command $args } result]
set ei $errorInfo
set ec $errorCode
}
return -code $rc -errorinfo $ei -errorcode $ec $result
}
regsub -all {@HWND@} $template $hWnd template
regsub -all {@MYNS@} $template [namespace current] template
proc ::$hWnd { command args } $template
}
# Example:
#
# namespace eval MyWidget {
# proc MyWidget { hWnd args } [
#
# # Main frame for the mega-widget
#
# frame $hWnd
#
# # ... other widgets created/packed in the main frame
#
# # Turn created frame into a mega-widget.
#
# MegaWidget $hWnd
# return $hWnd
# }
#
# proc dosomething { hWnd args } {
# # ...
# }
#
# proc dosomethingelse { hWnd args } {
# # ...
# }
# }
#
# MyWidget::MyWidget .mw
# .mw dosomething -option value ...
# .mw dosomethingelse -option value ...