Version 10 of Drag and Drop Notebook Tabs

Updated 2010-07-30 12:35:11 by wjg

CJB: An example of how to make drag and drop tabs with a ttk::notebook.


#Create notebook
set n [ttk::notebook .nb]
pack $n -fill both -expand 1

#Create tabs
foreach tab [list First Second Third] {
    set w [frame $n.[string tolower $tab]]
    label $w.src_lab -text "Clicked Tab Index:" -anchor e
    label $w.src_idx -textvariable src_index -width 3
    label $w.dst_lab -text "Released Tab Index:" -anchor e
    label $w.dst_idx -textvariable dst_index -width 3
    grid $w.src_lab $w.src_idx -sticky news
    grid $w.dst_lab $w.dst_idx -sticky news
    $n add $w -text $tab
}

#Bindings
bind all <KeyPress-question> {console show}
bind $n <ButtonPress>   [list click   %W %x %y]
bind $n <ButtonRelease> [list release %W %x %y]
bind $n <Motion>        [list motion  %W %X %Y]

#Sets index of tab clicked.
proc click   {W x y} {
    variable src_index [$W index @$x,$y]
    puts stderr "Clicked $src_index"
}

#Moves the tab to the position where it was dropped.
proc release {W x y} {
    variable src_index
    variable dst_index
    puts stderr "Released $src_index"
    #Check for a valid source
    if {[string is int -strict $src_index]} {
        set dst_index [$W index @$x,$y]
        #Check for a valid destination
        if {[string is int -strict $dst_index]} {
            set tab [lindex [$W tabs] $src_index]
            $W insert $dst_index $tab
            puts stderr "Insert $tab @ $dst_index"
        }
    }
}

#Passes mouse motion events to underlying widgets while dragging.
#Allows the notebook tabs to highlight on mouse-over.
proc motion  {W X Y} {
    set w [winfo containing $X $Y]
    if {$w ne $W && $w ne ""} {
        set x [expr {$X - [winfo rootx $w]}]
        set y [expr {$Y - [winfo rooty $w]}]
        event generate $w <Motion> -x $x -y $y
    }
}

peterc 2010-07-29: Looks good and it works well :) My only suggestion would be to change the cursor to a tab-like image during the drag (and change it back after button release) so the user has more visual feedback that they've picked up and are dragging the tab. Bonus points if you can include the tab title somehow :)


WJG (30/07/10) A similar behaviour is available with the gnoc::notebook widget. In addition to being able to reorganize widgets within a notebook, its also possible to drag and drop widgets within different notebooks in a common group. Here's the demo script.

# basic Tcl/Gnocl Script #!/bin/sh \ exec tclsh "$0" "$@"

package require Gnocl

set notebook1 [gnocl::notebook -scrollable 1 -tabPosition left -groupId 1 -onCreateWindow {

        puts "%w %w %x %y"
        }]

$notebook1 addPage gnocl::label -text "First Page" "%__First"

$notebook1 addPage gnocl::label -text "Second Page" "%__Second"

$notebook1 addPage gnocl::label -text "Third Page" "%__Third"

$notebook1 addPage gnocl::label -text "Fourth Page" "%__Fourth"

gnocl::window \

    -title "Notebook1" \
    -child $notebook1 \
        -x 100 \
    -y 100 \
    -width 200 \
    -height 100

set notebook2 gnocl::notebook -tabPosition right -groupId 1

$notebook2 addPage [gnocl::label \

    -text "Fifth Page"] "%__Fifth"

$notebook2 addPage [gnocl::label \

    -text "Sixth Page"] "%__Sixth"

gnocl::window \

    -title "Notebook2" \
    -child $notebook2 \
        -x 100 \
    -y 250 \
    -width 200 \
    -height 100

set notebook3 gnocl::notebook -groupId 2

$notebook3 addPage [gnocl::label \

    -text "Seventh Page"] "%__Seventh"

$notebook3 addPage [gnocl::label \

    -text "Eigth Page"] "%__Eigth"

gnocl::window \

    -title "Notebook3" \
    -child $notebook3 \
    -x 100 \
    -y 400 \
    -width 200 \
    -height 100