Updated 2005-05-21 21:07:33 by Googie

Googie (21.05.2005) - I've just created some Itcl-based modal widgets and I'd like to share them with others :) There are 2 widgets so far, meaby there'll be more... meaby someone else could add ones.

I know that there is tk_messageBox, but MsgBox looks better (I think so) under X11 and it's Tile-ready.

Modal window (it could be not-modal but callback-mode, optionally) grabs application input (with grab) and halts code execution at exec method call until user response in that window.

Here's example, what you can do with that widgets:
 package require Tk
 package require Itcl
 namespace import itcl::*
 source modal.tcl ;# this is base class for all modal widgets
 source msgbox.tcl
 source inputdialog.tcl

 MsgBox msg .question -message "Are you sure about that?" -title "Question" -buttons [list "Yes" "Not sure" "No" "I don't understeand"] -default 0
 # here some code, if you want
 switch -- [msg exec] {
     0 {
         puts "He/She wants!"
     }
     1 {
         puts "He/She isn't sure!"
     }
     2 {
         puts "He/She doesn't want!"
     }
     3 {
         puts "He/She doesn't understeand!"
     }
 }

 InputDialog dialog .input -message "Type some value" -title "Input dialog" -default "Some initial string"
 # here's some your custom code
 set ret [dialog exec]
 if {$ret != ""} {
     puts "Entered: $ret"
 } else {
     puts "Entered empty string or clicked cancel/close button"
 }

 proc test {button} {
     puts "Clicked button $button"
 }
 MsgBox msg .msg -modal 0 -command test -message "Just test"
 msg exec
 puts "This command is executed immediately, instead waiting for user respond in above MsgBox."

And here's code of 2 classes used above and the 3rd class needed by these 2:
 class Modal {
 	constructor {Path Args} {}
 	destructor {}

 	protected {
 		variable modal 1
 		variable command ""
 		variable path
 		variable parent ""
 		variable default 0
 		variable title "Modal"

 		method center {path {parent {}}}
 	}

 	public {
 		variable sleep

 		method exec {}
 		method clicked {btn}
 	}
 }

 body Modal::constructor {Path Args} {
 	set path $Path
 	foreach {opt val} $Args {
 		switch -- $opt {
 			"-modal" {
 				set modal $val
 			}
 			"-command" {
 				set command $val
 			}
 			"-title" {
 				set title $val
 			}
 			"-parent" {
 				set parent ""
 			}
 			"-default" {
 				set default $val
 			}
 		}
 	}
 }

 body Modal::destructor {} {
 	destroy $path
 }

 body Modal::center {path {parent {}}} {
 	update
 	if {$parent == ""} {
 		set sp [split $path .]
 		if {[llength $sp] > 2} {
 			set parent [join [lrange $sp 0 end-1] .]
 		} else {
 			set parent .
 		}
 	}
 	set sp [split [wm geometry $parent] +]
 	set px [lindex $sp 1]
 	set py [lindex $sp 2]
 	set wd [winfo reqwidth $path]
 	set ht [winfo reqheight $path]
 	set x [expr {$px+([winfo reqwidth $parent]-$wd)/2}]
 	set y [expr {$py+([winfo reqheight $parent]-$ht)/2}]
 	wm geometry $path +$x+$y
 }

 body Modal::clicked {btn} {
 }

 body Modal::exec {} {
 }

 class MsgBox {
 	inherit Modal

 	constructor {Path args} {
 		Modal::constructor $Path $args
 	} {}

 	private {
 		variable buttons "ok"
 		variable msg ""
 	}

 	public {
 		method exec {}
 		method clicked {btn}
 	}
 }

 body MsgBox::constructor {Path args} {
 	set title "MsgBox"
 	foreach {opt val} $args {
 		switch -- $opt {
 			"-message" {
 				set msg $val
 			}
 			"-buttons" {
 				set buttons $val
 			}
 		}
 	}
 }

 body MsgBox::exec {} {
 	toplevel $path

 	frame $path.u
 	pack $path.u -side top -fill both
 	label $path.u.l -text "" -font "helvetica 12 bold" -relief groove -bd 2
 	pack $path.u.l -side top -fill x -pady 0.1c -padx 0.2c
 	frame $path.d
 	pack $path.d -side bottom -fill x
 	frame $path.d.f
 	pack $path.d.f -side bottom

 	set i 0
 	foreach txt $buttons {
 		button $path.d.f.$i -text $txt -command "$this clicked $i"
 		pack $path.d.f.$i -side left -pady 3
 		incr i
 	}

 	bind $path <Destroy> "catch {$this configure -sleep $default}"

 	wm title $path $title
 	$path.u.l configure -text $msg
 	center $path $parent
 	update
 	wm resizable $path 0 0

 	if {$modal} {
 		if {$parent == ""} {
 			set sp [split $path .]
 			if {[llength $sp] > 2} {
 				set parent [join [lrange $sp 0 end-1] .]
 			} else {
 				set parent .
 			}
 		}
 		grab $path
 		vwait [scope sleep]
 		set retval $sleep
 		delete object $this
 		return $retval
 	}
 }

 body MsgBox::clicked {btn} {
 	if {$command != ""} {
 		eval $command $btn
 	} else {
 		set sleep $btn
 	}
 }

 class InputDialog {
 	inherit Modal

 	constructor {Path args} {
 		Modal::constructor $Path $args
 	} {}
 	destructor {}

 	private {
 		variable msg ""
 	}

 	public {
 		method exec {}
 		method clicked {btn}
 	}
 }

 body InputDialog::constructor {Path args} {
 	set path $Path
 	foreach {opt val} $args {
 		switch -- $opt {
 			"-message" {
 				set msg $val
 			}
 		}
 	}
 }

 body InputDialog::destructor {} {
 	destroy $path
 }

 body InputDialog::exec {} {
 	toplevel $path

 	frame $path.u
 	pack $path.u -side top -fill both
 	label $path.u.l -text "" -font "helvetica 12 bold" -justify left
 	pack $path.u.l -side top -fill x -pady 2 -padx 0.2c
 	entry $path.u.e
 	pack $path.u.e -side top -fill x -pady 0.1c -padx 0.2c
 	frame $path.d
 	pack $path.d -side bottom -fill x
 	frame $path.d.f
 	pack $path.d.f -side bottom

 	button $path.d.f.ok -text "Ok" -command "$this clicked ok"
 	pack $path.d.f.ok -side left -pady 3
 	button $path.d.f.cancel -text "Cancel" -command "$this clicked cancel"
 	pack $path.d.f.cancel -side left -pady 3

 	bind $path <Destroy> "catch {$this configure -sleep {}}"

 	wm title $path $title
 	$path.u.l configure -text $msg
 	center $path $parent
 	update
 	wm resizable $path 0 0

 	focus -force $path.u.e
 	$path.u.e insert end $default
 	$path.u.e selection range 0 end

 	if {$modal} {
 		if {$parent == ""} {
 			set sp [split $path .]
 			if {[llength $sp] > 2} {
 				set parent [join [lrange $sp 0 end-1] .]
 			} else {
 				set parent .
 			}
 		}
 		grab $path
 		vwait [scope sleep]
 		set retval $sleep
 		delete object $this
 		return $retval
 	}
 }

 body InputDialog::clicked {btn} {
 	if {$btn == "cancel"} {
 		set sleep ""
 		return
 	}

 	if {$command != ""} {
 		eval $command [$path.u.e get]
 	} else {
 		set sleep [$path.u.e get]
 	}
 }

[ Category Widget ]