Jump to content


Photo

ScrollMomentum


  • Please log in to reply
20 replies to this topic

#1 Lexikos

Lexikos
  • Administrators
  • 8845 posts

Posted 05 October 2007 - 07:57 AM

ScrollMomentum

Applies "momentum" to scroll bars. Works with:[*:11dbabe8]most standard windows scrollbars
[*:11dbabe8]Internet Explorer's scroll bars (tested on IE7.)Thanks to Sean for his help and excellent COM scripts, and FireGirl for the idea and insisting that I add IE support. ;)

Requires Standard Library COM.ahk

Known Issues:
[*:11dbabe8]Doesn't work in Explorer (i.e. the file browser; at least on Vista; let me know if this is not the case for your version of Windows.)
[*:11dbabe8]Doesn't work in Microsoft Document Explorer's Index.
[*:11dbabe8]There is a delay when grabbing Internet Explorer scroll bars before the script starts working. This is usually around 100-140 ms for the initial "grab," and 30-60 ms for subsequent "grabs."
;
; AutoHotkey Version: 1.0.47 (MINIMUM), tested on 1.0.47.06.L
; Language:       English
; Platform:       Windows 98 and later? (REQUIRED), tested on Windows Vista (IE7).
; Author:         Lexikos
;
; Momentum-related code and ideas were developed for a different script by various users at:
;   http://www.autohotkey.com/forum/viewtopic.php?t=19773
;
; Script Function:
;	Apply momentum to scroll bars.
;
; Idea by FireGirl:
;   http://www.autohotkey.com/forum/viewtopic.php?t=21790

#NoEnv
CoordMode, Mouse, Screen

;
; CONFIGURATION
;

UPDATE_RATE = 10 ; milliseconds
SENSITIVITY = 0.5
INERTIA = 0.95

;
; WIN32 DEFINES
;
; SetWinEventHook Flags
WINEVENT_OUTOFCONTEXT       = 0x0000
; Events
EVENT_SYSTEM_SCROLLINGSTART = 0x0012
EVENT_SYSTEM_SCROLLINGEND   = 0x0013
; Scrollbar Constants
SB_HORZ = 0
SB_VERT = 1

;
; INITIALIZATION
;

SpeedA := 1 - SENSITIVITY

; Register callback proc.
cbOnScrollEvent := RegisterCallback("OnScrollEvent")
; Set scrolling start/end hook to call OnScrollEvent().
hScrollHook := DllCall("SetWinEventHook"
    , "uint", EVENT_SYSTEM_SCROLLINGSTART   ; eventMin
    , "uint", EVENT_SYSTEM_SCROLLINGEND     ; eventMax
    , "uint", 0                             ; hmodWinEventProc (only for in-context hooks)
    , "uint", cbOnScrollEvent               ; lpfnWinEventProc
    , "uint", 0                             ; idProcess (0 = all processes)
    , "uint", 0                             ; idThread (0 = all threads)
    , "uint", WINEVENT_OUTOFCONTEXT)        ; dwflags

if (!hScrollHook)
    MsgBox, 48, Error, SetWinEventHook failed. Standard Windows scrollbars will not have momentum applied.

; IE support init.
WM_HTML_GETOBJECT := DllCall("RegisterWindowMessage", "str", "WM_HTML_GETOBJECT")
COM_CoInitialize()

OnExit, CleanupAndExit

return


CleanupAndExit:
    ; May not be necessary; but for good measure...
    if hScrollHook
        DllCall("UnhookWinEvent", "uint", hScrollHook)
ExitApp


ContinueScroll:
    Scroll_Speed *= INERTIA
    
    ; Continue until
    ;   the scroll bar slows to a stop,
    ;   the window is closed,
    ;   or something else moves the scroll bar.

    if (Abs(Scroll_Speed)>0.01
        && GetAbstractScrollInfo(Scroll_Container, Scroll_Type, Scroll_Pos_Int)
        && Scroll_Pos_Int = Last_Pos)
    {
        ; Apply momentum.
        Scroll_Pos += Scroll_Speed
        ; Check if it hit the end.
        if (Scroll_Pos <  Scroll_Min)
            Scroll_Pos := Scroll_Min
        if (Scroll_Pos >  Scroll_Max)
            Scroll_Pos := Scroll_Max
        ; If it has come to a stop, stop the timer.
        if (Scroll_Pos = Last_Pos) {
            Scroll_Speed = 0
            SetTimer, ContinueScroll, Off
            return
        }
        ; Move scroll bar.
        if SetAbstractScrollPos(Scroll_Container, Scroll_Type, Round(Scroll_Pos))
        {   ; Update position (to detect if something else moves the bar.)
            GetAbstractScrollInfo(Scroll_Container, Scroll_Type, Last_Pos)
            return
        }
    }
    Scroll_Speed = 0
    SetTimer, ContinueScroll, Off
    gosub ReleaseScrollContainerIfNecessary
return

TrackScrollBar:
    ; Update scroll position.
    if GetAbstractScrollInfo(Scroll_Container, Scroll_Type, Scroll_Pos)
    {   ; Recalculate velocity.
        Scroll_Speed := Scroll_Speed*SpeedA + (Scroll_Pos-Last_Pos)*SENSITIVITY
        Last_Pos := Scroll_Pos
    }
    else
    {   ; Error: maybe the window closed?
        SetTimer, TrackScrollBar, Off
        gosub ReleaseScrollContainerIfNecessary
    }
return

ReleaseScrollContainerIfNecessary:
    if Scroll_Type in scrollbarHThumb,scrollbarVThumb
        COM_Release(Scroll_Container)
    Scroll_Container = 0
return

~LButton::
    gosub GetIEDocumentAtMouse
    if !pDoc
        return
    pWin := COM_Invoke(pDoc, "parentWindow"), COM_Release(pDoc), pDoc := 0
    if !pWin
        return

    ; Currently only one scroll bar can be moving at a time.
    SetTimer, ContinueScroll, Off
    gosub ReleaseScrollContainerIfNecessary

    MouseGetPos, x, y

    if (GetDeepestScrollElement(pWin, x, y, Scroll_Container, pElementWin))
    {   ; Hit-test for scrollbarHThumb or scrollbarVThumb.
        cx:=COM_Invoke(pElementWin,"screenLeft"), cy:=COM_Invoke(pElementWin,"screenTop"), pWin!=pElementWin ? (cx-=2, cy-=2) : ""
        Scroll_Type := COM_Invoke(Scroll_Container,"componentFromPoint",x-cx,y-cy)
        if Scroll_Type not in scrollbarHThumb,scrollbarVThumb
            COM_Release(Scroll_Container),Scroll_Container:=0, Scroll_Type:=""
        pElementWin!=pWin ? COM_Release(pElementWin) . pElementWin:=0 : ""
    }
    COM_Release(pWin), pWin:=0
    
    if !Scroll_Container
        return

    GetAbstractScrollInfo(Scroll_Container, Scroll_Type, Scroll_Pos, Scroll_Min, Scroll_Max)
    Last_Pos := Scroll_Pos
    Scroll_Speed := 0

    ; Set the tracking timer.
    SetTimer, TrackScrollBar, %UPDATE_RATE%

    KeyWait, LButton
    
    ; Scrolling has stopped, so stop tracking the scroll position.
    SetTimer, TrackScrollBar, Off
    
    if Scroll_Container
    {   ; Apply momentum.
        SetTimer, ContinueScroll, %UPDATE_RATE%
        ; Update Last_Pos in case it has changed since the last TrackScrollBar iteration.
        GetAbstractScrollInfo(Scroll_Container, Scroll_Type, Last_Pos)
    }
return


;
; CALLBACK - Standard Windows Scrollbars
;
OnScrollEvent(hWinEventHook, event, hwnd, idObject, idChild, dwEventThread, dwmsEventTime)
{
    global
    local trackpos
    static lbutton_state
    
    static OBJID_VSCROLL = 0xFFFFFFFB, OBJID_HSCROLL = 0xFFFFFFFA
    
    if (event = EVENT_SYSTEM_SCROLLINGSTART) && (lbutton_state:=GetKeyState("LButton"))
    {
        gosub ReleaseScrollContainerIfNecessary
        
        Scroll_Container := hwnd
        Scroll_Type := (idObject=OBJID_HSCROLL) ? SB_HORZ : SB_VERT
        
        ; Get this scroll bar's parameters.
        GetScrollInfo(Scroll_Container, Scroll_Type, Scroll_Pos, trackpos, Scroll_Min, Scroll_Max, Scroll_Page)
        Last_Pos := Scroll_Pos
        
        ; Set the tracking timer.
        SetTimer, TrackScrollBar, %UPDATE_RATE%
        ; Currently only one scroll bar can be moving at a time.
        SetTimer, ContinueScroll, Off
    }
    else if (event = EVENT_SYSTEM_SCROLLINGEND) && lbutton_state
    {
        ; Scrolling has stopped, so stop tracking the scroll position.
        SetTimer, TrackScrollBar, Off
        ; Apply momentum.
        SetTimer, ContinueScroll, %UPDATE_RATE%
        ; Update Last_Pos in case it has changed since the last TrackScrollBar iteration.
        GetScrollInfo(Scroll_Container, Scroll_Type, Last_Pos)
    }
}


;
; FUNCTIONS - Standard Windows Scrollbars
;
GetScrollInfo(hwnd, fnBar, ByRef nPos=0, ByRef nTrackPos=0, ByRef nMin=0, ByRef nMax=0, ByRef nPage=0)
{
    WinGet, Style, Style, ahk_id %hwnd%
    if !(Style & (fnBar ? 0x200000 : 0x100000))
        return false ; Fix for some controls which report {min:0,max:100} even though scroll bar doesn't exist.

    VarSetCapacity(si, 28, 0) ; SCROLLINFO si
    NumPut(28  , si, 0) ; si.cbSize := sizeof(SCROLLINFO)
    NumPut(0x17, si, 4) ; si.fMask := SIF_ALL
    
    if ! DllCall("GetScrollInfo", "uint", hwnd, "int", fnBar, "uint", &si)
        return false
    
    nPos        := NumGet(si, 20)
    nTrackPos   := NumGet(si, 24)
    nMin        := NumGet(si,  8)
    nMax        := NumGet(si, 12)
    nPage       := NumGet(si, 16)
    return true
}

SetScrollPos(hwnd, fnBar, nPos)
{
    VarSetCapacity(si, 28, 0) ; SCROLLINFO si
    NumPut(28  , si,  0)    ; si.cbSize := sizeof(SCROLLINFO)
    NumPut(0x4 , si,  4)    ; si.fMask := SIF_POS
    NumPut(nPos, si, 20)    ; si.nPos := nPos
 
    ; Use SetScrollInfo first because it supports 32-bit positions.
    ; If an application supports positions < 0 or > 65535, it most likely
    ; ignores WM_#SCROLL's wParam and uses GetScrollPos or GetScrollInfo
    ; to get the actual scroll position.
    DllCall("SetScrollInfo", "uint", hwnd, "int", fnBar, "uint", &si, "int", 0)
    
    ; WM_HSCROLL or WM_VSCROLL must be sent for the window to update it's contents.
    msg := (fnBar=0) ? 0x114 : 0x115
    wParam := 4 ; SB_THUMBPOSITION
        | ((nPos&0xFFFF)<<16)
    SendMessage, msg, wParam,,, ahk_id %hwnd%
    
    return (ErrorLevel != "FAIL")
}


;
; ABSTRACT FUNCTIONS - for both standard Windows scrollbar and IE scrollbar support.
;
GetAbstractScrollInfo(Container, Type, ByRef Pos=0, ByRef Min=0, ByRef Max=0)
{
    if !Container
        return false
    if Type in 0,1 ; SB_HORZ,SB_VERT
        return GetScrollInfo(Container, Type, Pos, track_pos, Min, Max)
    if Type = scrollbarHThumb
        Pos:=COM_Invoke(Container,"scrollLeft"),  Max:=COM_Invoke(Container,"scrollWidth"),  Min:=0
    else if Type = scrollbarVThumb
        Pos:=COM_Invoke(Container,"scrollTop"),  Max:=COM_Invoke(Container,"scrollHeight"),  Min:=0
    return Pos!=""
}
SetAbstractScrollPos(Container, Type, Pos)
{
    if !Container
        return false
    if Type in 0,1 ; SB_HORZ,SB_VERT
        return SetScrollPos(Container, Type, Pos)
    ; TODO: determine success or failure for assignment.
    if Type = scrollbarHThumb
        return true, COM_Invoke(Container,"scrollLeft=",Pos)
    else if Type = scrollbarVThumb
        return true, COM_Invoke(Container,"scrollTop=",Pos)
    return false
}


;
; IE FUNCTIONS & SUBROUTINES
;
GetDeepestScrollElement(pWin, x, y, ByRef pElement, ByRef pElementWin)
{
    ; AddRef so we don't release pWin below. (Caller may still need it.)
    COM_AddRef(win:=pWin)
    Loop {
        if (!win)
            break ; Error
        if !(doc:=COM_Invoke(win,"document"))
            break ; Error
        if !(element:=COM_Invoke(doc,"elementFromPoint",x-COM_Invoke(win,"screenLeft"),y-COM_Invoke(win,"screenTop")))
            break ; Error
        if (tag:=COM_Invoke(element,"tagName")) != "FRAME"
        {   ; return element.isScrollable ? element : frame.body
            scrollHeight:=COM_Invoke(element,"scrollHeight"), clientHeight:=COM_Invoke(element,"clientHeight")
            if !(clientHeight && clientHeight < scrollHeight)
                COM_Release(element), element:=COM_Invoke(doc,"body")
            break
        }
        COM_Release(doc), doc:=0,  COM_Release(win), win:=0
        if !(win:=COM_Invoke(element,"contentWindow"))
        {
            COM_Release(element), element:=0
            break ; Error
        }
    }
    if doc
        COM_Release(doc), doc:=0
    if (win && !element)
        COM_Release(win), win:=0
    
    pElement := element
    pElementWin := win
    return pElement
}

GetIEDocumentAtMouse:
    MouseGetPos,,,, hIESvr, 2
    WinGetClass, class, ahk_id %hIESvr%
    if (!hIESvr or class != "Internet Explorer_Server")
    {   ; Try again with alternate method (for "Microsoft Document Explorer" support.)
        MouseGetPos,,,, hIESvr, 3
        WinGetClass, class, ahk_id %hIESvr%
        if (!hIESvr or class != "Internet Explorer_Server")
            return
    }
    
    SendMessage, WM_HTML_GETOBJECT,,,, ahk_id %hIESvr%
    lResult := ErrorLevel
    DllCall("oleacc\ObjectFromLresult", "uint", lResult
        , "uint", COM_GUID4String(IID_IHTMLDocument2,"{332C4425-26CB-11D0-B483-00C04FD90119}")
        , "int", 0, "uint*", pDoc)
return



!Up::
    Scroll_Type = 1
    Scroll_Speed_Boost = -2
goto KickActiveScrollBar
!Down::
    Scroll_Type = 1
    Scroll_Speed_Boost = +2
goto KickActiveScrollBar
!Left::
    Scroll_Type = 0
    Scroll_Speed_Boost = -10
goto KickActiveScrollBar
!Right::
    Scroll_Type = 0
    Scroll_Speed_Boost = +10
goto KickActiveScrollBar

; Scroll_Type should be set to 0 (horz) or 1 (vert) before calling this subroutine.
; Scroll_Speed should be set to:
;   a scalar value with decimal point (it is multiplied by (Scroll_Max-Scroll_Min))
; or
;   an absolute value with no decimal point.
KickActiveScrollBar:
    ControlGetFocus, ctl, A
    ControlGet, hCtl, Hwnd,, %ctl%, A
    
    SetTimer, ContinueScroll, Off
    gosub ReleaseScrollContainerIfNecessary
    
    ; Internet Explorer scrollbars.
    if InStr(ctl,"Internet Explorer_Server")
    {   ; Get the document object of this IE control.
        SendMessage, WM_HTML_GETOBJECT,,,, ahk_id %hCtl%
        lResult := ErrorLevel ; necessary because COM_GUID4String() changes ErrorLevel.
        DllCall("oleacc\ObjectFromLresult", "uint",lResult
            , "uint",COM_GUID4String(IID_IHTMLDocument2,"{332C4425-26CB-11D0-B483-00C04FD90119}")
            , "int",0, "uint*",pDoc)
        
        if (pDoc && (pWin:=COM_Invoke(pDoc,"parentWindow"), COM_Release(pDoc),pDoc:=0))
            if (pElement:=GetActiveScrollElement(pWin)),  COM_Release(pWin),pWin:=0
            {
                Scroll_Container := pElement
                Scroll_Type := Scroll_Type ? "scrollbarVThumb" : "scrollbarHThumb"
                GetAbstractScrollInfo(Scroll_Container, Scroll_Type, Scroll_Pos, Scroll_Min, Scroll_Max)
            }
    }

    ; Standard Windows scrollbars.
    if !Scroll_Container
    {
        Scroll_Container := hCtl
        Loop
            if GetScrollInfo(Scroll_Container, Scroll_Type, Scroll_Pos, trackpos, Scroll_Min, Scroll_Max, Scroll_Page)
                && Scroll_Min != Scroll_Max
                break
            else
                Scroll_Container := DllCall("GetParent","uint",Scroll_Container)
    }

    Last_Pos := Scroll_Pos
;     if InStr(Scroll_Speed,".")
;         Scroll_Speed *= Scroll_Max-Scroll_Min
    if Abs(Scroll_Speed_Boost) < 1
        Scroll_Speed_Boost *= Scroll_Max-Scroll_Min
    if Abs(Scroll_Speed) < 1
        Scroll_Speed = 0
    Scroll_Speed += Scroll_Speed_Boost

    ; Apply momentum.
    SetTimer, ContinueScroll, %UPDATE_RATE%
return


GetActiveScrollElement(win)
{
    if !win
        return 0
    COM_AddRef(win) ; caller is responsible for releasing win
    Loop {
        if (!win)
            break ; Error
        if !(doc:=COM_Invoke(win,"document")),  COM_Release(win),win:=0
            break ; Error
        if !(element:=COM_Invoke(doc,"activeElement"))
            break ; Error
        if (tag:=COM_Invoke(element,"tagName")) != "FRAME"
        {   ; return element.isScrollable ? element : frame.body
            scrollHeight:=COM_Invoke(element,"scrollHeight"), clientHeight:=COM_Invoke(element,"clientHeight")
            if !(clientHeight && clientHeight < scrollHeight)
                COM_Release(element), element:=COM_Invoke(doc,"body")
            break
        }
        COM_Release(doc),doc:=0
        if !(win:=COM_Invoke(element,"contentWindow"))
        {
            COM_Release(element), element:=0
            break ; Error
        }
    }
    doc ? COM_Release(doc) . doc:=0 : ""
    
    return element
}
2007-10-12
Made a few misc changes. Scrolling now halts "glide" of standard Windows scrollbars.
2007-10-13
Fixed a couple potential "reference leaks."
2007-10-14
Fixed GetDeepestScrollElement() attempting to release an invalid object when the script fails to retrieve the contentWindow of a frame.
Fixed potential "reference leak" in ~LButton hotkey.

2008-12-30
Released script under Lexikos' default copyright license.

2009-01-01
Added Alt+Arrow "kicker" hotkeys which I had posted elsewhere. Also fixed them to "cascade" properly - i.e. scroll the active control if possible, otherwise its parent, its grandparent, etc.
Changed GetScrollInfo to return false if WS_VSCROLL or WS_HSCROLL style (as applicable) is not set. This fixes kicker cascading for some applications.
Other misc changes.

#2 ahklerner

ahklerner
  • Members
  • 1382 posts

Posted 05 October 2007 - 10:41 AM

Hey, That is cool.

#3 William Sharkey

William Sharkey
  • Guests

Posted 05 October 2007 - 06:58 PM

Hello lexikos,

First off, very good job. When the momentum is on, it makes using the computer more cosy. I would consider running this all of the time if it worked for my major applications. I realize that this was not meant for me though.

It may be irrelevant, but I use firefox. It's not working with Firefox2.

I like the damper level you have set here. It's a good trade off considering how the speed varies depending on application.

What are your feelings on bouncing at the top and bottom, instead of dead stoping?

Thanks,
William

#4 cool

cool
  • Guests

Posted 05 October 2007 - 10:56 PM

bouncing would be nice with this :) :) :) :)

#5 FireGirl

FireGirl
  • Members
  • 102 posts

Posted 06 October 2007 - 12:22 AM

Truly outstanding work here!!! WOW!!!

Is adding this edge off bounce achievable?

I really can't think of anything else it would need to be *totally* perfectionist perfect.

Have a very great day! :D

%:FireGirl:%

#6 Lexikos

Lexikos
  • Administrators
  • 8845 posts

Posted 06 October 2007 - 03:10 AM

It may be irrelevant, but I use firefox. It's not working with Firefox2.

Yes, it's quite unfortunate. I use Firefox, but I don't know of any way to interact with its scroll bars (esp. from AutoHotkey.) Internet Explorer has its own COM interfaces.

I like the damper level you have set here.

ManaUser deserves credit for that one. The speed calculation and inertia code is mostly from ManaUser's EasyGlide script (posted in this thread.)

What are your feelings on bouncing at the top and bottom, instead of dead stoping?

It seems entirely unessential to me, but I'll probably add it eventually. :)

#7 FireGirl

FireGirl
  • Members
  • 102 posts

Posted 06 October 2007 - 04:15 AM

Thank you Sean & esp. lexikos, you are all the best. :)

#8 Sunnybreeze

Sunnybreeze
  • Guests

Posted 06 October 2007 - 04:54 AM

Lexikos, I was fiddling with this and have some questions.

I noticed this works real smooth on notepad, but in Internet Explorer is extremely choppy and doesn't always grab, even if I hold down the mouse button. Kinda useless there.

I was trying to get the mouse tracking position of the scrollbar to see what is up, and noted when I inserted

    if (event = EVENT_SYSTEM_SCROLLINGSTART)
    {
        Scroll_Container := hwnd
        Scroll_Type := (idObject=OBJID_HSCROLL) ? SB_HORZ : SB_VERT
        
        ; Get this scroll bar's parameters.
        GetScrollInfo(Scroll_Container, Scroll_Type, Scroll_Pos, trackpos, Scroll_Min, 

Scroll_Max, Scroll_Page)
        Last_Pos := Scroll_Pos
        [color=red]MsgBox, %Last_Pos%[/color] 
        ; Set the tracking timer.
        SetTimer, TrackScrollBar, %UPDATE_RATE%
        ; Currently only one scroll bar can be moving at a time.
        SetTimer, ContinueScroll, Off
    }

this chunk of code seems to be ignored on IE but in NotePad pops up. Is there something wrong that would be causing this? Really great overall concept however.

#9 Lexikos

Lexikos
  • Administrators
  • 8845 posts

Posted 06 October 2007 - 05:14 AM

That section of code is for standard Windows scroll bars only. IE's scroll bars do not raise those events (which are only for start/stop scroll) - the ~LButton hotkey is what detects IE's scroll bars. A better point to debug from would be TrackScrollBar, which calculates scroll speed, or ContinueScroll, which applies momentum. They are both used for both IE and standard Windows scroll bars. Use a ToolTip from either of those, or a MsgBox from ~LButton::

in Internet Explorer is extremely choppy and doesn't always grab, even if I hold down the mouse button.

It's perfectly smooth for me, and always grabs if I hold down the mouse button before moving. Which version of IE are you using? By "doesn't always grab," I assume you mean it intermittently fails. If there is any specific page it doesn't work on, let me know.

Does anyone else have problems with the script in IE?

Edit: It seems the screenLeft and screenTop properties of frames are off by 2 pixels. As a consequence, the script can't grab the top or left edge of a scrollbar that belongs to a frame.

Edit: I've applied a small "offset hack," now you can grab the top or left edge of a frame's scrollbar. Tested on XP (IE6) in Virtual PC and Vista (IE7). Both scroll very smoothly for me, btw.

#10 Sunnybreeze

Sunnybreeze
  • Guests

Posted 06 October 2007 - 02:14 PM

Precisely, I am running Windows XP SP2 with IE 6.0.2900.2180_SP2 (which I presume is the latest IE 6.0 build for XP).... Yes, it definitely seems to prevent me from grabbing the scrollbar properly (anyone else?), even if I hold down the mouse button for more than 1000ms it just ignores the grab, (except in notepad which is picture perfect and works like a true charm). It would be interesting to see if your additional mods makes a difference with that here...

#11 Lexikos

Lexikos
  • Administrators
  • 8845 posts

Posted 07 October 2007 - 02:15 AM

I am running Windows XP SP2 with IE 6.0.2900.2180_SP2

Same here, but from a fresh install (no updates) of Windows XP w/ SP2. Do you have any add-ons running?

Yes, it definitely seems to prevent me from grabbing the scrollbar properly (anyone else?), even if I hold down the mouse button for more than 1000ms it just ignores the grab,

Every time, or intermittently? If the former, maybe some of IE's COM components are damaged?

If anything demands CPU time, the script usually stutters (not just IE...) You could try adding this at the top of the script:
SetBatchLines, -1


#12 Sunnybreeze

Sunnybreeze
  • Guests

Posted 07 October 2007 - 06:27 AM

I can try this again on a restarted machine running these version specs, I will report back what I see soon.

#13 Sunnybreeze

Sunnybreeze
  • Guests

Posted 11 October 2007 - 06:10 PM

To make sure this was a fair and responsible test I re-ran it on a fresh install of Windows ...., no extra plug-ins etc.... , on a stock Windows XP SP2 (latest latest)..., running V6.0 Internet Explorer latest v., the version mentioned in my last post, and also a test running latest V7.0 IE (which is 7.0.5730.13). 1GB ram and 3Ghz machine so should not be any power or conflict issues.

I am getting same result on V6 and V7! The scrolling either vertically or horizontally doesn't seem to hook-in or 'catch', ... usually after 5 or 6 attempts it may once in awhile and glide along......., but there doesn't seem to be a precise technique to make it work, very just choppy unlike the way it works for me under Notepad, which seems 100% right on :)

What could be going wrong I can test or look into, or some other parameter I should try to compensate?

#14 Lexikos

Lexikos
  • Administrators
  • 8845 posts

Posted 12 October 2007 - 02:08 AM

Try downloading the script directly (right click.) There are sometimes copy-past errors on the forums (esp. with the "Copy" link), though it's doubtful that this is the problem.

I'm updating my (XP) virtual machine, to see if that has any negative impact. (Edit: no change.)

Actually, I have since tried it on another XP SP2 machine (I copied the script from the forum), with perfect results. I'm not sure how up to date it was, but it certainly wasn't a fresh install.

I assume you have the latest version of AutoHotkey?

1GB ram and 3Ghz machine so should not be any power or conflict issues.

True, when you're not doing anything else. I have tried the script when, for instance, starting up Virtual PC and simultaneously verifying a DVD I just burnt; it's not a pretty sight. ;)

I am getting same result on V6 and V7!

That doesn't surprise me. I get the same results on V6 and V7; the difference is that I get good results. :?

What could be going wrong I can test or look into, or some other parameter I should try to compensate?

SetBatchLines as I suggested previously, and maybe Process (priority). I think it is more likely some issue with the COM/MSHTML*, in which case script settings aren't likely to help. (I'm not ruling out the possibility of something in the script causing issues with MSHTML...)

#15 Lexikos

Lexikos
  • Administrators
  • 8845 posts

Posted 12 October 2007 - 03:38 AM

I've been messing around with hotkeys to "kick" the scroll bars. They should work fairly consistently with Notepad, but the "force" may be off for other apps, depending on the scale of their "scrollbar units."

This should be added to the bottom of the most recent version of the script in my first post.
!Up::
    Scroll_Type = 1
    Scroll_Speed = -2
goto KickActiveScrollBar
!Down::
    Scroll_Type = 1
    Scroll_Speed = +2
goto KickActiveScrollBar
!Left::
    Scroll_Type = 0
    Scroll_Speed = -10
goto KickActiveScrollBar
!Right::
    Scroll_Type = 0
    Scroll_Speed = +10
goto KickActiveScrollBar

; Scroll_Type should be set to 0 (horz) or 1 (vert) before calling this subroutine.
; Scroll_Speed should be set to:
;   a scalar value with decimal point (it is multiplied by (Scroll_Max-Scroll_Min))
; or
;   an absolute value with no decimal point.
KickActiveScrollBar:
    ControlGetFocus, ctl, A
    ControlGet, hCtl, Hwnd,, %ctl%, A
    
    SetTimer, ContinueScroll, Off
    gosub ReleaseScrollContainerIfNecessary
    
    ; Internet Explorer scrollbars.
    if InStr(ctl,"Internet Explorer_Server")
    {   ; Get the document object of this IE control.
        SendMessage, WM_HTML_GETOBJECT,,,, ahk_id %hCtl%
        lResult := ErrorLevel ; necessary because COM_GUID4String() changes ErrorLevel.
        DllCall("oleacc\ObjectFromLresult", "uint",lResult
            , "uint",COM_GUID4String(IID_IHTMLDocument2,"{332C4425-26CB-11D0-B483-00C04FD90119}")
            , "int",0, "uint*",pDoc)
        
        if (pDoc && (pWin:=COM_Invoke(pDoc,"parentWindow"), COM_Release(pDoc),pDoc:=0))
            if (pElement:=GetActiveScrollElement(pWin)),  COM_Release(pWin),pWin:=0
            {
                Scroll_Container := pElement
                Scroll_Type := Scroll_Type ? "scrollbarVThumb" : "scrollbarHThumb"
                GetAbstractScrollInfo(Scroll_Container, Scroll_Type, Scroll_Pos, Scroll_Min, Scroll_Max)
            }
    }

    ; Standard Windows scrollbars.
    if !Scroll_Container
    {
        Scroll_Container := hCtl
        if ! GetScrollInfo(Scroll_Container, Scroll_Type, Scroll_Pos, trackpos, Scroll_Min, Scroll_Max, Scroll_Page)
        {
            Scroll_Container:=0, Scroll_Type:="", Scroll_Speed:=0
            return
        }
    }

    Last_Pos := Scroll_Pos
    if InStr(Scroll_Speed,".")
        Scroll_Speed *= Scroll_Max-Scroll_Min

    ; Apply momentum.
    SetTimer, ContinueScroll, %UPDATE_RATE%
return


GetActiveScrollElement(win)
{
    if !win
        return 0
    COM_AddRef(win) ; caller is responsible for releasing win
    Loop {
        if (!win)
            break ; Error
        if !(doc:=COM_Invoke(win,"document")),  COM_Release(win),win:=0
            break ; Error
        if !(element:=COM_Invoke(doc,"activeElement"))
            break ; Error
        if (tag:=COM_Invoke(element,"tagName")) != "FRAME"
        {   ; return element.isScrollable ? element : frame.body
            scrollHeight:=COM_Invoke(element,"scrollHeight"), clientHeight:=COM_Invoke(element,"clientHeight")
            if !(clientHeight && clientHeight < scrollHeight)
                COM_Release(element), element:=COM_Invoke(doc,"body")
            break
        }
        COM_Release(doc),doc:=0
        if !(win:=COM_Invoke(element,"contentWindow"))
        {
            COM_Release(element), element:=0
            break ; Error
        }
    }
    doc ? COM_Release(doc) . doc:=0 : ""
    
    return element
}
As described in the comments, Scroll_Speed should be set to an integer representing an absolute scroll speed, or a floating point (i.e. with decimal point) scalar value (which is automatically multiplied by the maximum scroll in scroll units, not pixels.)

(I don't plan on updating the autohotkey.net link from my previous post.)