Code: Select all
#Requires AutoHotkey v2.0
WS_VSCROLL := 0x200000
WM_VSCROLL := 0x0115
SB_LINEUP := 0
SB_LINEDOWN := 1
wnd := Gui(WS_VSCROLL)
wnd.OnEvent('Close', (*) => ExitApp())
wnd.OnEvent('Size', UpdateScrollBars.Bind(wnd))
wnd.AddText(, "Please enter your name:")
Loop 30 {
wnd.AddText('xm y+15 w45 right', 'Line ' . A_Index)
wnd.AddEdit('x+5 yp-3 w200', 'Text ' . A_Index)
}
wnd.AddText('xm y+0') ; <—— invisible text to push back the lower border
wnd.Show('h240')
OnMessage(WM_VSCROLL, OnScroll)
#HotIf WinActive(wnd)
WheelUp::
WheelDown:: {
Loop 3 {
SendMessage WM_VSCROLL, A_ThisHotkey ~= 'Up' ? SB_LINEUP : SB_LINEDOWN,,, wnd
}
}
UpdateScrollBars(GuiObj, *) {
; SIF_RANGE = 0x1, SIF_PAGE = 0x2, SIF_DISABLENOSCROLL = 0x8, SB_HORZ = 0, SB_VERT = 1
; Calculate scrolling area.
WinGetClientPos(, , &GuiW, &GuiH, GuiObj.Hwnd)
L := T := 2147483647 ; Left, Top
R := B := -2147483648 ; Right, Bottom
For CtrlHwnd In WinGetControlsHwnd(GuiObj.Hwnd) {
ControlGetPos(&CX, &CY, &CW, &CH, CtrlHwnd)
L := Min(CX, L)
T := Min(CY, T)
R := Max(CX + CW, R)
B := Max(CY + CH, B)
}
L -= 8, T -= 8
R += 8, B += 8
ScrW := R - L ; scroll width
ScrH := B - T ; scroll height
; Initialize SCROLLINFO.
SI := Buffer(28, 0)
NumPut("UInt", 28, "UInt", 3, SI, 0) ; cbSize , fMask: SIF_RANGE | SIF_PAGE
; Update horizontal scroll bar.
NumPut("Int", ScrW, "Int", GuiW, SI, 12) ; nMax , nPage
DllCall("SetScrollInfo", "Ptr", GuiObj.Hwnd, "Int", 0, "Ptr", SI, "Int", 1) ; SB_HORZ
; Update vertical scroll bar.
; NumPut("UInt", SIF_RANGE | SIF_PAGE | SIF_DISABLENOSCROLL, SI, 4) ; fMask
NumPut("Int", ScrH, "UInt", GuiH, SI, 12) ; nMax , nPage
DllCall("SetScrollInfo", "Ptr", GuiObj.Hwnd, "Int", 1, "Ptr", SI, "Int", 1) ; SB_VERT
; Scroll if necessary
X := (L < 0) && (R < GuiW) ? Min(Abs(L), GuiW - R) : 0
Y := (T < 0) && (B < GuiH) ? Min(Abs(T), GuiH - B) : 0
If (X || Y)
DllCall("ScrollWindow", "Ptr", GuiObj.Hwnd, "Int", X, "Int", Y, "Ptr", 0, "Ptr", 0)
}
; ======================================================================================================================
OnWheel(W, L, M, H) {
If !(HWND := WinExist()) || GuiCtrlFromHwnd(H)
Return
HT := DllCall("SendMessage", "Ptr", HWND, "UInt", 0x0084, "Ptr", 0, "Ptr", l) ; WM_NCHITTEST = 0x0084
If (HT = 6) || (HT = 7) { ; HTHSCROLL = 6, HTVSCROLL = 7
SB := (W & 0x80000000) ? 1 : 0 ; SB_LINEDOWN = 1, SB_LINEUP = 0
SM := (HT = 6) ? 0x0114 : 0x0115 ; WM_HSCROLL = 0x0114, WM_VSCROLL = 0x0115
OnScroll(SB, 0, SM, HWND)
Return 0
}
}
; ======================================================================================================================
OnScroll(WP, LP, M, H) {
Static SCROLL_STEP := 10
Bar := (M = 0x0115) ; SB_HORZ=0, SB_VERT=1
SI := Buffer(28, 0)
NumPut("UInt", 28, "UInt", 0x17, SI) ; cbSize, fMask: SIF_ALL
If !DllCall("GetScrollInfo", "Ptr", H, "Int", Bar, "Ptr", SI)
Return
RC := Buffer(16, 0)
DllCall("GetClientRect", "Ptr", H, "Ptr", RC)
NewPos := NumGet(SI, 20, "Int") ; nPos
MinPos := NumGet(SI, 8, "Int") ; nMin
MaxPos := NumGet(SI, 12, "Int") ; nMax
Switch (WP & 0xFFFF) {
Case 0: NewPos -= SCROLL_STEP ; SB_LINEUP
Case 1: NewPos += SCROLL_STEP ; SB_LINEDOWN
Case 2: NewPos -= NumGet(RC, 12, "Int") - SCROLL_STEP ; SB_PAGEUP
Case 3: NewPos += NumGet(RC, 12, "Int") - SCROLL_STEP ; SB_PAGEDOWN
Case 4, 5: NewPos := WP >> 16 ; SB_THUMBTRACK, SB_THUMBPOSITION
Case 6: NewPos := MinPos ; SB_TOP
Case 7: NewPos := MaxPos ; SB_BOTTOM
Default: Return
}
MaxPos -= NumGet(SI, 16, "Int") ; nPage
NewPos := Min(NewPos, MaxPos)
NewPos := Max(MinPos, NewPos)
OldPos := NumGet(SI, 20, "Int") ; nPos
X := (Bar = 0) ? OldPos - NewPos : 0
Y := (Bar = 1) ? OldPos - NewPos : 0
If (X || Y) {
; Scroll contents of window and invalidate uncovered area.
DllCall("ScrollWindow", "Ptr", H, "Int", X, "Int", Y, "Ptr", 0, "Ptr", 0)
; Update scroll bar.
NumPut("Int", NewPos, SI, 20) ; nPos
DllCall("SetScrollInfo", "ptr", H, "Int", Bar, "Ptr", SI, "Int", 1)
}
}