TIP 736: Scroll entry with mouse wheel

Login
Bounty program for improvements to Tcl and certain Tcl packages.
 Author:         Csaba Nemethi <csaba.nemethi@t-online.de>
 State:          Final
 Type:           Project
 Tcl-Version:    9.1
 Vote:           Done
 Created:        04-Nov-2025
 Keywords:       mouse wheel, touchpad, scroll, event, binding, entry
 Tk-Branch:      tip-736
 Vote-Summary:   Accepted 8/0/0
 Votes-For:      HO, JN, KW, SL, RA, MC, APN, AK
 Votes-Against:  none
 Votes-Present:  none

Abstract

This TIP proposes to add mouse wheel and <TouchpadScroll> event bindings for the widget classes Entry and TEntry. The binding scripts should scroll the entry or ttk::entry widget by invoking its xview scroll amount units subcommand.

Rationale

René Zaumseil proposed recently on Tcl Core List to add the following bindings to Tk:

bind Entry  <MouseWheel> {tk::MouseWheel %W x %D -40.0 units}
bind TEntry <MouseWheel> {tk::MouseWheel %W x %D -40.0 units}

The binding script above translates to

%W xview scroll [expr {%D/-40.0}] units

While it is surely a good idea to enable the user to scroll an entry or ttk::entry widget with the mouse wheel, the binding script proposed by René needs several adaptations so it can properly handle two-finger touchpad gestures, which on x11 give rise to intermixed <MouseWheel> and <Shift-MouseWheel> events. In addition, an extra binding for <TouchpadScroll> events is needed for handling two-finger gestures on win32 and aqua.

This TIP aims at adapting René's proposal in such a way that it works as expected with both the mouse wheel and the touchpad, on all three windowing systems.

Specification

The TIP proposes to add the following bindings to the file library/entry.tcl:

bind Entry <Enter> {+
    set tk::Priv(xWheelEvents) 0; set tk::Priv(yWheelEvents) 0
}
bind Entry <MouseWheel> {
    tk::EntryScrollByUnits %W y %D -40.0
}
bind Entry <Option-MouseWheel> {
    tk::EntryScrollByUnits %W y %D -12.0
}
bind Entry <Shift-MouseWheel> {
    tk::EntryScrollByUnits %W x %D -40.0
}
bind Entry <Shift-Option-MouseWheel> {
    tk::EntryScrollByUnits %W x %D -12.0
}
bind Entry <TouchpadScroll> {
    if {%# %% 5 == 0} {
        lassign [tk::PreciseScrollDeltas %D] tk::Priv(deltaX) tk::Priv(deltaY)
        if {$tk::Priv(deltaX) != 0} {
            %W xview scroll [expr {-$tk::Priv(deltaX)}] units
        }
    }
}

# ::tk::EntryScrollByUnits --
#
# Scrolls the entry by units.
#
# Arguments:
# w -           The entry window that received a mouse wheel event.
# axis -        x if the event contains the Shift modifier and y otherwise.
# amount -      Delta value from the mouse wheel event.
# factor -      $amount/$factor = number of scroll units for $w xview scroll.

proc ::tk::EntryScrollByUnits {w axis amount factor} {
    # Count both the <MouseWheel> and <Shift-MouseWheel>
    # events, and ignore the non-dominant ones
    variable ::tk::Priv
    incr Priv(${axis}WheelEvents)
    if {($Priv(xWheelEvents) + $Priv(yWheelEvents) > 10) &&
            ($axis eq "x" && $Priv(xWheelEvents) < $Priv(yWheelEvents) ||
             $axis eq "y" && $Priv(yWheelEvents) < $Priv(xWheelEvents))} {
        return
    }

    tk::MouseWheel $w x $amount $factor units
}

Further, create similar bindings for the widget class TEntry by adding the following lines to the file library/ttk/entry.tcl:

# Copy the mouse wheel event bindings from Entry to TEntry
#
bind TEntry <Enter> {+
    set tk::Priv(xWheelEvents) 0; set tk::Priv(yWheelEvents) 0
}
foreach event {<MouseWheel> <Option-MouseWheel>
               <Shift-MouseWheel> <Shift-Option-MouseWheel>
               <TouchpadScroll>} {
    bind TEntry $event [bind Entry $event]
}
unset event

Reference Implementation

A ready-to-use reference implementation can be found in the Tk branch tip-736. Besides the already mentioned files library/entry.tcl and library/ttk/entry.tcl, it contains also slightly modified versions of library/ttk/combobox.tcl and library/ttk/spinbox.tcl, needed because the widget classes TCombobox and TSpinbox inherit (and partly override) the TEntry bindings.

Copyright

This document has been placed in the public domain.

History