1

Проблемы

Предположим, Windows 7 машина, особенно ноутбук.

  1. Ни один драйвер мыши не позволяет удерживать правую кнопку мыши и перемещать мышь для прокрутки, но при этом сохраняет функциональность правой кнопки мыши. Это полезно, если у вас нет средней кнопки.
  2. Драйверы мыши не работают в некоторых приложениях, а некоторые, такие как UltraNav, чувствуют себя очень резко в большинстве приложений, где они работают.
  3. Драйвер UltraNav для ThinkPad не позволяет средней кнопке функционировать как средний щелчок одновременно с прокруткой, удерживая ее нажатой и перемещая мышь.
  4. Драйвер UltraNav также не позволяет щелкать и перетаскивать одновременно с прокруткой, что чрезвычайно полезно при редактировании документов.
  5. Ускоренная прокрутка невозможна на колесе мыши и плохо выполняется драйвером UltraNav.
  6. При использовании драйвера мыши по умолчанию максимально возможная скорость указателя может быть слишком медленной и утомительной, особенно для трековых точек ThinkPad.

То, что я хотел - это скрипт AutoHotkey, чтобы решить все эти проблемы, но ничего, что я нашел в Интернете, не приблизилось даже к решению некоторых из них. Через два года я разработал полное решение ниже. Конечно, некоторые методы, которые я использовал, были адаптированы из различных постов в Интернете, но часто у каждого поста был только один или два метода, и поэтому они не работают для многих приложений.

Комментарии приветствуются! Однако, пожалуйста, поймите, что у меня может не быть времени для реализации предложений или устранения неполадок, если это не работает для вас. Спасибо!

3 ответа3

1

Я разработал свой собственный скрипт AutoHotkey, который использует низкоуровневый хук мыши для решения всех вышеуказанных проблем. Код слишком длинный, поэтому смотрите другой ответ для кода.

Важные инструкции

Используйте 32-битную версию AutoHotkey, даже если вы используете 64-битную Windows, иначе она просто не будет работать в некоторых местах! Запустите сценарий при входе в систему с максимально возможными привилегиями для пользователя, используя планировщик заданий, иначе он не будет работать для пользователя с правами администратора в приложениях, работающих с полными правами администратора. Если у вас установлен UltraNav, выберите "Smooth" для средней кнопки мыши, в противном случае он будет мешать скрипту по неизвестной причине и прокрутка не будет работать.

В редких случаях что-то не так с крючком мыши, особенно если вы пытаетесь прокрутить, когда приложение меняет свой графический интерфейс. Я подозреваю, что это потому, что я не запускаю его с приоритетом в реальном времени, но я думаю, что это слишком рискованно, потому что, если он застрянет, то, возможно, не будет никакого способа остановить это. В настоящее время он настроен на работу с высоким приоритетом. Если что-то пойдет не так, попробуйте следующие горячие клавиши по порядку.

LCtrl-RCtrl: Перезагрузить. RCtrl-LCtrl: отключить перехват мыши. (Если ваша мышь теперь работает, перезагрузите.) RCtrl-Esc: отключить весь скрипт. (Вам нужно будет запустить его снова.)

Характеристики

  1. По умолчанию прокрутка выполняется, когда средняя кнопка мыши нажата, а мышь перемещена. Можно установить rightbuttonscroll:=1 чтобы вместо этого использовать правую кнопку мыши (особенно для ноутбуков, у которых нет средней кнопки мыши), в этом случае, чтобы получить оригинальную функциональность перетаскивания правой кнопкой мыши, может потребоваться нажать дополнительный Ctrl/Shift/Alt до этого, в зависимости от конкретного приложения. (В большинстве приложений хотя бы одна из этих комбинаций делает то же самое, что и исходный щелчок правой кнопкой мыши.)
  2. Если у человека нет средней кнопки мыши, можно также установить leftrighttomiddle:=1 , а затем нажатие левой и правой кнопок мыши в пределах clicklimit (миллисекунды) генерирует нажатие средней кнопки, а отпускание обеих генерирует отпускание средней кнопки. Чтобы удерживать сгенерированную среднюю кнопку нажатой, достаточно удерживать нажатой только одну из двух реальных кнопок.
  3. Прокрутка функционирует независимо от других кнопок мыши, поэтому, например, можно прокручивать в любое время, удерживая левую кнопку мыши во время операции выбора, если приложение поддерживает ее. (Некоторые приложения не допускают прокрутку при выборе по какой-то странной причине.)
  4. По умолчанию после нажатия и отпускания кнопки прокрутки (средней или правой кнопки мыши) в пределах clicklimit генерируется исходное нажатие кнопки прокрутки. Если кнопка прокрутки удерживается дольше, прокрутка включена и отпускание кнопки прокрутки не вызовет щелчка. Чтобы включить прокрутку даже во время этой начальной задержки, установите scrollbeforeclick:=1 . С этим параметром щелчок возможен, даже если небольшая прокрутка уже была сделана, если она не превышает scrolllimit .
  5. Прокрутка немного запутана, что означает, что если кто-то случайно resetdelay кнопку прокрутки во время прокрутки с меньшей задержкой сброса , то он будет обрабатываться так, как если бы он не происходил, и прокрутка продолжается. В частности, это означает, что случайное нажатие в конце прокрутки не будет генерировать исходное нажатие кнопки прокрутки.
  6. Нажатие также слегка залипает, что означает, что если нажать кнопку мыши в пределах resetdelay после отпускания кнопки мыши, то предполагается, что один
  7. Некоторые приложения очень медленно работают с прокруткой, поэтому мы не можем подать слишком много команд для прокрутки. interval - это интервал между партиями команд, связанных с прокруткой, отправляемых приложению. В каждом интервале все действия пользователя по прокрутке объединяются в одно. Уменьшение интервала делает прокрутку более отзывчивой для пользователя, но может задушить плохо разработанные приложения. Если необходим точный контроль, всегда можно установить значение по умолчанию в начале gettarget и настроить его в зависимости от приложения.
  8. Некоторым приложениям действительно требуется слишком много времени для обработки команд прокрутки, и поэтому, если команда не обрабатывается в установленные timelimit , все остальные команды в пакете для этого интервала отменяются.
  9. Можно точно настроить прокрутку, отредактировав scrollamount чтобы изменить скорость прокрутки, учитывая скорость мыши, или отредактировав scrolladjust чтобы изменить способ прокрутки, выполняемый с учетом накопленных значений прокрутки. По умолчанию прокрутка привязывается к вертикали или горизонтали, если количество прокрутки в этом направлении более чем в 5 раз больше, чем в другом направлении.
  10. Можно также точно настроить скорость и ускорение мыши. Нажмите LCtrl-Ralt-D чтобы показать текущую скорость мыши, LCtrl-Ralt-S чтобы уменьшить скорость, и LCtrl-Ralt-F чтобы увеличить скорость. Это изменит скорость указателя в настройках мыши. Отредактируйте moveadjust чтобы изменить способ, которым движения указателя преобразуются в движения мыши. По умолчанию низкие скорости остаются неизменными, а высокие скорости умножаются на 3, и между ними происходит плавный переход, а также каждое движение мыши распространяется на текущую и следующую, потому что при высоких скоростях указателя аппаратное обеспечение или значение по умолчанию Драйвер мыши производит прерывистый вывод. Удостоверьтесь, что moveadjust работает быстро!

методы

Существует 11 различных способов прокрутки, которые я обнаружил в различных приложениях. Некоторые приложения отвечают только на один из них, что довольно раздражает. Методы описаны в комментариях в самом коде, но нет надежного способа определить правильный метод для использования, поэтому приходится вручную настраивать gettarget чтобы выбрать метод для каждого приложения, которое не работает с сообщениями колеса по умолчанию. , Попробуйте методы в порядке их появления в scroll . Для каждого метода вам, возможно, придется поэкспериментировать с изменением целей сообщений, а именно ctrl,window,parent в зависимости от ситуации.

Чтобы включить отладку, нажмите LCtrl-AppMenu . Он показывает необработанные движения мыши с установленными величинами как для обычного движения, так и для прокрутки. Если вы удерживаете кнопку прокрутки, не перемещая мышь, она показывает происхождение цели под мышью и элемента управления, выбранного gettarget . Иногда мне приходилось также использовать Spy++, чтобы увидеть иерархию элементов управления и угадать, куда отправлять сообщения.

Комментарии

64-битный AutoHotkey приводит к сбою моего скрипта для Блокнота программиста, в то время как 32-битный AutoHotkey работает отлично, и я не знаю почему. Другие приложения, похоже, не имеют такой проблемы. Если это ошибка в моем сценарии, я буду рад узнать!

1

Код слишком длинный, поэтому он разбит на два ответа, и см. Третий ответ для инструкций.

Код Часть 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
            }
        }
    }
}
1

Код слишком длинный, поэтому он разбит на два ответа, и см. Третий ответ для инструкций.

Код Часть 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)
}

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .