问题
假设有一台 Windows 7 机器,尤其是一台笔记本电脑。
- 没有鼠标驱动程序允许按住鼠标右键并移动鼠标进行滚动,同时保留鼠标右键功能。如果您没有中键,这很有用。
- 鼠标驱动程序对于某些应用程序不起作用,而像 UltraNav 这样的一些驱动程序对于大多数可以工作的程序来说感觉很不稳定。
- ThinkPad 的 UltraNav 驱动程序不允许中间按钮发挥中键单击的功能,同时还不允许通过按住中间按钮并移动鼠标来进行滚动。
- UltraNav 驱动程序也不允许在滚动的同时进行单击和拖动,这在编辑文档时非常有用。
- 滚轮鼠标无法实现加速滚动,而且 UltraNav 驱动程序的实现效果很差。
- 使用默认鼠标驱动程序,最快的指针速度可能仍然太慢且令人疲劳,特别是对于 ThinkPad 指点杆而言。
我想要一个 AutoHotkey 脚本来解决所有这些问题,但我在网上找到的任何东西都无法解决其中的一些问题。经过两年的时间,我开发了一个完整的解决方案。当然,我使用的一些技术是从互联网上的各种帖子改编而来的,但通常每篇帖子只有一两种方法,因此它们不适用于许多应用程序。
欢迎评论!不过,请理解,如果它不适合您,我可能没有时间实施建议或排除故障。谢谢!
答案1
我设计了自己的 AutoHotkey 脚本,使用低级鼠标钩子来解决上述所有问题。代码太长,请参阅其他答案以获取代码。
重要说明
即使您使用的是 64 位 Windows,也请使用 32 位版本的 AutoHotkey,否则它在某些地方将无法工作!使用任务计划程序以用户的最高权限登录时运行脚本,否则它将无法在以完全管理员权限运行的应用程序中为管理员用户工作。如果您安装了 UltraNav,请为鼠标中键选择“平滑”,否则它会因某些未知原因干扰脚本,并且滚动将不起作用。
鼠标钩子偶尔会出现问题,特别是当您尝试在应用程序更改其 GUI 时滚动时。我怀疑这是因为我没有以实时优先级运行它,但我认为这太冒险了,因为如果它卡住了,那么可能就没有办法阻止它。目前它设置为以高优先级运行。如果出现任何问题,请按顺序尝试以下热键。
LCtrl-RCtrl
:重新加载。
RCtrl-LCtrl
:禁用鼠标挂钩。(如果您的鼠标现在可以工作,请重新加载。)
RCtrl-Esc
:禁用整个脚本。(您需要再次运行它。)
特征
- 默认情况下,按住鼠标中键并移动鼠标时会执行滚动。您可以设置
rightbuttonscroll:=1
为使用鼠标右键(尤其是对于没有鼠标中键的笔记本电脑),在这种情况下,要获得原始的右键单击拖动功能,可能必须在此之前再按一次 Ctrl/Shift/Alt,具体取决于特定的应用程序。(在大多数应用程序中,这些组合中的至少一个与原始的右键单击相同。) - 如果没有鼠标中键,也可以设置
leftrighttomiddle:=1
,然后在clicklimit
(毫秒) 内同时按下鼠标左键和右键将产生按下中键的效果,而同时松开鼠标左键和右键将产生释放中键的效果。要保持生成的中键按下,只需保持两个真实按钮中的一个按下即可。 - 滚动功能独立于其他鼠标按钮,因此,例如,只要应用程序支持,就可以在选择操作期间按住鼠标左键随时滚动。(出于某些奇怪的原因,某些应用程序不允许在选择时滚动。)
- 默认情况下,在 内按下并释放滚动按钮(无论是鼠标中键还是鼠标右键)后
clicklimit
,将产生原始滚动按钮点击。如果滚动按钮按住的时间超过 ,则启用滚动,释放滚动按钮将不会产生任何点击。要启用即使在初始延迟期间的滚动,请设置scrollbeforeclick:=1
。使用此设置,即使已经进行了轻微滚动,只要不超过 ,也可以进行点击scrolllimit
。 - 滚动略有粘滞,这意味着如果在滚动过程中意外释放滚动按钮的时间少于
resetdelay
,则将被视为未发生该操作,滚动将继续。具体而言,这意味着在滚动结束时意外点击不会产生原始滚动按钮点击。 - 点击也有点粘滞,这意味着如果
resetdelay
在释放鼠标按钮后按下鼠标按钮,则假定 - 有些应用程序在处理滚动时非常迟缓,因此我们无法向它们发送太多命令来执行滚动。
interval
是发送给应用程序的滚动相关命令批次之间的间隔。在每个间隔中,用户的所有滚动操作都合并为一个。减少间隔可使滚动对用户更具响应性,但可能会使设计不良的应用程序受阻。如果需要精细控制,可以始终在开始时设置默认值,gettarget
并根据应用程序进行调整。 - 某些应用程序确实需要花费太长时间来处理滚动命令,因此如果某个命令未在 内得到处理
timelimit
,则该间隔内批次中所有剩余的命令都将被中止。 - 可以通过编辑来微调滚动
scrollamount
,根据鼠标速度更改滚动速度,或scrolladjust
根据累积滚动量更改滚动方式。默认情况下,如果某个方向的滚动量是另一个方向的 5 倍以上,则滚动会捕捉到垂直或水平方向。 - 您还可以微调鼠标速度和加速度。
LCtrl-Ralt-D
按 显示当前鼠标速度,LCtrl-Ralt-S
按 降低速度,LCtrl-Ralt-F
按 增加速度。它将更改鼠标设置中的指针速度。编辑moveadjust
以更改指针移动转换为鼠标移动的方式。默认情况下,低速保持不变,高速乘以 3,两者之间有平滑的过渡,而且每个鼠标移动都分布在当前移动和下一个移动中,因为在高指针速度下,硬件或默认鼠标驱动程序会产生不稳定的输出。确保moveadjust
运行速度快!
方法
我发现各种应用程序中有 11 种不同的滚动方法。有些应用程序只响应其中一种,这很烦人。这些方法在代码本身的注释中进行了描述,但没有可靠的方法来确定要使用的正确方法,因此必须手动自定义gettarget
以选择每个不适用于默认滚轮消息的应用程序的方法。按照它们出现的顺序尝试这些方法scroll
。对于每种方法,您可能必须尝试根据需要更改消息的目标ctrl,window,parent
。
要切换调试,请按LCtrl-AppMenu
。它显示鼠标的原始移动和调整量,包括正常移动和滚动。如果您按住滚动按钮而不移动鼠标,它会显示鼠标下的目标和 选择的控件的祖先gettarget
。我有时还必须使用 Spy++ 来查看控件层次结构并猜测将消息发送到哪里。
评论
64 位 AutoHotkey 导致我的脚本无法在 Programmer's Notepad 上运行,而 32 位 AutoHotkey 却运行正常,我不知道为什么。其他应用程序似乎没有这样的问题。如果这是我的脚本中的错误,我会很高兴知道!
答案2
代码太长,因此分为两个答案,请参阅第三个答案以获取说明。
代码第 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
}
}
}
}
答案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)
}