web-dev-qa-db-ja.com

AutoHotkeyのスクロールとミドルクリックとマウスの加速

問題

Windows 7マシン、特にラップトップを想定します。

  1. マウスドライバでは、マウスの右ボタンを押したままマウスを動かしてスクロールすることはできませんが、マウスの右ボタンの機能は保持されます。これは、中央のボタンがない場合に便利です。
  2. マウスドライバーは一部のアプリケーションでは機能しません。また、UltraNavのようなものは、機能するほとんどのアプリケーションで非常にぎくしゃくした感じになります。
  3. ThinkPad用のUltraNavドライバーでは、中央のボタンを押したままマウスを動かすことでスクロールできると同時に、中央のボタンを中クリックとして機能させることはできません。
  4. UltraNavドライバーでは、スクロールと同時にクリックアンドドラッグすることもできません。これは、ドキュメントの編集に非常に役立ちます。
  5. ホイールマウスでは高速スクロールは不可能であり、UltraNavドライバーでは不十分です。
  6. デフォルトのマウスドライバーを使用すると、特にThinkPadトラックポイントの場合、可能な限り最速のポインター速度が非常に遅くなり、疲れる可能性があります。

私が望んでいたのは、これらすべての問題を解決するAutoHotkeyスクリプトでしたが、オンラインで見つけたものは、それらのいくつかを解決することすらできませんでした。 2年後、私は以下の完全なソリューションを開発しました。もちろん、私が使用したいくつかの手法は、インターネット上のさまざまな投稿から採用されましたが、多くの場合、各投稿には1つまたは2つの方法しかないため、多くのアプリケーションでは機能しません。

コメントは大歓迎です!ただし、うまくいかない場合は、提案を実装したり、トラブルシューティングを行ったりする時間がない場合があることを理解してください。ありがとう!

2
user21820

私は、上記のすべての問題を解決するために低レベルのマウスフックを使用する独自のAutoHotkeyスクリプトを設計しました。コードが長すぎるため、コードの他の回答を参照してください。

重要な指示

64ビットのWindowsを使用している場合でも、32ビットバージョンのAutoHotkeyを使用してください。そうしないと、一部の場所で機能しなくなります。タスクスケジューラを使用して、ユーザーに対して可能な限り高い権限でログイン時にスクリプトを実行します。そうしないと、完全な管理者権限で実行されているアプリケーションの管理者ユーザーに対してスクリプトが機能しません。 UltraNavがインストールされている場合は、マウスの中ボタンに[スムーズ]を選択します。そうしないと、不明な理由でスクリプトに干渉し、スクロールが機能しなくなります。

まれに、特にアプリケーションがGUIを変更しているときにスクロールしようとすると、マウスフックに問題が発生することがあります。リアルタイム優先で実行していないせいかもしれませんが、行き詰まってしまうと止められないのでリスクが高すぎると思います。現在、高い優先度で実行するように設定されています。問題が発生した場合は、次のホットキーを順番に試してください。

LCtrl-RCtrl:リロードします。 RCtrl-LCtrl:マウスフックを無効にします。 (マウスが機能するようになったら、リロードします。)RCtrl-Esc:スクリプト全体を無効にします。 (もう一度実行する必要があります。)

機能

  1. デフォルトでは、マウスの中ボタンを押したままマウスを動かすと、スクロールが実行されます。代わりにマウスの右ボタンを使用するようにrightbuttonscroll:=1を設定できます(特にマウスの中ボタンがないラップトップの場合)。その場合、元の右クリックドラッグ機能を取得するには、追加のボタンを押す必要があります。特定のアプリケーションに応じて、その前にCtrl/Shift/Altを押します。 (ほとんどのアプリケーションでは、これらの組み合わせの少なくとも1つは、元の右クリックと同じです。)
  2. マウスの中ボタンがない場合は、leftrighttomiddle:=1を設定し、clicklimit(ミリ秒)内でマウスの左ボタンと右ボタンの両方を押すと中ボタンが押され、両方を離すと中ボタンが生成されます。ボタンを離します。生成された中央のボタンを押したままにするには、2つの実際のボタンの1つだけを押したままにするだけで十分です。
  3. スクロールは他のマウスボタンとは独立して機能するため、たとえば、アプリケーションがサポートしている限り、選択操作中にマウスの左ボタンを押したままいつでもスクロールできます。 (一部のアプリケーションでは、奇妙な理由で選択中にスクロールできません。)
  4. デフォルトでは、clicklimit内のスクロールボタン(マウスの中ボタンまたは右ボタン)を押して放した後、元のスクロールボタンクリックが生成されます。スクロールボタンをそれより長く押し続けると、スクロールが有効になり、スクロールボタンを離してもクリックは発生しません。その初期遅延中でもスクロールを有効にするには、scrollbeforeclick:=1を設定します。この設定では、scrolllimitを超えない限り、わずかなスクロールがすでに行われていてもクリックできます。
  5. スクロールは少し粘着性があります。つまり、スクロール中に誤ってresetdelay未満のスクロールボタンを離した場合、それは発生しなかったかのように扱われ、スクロールが続行されます。特にこれは、スクロールの最後で誤ってクリックしても、元のスクロールボタンのクリックが生成されないことを意味します。
  6. クリックもわずかに粘着性があります。つまり、マウスボタンを離した後にresetdelay内でマウスボタンを押すと、次のようになります。
  7. 一部のアプリケーションはスクロールの処理が非常に遅いため、スクロールを実行するためにあまりにも多くのコマンドをアプリケーションに送り込むことはできません。 intervalは、アプリケーションに送信されるスクロール関連のコマンドのバッチ間の間隔です。各間隔で、ユーザーによるすべてのスクロールアクションが1つに結合されます。間隔を短くすると、スクロールの応答性が向上しますが、設計が不十分なアプリケーションが窒息する可能性があります。微調整が必​​要な場合は、いつでもgettargetの先頭にデフォルト値を設定し、アプリケーションに応じて調整できます。
  8. 一部のアプリケーションは、スクロールコマンドの処理に実際に時間がかかりすぎるため、コマンドがtimelimit内で処理されない場合、その間隔のバッチ内の残りのすべてのコマンドが中止されます。
  9. scrollamountを編集してマウスの速度を指定してスクロール速度を変更するか、scrolladjustを編集して累積スクロール量を指定してスクロールの実行方法を変更することにより、スクロールを微調整できます。デフォルトでは、その方向のスクロール量が他の方向のスクロール量の5倍を超える場合、スクロールは垂直または水平にスナップされます。
  10. マウスの速度と加速度を微調整することもできます。 LCtrl-Ralt-Dを押して現在のマウス速度を表示し、LCtrl-Ralt-Sを押して速度を下げ、LCtrl-Ralt-Fを押して速度を上げます。マウス設定にあるポインタ速度を変更します。 moveadjustを編集して、ポインターの動きをマウスの動きに変換する方法を変更します。デフォルトでは、低速は同じままで、高速は3倍され、その間にスムーズな移行があります。また、高いポインタ速度ではハードウェアまたはデフォルトであるため、すべてのマウスの動きは現在の動きと次の動きに広がります。マウスドライバは途切れ途切れの出力を生成します。 moveadjustが高速に実行されることを確認してください!

メソッド

私がさまざまなアプリケーションで発見したスクロールの11の異なる方法があります。一部のアプリケーションはこれらの1つにしか応答しませんが、これは非常に面倒です。メソッドはコード自体のコメントに記述されていますが、使用する正しいメソッドを決定する信頼できる方法がないため、動作しない各アプリケーションのメソッドを選択するには、手動でgettargetをカスタマイズする必要があります。デフォルトのホイールメッセージで。 scrollで発生する順序でメソッドを試してください。メソッドごとに、メッセージのターゲット、つまりctrl,window,parentを適切に変更してみる必要がある場合があります。

デバッグを切り替えるには、LCtrl-AppMenuを押します。通常の動きとスクロールの両方について、調整された量のマウスの生の動きを示します。マウスを動かさずにスクロールボタンを押し続けると、マウスの下にあるターゲットとgettargetによって選択されたコントロールの両方の祖先が表示されます。コントロール階層を確認し、メッセージの送信先を推測するために、Spy ++も使用する必要がある場合がありました。

コメント

64ビットのAutoHotkeyを使用すると、プログラマーのメモ帳でスクリプトが失敗しますが、32ビットのAutoHotkeyは完​​全に機能しますが、その理由はわかりません。他のアプリケーションにはそのような問題はないようです。それが私のスクリプトのバグであるなら、私は知ってうれしいです!

2
user21820

コードが長すぎるため、2つの回答に分割されます。手順については、3番目の回答を参照してください。

コードパート1

#commentflag // ; Change to C++ comment style

// Settings //

global rightbuttonscroll:=0 // set to 1 iff the right button is to be used for scrolling instead of the middle button
global scrollbeforeclick:=1 // set to 1 iff scrolling is allowed before the original button click otherwise scrolling is delayed for (clicklimit)
global leftrighttomiddle:=0 // set to 1 iff pressing both left+right buttons together is to be used to generate the middle button press
global scrolllimit:=3.5     // maximum scroll allowed between scroll button press and release for the original button click to be generated
global clicklimit:=210      // maximum time allowed between scroll button press and release for the original button click to be generated
global resetdelay:=210      // minimum time required between scroll button release and press for the original button click to be allowed
global interval:=35         // minimum interval between scrolling updates
global timelimit:=70        // maximum time allowed for continuing the scrolling update in each direction
global dbg:=false

// Initialization //

#singleinstance,force
#keyhistory 0
#usehook on
process priority,,H
setkeydelay 1
setcontroldelay -1
coordmode mouse,screen

global buttondown:=( rightbuttonscroll==1 ? 0x204 : 0x207 )
global buttonup:=( rightbuttonscroll==1 ? 0x205 : 0x208 )
global buttonoriginal:=( rightbuttonscroll==1 ? "RButton" : "MButton" )

global speed
global handling:=0
global scrolling:=0
global scrolldrag:=0
global scrollsticky:=0
global clicksticky:=0
global leftphysical:=0
global rightphysical:=0
global lefttopress:=0
global righttopress:=0
global middlepressed:=0

global mx,my
global dx,dy
global lx:=0
global ly:=0
global sx:=0
global sy:=0
global totalx:=0
global totaly:=0
global ctrl,window,parent
global methodx
global methody
global scrollbarx
global scrollbary

global max16bit:=32767
global sbinfo
varsetcapacity(sbinfo,28)
numput(28,sbinfo,0)
numput(23,sbinfo,4)

global mousehook:=dllcall("SetWindowsHookEx","int",14,"uint",RegisterCallback("handlemouse","fast"),"uint",0,"uint",0)

message("AutoHotkey loaded")
return

// Reload & Debug & Exit & Disable Mousehook //

LCtrl & RCtrl::reload
LCtrl & AppsKey::dbg:=!dbg
RCtrl & Esc::exitapp
RCtrl & LCtrl::dllcall("UnhookWindowsHookEx","uint",mousehook)

// Message //

messageoff:
    tooltip
return

message(text)
{
    tooltip % text  //,a_screenwidth-strlen(text)*7,a_screenheight-42
    settimer messageoff,-1000
}

// Mouse Movement //

moveadjust(byref x,byref y)
{
    movespread(x,y)
    z2:=x**2+y**2
    r:=(70+z2*4)/(70+z2)
    x:=rtoz(x*r)
    y:=rtoz(y*r)
}
movespread(byref x,byref y)
{
    nx:=rtoz(x/2)
    ny:=rtoz(y/2)
    x-=nx
    y-=ny
    x+=lx
    y+=ly
    lx:=nx
    ly:=ny
    settimer movereset,-210
}
movereset:
    lx:=0
    ly:=0
return

getspeed()
{
    DllCall("SystemParametersInfo","Int",112,"Int",0,"UIntP",speed,"Int",0)
}
setspeed()
{
    DllCall("SystemParametersInfo","Int",113,"Int",0,"UInt",speed,"Int",2)
}
showspeed()
{
    message("Mouse speed = " . speed)
}

<^>!d::
    getspeed()
    showspeed()
return
<^>!s::
    getspeed()
    if( speed>1 )
    {
        speed:=speed-1
    }
    setspeed()
    showspeed()
return
<^>!f::
    getspeed()
    if( speed<20 )
    {
        speed:=speed+1
    }
    setspeed()
    showspeed()
return

// Mouse Scrolling //

/*
    Usage
        Combination : Function
            ScrollButton-Release : ScrollButton-Click  (where the intervening time and scroll do not exceed the limits set)
            ScrollButton-Modifiers-Move : Modifiers-Scroll  (where Modifiers can be any combination of Shift and Ctrl and Alt)
        If rightbuttonscroll = 1 :
            LButton-RButton : MButton  (where the intervening time does not exceed the limit set)
            RButton-LButton : MButton  (where the intervening time does not exceed the limit set)
    Methods
        Send wheel messages (0x20a/0x20e) to the control
            The wheel amount is only 16-bit which is at most only 32767/120 wheel notches
            Some applications do not handle the wheel amount correctly so you may need to send integer multiples of wheel notches or many wheel messages
            Microsoft Word apparently assumes that wheel messages are always posted and behaves incorrectly unless PostMessage is used
        Send scrollbars' thumb position to the control
            This works for many scrollable controls but not all
        Send scroll messages (0x115/0x114) to the control unless its scrollbar thumb position is already at the end
            Many applications respond rather slowly to scroll messages
        Send scroll messages to the scrollbars
            This works for some applications and should be slightly faster than scroll messages to the parent
            Many applications respond rather slowly so wheel messages are preferable if they work
        Send scroll messages to the scrollbars' parents
            This is supposed to be a standard way to control scrollbars that are separate from the control
            Many applications respond rather slowly so wheel messages are preferable if they work
        Send keys (Up/Down/Left/Right/PgUp/PgDn) to the scrollbars
            This is equivalent to clicking the arrows or the empty space on either side of the thumb track
            Some applications respond slowly so keys should not be sent too fast
        Send keys to the control
            Only for rare cases that do not respond to anything else
        Call Office function SmallScroll
            This function only exists for Office applications and even then it is broken in some like PowerPoint
    Customization
        scrollamount(x)
            Can assume that x is non-negative
            Must return non-negative output
        scrolladjust(x,y)
            Can modify scroll amounts (x,y)
        gettarget()
            Can assume that (mx,my) is the current mouse position
            Must set ctrl to the handle of the control that is to be scrolled
            Must set parent to getparent(ctrl) if scrolltoscrollbarparent() or keystoscrollbar() is used
*/

scrollamount(x)
{
    return x**1.5/8
}

scrolladjust(byref x,byref y)
{
    ax:=abs(x)
    ay:=abs(y)
    z:=sqrt(x**2+y**2)
    if( ax>ay*5 )
    {
        x:=( x>0 ? z : -z )
        y:=0
    }
    if( ay>ax*5 )
    {
        y:=( y>0 ? z : -z )
        x:=0
    }
}

gettarget()
{
    gosub messageoff
    ctrl0:=getctrlat(mx,my)
    window:=getwindow(ctrl0)
    class:=getclass(window)
    title:=gettitle(window)
    ctrl:=ctrl0
    loop
    {
        ctrlname:=getnameaschild(ctrl)
        ctrlclass:=getclass(ctrl)
        parent:=getparent(ctrl)
        parentname:=getnameatroot(parent)
        parentclass:=getclass(parent)
        if( ctrl!=window and ( regexmatch(ctrlclass,"^(Button|T?ComboBox|CtrlNotifySink|SysLink)$")==1 or parentclass=="ComboBox" ) )
        {
            ctrl:=parent
            continue
        }
        break
    }
    gp:=getparent(parent)
    ggp:=getparent(gp)
    gpname:=getnameaschild(gp)
    ggpname:=getnameaschild(ggp)
    methodx:="wheel"        // needed for: Firefox , Gimp , ...
    methody:="wheel"        // needed for: Firefox , File Chooser , Explorer , Word , Outlook , IE , ...
    scrollbarx:=""
    scrollbary:=""
    if( ctrlclass=="ComboLBox" )            // Standard Combo Boxes dropdown list
    {
        methodx:="thumbpos"
        methody:="thumbpos"
    }
    if( ctrlclass=="ListBox" )          // Standard List Boxes
    {
        methodx:="thumbpos"
        methody:="thumbpos"
    }
    if( ctrlclass=="ScrollBar" )            // Standard ScrollBar controls
    {
        methodx:="scrolltoscrollbarparent"  // "scroll" works for most places but not some ( Character Map , ... )
        methody:="scrolltoscrollbarparent"  // "scroll" works for most places but not some ( Character Map , ... )
        scrollbarx:="scrollbar1"
        scrollbary:="scrollbar1"
    }
    if( class=="OpusApp" )          // Microsoft Word
    {
        methodx:="office"
        methody:="postwheel"
        if( gpname=="_WwB1" )
        {
            ctrl:=getdescendant(window,"_WwG1")
        }
    }
    if( class=="XLMAIN" )           // Microsoft Excel
    {
        methodx:="office"
        if( gpname=="Excel71" )
        {
            ctrl:=gp
        }
    }
    if( class=="PPTFrameClass" or class=="PP12FrameClass" )         // Microsoft PowerPoint
    {
        methody:="wheelsingle"
        ctrlnameatroot:=getnameatroot(ctrl)
        if( ctrlnameatroot=="NetUIHWND3" or ctrlnameatroot=="NetUIHWND4" )
        {
            ctrl:=getdescendant(window,"paneClassDC1")
            ctrlname:="paneClassDC1"
            parent:=getparent(ctrl)
        }
        if( ctrlname=="paneClassDC1" )
        {
            methodx:="scrolltoscrollbarparent"
            methody:="scrolltoscrollbarparent"  // PowerPoint scroll up is broken when there are 9 slides at 100% zoom in normal mode
            scrollbarx:="NUIScrollbar2"
            scrollbary:="NUIScrollbar1"
            //methody:="office" // PowerPoint does not update the view pane immediately and so it is disorienting
        }
        if( ctrlnameatroot=="NetUIHWND5" )
        {
            ctrl:=getdescendant(window,"paneClassDC2")
        }
    }
    if( class=="rctrl_renwnd32" )           // Microsoft Outlook
    {
        methodx:="office"
        gpclass:=getclass(gp)
        ctrlnameatroot:=getnameatroot(ctrl)
        if( ctrlclass=="SUPERGRID" )
        {
            methodx:="scroll"
            methody:="scroll"
        }
        if( gpclass=="SUPERGRID" )
        {
            methodx:="scroll"
            methody:="scroll"
            ctrl:=gp
        }
        if( gpname=="_WwB1" )
        {
            ctrl:=getdescendant(window,"_WwG1")
        }
    }
    if( ctrlclass=="OUTEXVLB" )         // Microsoft Outlook { Address Book , Group membership , ... }
    {
        methodx:="thumbpos"
        if( regexmatch(title,"Global Address List")==0 )
        {
            methody:="scroll"
        }
    }
    if( title=="Symbol" and regexmatch(class,"bosa_sdm_(msword|Microsoft Office Word 12.0|XL9|Mso96)")==1 )         // Microsoft Office Insert Symbol
    {
        controlget v,visible,,ScrollBar1,ahk_id %ctrl%
        if( v==0 )
        {
            ctrl:=getdescendant(window,"Edit1")
            methody:="thumbpos"
        }
    }
    if( class=="wndclass_desked_gsk" )          // Microsoft Visual Basic
    {
        if( ctrlclass=="VbaWindow" )
        {
            methodx:="scrolltoscrollbarparent"
            parent:=ctrl
            scrollbarx:="scrollbar2"
        }
    }
    if( regexmatch(ctrlclass,"^RichEdit20W(PT)?$")==1 )         // Windows Text Areas
    {
        methodx:="wheel"
        methody:="scroll"
    }
    if( class=="AcrobatSDIWindow" )         // Adobe Reader
    {
        if( regexmatch(parentname,"AVL_AVView")==1 )
        {
            ctrl:=getdescendant(parent,"AVL_AVView4")
            if( ctrl=="" )
            {
                ctrl:=getdescendant(parent,"AVL_AVView1")
            }
            methodx:="scrolltoscrollbarparent"
            scrollbarx:="scrollbar1"
            methody:="wheel"
        }
    }
    if( ggpname=="SHELLDLL_DefView1" )          // Windows Explorer Scrollbars
    {
        ctrl:=gp
        ctrlname:=gpname
        parentname:=ggpname
    }
    if( ctrlname=="DirectUIHWND1" )
    {
        if( parentname=="SHELLDLL_DefView1" )           // Windows Explorer (including Standard File Choosers)
        {
            methodx:="scrolltoscrollbar"    // "scrolltoscrollbarparent" also works
            scrollbarx:="scrollbar1"
            controlget v,visible,,ScrollBar2,ahk_id %ctrl%
            methody:=( v==1 ? "wheel" : "" )
        }
        if( class=="CabinetWClass" )
        {
            if( parentname=="XBabyHost1" )          // Control Panel
            {
                methody:="scrolltoscrollbar"    // "scrolltoscrollbarparent" also works
                scrollbary:="scrollbar1"
                if( title=="Personalization" )
                {
                    scrollbary:="scrollbar3"
                }
            }
        }
    }
    if( ctrlclass=="CharGridWClass" )           // Character Map
    {
        methody:="scrolltoscrollbarparent"
        scrollbary:="scrollbar1"
    }
    if( class=="ConsoleWindowClass" )           // Console Window
    {
        methodx:="thumbpos"
        methody:="thumbpos"
    }
    if( class=="WordPadClass" )         // WordPad
    {
        methodx:="scroll"
        methody:="scroll"   // WordPad scroll down is broken when scrolling horizontally at the same time
    }
    if( class=="ATL:006AD5B8" )         // Programmer's Notepad
    {
        methodx:="scroll"
    }
    if( class=="SWT_Window0" or ctrlclass=="Internet Explorer_Server" )         // Eclipse
    {
        methodx:="scroll"
    }
    if( ctrlclass=="TSynEdit" )         // TSynEdit ; Dev C++ , ...
    {
        methodx:="thumbpos"
        methody:="thumbpos"
    }
    if( ctrlclass=="TListView" )            // TlistView ; Dev C++ , ...
    {
        methodx:="scroll"
    }
    if( ctrlclass=="TPSSynEdit" )           // TPSSynEdit ; PSPad , ...
    {
        methodx:="thumbpos"
    }
    if( class=="QWidget" or class=="Qt5QWindowIcon" )
    {
        if( regexmatch(title,"LyX")==1 )            // Lyx
        {
            methodx:=""
                // prevents horizontal scrolling from becoming vertical scrolling in the edit pane
                // but disables horizontal scrolling everywhere else
        }
        if( regexmatch(title,"TeXworks$")>0 )           // TeXWorks
        {
            methodx:="wheelint"
            methody:="wheelint"
        }
    }
    if( class=="gdkWindowToplevel" )            // Gimp
    {
        methody:="wheelsingle"  // Gimp performs horizontal scrolling when the mouse is scrolled over the horizontal scrollbar
    }
    if( class=="SunAwtDialog" )         // Java AWT Dialogs ; GeoGebra , Logisim , ...
    {
        methody:="wheelint"
    }
    if( regexmatch(ctrlname,"IupCanvas")==1 )           // IupCanvas
    {
        methodx:="scroll"
    }
    if( class=="SunAwtFrame" )
    {
        if( regexmatch(title,"GeoGebra|.*\.ggb$")==1 )          // GeoGebra
        {
            methody:="wheelint"
        }
        if( regexmatch(title,"Logisim")==1 )            // Logisim
        {
            methodx:="keys" // performs scrolling only if the drawing area has the focus
        }
    }
    if( class=="MSPaintApp" )           // MSPaint
    {
        if( parentname=="MSPaintView1" )
        {
            methodx:="thumbpos"
            methody:="thumbpos"
        }
    }
    if( class=="ATL:643E3490" )         // Real World Paint
    {
        if( ctrlclass=="RWViewImageEdit" )
        {
            methodx:="scroll"
            methody:="scroll"
        }
    }
    if( ctrlclass=="DSUI:PagesView" )           // PDF-XChange Viewer (also as a browser plugin)
    {
        methodx:="scroll"
        methody:="wheelint"
    }
    if( ctrlclass=="PuTTY" )            // PuTTY
    {
        methody:="scroll"
    }
if( dbg )
{
p:=getparent(ctrl)
gp:=getparent(p)
ggp:=getparent(gp)
message( "Root class = " class 
    . "`nRoot title = " title 
    . "`nTarget = [" ctrl0 "]" 
        . "`n`t(as child) " getnameaschild(ctrl0) 
        . "`n`t(at root)  " getnameatroot(ctrl0) 
    . "`nControl = [" ctrl "]" 
        . "`n`t(at child) " getnameaschild(ctrl) 
        . "`n`t(as root)  " getnameatroot(ctrl) 
    . "`nControl ancestors = " 
        . "`n`t < [" p "] " getnameatroot(p) 
        . "`n`t < [" gp "] " getnameatroot(gp) 
        . "`n`t < [" ggp "] " getnameatroot(ggp) 
    . "`nMethod = (" methodx "," methody ")" 
    . "`nScrollbars = (" scrollbarx "," scrollbary ")" )
}
}

scroll:
    critical on
    if( getwindow(getctrlat(mx,my))!=window )
    {
        scrolling:=0
    }
    if( scrolling==0 )
    {
        return
    }
    settimer scroll,-%interval%
    if( scrollbeforeclick!=1 and scrolldrag==0 )
    {
        sx:=0
        sy:=0
        return
    }
    tx:=sx
    ty:=sy
    sx-=tx
    sy-=ty
    totalx+=tx
    totaly+=ty
    if( totalx**2+totaly**2>scrolllimit )
    {
        scrolldrag:=1
    }
    scrolladjust(tx,ty)
    rx:=0
    ry:=0
    comobjerror(false)
    if( tx!=0 )
    {
        if( methodx=="wheel" )
        {
            sendwheel("h",tx)
        }
        else if( methodx=="postwheel" )
        {
            postwheel("h",tx)
        }
        else
        {
            txi:=rtoz(tx)
            rx:=tx-txi
            if( txi!=0 )
            {
                if( methodx=="wheelint" )
                {
                    sendwheel("h",txi)
                }
                else if( methodx=="wheelsingle" )
                {
                    sendwheelsingle("h",txi)
                }
                else if( methodx=="thumbpos" )
                {
                    sendthumbpos("h",txi)
                }
                else if( methodx=="scroll" )
                {
                    sendscroll("h",txi)
                }
                else if( methodx=="scrolltoscrollbar" )
                {
                    sendscrolltoscrollbar(scrollbarx,txi)
                }
                else if( methodx=="scrolltoscrollbarparent" )
                {
                    sendscrolltoscrollbarparent(scrollbarx,"h",txi)
                }
                else if( methodx=="keys" )
                {
                    sendkeys("h",txi)
                }
                else if( methodx=="keystoscrollbar" )
                {
                    sendkeystoscrollbar(scrollbarx,txi)
                }
                else if( methodx=="office" )
                {
                    Acc_ObjectFromWindow(ctrl,-16).SmallScroll(0,0,(txi>0?txi:0),(txi<0?-txi:0))
                }
            }
        }
    }
    if( ty!=0 )
    {
        if( methody=="wheel" )
        {
            sendwheel("v",-ty)
        }
        else if( methody=="postwheel" )
        {
            postwheel("v",-ty)
        }
        else
        {
            tyi:=rtoz(ty)
            ry:=ty-tyi
            if( tyi!=0 )
            {
                if( methody=="wheelint" )
                {
                    sendwheel("v",-tyi)
                }
                else if( methody=="wheelsingle" )
                {
                    sendwheelsingle("v",-tyi)
                }
                else if( methody=="thumbpos" )
                {
                    sendthumbpos("v",tyi)
                }
                else if( methody=="scroll" )
                {
                    sendscroll("v",tyi)
                }
                else if( methody=="scrolltoscrollbar" )
                {
                    sendscrolltoscrollbar(scrollbary,tyi)
                }
                else if( methody=="scrolltoscrollbarparent" )
                {
                    sendscrolltoscrollbarparent(scrollbary,"v",tyi)
                }
                else if( methody=="keys" )
                {
                    sendkeys("v",tyi)
                }
                else if( methody=="keystoscrollbar" )
                {
                    sendkeystoscrollbar(scrollbary,tyi)
                }
                else if( methody=="office" )
                {
                    Acc_ObjectFromWindow(ctrl,-16).SmallScroll((tyi>0?tyi:0),(tyi<0?-tyi:0))
                }
            }
        }
    }
    comobjerror(true)
    sx:=rx
    sy:=ry
return

sendwheel(dir,amount)
{
    t:=a_tickcount
    msg:=( dir=="v" ? 0x20a : 0x20e )
    flags:=getkeystate("Ctrl")<<3|getkeystate("Shift")<<2
    amount*=120
    while( amount>max16bit )
    {
        sendmessage msg,max16bit<<16|flags,mx|my<<16,,ahk_id %ctrl%,,,,timelimit
        amount-=max16bit
        if( a_tickcount-t>=timelimit )
        {
            return
        }
    }
    while( amount<-max16bit )
    {
        sendmessage msg,-max16bit<<16|flags,mx|my<<16,,ahk_id %ctrl%,,,,timelimit
        amount+=max16bit
        if( a_tickcount-t>=timelimit )
        {
            return
        }
    }
    sendmessage msg,round(amount)<<16|flags,mx|my<<16,,ahk_id %ctrl%,,,,timelimit
}
postwheel(dir,amount)
{
    msg:=( dir=="v" ? 0x20a : 0x20e )
    flags:=getkeystate("Ctrl")<<3|getkeystate("Shift")<<2
    amount*=120
    while( amount>max16bit )
    {
        postmessage msg,max16bit<<16|flags,mx|my<<16,,ahk_id %ctrl%
        amount-=max16bit
    }
    while( amount<-max16bit )
    {
        postmessage msg,-max16bit<<16|flags,mx|my<<16,,ahk_id %ctrl%
        amount+=max16bit
    }
    postmessage msg,round(amount)<<16|flags,mx|my<<16,,ahk_id %ctrl%
}
sendwheelsingle(dir,amount)
{
    t:=a_tickcount
    msg:=( dir=="v" ? 0x20a : 0x20e )
    flags:=getkeystate("Ctrl")<<3|getkeystate("Shift")<<2
    loop % abs(amount)
    {
        sendmessage msg,(amount<0?-120:120)<<16|flags,mx|my<<16,,ahk_id %ctrl%,,,,timelimit
        if( a_tickcount-t>=timelimit )
        {
            return
        }
    }
}
sendthumbpos(dir,amount)
{
    msg:=( dir=="v" ? 0x115 : 0x114 )
    sb:=dllcall("GetScrollInfo","uint",ctrl,"int",(dir=="v"?1:0),"uint",&sbinfo)
    if( sb )
    {
        sbmin:=numget(sbinfo,8,"int")
        sbmax:=numget(sbinfo,12,"int")
        sbpos:=numget(sbinfo,20,"int")
        if( amount>max16bit )
        {
            amount=max16bit
        }
        if( amount<-max16bit )
        {
            amount=-max16bit
        }
        pos:=sbpos+amount
        if( pos<sbmin )
        {
            pos:=sbmin
        }
        if( pos>sbmax )
        {
            pos:=sbmax
        }
        sendmessage msg,pos<<16|4,,,ahk_id %ctrl%,,,,timelimit
    }
}
sendscroll(dir,amount)
{
    t:=a_tickcount
    msg:=( dir=="v" ? 0x115 : 0x114 )
    flag:=( amount<0 ? 0 : 1 )
    loop % abs(amount)
    {
        sb:=dllcall("GetScrollInfo","uint",ctrl,"int",(dir=="v"?1:0),"uint",&sbinfo)
        if( sb )
        {
            sbmin:=numget(sbinfo,8,"int")
            sbmax:=numget(sbinfo,12,"int")
            sbpage:=numget(sbinfo,16,"uint")
            sbpos:=numget(sbinfo,20,"int")
            if( ( sbpos==sbmin and amount<0 ) or ( sbpos+sbpage==sbmax+1 and amount>0 ) )
            {
                return
            }
        }
        sendmessage msg,flag,,,ahk_id %ctrl%,,,,timelimit
        if( a_tickcount-t>=timelimit )
        {
            return
        }
    }
}
sendscrolltoscrollbar(name,amount)
{
    t:=a_tickcount
    flag:=( amount<0 ? 0 : 1 )
    loop % abs(amount)
    {
        sendmessage 0x115,flag,,%name%,ahk_id %ctrl%,,,,timelimit
        if( a_tickcount-t>=timelimit )
        {
            return
        }
    }
}
sendscrolltoscrollbarparent(name,dir,amount)
{
    sb:=getdescendant(parent,name)
    sbp:=getparent(sb)
    t:=a_tickcount
    msg:=( dir=="v" ? 0x115 : 0x114 )
    flag:=( amount<0 ? 0 : 1 )
    loop % abs(amount)
    {
        sendmessage msg,flag,sb,,ahk_id %sbp%,,,,timelimit
        if( a_tickcount-t>=timelimit )
        {
            return
        }
    }
}
sendkeys(dir,amount)
{
    t:=a_tickcount
    key:=( dir=="v" ? ( amount<0 ? "{Up}" : "{Down}" ) : ( amount<0 ? "{Left}" : "{Right}" ) )
    loop % abs(amount)
    {
        controlsend, ,%key%,ahk_id %ctrl%
        if( a_tickcount-t>=timelimit )
        {
            return
        }
    }
}
sendkeystoscrollbar(name,amount)
{
    t:=a_tickcount
    key:=( amount<0 ? "{Up}" : "{Down}" )
    controlget e,enabled,,%name%,ahk_id %parent%
    if( e==1 )
    {
        loop % abs(amount)
        {
            controlsend %name%,%key%,ahk_id %parent%
            if( a_tickcount-t>=timelimit )
            {
                break
            }
        }
    }
}
2
user21820

コードが長すぎるため、2つの回答に分割されます。手順については、3番目の回答を参照してください。

コードパート2

scrollbuttonreset:
    scrollsticky:=0
return

scrollbuttoncannotclick:
    scrolldrag:=1
    righttopress:=0
return

clickreset:
    clicksticky:=0
return

scrollbuttondown:
    critical on
    mousegetpos mx,my
    gettarget()
    sx:=0
    sy:=0
    totalx:=0
    totaly:=0
    settimer scroll,-%interval%
    settimer scrollbuttonreset,off
return

scrollbuttonup:
    critical on
    if( scrolldrag==0 and scrollsticky==0 )
    {
        handling++
        sendevent {Blind}{%buttonoriginal% down}
        sendevent {Blind}{%buttonoriginal% up}
        handling--
        scrolling:=0
    }
    scrolldrag:=0
    righttopress:=0
    settimer scrollbuttondown,off
    settimer scrollbuttoncannotclick,off
    scrollsticky:=1
    settimer scrollbuttonreset,-%resetdelay%
return

leftdown:
    critical on
    lefttopress:=0
    handling++
    sendevent {Blind}{LButton down}
    handling--
return

rightdown:
    critical on
    righttopress:=0
    handling++
    sendevent {Blind}{RButton down}
    handling--
return

middledown:
    critical on
    scrolling:=0
    scrolldrag:=0
    middlepressed:=1
    settimer scrollbuttondown,off
    settimer scrollbuttoncannotclick,off
    handling++
    sendevent {Blind}{MButton down}
    handling--
return

leftup:
    critical on
    handling++
    sendevent {Blind}{LButton up}
    handling--
    clicksticky:=1
    settimer clickreset,-%resetdelay%
return

rightup:
    critical on
    handling++
    sendevent {Blind}{RButton up}
    handling--
    clicksticky:=1
    settimer clickreset,-%resetdelay%
return

middleup:
    critical on
    middlepressed:=0
    handling++
    sendevent {Blind}{MButton up}
    handling--
    clicksticky:=1
    settimer clickreset,-%resetdelay%
return

leftclick:
    critical on
    gosub leftdown
    gosub leftup
return

rightclick:
    critical on
    gosub rightdown
    gosub rightup
return

// Mouse handler //

handlemouse(nCode,wParam,lParam)
{
    critical on
    if( a_ispaused==1 )
    {
        exitapp // something goes wrong if it remains paused
    }
    o:=0
    if( handling==0 && nCode>=0 )
    {
        if( wParam==0x201 )
        {
            leftphysical:=1
        }
        else if( wParam==0x204 )
        {
            rightphysical:=1
        }
        else if( wParam==0x202 )
        {
            leftphysical:=0
        }
        else if( wParam==0x205 )
        {
            rightphysical:=0
        }
        if( wParam==0x200 )
        {
            // Handle mouse move //
            mousegetpos mx,my
            x:=numget(lParam+0,0,"int") // the "+0" is necessary!
            y:=numget(lParam+0,4,"int") // the "+0" is necessary!
            dx:=x-mx
            dy:=y-my
            if( scrolling==0 )
            {
                // Click immediately on mouse drag //
                if( leftrighttomiddle==1 )
                {
                    if( lefttopress==1 )
                    {
                        settimer leftdown,-0
                    }
                    if( righttopress==1 )
                    {
                        settimer rightdown,-0
                    }
                }
                // Adjust mouse movement //
                moveadjust(dx,dy)
                handling++
                mousemove dx,dy,0,R
                handling--
if( dbg )
{
message( "Origin = " mx " " my "`nMove = " x-mx " " y-my "`n         -> " dx " " dy )
}
                // Release mouse buttons if out of sync with physical state //
                if( getkeystate("LButton")>leftphysical )
                {
                    settimer leftup,-0
                }
                if( getkeystate("RButton")>rightphysical )
                {
                    settimer rightup,-0
                }
                o:=1
            }
            else
            {
                // Handle mouse move when scrolling //
                if( dx!=0 or dy!=0 )
                {
                    sx+=( dx>0 ? scrollamount(dx) : -scrollamount(-dx) )
                    sy+=( dy>0 ? scrollamount(dy) : -scrollamount(-dy) )
if( dbg )
{
message( "Origin = " mx " " my "`nMove = " dx " " dy "`n         -> " round(sx,2) " " round(sy,2) )
}
                }
                o:=1
            }
        }
        else if( scrolling==0 )
        {
            if( wParam==buttondown and middlepressed==0 and lefttopress==0 )
            {
                // Handle scroll button down //
                if( getkeystate("Ctrl")==0 and getkeystate("Shift")==0 and getkeystate("Alt")==0 )
                {
                    // Handle scroll start //
                    scrolling:=1
                    scrolldrag:=0
                    if( buttondown==0x204 )
                    {
                        righttopress:=1
                    }
                    settimer scrollbuttondown,-0
                    settimer scrollbuttoncannotclick,-%clicklimit%
                    o:=1
                }
            }
        }
        else if( scrolling==1 )
        {
            if( wParam==buttonup )
            {
                // Handle scroll button up //
                scrolling:=0
                settimer scrollbuttonup,-0
                o:=1
            }
        }
        if( leftrighttomiddle==1 and o==0 )
        {
            if( middlepressed==1 )
            {
                // Release middle button only when both left and right buttons are released //
                if( leftphysical==0 and rightphysical==0 )
                {
                    settimer middleup,-0
                }
                o:=1
            }
            else if( scrolldrag==0 )
            {
                // Process left+right=middle //
                if( wParam==0x201 and scrollsticky==0 )
                {
                    // Handle left button down //
                    if( righttopress==0 )
                    {
                        if( clicksticky==1 )
                        {
                            settimer leftdown,-0
                        }
                        else
                        {
                            lefttopress:=1
                            settimer leftdown,-%clicklimit%
                        }
                    }
                    else
                    {
                        righttopress:=0
                        settimer rightdown,off
                        settimer middledown,-0
                    }
                    o:=1
                }
                else if( wParam==0x204 and scrollsticky==0 )
                {
                    // Handle right button down //
                    if( lefttopress==0 )
                    {
                        if( clicksticky==1 )
                        {
                            settimer rightdown,-0
                        }
                        else
                        {
                            righttopress:=1
                            settimer rightdown,-%clicklimit%
                        }
                    }
                    else
                    {
                        lefttopress:=0
                        settimer leftdown,off
                        settimer middledown,-0
                    }
                    o:=1
                }
                else if( wParam==0x202 )
                {
                    // Handle left button up //
                    if( lefttopress==1 )
                    {
                        settimer leftdown,off
                        settimer leftclick,-0
                    }
                    else
                    {
                        settimer leftup,-0
                    }
                    o:=1
                }
                else if( wParam==0x205 )
                {
                    // Handle right button up //
                    if( righttopress==1 )
                    {
                        settimer rightdown,off
                        settimer rightclick,-0
                    }
                    else
                    {
                        settimer rightup,-0
                    }
                    o:=1
                }
            }
        }
    }
    // Pass on any other mouse events //
    if( o==0 )
    {
        o:=dllcall("CallNextHookEx","uint",mousehook,"int",nCode,"uint",wParam,"uint",lParam)
    }
    return o
}

// Utilities //

rtoz(r)
{
    return ( r>0 ? floor(r) : ceil(r) )
}
getparent(handle)
{
    return dllcall("GetParent","uint",handle)
}
getancestor(handle,steps)
{
    if( steps==0 )
    {
        return handle
    }
    if( steps>0 )
    {
        return getancestor(getparent(handle),steps-1)
    }
    return ""
}
getname(root,handle)
{
    local CH,CN,S,P
    WinGet, CH, ControlListHwnd, ahk_id %root%
    WinGet, CN, ControlList, ahk_id %root%
    setformat integerfast,h
    handle+=0
    handle.=""
    setformat integerfast,d
    LF:= "`n",  CH:= LF CH LF, CN:= LF CN LF,  S:= SubStr( CH, 1, InStr( CH, LF handle LF ) )
    StringReplace, S, S,`n,`n, UseErrorLevel
    StringGetPos, P, CN, `n, L%ErrorLevel%
    Return SubStr( CN, P+2, InStr( CN, LF, 0, P+2 ) -P-2 )
}
getdescendant(handle,name)
{
    local CH,CN,S,P
    WinGet, CH, ControlListHwnd, ahk_id %handle%
    WinGet, CN, ControlList, ahk_id %handle%
    setformat integerfast,h
    handle+=0
    handle.=""
    setformat integerfast,d
    LF:= "`n",  CH:= LF CH LF, CN:= LF CN LF,  S:= SubStr( CN, 1, InStr( CN, LF name LF ) )
    StringReplace, S, S,`n,`n, UseErrorLevel
    StringGetPos, P, CH, `n, L%ErrorLevel%
    Return SubStr( CH, P+2, InStr( CH, LF, 0, P+2 ) -P-2 )*1
}
getnameatroot(handle)
{
    return getname(dllcall("GetAncestor","uint",handle,"uint",2),handle)
}
getnameaschild(handle)
{
    return getname(getparent(handle),handle)
}
getclass(handle)
{
    local class
    wingetclass class,ahk_id %handle%
    return class
}
gettitle(handle)
{
    local title
    wingettitle title,ahk_id %handle%
    return title
}
getposition(handle,byref left,byref top,byref right,byref bottom)
{
    local rect
    varsetcapacity(rect,16)
    dllcall("GetWindowRect","uint",handle,"uint",&rect)
    left:=numget(rect,0,"int")
    top:=numget(rect,4,"int")
    right:=numget(rect,8,"int")
    bottom:=numget(rect,12,"int")
}
getctrlat2(x,y,first,current)
{
    /*
        Pushes the following invisible container controls to the back because they are in front of their contents for no reason
            SysTabControl32 : The usual class that contains tabbed panes ( Mouse properties , ... )
            Static : A class occasionally used to contain tabbed panes ( Programmer's Notepad Options > Fonts and Colours > Advanced , ... )
            Button : A typical class used to contain a List Box ( Outlook Contact > Properties > General > Members , ... )
        Executes WindowFromPoint again to access the contents of such container controls
    */
    local handle,class,style
    class:=getclass(current)
    winget style,style,ahk_id %current%
    if( class=="SysTabControl32" or class=="Static" or ( class=="Button" and (style&0x7)==0x7 ) )
    {
        dllcall("SetWindowPos","uint",current,"uint",1,"int",0,"int",0,"int",0,"int",0,"uint",0x3)  // Push it to the back where it belongs
        handle:=dllcall("WindowFromPoint","int",x,"int",y)
        //handle:=DllCall( "WindowFromPoint", "int64", (y << 32) | (x & 0xFFFFFFFF), "Ptr") // for negative 64-bit
        if( handle==first )
        {
            return first
        }
        return getctrlat2(x,y,first,handle)
    }
    return current
}
getctrlat(x,y)
{
    local handle
    handle:=dllcall("WindowFromPoint","int",x,"int",y)
    //handle:=DllCall( "WindowFromPoint", "int64", (y << 32) | (x & 0xFFFFFFFF), "Ptr") // for negative 64-bit
    return getctrlat2(x,y,handle,handle)
}
getwindow(handle)
{
    return dllcall("GetAncestor","uint",handle,"uint",2)
}

Acc_Init()
{
    Static h
    If Not h
        h:=DllCall("LoadLibrary","Str","oleacc","Ptr")
}
Acc_ObjectFromWindow(hWnd, idObject = -4)
{
    local o
    Acc_Init()
    o:=DllCall("oleacc\AccessibleObjectFromWindow"
        , "Ptr", hWnd
        , "UInt", idObject&=0xFFFFFFFF
        , "Ptr", -VarSetCapacity(IID,16)+NumPut(idObject==0xFFFFFFF0?0x46000000000000C0:0x719B3800AA000C81
            ,NumPut(idObject==0xFFFFFFF0?0x0000000000020400:0x11CF3C3D618736E0,IID,"Int64")
            ,"Int64") 
        ,"Ptr*", pacc)
    if( o==0 )
        Return ComObjEnwrap(9,pacc,1)
}
1
user21820