Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

DragToScroll - Universal Drag & Fling/Flick Scrolling


  • Please log in to reply
210 replies to this topic
cheek
  • Members
  • 71 posts
  • Last active: Jun 21 2014 03:24 AM
  • Joined: 01 Apr 2009
DragToScroll.ahk allows you to scroll any window or control by right clicking (RButton) and dragging the mouse. It works very similarly to the "Hand Tool" in Adobe products, such as Adobe PDF Reader.
[*:19ebmxny] Scroll both horizontally and vertically. Works in Firefox, IE, Chrome, Text Editors, Explorer, even controls within scrollable document like EditBoxes, ListViews and DropDownLists.
[*:19ebmxny] Works with background windows. Scrolls whatever you drag.
[*:19ebmxny] Will not interfere with normal RButton behavior. Only activated when clicking and dragging.
[*:19ebmxny] Control+RButton forces normal behavior
[*:19ebmxny] Great for reading or scrolling long documents. Click and drag slowly to follow along as you read, or quickly drag across the screen to jump to the end or anywhere in between.
[*:19ebmxny] Customizable! Properties for scrolling method, speed, acceleration, etc.
[*:19ebmxny] Perfect for laptops, netbooks, notebooks, or a portable mouse without a scroll wheel or middle button.
[*:19ebmxny] Grab and fling; scrolling momentum. Drag any window like TouchFlo, iPhone or Android
Download v2.4 (~250 kB)I may post updates or new info below, but this first post will always contain the updated 'trunk' version. Check back here for updates! Enjoy, and Let me know if you have any questions.

Thanks to the recent contributors & those reporting bugs. It's really satisfying to see a little project like this continue to grow, and to see people are finding use in it. Please feel free to post questions or ideas; i'd love to hear them! :)




[*:19ebmxny] DragDelay is the minimum time to hold RButton before scrollingIncrease this value if you are having trouble getting normal behavior out of the button; this allows you more time to click and release, which sends the normal click behavior.[*:19ebmxny] Confine to target immediately stops drag/scrolling if mouse leaves target window or control
[*:19ebmxny] Changing the Scroll Method may yield different results with different applications; see below for more on the scroll method.
[*:19ebmxny] SpeedY/SpeedX globally adjusts speed of scrolling. 1.0 is default, .5 would slow scrolling by 50%, 1.5 would increase by 50%.
[*:19ebmxny] New Feature: Momentum!Recent updates adds momentum to your dragging and scrolling. If you drag with sufficient speed and release the mouse, the window will scroll slowly to a stop. I'm pretty pleased with the results, and am kind of surprised I hadn't thought of it before. I've been working on & using momentum for the past few days, and think I have the settings in a place that feels fluid and easy to use; you may still wish to adjust the Inertia or StopSpeed. To work properly & fluidly, I was also forced to change the DragThreshold to 0, instead of the previous chunking behavior with 5. This means that overall, scrolling should be smoother (specifically with the default WheelMessage mode), but now you can drag so slow as to not scroll at all; feel free to adjust it.

Momentum does not work well with Smooth Scrolling; a feature found in both FireFox and IE. Smooth Scrolling is generally processor intensive and can cause slow scrolling on older machines, webpages with lots of video, etc. This script sends a lot of scroll messages, which can yield sluggish results. This may just be related to CPU power, but I always disable smooth scrolling anyway; I'd recommend you do the same, in your webbrowser options menu :) [*:19ebmxny] UseMovementCheck will only start the drag if you click-hold and move the mouse. Clicking and holding while stationary will simply hold the binding button down.
[*:19ebmxny] Settings & per-app settingsDragToScroll saves your settings in an INI file, so that your preferences are persisted between versions of the script.
DtS also allows you to customize these settings for individual applications, meaning you can bring compliance to non-standard apps, or enable features only for specific applications.[/list]


v1.0 Jun.28.2010
[*:19ebmxny] First release
[*:19ebmxny] Adds simple scroll acceleration to mirror mouse move speed

v1.1 Jun.30.2010
[*:19ebmxny] General Cleanup, Efficiency improvements
[*:19ebmxny] Better scroll implemetations, full choice of method
[*:19ebmxny] Added scrolling by WM_MOUSEWHEEL

v1.2 Jul.1.2010
[*:19ebmxny] Updated options menu
[*:19ebmxny] Increased precision in WM_MOUSEWHEEL
[*:19ebmxny] Added Nonlinear acceleration

v1.3 Jul.09.2010
[*:19ebmxny] Adds separable settings for vertical and horizontal scroll methods
[*:19ebmxny] New defaults: WheelMessage for Vertical, ScrollMessage for Horizontal
[*:19ebmxny] Better compatibility for horizontal scrolling

v1.5 Sept.15.2010
[*:19ebmxny] Code & docs cleanup, formatting fixes
[*:19ebmxny] Clean up, and inclusion of recent contributions (thanks all!)
[*:19ebmxny] Enable/Disable scrolling
[*:19ebmxny] User-defined RButton double-click action
[*:19ebmxny] Timer based hotkey activation
[*:19ebmxny] Tray Icons, if set & they exist
[*:19ebmxny] Edge Scrolling
[*:19ebmxny] Usage of 'Critical' hotkeys should finally end unintended drag-sticking

v1.6 Sept.17.2010
[*:19ebmxny] Bugfixes & Cleanup
[*:19ebmxny] Edge Scrolling update; works with all windows, not just active
[*:19ebmxny] Added Scrolling Momentum!!

v1.7 Sept.21.2010
[*:19ebmxny] Now Includes embedded icons
[*:19ebmxny] Update to the tray menu
[*:19ebmxny] Invertable drag

v1.8 Oct.11.2010
[*:19ebmxny] Toggle slow mode (new double-click action)
[*:19ebmxny] Configurable selection of Hotkey
[*:19ebmxny] Movement Checking

v1.9 Oct.20.2010
[*:19ebmxny] Rolls up beta releases & new features
[*:19ebmxny] Lots of bugfixes and cleanup
[*:19ebmxny] "Full" disable
[*:19ebmxny] User Settings saved in INI
[*:19ebmxny] Per-App customizations

v2.0 Oct.21.2010
[*:19ebmxny] Automatic update check
[*:19ebmxny] Retroactive version numbers (yay)
[*:19ebmxny] Menu item for opening settings INI
[*:19ebmxny] Adds more per-app settings

v2.1 Oct.27.2010
[*:19ebmxny] Automatic activation of windows for WheelKey method
[*:19ebmxny] Able to "catch" the scrolling window and re-drag
[*:19ebmxny] Supports infinite momentum (i.e. zero friction)
[*:19ebmxny] Per-app settings : invert drag
[*:19ebmxny] Per-app settings : full .exe process path (WinXP or later)
[*:19ebmxny] Tweak of default settings and app polish
[*:19ebmxny] Now Includes compiled version

v2.2 Nov.04.2010
[*:19ebmxny] New Settings GUIs
[*:19ebmxny] Reworking of settings and menus
[*:19ebmxny] Limits Update Check trips during reload

v2.3 Feb.15.2011
[*:19ebmxny] NEW Gesture support for flicking, scrolls one page
[*:19ebmxny] NEW Change the cursor while dragging
[*:19ebmxny] Bugfix in Momentum while using InvertDrag
[*:19ebmxny] Update to widen All Settings GUI; getting too tall.

v2.4 Apr.08.2011
[*:19ebmxny] Updated Gesture Defaults - Back/Forward for browsers, Adobe Reader
[*:19ebmxny] Compatibility fixes - FFX4, VS2010, Apps w/o control IDs
[*:19ebmxny] Bugfix in mouse cursor changing
[*:19ebmxny] Highlighting for non-default values in All-Settings



Wicked
  • Members
  • 504 posts
  • Last active: Nov 18 2018 02:17 AM
  • Joined: 07 Jun 2008
Very cool! Thank you for sharing!

irkregent
  • Members
  • 1 posts
  • Last active: Jun 30 2010 07:42 PM
  • Joined: 30 Jun 2010
This is very slick. I've been bummed at the lack of 64-bit window support by MouseImp, and this solves that problem.

The first thing I noticed, however, is that the scrolling can continue even after I stop the mouse. Can I change that by altering some of the values in the script?

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
Nice!
Horizontal scrolling does not work for me in many applications (FireFox, MS Word...), but it works in MotePad++, Internet Explorer...

cheek
  • Members
  • 71 posts
  • Last active: Jun 21 2014 03:24 AM
  • Joined: 01 Apr 2009

The first thing I noticed, however, is that the scrolling can continue even after I stop the mouse. Can I change that by altering some of the values in the script?


I have noticed this behavior on occasion as well, though it generally only rarely happens to me. I haven't been able to track down anything specific that would cause this behavior. It will sometimes get stuck in the wrong state (and keep scrolling) if you click the right mouse button repeatedly very quickly... I have also found that another few clicks of the right button generally resets things and restores proper functionality.

I made several improvements today that may help; I'd recommend you replace your script with the updated source above. Let me know if you still see the behavior, and any specific patterns for when it happens, that you might be able to identify.


Horizontal scrolling does not work for me in many applications (FireFox, MS Word...), but it works in MotePad++, Internet Explorer...


Scrolling is a bit tricky, because there are a few different ways to request that an application scroll. The exact "target" of a scroll request is important (e.g. a window within a window), and sometimes scrolling means different things to different apps (think google maps, or photoshop, or audio editing). Horizontal scrolling is even less standardized, as some applications simply do not scroll horizontally, or do not adhere to the standards in regards to MS-Windows scrolling.

The latest update includes a set of options to choose which method you would like to use to scroll. I have tried the options with various applications, and decided to let you have the choice. You should experiment with the different methods to see which works best for the applications you use. Personally I am using "WheelMessage" to try it out for a while; this is also the new default. "WheelKey" is probably the next best bet for general use, though it seems horizontal scrolling may not work for some people or applications.

See my next post for a comparison of the methods.

Wicked - Guest
  • Guests
  • Last active:
  • Joined: --
Came at a perfect time. My scroll wheel's decided to stop working when I scroll it so I set this to when I hold down the MButton. :D.

TY again! :).

cheek
  • Members
  • 71 posts
  • Last active: Jun 21 2014 03:24 AM
  • Joined: 01 Apr 2009
Just posted an update to this script to allow separate scroll method settings for vertical and horizontal scrolling. The new defaults use WheelMessage for vertical scrolling and ScrollMessage for horizontal. WM_HSCROLL seems to yield the best results for windows with scrollbars.

IE, Word, etc, still refuse to cooperate with horizontal scrolling, but this update brings compatibility to a lot more applications, that respect WM_HSCROLL.

Stay tuned for more updates!


edit Sep.21.2010--
On the different scroll methods


There are a few different ways to attempt to 'scroll' a window, and even more scrolling usages of the wheel than just to scroll a window scrollbar. The biggest problem with a script like this tends to be wide compatibility, so that it works for everyone in every app they use. This is a difficult, and nearly impossible task to accomplish, so it has been on my mind since I started writing this.

Drag To Scroll offers three different ways to scroll a window; depending on how you use this and with which applications, you may find one of the scroll methods to work better than another. I have attempted to make the experience identical, regardless of which method you choose, but you may need to tweak some of the tolerances & settings. The defaults I have chosen seem to work well across most applications, with a few exceptions.

Scroll Method
[*:1d1x3nxu] Set the method by setting varibales ScrollMethodY and ScrollMethodX
[*:1d1x3nxu] choose which method to use for each direction. One of:
[*:1d1x3nxu] mWheelKey
[*:1d1x3nxu] Simulates a "Mousewheel" key stroke
[*:1d1x3nxu] AHK help: "WheelLeft/Right require v1.0.48+, but have no effect on operating systems older than Windows Vista."
[*:1d1x3nxu] Probably the most compatible, though brutish.
[*:1d1x3nxu] Works without scroll bars; for example, scrolling application menus, the XP start menu, changing volume in media players, etc.
[*:1d1x3nxu] Only can scroll controls if hovering over them, and ONLY in the active window (aaccording to default windows behavior). Scroll is sent to control under the mouse, which can change when dragging mouse around.
[*:1d1x3nxu] mWheelMessage
[*:1d1x3nxu] Sends WM_MOUSEWHEEL message to the target application
[*:1d1x3nxu] Always scrolls the control under the mouse when first clicking RButton
[*:1d1x3nxu] Allows you to scroll background windows without activating
[*:1d1x3nxu] The default. Works best for standard scrolling windows, should be smooth reliable scrolling.
[*:1d1x3nxu] Works best with momentum
[*:1d1x3nxu] mScrollMessage
[*:1d1x3nxu] Sends WM_VSCROLL/WM_HSCROLL message to the target application
[*:1d1x3nxu] Sometimes works in applications where other methods do not (e.g. Visual Studio)
[/list][/list]

LC1207h
  • Members
  • 1 posts
  • Last active: Aug 18 2010 12:50 AM
  • Joined: 18 Aug 2010
Does this script work in Win 7 64-bit? It doesn't seemt o work for me. Was wondering if it only works in 32-bit environments.

  • Guests
  • Last active:
  • Joined: --
Yes, I use it every day at work on my x64 win7 machine. The more likely cause is that you're using it on an application which does not respect the selected/default scrolling method.

As I mention above, different applications handle scrolling differently, so it can be tricky to choose a method which covers all your needs (for the apps you use.) Fortunately, the defaults work in nearly all the apps I use regularly, and seem to offer the best coverage.

I would suggest you:
- Right click on the systray icon and use the settings menu to temporarily change the scroll method, to see if that fixes your issue.
- Report back on WHICH application(s) you are having trouble scrolling

Also,
I have considered adding the ability to choose a scrolling method per application/control, which would allow you to cover edge cases or applications with otherwise strange handling. I have not seen a huge need for this yet, but anyone feel free to chime in if it is needed, and I will see about adding it.

Unambiguous
  • Members
  • 16 posts
  • Last active: Mar 10 2011 06:42 PM
  • Joined: 18 May 2005
Hi,

Been using your script a lot and added a few things to work better for me. I added sustained scroll so that when your mouse goes to the edge of the screen, it will continue scrolling at a set speed (useful for very long documents)

EdgeScroll := 12
Threshold = 5  
;Calculate/Scroll - X
DiffX := Abs( NewX - OldX )
If (DiffX > Threshold)
{
	GoSub HScroll
	if NewX == 0 
	{
		NewX := EdgeScroll
	}
	if (NewX == A_ScreenWidth - 1)
	{
		NewX := A_ScreenWidth - EdgeScroll
	}
	OldX := NewX
}

;Calculate/Scroll  - Y
DiffY := Abs( NewY - OldY )
If (DiffY > Threshold)
{
	GoSub VScroll
	if (NewY == 0 )
	{
		NewY := EdgeScroll
	}
	if (NewY == A_ScreenHeight - 1)
	{
		NewY := A_ScreenHeight - EdgeScroll - 1
	}
	OldY := NewY
}


Romuald
  • Members
  • 1 posts
  • Last active: Aug 21 2010 08:52 AM
  • Joined: 21 Aug 2010
What about MS Office programs?
Report, if you found a decision problem of horisontal scrolling

Deep-Silence
  • Members
  • 89 posts
  • Last active: May 03 2015 05:54 AM
  • Joined: 24 Apr 2009
Thanks, that is awesome.
solved my "Laptop-mouse without Wheel-Problem" ;)

Does not work with Opera.

  • Guests
  • Last active:
  • Joined: --
I have added a few features to the script, as well as documenting its inner workings in excruciating detail for everyone's benefit. Enjoy!

For best results, view in SciTE4AutoHotKey.

Changes:
*Code documented
*Mouse routines replaced
*Added custom system tray icon (just put 2 icons labelled "scrollwheel.ico" and "scrollwheeldisabled.ico" in the script's directory).
*Added option to enable and disable drag-and-scroll functionality via tray menu or double-right-clicking.
; DragToScroll.ahk
;
; Scroll any active window by clicking and dragging with
; the right mouse button. Should not interfere with normal
; right clicking.
;
;
; Changes:
; Jun.28.2010
;  * First release
;  * Adds simple scroll acceleration to mirror mouse move speed
;
; Jun.30.2010
;  * General Cleanup, Efficiency improvements
;  * Better scroll implemetations, full choice of method
;  * Added scrolling by WM_MOUSEWHEEL
;
; Jul.1.2010
;  * Updated options menu
;  * Increased precision in WM_MOUSEWHEEL
;  * Nonlinear acceleration
;
; Jul.09.2010
;  * Adds separable settings for vertical and horizontal scroll methods
;  * New defaults: WheelMessage for Vertical, ScrollMessage for Horizontal
;  * Better compatibility for horizontal scrolling
;
; Aug.31.2010
;  *Code documented
;  *Mouse routines replaced
;  *Added custom system tray icon (just put 2 icons labelled "scrollwheel.ico" and "scrollwheeldisabled.ico" in the script's directory).
;  *Added option to enable and disable drag-and-scroll functionality via tray menu or double-right-clicking.

DoubleClickThreshold := DllCall("GetDoubleClickTime")
DragDelay := DoubleClickThreshold ;in ms
PollFrequency = 20    ; in ms
ConfineToTarget := false

; scroll method
;  mWheelKey - Simulate actual mouse wheel movement
;  mWheelMessagee - Send messages WM_MOUSEWHEEL to the target control
;  mScrollmessage - Send messages WM_HSCROLL and WM_VSCROLL
mWheelKey := "WheelKey"
mWheelMessage := "WheelMessage"
mScrollMessage := "ScrollMessage"
;
; choose one of above
; WheelMessage & WheelKey are preferred; your results may vary
ScrollMethodX := mScrollMessage
ScrollMethodY := mWheelMessage


; Speed & acceleration
Threshold = 5          ; in pixels
MaxAcceleration := 10  ; in levels, roughtly 1-10 suggested, 0 to disable
SpeedX := 1.0          ; as a multiplication constant
SpeedY := 1.0          ; as a multiplication constant

;What the acceleration function does is to precompute values of the acceleration function at 
;certain points along the function and store them into a 1D array for easier retrieval.

; acceleration function -- modify carefully!!
;  default is a pretty shallow parabolic curve
Loop, %MaxAcceleration%
   Accel%A_Index% := .006 * A_Index **2 + 1


; Constants
;--------------------------------
WM_HSCROLL = 0x114
WM_VSCROLL = 0x115
WM_MOUSEWHEEL = 0x20A
WM_HMOUSEWHEEL = 0x20E
WHEEL_DELTA = 50
SB_LINEDOWN = 1
SB_LINEUP = 0
SB_LINELEFT = 0
SB_LINERIGHT = 1
;DragStatus
DS_NEW = 0
DS_DRAGGING = 1
DS_HANDLED = 2


; Init
;--------------------------------
OldY := ""
OriginalX := ""
OriginalY := ""
DragStatus := DS_NEW


;Mouse Routine Init
;--------------------------------
TimeOfLastRButtonDown := 0
TimeOf2ndLastRButtonDown:= 0
TimeOfLastRButtonUp := 0
ScrollDisabled := 0

Gosub MenuInit
Return


; Hotkeys
;--------------------------------
;I have torn out and replaced the old mouseclick detection routines with a more robust model (i.e. it works for me, whereas the old one didn't).
;
;Explanation of the hotkey routines:
;The routines are run every time the mouse button is pressed and released.
;When pressed, we log the time that it was pressed at; when released, we also log the time it was released.
;We keep a memory of the last 2 mouse button presses that have occurred, in a buffer of sorts, to help us detect double-clicks.
;We only keep a memory of the last button release that has occurred, and not the last 2 releases, because that would be unnecessary.

RButton::
Critical 
;This is to force the RButton Down thread to be attended to before the RButton Up thread, so that a click (press-release event) is properly registered as a click. 
;If not, a rapid click could cause the Button Up event to be processed before the Button Down event, thanks to AHK's pseudo-multithreaded handling of hotkeys.

   TimeOf2ndLastRButtonDown := TimeOfLastRButtonDown    ;Move the time of the 2nd last button press event down a space to make room for the latest button press event.
                                                        ;The stack has only 2 spaces; even older values are discarded. 
   TimeOfLastRButtonDown := A_TickCount                 ;Push the time for the latest button press onto the stop of the stack. 
   
   DragStatus := DS_NEW                                 ;For compatibility with the original code.
   
   If (ScrollDisabled != 1)                             ;These lines check if the user has disabled the drag-and-scroll function.
      SetTimer, DragCatcher, % -1 * DragDelay           ;If not, it turns on the hold/drag watchdog timer to detect drags.
Return

RButton Up::
Critical
   TimeOfLastRButtonUp := A_TickCount                   ;Log the time of the latest button release event.
   SetTimer, DragCatcher, Off                           ;Stop the hold/drag watchdog timer, because we're not dragging anymore (if we were to begin with).
   GoSub, DClickTest                                    ;Test if this was the 2nd click of a double-click.
   
   DragStatus := DS_HANDLED                             ;For compatibility with the original code.
Return

; Implementation
;--------------------------------

;This entire subroutine is executed once the mouse button has been held for a certain threshold.
DragCatcher:
   DragStatus := DS_HANDLED     ;Set DragStatus to show that a drag has been registered and being handed over to the drag-and-scroll routines.
   GoSub DragStart              ;Start the drag-and-scroll routines.
Return

DClickTest:
;Here, we assume that if the mouse button was released, then it had to be pressed down to begin with (reasonable?).

;We then check the time of the 2nd last button press event. (So, to recap, the mouse went press-release-press-release, and we are checking the 2nd last press).
   DClickCalc := TimeOfLastRButtonUp - TimeOf2ndLastRButtonDown

;If the time difference between the 2nd last button press event and the latest button release is less than the DoubleClickThreshold, then we treat it as a double-click.
   If (DClickCalc <= DoubleClickThreshold)
   {
      ;The functions to be performed upon a double-click go here. I've set it to toggle the drag-and-scroll function.
      
      ScrollDisabled := !ScrollDisabled ;Toggle the enable status of the drag-and-scroll function.
      GoSub mnuCheckEnable              ;Update the tray icon and settings menu to reflect the status.
   }

;If not, then it is treated as a single click, or the end of a hold.
;Note, however, that the first click of a double-click is always treated as a single click first, i.e. if you were to log the events it would be
;   <Time of first click> -- (Single Click)
;   <Time of 2nd click> -- (Double Click)
;because the time lag between the first click of the double-click and the previous single click would be longer than the DoubleClickThreshold.
;
;A solution to eliminate this would be to wait until the DoubleClickThreshold has passed, then process the event as a single click.
;For me, this solution introduces unacceptable lag, making single clicks take longer to register than double clicks, which is weird.

   If (DragStatus = DS_NEW)         ;If this was NOT the end of a hold,
      GoSub DragSkip                ;Pass the mouse click to the program.
   Else
      GoSub DragStop                ;Otherwise, stop the scrolling routines
Return

;This just passes the mouse click.
DragSkip:
   Click Right    ;Gotta love this function; it works better than Send.
Return

DragStop:
   DragStatus := DS_HANDLED			;Reset the DragStatus flag.
   OldX := ""						;Reset all other parameters as well
   OldY := ""
   NewX := ""
   NewY := ""
   OriginalX := ""
   OriginalY := ""
   SetTimer, DragStart, Off			;Disable the timer that DragStart uses to trigger its main loop, stopping DragStart as a result
Return


DragStart:
   SetTimer, DragStart, % -1 * Abs(PollFrequency)	;Trigger this subroutine to run again once the Polling period has elapsed.
   
   If !StrLen( OriginalY )							;If OriginalY contains a zero-length string (i.e. nothing stored in it, because the user has just started to drag)
   {
     MouseGetPos, OldX, OldY,, Hwnd, 3				;Get the mouse cursor position, store it in OldX and OldY, 
                                                    ;store the HWND of the window the mouse is hovering over, for use in the "Constrain to Active Window" mode.
     
     OriginalX := OldX								;Change OriginalX to match OldX, Change OriginalY to match OldY.
     OriginalY := OldY								;These coordinates will be where the cursor returns to once the dragging ends.
   }
   Else												;If we're in the middle of a drag,
   {
      DragStatus := DS_DRAGGING						;Change DragStatus to show that we're dragging
      MouseGetPos, NewX, NewY,, NewHwnd, 3			;Get the most current mouse cursor position and the HWND of the window currently being hovered over.
	  
     if (Hwnd != NewHwnd && ConfineToTarget)		;If the previous HWND does not match this new HWND, the "Constrain" mode was on, 
      GoSub DragStop								;it's the end of the line. You have moved out of the active window. Stop the routines.
    
     ;Calculate/Scroll - X
     DiffX := Abs( NewX - OldX )					;Calculate the absolute difference in X values between the cursor's current and previous position.
      If (DiffX > Threshold)						;If the difference is over the threshold needed to start scrolling,
      {
       GoSub HScroll								;start scrolling (horizontally)
         OldX := NewX								;and update the mouse cursor coordinates
         OldY := NewY
      }
    
     ;Calculate/Scroll  - Y
     DiffY := Abs( NewY - OldY )					;Do the same for the Y coordinates
      If (DiffY > Threshold)
      {
      GoSub VScroll
        OldX := NewX
        OldY := NewY
      }
   }
Return

VScroll:											;This is the business end; it simulates mousewheel/cursor input to scroll the active window vertically.

  yTicks := DiffY / Threshold						;How fast is the mouse cursor's Y-coordinate changing, in terms of Ticks?
  yFactor := yTicks * SpeedY						;Multiply this by the contant SpeedY. This is the vertical scroll speed.
  
  if (MaxAcceleration > 0)							;If the acceleration routine is not disabled (i.e not set to 0), run it.
  {
   ;The acceleration routine looks up pre-computed values of the acceleration function,
   ;which are stored in a 1D array. It then retreives the value and multiplies yFactor by that amount.
   ;The end result is that as you move the mouse cursor, the scrolling speed accelerates based on your cursor's speed.
   
   yAccelIndex := Ceil(yFactor)						;This sets the index variable for the array lookup.
    ;If the index's value exceeds the bounds of the array, then set it to point to the last cell in the array.
   yAccelIndex := (yAccelIndex<=MaxAcceleration ? yAccelIndex : MaxAcceleration)
    yAccel := Accel%yAccelIndex%					;Retreive the value of the cell.
   yFactor *= yAccel								;Multiply yFactor by that value and store it back in yFactor.
   
   ;_DEBUG_ ToolTip, t%yTicks% -> i%yAccelIndex% a%yAccel% -> f%yFactor%            <--This is for debugging purposes.
  }

  ;_DEBUG_ ToolTip Y t%yTicks% -> f%yFactor%                                        <--This is for debugging purposes.
  
  if (ScrollMethodY = mWheelMessage)                ;If we are handling scrolling by passing WheelMessages,
  {
    ;Multiply the base wheel scrolling speed by yFactor,
    ;and adjust its direction depending on whether the user moved his mouse up or down
    wparam := WHEEL_DELTA * (NewY < OldY ? -1 : 1) * yFactor
    
    ;Then format the message properly and send it to the window the mouse is hovering over.
    PostMessage, WM_MOUSEWHEEL, (wparam<<16), (OriginalY<<16)|OriginalX,, Ahk_ID %Hwnd% 
  }   
  else if (ScrollMethodY = mWheelKey)				;If we are handling scrolling by sending WheelUp and WheelDown messages
  {
   ;Check if we have to send WheelUp or WheelDown messages depending on whether the user moved his mouse up or down
   wparam := NewY < OldY ? "{WheelDown}" : "{WheelUp}"
   ;And send as many WheelUp/WheelDown messages as needed to scroll at the desired speed
   Loop, %yFactor%									
     Send, %wparam%
  }
  else if (ScrollMethodY = mScrollMessage)			;If we are handling scrolling by sending Scroll messages (which work in terms of scrolling by lines)
  {
   wparam := NewY < OldY ? SB_LINEDOWN : SB_LINEUP	;Check if we have to send LINEDOWN or LINEUP messages
   Loop, %yFactor%									;and send as many as needed to scroll at the desired speed
     PostMessage, WM_VSCROLL, wparam, 0,, Ahk_ID %Hwnd%
  }
Return


HScroll:											;Same thing for Horizontal Scrolling
  xTicks := DiffX / Threshold
  xFactor := xTicks * SpeedX
  ;_DEBUG_ ToolTip Y t%xTicks% -> f%xFactor%        <-- This is for debugging purposes
  if (ScrollMethodX = mWheelMessage)
  {
    wparam := WHEEL_DELTA * (NewX < OldX ? -1 : 1) * xFactor
    PostMessage, WM_HMOUSEWHEEL, wparam, 0,, Ahk_ID %Hwnd%
  }
  else if (ScrollMethodX = mWheelKey)
  {
   wparam := NewX < OldX ? "{WheelLeft}" : "{WheelRight}"
   Loop, %xFactor%
     Send, %wparam%
  }
  else if (ScrollMethodX = mScrollMessage)
  {
    wparam := NewX < OldX ? SB_LINERIGHT : SB_LINELEFT
   Loop, %xFactor%
     PostMessage, WM_HSCROLL, wparam, 0,, Ahk_ID %Hwnd%
  }
Return

; Menu
;--------------------------------
MenuInit:

; Enable/Disable
Menu, mnuSettings, ADD, Enable, mnuEnable
GoSub mnuCheckEnable

; Confine
Menu, mnuSettings, ADD
Menu, mnuSettings, ADD, Confine to Target Window, mnuConfineWindow
GoSub mnuConfineWindowInit

; Method
Menu, mnuSettings, ADD
Menu, mnuSettings, ADD, Wheel Key, mnuMethodWheelKey
Menu, mnuSettings, ADD, Wheel Message, mnuMethodWheelMessage
Menu, mnuSettings, ADD, Scroll Message, mnuMethodScrollMessage

if (ScrollMethodY = mWheelKey)
GoSub mnuMethodWheelKey
else if (ScrollMethodY = mWheelMessage)
GoSub mnuMethodWheelMessage
else if (ScrollMethodY = mScrollMessage)
GoSub mnuMethodScrollMessage

;add the whole menu to tray
Menu, TRAY, ADD
Menu, TRAY, ADD, Settings, :mnuSettings
Return


; Menu Handlers
;--------------------------------

mnuMethodWheelKey:
   ScrollMethodY := mWheelKey
   Menu, mnuSettings, Uncheck, Wheel Message
   Menu, mnuSettings, Uncheck, Scroll Message
   Menu, mnuSettings, Check, Wheel Key
Return                         

mnuMethodWheelMessage:
   ScrollMethodY := mWheelMessage
   Menu, mnuSettings, Uncheck, Wheel Key
   Menu, mnuSettings, Check, Wheel Message
   Menu, mnuSettings, Uncheck, Scroll Message
Return

mnuMethodScrollMessage:
   ScrollMethodY := mScrollMessage
   Menu, mnuSettings, Uncheck, Wheel Key
   Menu, mnuSettings, Uncheck, Wheel Message
   Menu, mnuSettings, Check, Scroll Message
Return

mnuConfineWindow:
   ConfineToTarget := !ConfineToTarget
   mnuConfineWindowInit:
   if (ConfineToTarget)
      Menu, mnuSettings, Check, Confine to Target Window
   else
      Menu, mnuSettings, Uncheck, Confine to Target Window
Return 

mnuEnable:
   ScrollDisabled := !ScrollDisabled
   mnuCheckEnable:
   If (ScrollDisabled = 0) {
      Menu, mnuSettings, Check, Enable
      Menu, TRAY, icon, %A_ScriptDir%\scrollwheel.ico, 0
      Menu, TRAY, tip, Scrolling Enabled
      return
   } else {
      Menu, mnuSettings, Uncheck, Enable
      Menu, TRAY, icon, %A_ScriptDir%\scrollwheeldisabled.ico, , 0
      Menu, TRAY, tip, Scrolling Disabled
    }
Return


cheek
  • Members
  • 71 posts
  • Last active: Jun 21 2014 03:24 AM
  • Joined: 01 Apr 2009
Just wanted to let everyone know I have updated this script. This update is mostly maintenance work, cleanup, documentation, etc, but it also includes some new additions. I have incorporated refined versions of the additions by followup posts in this thread.

Many thanks again to the contributors for posting their source & ideas!

Notably, this version contains:
[*:18bh8o6a] Enable/Disable scrolling
[*:18bh8o6a] User-defined RButton double-click action
[*:18bh8o6a] Timer based hotkey activation
[*:18bh8o6a] Tray Icons, if set & they exist
[*:18bh8o6a] Edge Scrolling
[*:18bh8o6a] Usage of 'Critical' hotkeys should finally end unintended drag-sticking

cheek
  • Members
  • 71 posts
  • Last active: Jun 21 2014 03:24 AM
  • Joined: 01 Apr 2009
I got re-interested in this script after I started looking again, and just had to complete a killer feature. Latest update adds scroll momentum. See the first post and code comments for more info!

As always, I appreciate the feedback :)