Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Scrollable Gui - Proof of Concept


  • Please log in to reply
59 replies to this topic
ahklerner
  • Members
  • 1386 posts
  • Last active: Oct 08 2014 10:29 AM
  • Joined: 26 Jun 2006
Many Thanks to the Guest Jamey in this post: <!-- m -->http://www.autohotke...opic.php?t=3730<!-- m -->
If it originally came form someone else, let me know please.


This is just something I was playing with....maybe someone can make it better. Just posting for fun.

There are currently multiple bugs....tabbing through controls does not work....some issues with resizing....etc etc...

I do not care to really do anything more with it, so if some one makes enhancements, post them here, and I will refer to them from the first post.

Here is the code:
DetectHiddenWindows, On
Gui, 1:+LastFound +0x200000 +Resize +0x2000000
hGui := WinExist()
Gui, 1:Add, Edit, r2 w150 , Edit 1
Gui, 1:Add, Edit, r2 w150 , Edit 2
Gui, 1:Add, Edit, r2 w150 , Edit 3
Gui, 1:Add, Edit, r2 w150 , Edit 4
Gui, 1:Add, Edit, ym r2 w150 , Edit 5
Gui, 1:Add, Edit, r2 w150 , Edit 6
Gui, 1:Add, Edit, r2 w150 , Edit 7
Gui, 1:Add, Edit, r2 w150 , Edit 8
Gui, 1:Add, Button, gGo, Go
Gui, 1:Show, Hide
ScrollInit() 
Gui, 1:Show, h100	
Return

ScrollInit() {
	Global
	VarSetCapacity(SCROLLBAR_INFO, 28, 0)   ;Allocate SCROLLBAR_INFO structure and zero it
	NumPut(28, &SCROLLBAR_INFO)         ;Initialize its count-bytes parameter
	NumPut(0x17, &SCROLLBAR_INFO + 4)      ;Initialize the mask for what properties to get or set, SIF_ALL = 0x17
	SetScrollBar(hGui, 0, 100, 10, 0)
	C_Create(1)
	GuiWinProc := RegisterCallback("GuiWindowProc", ""  ; "" to avoid fast-mode for subclassing.
		 , 4, hGui)  ; Must specify exact ParamCount when EventInfo parameter is present.
	GuiWinProcOld := DllCall("SetWindowLong", UInt, hGui, Int, -4  ; -4 is GWL_WNDPROC
		 , Int, GuiWinProc, UInt)  ; Return value must be set to UInt vs. Int.
	ConWinProc := RegisterCallback("ConWinProc", ""  ; "" to avoid fast-mode for subclassing.
		 , 4, hGui)  ; Must specify exact ParamCount when EventInfo parameter is present.
	ConWinProcOld := DllCall("SetWindowLong", UInt, hContainer, Int, -4  ; -4 is GWL_WNDPROC
		 , Int, ConWinProc, UInt)  ; Return value must be set to UInt vs. Int.
	}

C_Create(GuiNum,Height=0,Width=0) {
	global hContainer, Container
	Gui %GuiNum%:+LastFound
	hGui := WinExist()
	If !Height && !Width 
		WinGetPos,,, Width, Height, ahk_id %hGui%
	Gui %GuiNum%:Add, Text,x0 y0 h%Height% w%Width% +0x4000000 +0x2000000 hwndhContainer vContainer
	WinGet, CList, ControlListhWnd, ahk_id %hGui%
	;MsgBox % CList
	Loop, Parse, CList, `n
		DllCall("SetParent", "uint", A_LoopField, "uint", hContainer)
	}

GuiSize:

VScrollPixelsPerLine := A_GuiHeight / 100
Return



Esc::ExitApp

ConWinProc(hwnd, uMsg, wParam, lParam) {
	global ConWinProcOld, GuiWinProcOld, GuiWinProc
	;Critical
	OldFormat := A_FormatInteger
	SetFormat, Integer, Hex
	MsgLst := WM_COMMAND := 0x111
	MsgLst .= "," . WM_SYSCOMMAND := 0x112
	uMsg += 0
	SetFormat, Integer, %OldFormat%
	if uMsg in %MsgLst%
		{	
		ReturnVal := DllCall("CallWindowProcA", UInt, GuiWinProcOld, UInt, A_EventInfo, UInt, uMsg, UInt, wParam, UInt, lParam)
		return ReturnVal
		}
	return DllCall("CallWindowProcA", UInt, ConWinProcOld, UInt, hwnd, UInt, uMsg, UInt, wParam, UInt, lParam)
	}

Go:
MsgBox
Return

GuiWindowProc(hwnd, uMsg, wParam, lParam) {
	;Critical
	OldFormat := A_FormatInteger
	SetFormat, Integer, Hex
	global GuiWinProcOld, GuiWinProc, VScrollPixelsPerLine, Container, hContainer
	MsgLst := WM_VSCROLL := 0x115
	uMsg += 0
	SetFormat, Integer, %OldFormat%
	if uMsg in %MsgLst%
		{	
		global hGui      ;Only handle messages for the window we want to scroll
		if (hwnd != hGui)
			return DllCall("CallWindowProcA", UInt, GuiWinProcOld, UInt, hwnd, UInt, uMsg, UInt, wParam, UInt, lParam)

		wParamWordLow := Mod(wParam, 0x10000)
		wParamWordHigh := (wParam - wParamWordLow) / 0x10000

		if (wParamWordLow = 5 or wParamWordLow = 8)      ;SB_THUMBTRACK or SB_ENDSCROLL
			return DllCall("CallWindowProcA", UInt, GuiWinProcOld, UInt, hwnd, UInt, uMsg, UInt, wParam, UInt, lParam)

		QueryScrollBar(hwnd, nMin, nMax, nPage, nPos, nTrackPos)

		if (wParamWordLow = 7)            ;SB_BOTTOM
			a:= "" ; MsgBox, SB_BOTTOM
		else if (wParamWordLow = 6)            ;SB_TOP
			a:= "" ; MsgBox, SB_TOP
		else if (wParamWordLow = 1) {            ;SB_LINEDOWN 
			SetScrollBar(hwnd, nMin, nMax, nPage, NewPos := nPos+1)
			GuiControl,1:Move, Container, % "y" . -NewPos * VScrollPixelsPerLine
			}else if (wParamWordLow = 0) {            ;SB_LINEUP
			SetScrollBar(hwnd, nMin, nMax, nPage, NewPos := nPos-1)
			GuiControl,1:Move, Container, % "y" . -NewPos * VScrollPixelsPerLine
				}else if (wParamWordLow = 3) {            ;SB_PAGEDOWN
			SetScrollBar(hwnd, nMin, nMax, nPage, NewPos := nPos+nPage)
			GuiControl,1:Move, Container, % "y" . -NewPos * VScrollPixelsPerLine
		}else if (wParamWordLow = 2) {            ;SB_PAGEUP
			SetScrollBar(hwnd, nMin, nMax, nPage, NewPos := nPos-nPage)
			GuiControl,1:Move, Container, % "y" . -NewPos * VScrollPixelsPerLine
		}else if (wParamWordLow = 4) {            ;SB_THUMBPOSITION
			SetScrollBar(hwnd, nMin, nMax, nPage, NewPos := wParamWordHigh)
			GuiControl,1:Move, Container, % "y" . -NewPos * VScrollPixelsPerLine
			}
;		ToolTip wParamWordLow = %wParamWordLow%`nContainerY = %ContainerY%`nVScrollPixelsPerLine = %VScrollPixelsPerLine%`nhwnd = %hwnd%`nnMin = %nMin%`nnMax = %nMax%`nnPage = %nPage%`nnPos = %nPos%`nnTrackPos = %nTrackPos% ;`nMsgLst= |%MsgLst%|
		return DllCall("CallWindowProcA", UInt, GuiWinProcOld, UInt, hwnd, UInt, uMsg, UInt, wParam, UInt, lParam)
		}
; Otherwise (since above didn't return), pass all unhandled events to the original WindowProc.
;	SetFormat, Integer, %OldFormat%
	return DllCall("CallWindowProcA", UInt, GuiWinProcOld, UInt, hwnd, UInt, uMsg, UInt, wParam, UInt, lParam)
	}

QueryScrollBar(hwnd, ByRef nMin, ByRef nMax, ByRef nPage, ByRef nPos, ByRef nTrackPos)
{
   ;Win32 API:   BOOL GetScrollInfo( HWND hwnd, int fnBar, LPSCROLLINFO lpsi )

   global SCROLLBAR_INFO

   bSuccess := DllCall("GetScrollInfo", UInt, hwnd, Int, 1, UInt, &SCROLLBAR_INFO)   ;SB_VERT = 1
   if (!bSuccess)
      return false

   nMin := NumGet(&SCROLLBAR_INFO, 8)
   nMax := NumGet(&SCROLLBAR_INFO, 12)
   nPage := NumGet(&SCROLLBAR_INFO, 16)
   nPos := NumGet(&SCROLLBAR_INFO, 20)
   nTrackPos := NumGet(&SCROLLBAR_INFO, 24)

   return true
}
;---------------------------------------------------------------------------------------------------------------
SetScrollBar(hwnd, nMin, nMax, nPage, nPos)
{
   ;Win32 API:   int SetScrollInfo( HWND hwnd, int fnBar, LPCSCROLLINFO lpsi, BOOL fRedraw )

   global SCROLLBAR_INFO
   NumPut(nMin, &SCROLLBAR_INFO + 8)      ;Min
   NumPut(nMax, &SCROLLBAR_INFO + 12)      ;Max
   NumPut(nPage, &SCROLLBAR_INFO + 16)      ;Page
   NumPut(nPos, &SCROLLBAR_INFO + 20)      ;Pos
   iReturnPos := DllCall("SetScrollInfo", UInt, hwnd, Int, 1, UInt, &SCROLLBAR_INFO, Int, true)   ;SB_VERT = 1
   return (iReturnPos == nPos)
}
;---------------------------------------------------------------------------------------------------------------
GuiClose:
   ExitApp
return
;---------------------------------------------------------------------------------------------------------------

Posted Image
ʞɔпɟ əɥʇ ʇɐɥʍ

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
Here is my attempt (based on some C++ code I wrote long ago.)
#NoEnv

OnMessage(0x115, "OnScroll") ; WM_VSCROLL
OnMessage(0x114, "OnScroll") ; WM_HSCROLL

Gui, +Resize +0x300000 ; WS_VSCROLL | WS_HSCROLL

Loop 8
    Gui, Add, Edit, R5 W400, Edit %A_Index%
Gui, Add, Button,, Do absolutely nothing
Gui, Show, W200 H200

Gui, +LastFound
GroupAdd, MyGui, % "ahk_id " . WinExist()

return

GuiSize:
    UpdateScrollBars(A_Gui, A_GuiWidth, A_GuiHeight)
return

GuiClose:
ExitApp

#IfWinActive ahk_group MyGui
WheelUp::
WheelDown::
+WheelUp::
+WheelDown::
    ; SB_LINEDOWN=1, SB_LINEUP=0, WM_HSCROLL=0x114, WM_VSCROLL=0x115
    OnScroll(InStr(A_ThisHotkey,"Down") ? 1 : 0, 0, GetKeyState("Shift") ? 0x114 : 0x115, WinExist())
return
#IfWinActive

UpdateScrollBars(GuiNum, GuiWidth, GuiHeight)
{
    static SIF_RANGE=0x1, SIF_PAGE=0x2, SIF_DISABLENOSCROLL=0x8, SB_HORZ=0, SB_VERT=1
    
    Gui, %GuiNum%:Default
    Gui, +LastFound
    
    ; Calculate scrolling area.
    Left := Top := 9999
    Right := Bottom := 0
    WinGet, ControlList, ControlList
    Loop, Parse, ControlList, `n
    {
        GuiControlGet, c, Pos, %A_LoopField%
        if (cX < Left)
            Left := cX
        if (cY < Top)
            Top := cY
        if (cX + cW > Right)
            Right := cX + cW
        if (cY + cH > Bottom)
            Bottom := cY + cH
    }
    Left -= 8
    Top -= 8
    Right += 8
    Bottom += 8
    ScrollWidth := Right-Left
    ScrollHeight := Bottom-Top
    
    ; Initialize SCROLLINFO.
    VarSetCapacity(si, 28, 0)
    NumPut(28, si) ; cbSize
    NumPut(SIF_RANGE | SIF_PAGE, si, 4) ; fMask
    
    ; Update horizontal scroll bar.
    NumPut(ScrollWidth, si, 12) ; nMax
    NumPut(GuiWidth, si, 16) ; nPage
    DllCall("SetScrollInfo", "uint", WinExist(), "uint", SB_HORZ, "uint", &si, "int", 1)
    
    ; Update vertical scroll bar.
;     NumPut(SIF_RANGE | SIF_PAGE | SIF_DISABLENOSCROLL, si, 4) ; fMask
    NumPut(ScrollHeight, si, 12) ; nMax
    NumPut(GuiHeight, si, 16) ; nPage
    DllCall("SetScrollInfo", "uint", WinExist(), "uint", SB_VERT, "uint", &si, "int", 1)
    
    if (Left < 0 && Right < GuiWidth)
        x := Abs(Left) > GuiWidth-Right ? GuiWidth-Right : Abs(Left)
    if (Top < 0 && Bottom < GuiHeight)
        y := Abs(Top) > GuiHeight-Bottom ? GuiHeight-Bottom : Abs(Top)
    if (x || y)
        DllCall("ScrollWindow", "uint", WinExist(), "int", x, "int", y, "uint", 0, "uint", 0)
}

OnScroll(wParam, lParam, msg, hwnd)
{
    static SIF_ALL=0x17, SCROLL_STEP=10
    
    bar := msg=0x115 ; SB_HORZ=0, SB_VERT=1
    
    VarSetCapacity(si, 28, 0)
    NumPut(28, si) ; cbSize
    NumPut(SIF_ALL, si, 4) ; fMask
    if !DllCall("GetScrollInfo", "uint", hwnd, "int", bar, "uint", &si)
        return
    
    VarSetCapacity(rect, 16)
    DllCall("GetClientRect", "uint", hwnd, "uint", &rect)
    
    new_pos := NumGet(si, 20) ; nPos
    
    action := wParam & 0xFFFF
    if action = 0 ; SB_LINEUP
        new_pos -= SCROLL_STEP
    else if action = 1 ; SB_LINEDOWN
        new_pos += SCROLL_STEP
    else if action = 2 ; SB_PAGEUP
        new_pos -= NumGet(rect, 12, "int") - SCROLL_STEP
    else if action = 3 ; SB_PAGEDOWN
        new_pos += NumGet(rect, 12, "int") - SCROLL_STEP
    else if (action = 5 || action = 4) ; SB_THUMBTRACK || SB_THUMBPOSITION
        new_pos := wParam>>16
    else if action = 6 ; SB_TOP
        new_pos := NumGet(si, 8, "int") ; nMin
    else if action = 7 ; SB_BOTTOM
        new_pos := NumGet(si, 12, "int") ; nMax
    else
        return
    
    min := NumGet(si, 8, "int") ; nMin
    max := NumGet(si, 12, "int") - NumGet(si, 16) ; nMax-nPage
    new_pos := new_pos > max ? max : new_pos
    new_pos := new_pos < min ? min : new_pos
    
    old_pos := NumGet(si, 20, "int") ; nPos
    
    x := y := 0
    if bar = 0 ; SB_HORZ
        x := old_pos-new_pos
    else
        y := old_pos-new_pos
    ; Scroll contents of window and invalidate uncovered area.
    DllCall("ScrollWindow", "uint", hwnd, "int", x, "int", y, "uint", 0, "uint", 0)
    
    ; Update scroll bar.
    NumPut(new_pos, si, 20, "int") ; nPos
    DllCall("SetScrollInfo", "uint", hwnd, "int", bar, "uint", &si, "int", 1)
}
The main differences are:
[*:183xvzu4]OnMessage is used rather than subclassing the window.
[*:183xvzu4]SB_THUMBTRACK is handled instead of SB_THUMBPOSITION, so the controls scroll in real-time (before you release the button.)
[*:183xvzu4]Resizing is handled better.
[*:183xvzu4]Tabbing works, though it doesn't scroll automatically (so the control tabbed to may not be visible.)
[*:183xvzu4]ScrollWindow() is used to move all of the controls.Bug: The horizontal scroll bar isn't accounted for until the next resize after it is shown. (This is usually only noticeable when restoring the window after maximizing.)

Updated to support mouse wheel.

Updated to support ScrollMomentum. (2008/12/09)

ahklerner
  • Members
  • 1386 posts
  • Last active: Oct 08 2014 10:29 AM
  • Joined: 26 Jun 2006
Well now, That is just freaking awesome!!!!!

Thanks for sharing.
Posted Image
ʞɔпɟ əɥʇ ʇɐɥʍ

AdamPash
  • Members
  • 25 posts
  • Last active: Apr 01 2008 05:48 AM
  • Joined: 27 Sep 2007
This really is incredible, thanks a lot. It's perfect for a script I'm putting together right now. One question, though: Is it possible to get scrolling to work with the mouse scroll wheel? Or even from the keyboard with the up/down arrows?

engunneer
  • Moderators
  • 9162 posts
  • Last active: Sep 12 2014 10:36 PM
  • Joined: 30 Aug 2005
Adam, Is your code based on lexiKos' or ahklerner's code?

AdamPash
  • Members
  • 25 posts
  • Last active: Apr 01 2008 05:48 AM
  • Joined: 27 Sep 2007
Ah, sorry about that. It's based on lexiKos'.

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
I originally tried catching WM_MOUSEWHEEL, but apparently that is only sent to the focused control. I've updated the script to support the mouse wheel, via hotkeys.

Wdb
  • Members
  • 24 posts
  • Last active: Sep 23 2011 05:14 PM
  • Joined: 27 Feb 2006

Here is my attempt (based on some C++ code I wrote long ago.)

This is a very good job lexiKos, thank you!

And is it possible to retrieve the absolute coordinates of the displayed page, specially when I scroll down the wheel mouse ??? (i.e: if the page dimension exceed the screen widht?)

AdamPash
  • Members
  • 25 posts
  • Last active: Apr 01 2008 05:48 AM
  • Joined: 27 Sep 2007
That mouse scroll update worked like a charm, thanks a lot lexiKos!

I also included the up and down keys with the rest of the hotkeys to scroll the window using those (there's user input where that'll be a problem with my GUI window, so there's no concern for conflict). So now it just looks like this:

WheelUp::
WheelDown::
+WheelUp::
+WheelDown::
Up::
Down::
    ; SB_LINEDOWN=1, SB_LINEUP=0, WM_HSCROLL=0x114, WM_VSCROLL=0x115
    OnScroll(InStr(A_ThisHotkey,"Down") ? 1 : 0, 0, GetKeyState("Shift") ? 0x114 : 0x115, WinExist())
return


Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

And is it possible to retrieve the absolute coordinates of the displayed page, specially when I scroll down the wheel mouse ??? (i.e: if the page dimension exceed the screen widht?)

You could get the position of the scroll bar using GetScrollPos or similar. There is no "page" - ScrollWindow just offsets each control by the difference in scroll bar position (when it moves.)

Wdb
  • Members
  • 24 posts
  • Last active: Sep 23 2011 05:14 PM
  • Joined: 27 Feb 2006

And is it possible to retrieve the absolute coordinates of the displayed page, specially when I scroll down the wheel mouse ??? (i.e: if the page dimension exceed the screen widht?)

You could get the position of the scroll bar using GetScrollPos or similar. There is no "page" - ScrollWindow just offsets each control by the difference in scroll bar position (when it moves.)


Ok, thank you.

Another question:
what happens if I de-comment your statements NumPut:
    ; Update vertical scroll bar.
;     NumPut(SIF_RANGE | SIF_PAGE | SIF_DISABLENOSCROLL, si, 4) ; fMask


Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
That adds the SIF_DISABLENOSCROLL flag, which causes the scroll bar to be disabled rather than hidden when it is not needed.

automaticman
  • Members
  • 658 posts
  • Last active: Nov 20 2012 06:10 PM
  • Joined: 27 Oct 2006
A zoomable GUI (=ZUI), proof of concept would be also interesting.

YokoiL
  • Members
  • 3 posts
  • Last active: Jun 20 2008 10:50 AM
  • Joined: 18 Jun 2008

Tabbing works, though it doesn't scroll automatically (so the control tabbed to may not be visible.)


Is there anyway to make it so that you can use tab and it scrolls to where the cursor is?

Dra_Gon
  • Members
  • 373 posts
  • Last active: Oct 27 2009 04:20 PM
  • Joined: 25 May 2007
Only saw this when SKAN pointed it out, but it looks great!

Ciao,
Dra'Gon
Posted Image
For a good laugh {hopefully} >> megamatts.50megs.com

My WritersCafe profile>>
http://www.writersca...BlueDragonFire/