Welcome, guest | Sign In | My Account | Store | Cart
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 ...

History

  • revision 3 (20 years ago)
  • previous revisions are not available