always on top

"How can I keep my toplevel on top?" is a FAQ. See http://tcl.sourceforge.net/faqs/tk/#wm/ontop for portable Tk solutions, and raise for related information.


Win32

MPJ - June 21, 2002

Now that topmost is supported in the Tcl core (8.4 and higher) for the MsWindows platform you can just use:

wm attributes (window) -topmost 1

The example below will cause the topmost setting to be toggled when the F3 key is pressed.

bind . <F3> "setontop"
proc setontop {} {wm attributes . -topmost [expr [wm attributes . -topmost] ^ 1]}


Miko Don't work for me. Wish is running but the window is invisible ( tested on Win 2K and XP). MPJ ~ Works fine for me on 95/98/2k/XP. To test run wish and type "wm attributes . -topmost 1" which will keep the wish window on top even if it does not have focus.


The API function needed is (see also [L1 ]):

  SetWindowPos(HWND hWnd,       // handle to window
               HWND hWndAfter,  // placement order handle
               int x, int y,    // position (horizontal, vertical)
               int cx, int cy,  // size (width, height) 
               UINT uFlags)     // positioning flags

The placement order handle can be either another HWND or once of the following constants:

  • HWND_BOTTOM (1) Moves to the bottom
  • HWND_NOTOPMOST (-2) Place above all non-topmost windows.
  • HWND_TOP (0) Move to the top
  • HWND_TOPMOST (-1) Move to the top and keep it there.

The Flags can be one of a number but for this page probably SWP_NOMOVE (2) and SWP_NOSIZE (1) are likely the most useful. So:

  SetWindowPos([winfo id .], HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE)

which turns out to be the code used by XRaiseWindow in win/tkWinWindow.c


Melissa Schrumpf recommends the GPLed WinCtl [L2 ] utility, which makes it possible to write

    exec WinCtlW.exe -id [wm frame .] topmost &

(or might there be more punctuation in the vicinity of the id?).


Martin Lemburg - 25th February 2002, 13:16 GMT

I found the original topmost package made by John Rosauer in 12.1997. It works only with tcl/tk v.8.0.

The URL is: ftp://ftp.dcade.de/pub/ml/tcl/packages/topmost10.zip [L3 ]

I created a stubs enabled topmost package, usable with tcl/tk 8.1.1 and higher.

The URL is:

ftp://ftp.dcade.de/pub/ml/tcl/packages/topmost20.zip [L4 ]

LES on March 30 2004: the two links above are broken

Usage:

   package require topmost 2.0;

   topmost widget ?boolean?

Much fun!

Martin Lemburg


MPJ - March 9th 2002

Using the topmost source as a guideline I wrote this tcl proc using Ffidl. Its usage is similar to the topmost package except that it does not want a widget name but the window title.

 load ffidl05.dll
 ffidl::callout dll_SetWindowPos     {int int int int int int int} int   [ffidl::symbol user32.dll SetWindowPos]
 ffidl::callout dll_GetWindowLong    {int int} int                       [ffidl::symbol user32.dll GetWindowLongA]
 ffidl::callout dll_FindWindowTitle  {int pointer-utf8} int              [ffidl::symbol user32.dll FindWindowA]
 # windowname is based on wm title
 # state is 1 to set or anything else to reset, empty returns the current state
 proc topmost {windowname {state ""}} {
        set hwnd [dll_FindWindowTitle 0 $windowname]
        if {$hwnd == 0 } {
                return "Not a valid top window"
        }
        set WS_EX_TOPMOST 0x8
        set GWL_EXSTYLE -20
        set TOPMOST -1
        set NOTOPMOST -2
        set SWP_NOACTIVATE 0x10
        set SWP_NOMOVE 0x2
        set SWP_NOSIZE 0x1
        set screenwidth [lindex [split [wm geometry .] x+] 0]
        set screenheight [lindex [split [wm geometry .] x+] 1]
        set result [expr ([dll_GetWindowLong $hwnd $GWL_EXSTYLE] & $WS_EX_TOPMOST )>>3]
        if {$state == "" } {
                return $result
        }
        if {$state == 1} {
                dll_SetWindowPos $hwnd $TOPMOST 0 0 \
                           $screenwidth $screenheight \
                           [expr $SWP_NOACTIVATE | $SWP_NOMOVE | $SWP_NOSIZE]
        } else {
                dll_SetWindowPos $hwnd $NOTOPMOST 0 0 \
                           $screenwidth $screenheight \
                           [expr $SWP_NOACTIVATE | $SWP_NOMOVE | $SWP_NOSIZE]
        }
        return [expr ([dll_GetWindowLong $hwnd $GWL_EXSTYLE] & $WS_EX_TOPMOST )>>3]
 }

Usage:

 topmost [wm title .] ?boolean?


UNIX Systems

The -topmost attribute is also supported there provided you've got Tk 8.5 (and provided your WM takes notice, which most do).

wm attribute .theWindow -topmost 1

MGS 2003/03/26 - For non-Windows systems, you could something like this:

 proc ontop {W boolean} {
   set bindtags [bindtags $W]
   set index [lsearch $bindtags bind$W]
   if { $boolean } {
     if { $index == -1 } {
       bindtags $W [linsert $bindtags 0 bind$W]
       bind bind$W <Visibility> [list ontop:state %W %s]
     }
   } else {
     if { $index != -1 } {
       bindtags $W [lreplace $bindtags $index $index]
       bind bind$W <Visibility> {}
     }
   }
 }
 
 proc ontop:state {W state} {
   puts "ontop \[$W\] \[$state\]"
   if { ![string equal [winfo toplevel $W] $W] } {
     puts "  window \[$W\] is not a toplevel"
     return
   }
   if { ![string equal $state VisibilityUnobscured] } { raise $W }
 }
 
   # demo code
   toplevel .t
   ontop .t 1
 
   checkbutton .top \
     -text "On Top" \
     -variable ontop(.) \
     -command "ontop . \$ontop(.)"
 
   button .exit -text exit -default active -command exit
 
   pack .exit -side bottom -anchor se
   pack .top  -side top    -padx 50 -pady 50

which will raise a window whenever it is obscured. But beware, if you have more than one window, you can have multiple windows fighting over who gets to be on top (if they overlap).


TFW - Jan 9, 2003

I find it convienent to add a checkbox to the system menu to toggle always on top on/off.


Related topic:

    wm transient $higher_widget $lower_widget

keeps $higher_widget on top of $lower_widget. Sometimes. As DKF explains, "This doesn't actually enforce it, it just gives a strong hint to the window manager (or OS, depending on platform) that the child is an agent acting on behalf of the parent. Most WMs interpret this as meaning keep it on top of its parent and decorate it like a dialog, but not all. However, that's usually good enough anyway."

For more on the subject, see "Modal dialogs".


SRT - Mar 30, 2004

KDE and probably GNOME-compliant window managers have support for the _NET_WM_STATE_STAYS_ON_TOP porperty. The perl/Tk code below may be used to set this property on toplevel windows. Unfortunately, a direct translation to Tcl/Tk is not possible as Tcl/Tk has no support for the "property" method. Nevertheless it would be nice if wm attributes -topmost had similar support builtin.

 sub keep_on_top {
     my $w = shift;
     my($wrapper) = $w->toplevel->wrapper;
     eval {
         if (!grep { $_ eq '_NET_WM_STATE_STAYS_ON_TOP' } $w->property('get', '_NET_SUPPORTED', 'root')) {
             die "_NET_WM_STATE_STAYS_ON_TOP not supported";
         }
         $w->property('set', '_NET_WM_STATE', "ATOM", 32,
                      ["_NET_WM_STATE_STAYS_ON_TOP"], $wrapper);
     };
     if ($@) {
         warn $@;
         0;
     } else {
         1;
     }
 }

DKF: We don't support a property method because it's too easy to cause damage with it. We prefer a small extension for the task. OTOH, the above is useful grist to the mill of ongoing wm attributes development.

DKF: And we support it in Tk 8.5.


For ptk, the natural solution is Tk:StayOnTop [L5 ]. Chris Whiting gives this [L6 ] example of its use.