Pragmatic use of Pernicious Popups

This page was started on Saturday, July 13, 2002.

Rohan Pall writes about his short but lively experience with tk_popup:

Why do I care about this?

My ultrat (ultra table) package makes it easy for me to quickly make auto-scrolled tables that have a cool way of hiliting the item under the mouse. I also wanted a popup when the right-button is clicked over an item, like you've prolly seen in lots of progs. The problem was that tk_popup is inconsistent on different platforms. So I worked around it. Now I'm showing you what I figured out in the hopes that it helps y'all. And this way I'll remember a year from now what I was doing ;)

A lucid description of the issues

Here's another guy struggling with the same prob I had! Nice description, worth a read. [L1 ]

Platform diffs

I've noticed some diffs with tk_popup on Windows and on Unix, specifically on Windows98 and on Linux. I use $::tcl_platform(platform) to test what platform I'm on, i.e. "windows" or "unix".


  • The <Unmap> binding isn't fired when the popup dissapears, which includes when the user doesn't select one of the menu items.
  • All processing of the script stops as soon as the popup is displayed. The script continues where it left off after the popup dissapears.

I'm assuming this happens because the native windows menu widget is being called, and not a Tk toplevel. All I know for sure is that I have observed this widget phenomena.


  • Here the <Unmap> binding actually gets fired when the popup dissapears.
  • Processing of the script continues after the popup is shown. The rest of the script doesn't wait for the popup to dissapear.

This inconsistent behavior causes problems when doing cool things with The Cool Language.

Code that shows you the prob, bob

Run this code on Windows and Unix. Notice how as soon as the popup is displayed on unix, the message that the script is resuming is displayed. See how on windows this only happens after the popup disappears (is dismissed, whatever).

  set noob [label .noob -text noob -bg purple -fg red]
  pack $noob

  set t [text .t -height 20 -width 70 -wrap none]
  pack $t -fill both -expand 1

  proc ins {msg} {
    global t
    $t insert end \
      "[clock format [clock seconds] -format %T]  $msg\n"

  set m [menu .m -tearoff 0]
  $m add command \
    -label Party! \
    -command [list ins "Noob Party!"] \
    -underline 0

  bind $noob <Button-3> [list showpopup %X %Y]

  proc showpopup {X Y} {
    global m t
    tk_popup $m $X $Y
    ins "script resumes now after tk_popup command..."

So how do I fix this!?

Pretty simple. Well umm it took me a while to figure it though ;)

So I want the item that was right-clicked on to stay hilited when showing the popup. If the mouse moves over any other item, I don't want the hilited item to change. This is because the user doesn't remember what item was clicked on -- jeez, I am the user and I can't remember much for long!

So here's the algo:

The item gets right clicked:

  • Hilite that item and suspend hiliting (use a namespace'd bool).
  • On windows, you just need to re-enable hiliting after the tk_popup command. But wait! On unix you can't do that, since as soon as the popup is shown, tk_popup returns! So for unix, you bind to <Unmap> and re-enable hiliting in the binded proc.

Ta da!

I hope you enjoyed your stay ;)

LV a note recently posted on comp.lang.tcl suggests that if you no longer want the native behavior, you could rename the command and then source in the Tcl script implementing the Unix behavior. Some people prefer to have cross platform identical look and feel and behavior, while others prefer the native approach, even when it means that things don't work the same across platforms.

Vince: surely the fact that the 'unmap' binding isn't called is simply a bug in WinTk and should be reported on sourceforge so a maintainer can perhaps fix it...

Ro: Yeah, I agree, the <Unmap> event really should be fired on Windows. But thats not the only difference. Witness how in unix the script continues after the popup is shown, which is not what happens in Windows.

Vince: and that indeed is another bug (at the very least it should be documented as a platform-difference, and preferably fixed for consistency). Please do report these problems a bugs to the tktoolkit project on sourceforge.

Ro: July 22, 2002 - Done [L2 ]. Thanks Vince.


  #!/usr/bin/wish -f

  # tk_popup at <ButtonPress-1> eats <ButtonRelease-1> !!!!
  # tk8.4, tested at 2 Linux boxes                     !!!!

  canvas .cv -height 200 -width 200
  .cv create rectangle 1 1 200 200 -fill red
  pack .cv -fill both  -expand yes

  .cv bind all <ButtonPress-1>   {puts <ButtonPress-1>; create_menu %X %Y}
  .cv bind all <ButtonRelease-1> {puts {<ButtonRelease-1> never put}}

  #Variant2: (same Problem)
  #bind .cv <ButtonPress-1>   {puts <ButtonPress-1>; create_menu %X %Y}
  #bind .cv <ButtonRelease-1> {puts {<ButtonRelease-1> never put}}

  #Variant3: (same Problem) bind all <ButtonPress-1>   {puts <ButtonPress-1>; create_menu %X %Y}
  #bind .cv     <ButtonRelease-1> {puts {<ButtonRelease-1> never put}}

  #Variant4: (same Problem)
  #bind .cv     <ButtonPress-1>   {puts <ButtonPress-1>; create_menu %X %Y} bind all <ButtonRelease-1> {puts {<ButtonRelease-1> never put}}

  proc create_menu {X Y} {
      puts "Creating the menu"
      catch { destroy .cv.m }
      menu .cv.m -tearoff 1
      .cv.m add command      -label "Something"  -command "puts Something"

      tk_popup .cv.m $X $Y
      puts "Menu popped up"
      #... and <ButtonRelease-1> gone