Class_ScrollGUI - updated on 2015-03-13

Post your working scripts, libraries and tools for AHK v1.1 and older
just me
Posts: 9423
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Class_ScrollGUI - updated on 2015-03-13

15 Sep 2015, 03:58

I'm not sure what you want to achieve. Doesn't this work as you are expecting?

Code: Select all

#SingleInstance force
; #Include <attach>
; #Include <Class_ScrollGUI>
#Include attach.ahk
#Include Class_ScrollGUI.ahk

mc := new MyClass()

class MyClass {
	__New(){
		Gui +resize
		Gui, Add, Tab2, hwndhTab w200 h200, Tab A|Tab B
		fn := this.OnSize.Bind(this)
		Gui +HwndhDef
		Gui, Add, Text, hwndhEdit w180 h160
		Gui, New, hwndhChild -Border
		this.hChild := hChild
		Gui, Color, FF0000
		Loop 10 {
			Gui, Add, Edit, xm w300
		}
		; Gui, Show, x0 y0 w180 h160
		this.SG1 := New ScrollGUI(hChild, 180, 160, "+Resize +LabelGui1 -Border", 3, 4)
	   this.SG1.Show("ScrollGUI1", "x0 y0 w180 h160")
		Gui, % "+Parent" hEdit
		Attach(hTab,"w1 h1")
		Attach(hEdit,"w1 h1")
		Gui, % hDef ":Show"
		OnMessage(0x0005, fn)
	}

	OnSize(wParam, lParam){
		; dllcall("MoveWindow", "Ptr", this.hChild, "int", 0,"int", 0, "int", lParam & 0xffff, "int", lParam >> 16, "int", 0)
		dllcall("MoveWindow", "Ptr", this.SG1.HWND, "int", 0,"int", 0, "int", lParam & 0xffff, "int", lParam >> 16, "int", 0)
	}
}
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: Class_ScrollGUI - updated on 2015-03-13

15 Sep 2015, 06:12

That was pretty close, I was able to get even closer by disabling +Resize for ScrollGui:

Code: Select all

#SingleInstance force
#Include <attach>
#Include <Class_ScrollGUI>
;#Include attach.ahk
;#Include Class_ScrollGUI.ahk
 
mc := new MyClass()
 
class MyClass {
	__New(){
		Gui +resize
		Gui, Add, Tab2, hwndhTab w200 h200, Tab A|Tab B
		fn := this.OnSize.Bind(this)
		Gui +HwndhDef
		Gui, Add, Text, hwndhEdit w180 h160
		Gui, New, hwndhChild -Border
		this.hChild := hChild
		Gui, Color, FF0000
		Loop 10 {
			Gui, Add, Edit, xm w300
		}
		; Gui, Show, x0 y0 w180 h160
		this.SG1 := New ScrollGUI(hChild, 0 , 0, "+LabelGui1 -Border", 3, 4)
	   this.SG1.Show("ScrollGUI1", "x0 y0 w180 h160")
		Gui, % "+Parent" hEdit
		Attach(hTab,"w1 h1")
		Attach(hEdit,"w1 h1")
		Gui, % hDef ":Show"
		OnMessage(0x0005, fn)
	}
 
	OnSize(wParam, lParam){
		; dllcall("MoveWindow", "Ptr", this.hChild, "int", 0,"int", 0, "int", lParam & 0xffff, "int", lParam >> 16, "int", 0)
		dllcall("MoveWindow", "Ptr", this.SG1.HWND, "int", 0,"int", 0, "int", (lParam & 0xffff) - 50, "int", (lParam >> 16) - 55)
	}
}
However, it seems that the child GUI (The one with the red background) does not expand to fill the available area.
The whole of the interior of Tab A (ie everything inside the ScrollGui) should always be filled with red.

Edit: I guess this is because Class_ScrollGui limits the maximum size of a scrollable window, which is something that I seem to remember commenting about before.
Is this something that could be made optional? I have tried looking in the source, but cannot work out where it limits the size of the window.
ie, in your example script, how would you stop Class_ScrollGui from limiting the maximum size of the window?

To 100% clarify, here is what I want, implemented with Lexikos' scrollable gui proof of concept code:
Image

Code: Select all

#SingleInstance force
outputdebug DBGVIEWCLEAR
 
mc := new MyClass()
 
class MyClass {
	__New(){
		Gui, Add, Tab2, hwndhTab w200 h200, Tab A|Tab B
		Gui +HwndhDef
		Gui, Margin, x0 y0
		Gui, Add, Text, hwndhEdit w190 h170 xp+5 yp+25
		Gui, New, hwndhChild -Caption
		this.hChild := hChild
		Gui, Color, FF0000
		Loop 10 {
			Gui, Add, Edit, xm w300
		}
		Gui, Show, x0 y0 w180 h160
		Gui, % "+Parent" hEdit
		Attach(hTab,"w1 h1")
		Attach(hEdit,"w1 h1")
		Gui, % hDef ":Show"
		fn := this.OnSize.Bind(this)
		OnMessage(0x0005, fn)
		fn := this.OnScroll.Bind(this)
		OnMessage(0x115, fn) ; WM_VSCROLL
		OnMessage(0x114, fn) ; WM_HSCROLL
		Gui % hDef ":+resize"
	}
	
	OnSize(wParam, lParam, msg, hwnd){
		dllcall("MoveWindow", "Ptr", this.hChild, "int", 0,"int", 0, "int", (lParam & 0xffff) - 20, "int", (lParam >> 16) - 40)
		this.UpdateScrollBars(this.hChild, lParam & 0xffff, lParam >> 16)
	}

	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)
	}

}

/*
	Function:		Attach
					Determines how a control is resized with its parent.

	hCtrl:			
					- hWnd of the control if aDef is not empty.					
					- hWnd of the parent to be reset if aDef is empty. If you omit this parameter function will use
					the first hWnd passed to it.
					With multiple parents you need to specify which one you want to reset.					
					- Handler name, if parameter is string and aDef is empty. Handler will be called after the function has finished 
					moving controls for the parent. Handler receives hWnd of the parent as its only argument.

	aDef:			
					Attach definition string. Space separated list of attach options. If omitted, function working depends on hCtrl parameter.
					You can use following elements in the definition string:
					
					- 	"x,y,w,h" letters along with coefficients, decimal numbers which can also be specified in m/n form (see example below).
					-   "r". Use "r1" (or "r") option to redraw control immediately after repositioning, set "r2" to delay redrawing 100ms for the control
						(prevents redrawing spam).
					-	"p" (for "proportional") is the special coefficient. It will make control's dimension always stay in the same proportion to its parent 
						(so, pin the control to the parent). Although you can mix pinned and non-pinned controls and dimensions that is rarely what you want. 
						You will generally want to pin every control in the parent.
					-	"+" or "-" enable or disable function for the control. If control is hidden, you may want to disable the function for 
						performance reasons, especially if control is container attaching its children. Its perfectly OK to leave invisible controls 
						attached, but if you have lots of them you can use this feature to get faster and more responsive updates. 
						When you want to show disabled hidden control, make sure you first attach it back so it can take its correct position
						and size while in hidden state, then show it. "+" must be used alone while "-" can be used either alone or in Attach definition string
						to set up control as initially disabled.

	Remarks:
					Function monitors WM_SIZE message to detect parent changes. That means that it can be used with other eventual container controls
					and not only top level windows.

					You should reset the function when you programmatically change the position of the controls in the parent control.
					Depending on how you created your GUI, you might need to put "autosize" when showing it, otherwise resetting the Gui before its 
					placement is changed will not work as intented. Autosize will make sure that WM_SIZE handler fires. Sometimes, however, WM_SIZE
					message isn't sent to the window. One example is for instance when some control requires Gui size to be set in advance in which case
					you would first have "Gui, Show, w100 h100 Hide" line prior to adding controls, and only Gui, Show after controls are added. This
					case will not trigger WM_SIZE message unless AutoSize is added.
				
				
	Examples:
	(start code)
					Attach(h, "w.5 h1/3 r2")	;Attach control's w, h and redraw it with delay.
					Attach(h, "-")				;Disable function for control h but keep its definition. To enable it latter use "+".
					Attach(h, "- w.5")			;Make attach definition for control but do not attach it until you call Attach(h, "+").
					Attach()					;Reset first parent. Use when you have only 1 parent.
					Attach(hGui2)				;Reset Gui2.
					Attach("Win_Redraw")		;Use Win_Redraw function as a Handler. Attach will call it with parent's handle as argument.
					Attach(h, "p r2")			;Pin control with delayed refreshing.

					
					; This is how to do delayed refresh of entire window.
					; To prevent redraw spam which can be annoying in some cases, 
					; you can choose to redraw entire window only when user has finished resizing it.
					; This is similar to r2 option for controls, except it works with entire parent.
					
					Attach("OnAttach")			;Set Handler to OnAttach function
					...
					
					OnAttach( Hwnd ) {
						global hGuiToRedraw := hwnd
						SetTimer, Redraw, -100
					}

					Redraw:
						Win_Redraw(hGuiToRedraw)
					return
	(end code)
	Working sample:
	(start code)
		#SingleInstance, force
			Gui, +Resize
			Gui, Add, Edit, HWNDhe1 w150 h100
			Gui, Add, Picture, HWNDhe2 w100 x+5 h100, pic.bmp 

			Gui, Add, Edit, HWNDhe3 w100 xm h100
			Gui, Add, Edit, HWNDhe4 w100 x+5 h100
			Gui, Add, Edit, HWNDhe5 w100 yp x+5 h100
			
			gosub SetAttach					;comment this line to disable Attach
			Gui, Show, autosize			
		return

		SetAttach:
			Attach(he1, "w.5 h")		
			Attach(he2, "x.5 w.5 h r")
			Attach(he3, "y w1/3")
			Attach(he4, "y x1/3 w1/3")
			Attach(he5, "y x2/3 w1/3")
		return
	(end code)

	About:
			o 1.1 by majkinetor
			o Licensed under BSD <http://creativecommons.org/licenses/BSD/> 
 */
Attach(hCtrl="", aDef="") {
	 Attach_(hCtrl, aDef, "", "")
}

Attach_(hCtrl, aDef, Msg, hParent){
	static
	local s1,s2, enable, reset, oldCritical

	if (aDef = "") {							;Reset if integer, Handler if string
		if IsFunc(hCtrl)
			return Handler := hCtrl
	
		ifEqual, adrWindowInfo,, return			;Resetting prior to adding any control just returns.
		hParent := hCtrl != "" ? hCtrl+0 : hGui
		loop, parse, %hParent%a, %A_Space%
		{
			hCtrl := A_LoopField, SubStr(%hCtrl%,1,1), aDef := SubStr(%hCtrl%,1,1)="-" ? SubStr(%hCtrl%,2) : %hCtrl%,  %hCtrl% := ""
			gosub Attach_GetPos
			loop, parse, aDef, %A_Space%
			{
				StringSplit, z, A_LoopField, :
				%hCtrl% .= A_LoopField="r" ? "r " : (z1 ":" z2 ":" c%z1% " ")
			}
			%hCtrl% := SubStr(%hCtrl%, 1, -1)				
		}
		reset := 1,  %hParent%_s := %hParent%_pw " " %hParent%_ph
	}

	if (hParent = "")  {						;Initialize controls 
		if !adrSetWindowPos
			adrSetWindowPos		:= DllCall("GetProcAddress", "uint", DllCall("GetModuleHandle", "str", "user32"), A_IsUnicode ? "astr" : "str", "SetWindowPos")
			,adrWindowInfo		:= DllCall("GetProcAddress", "uint", DllCall("GetModuleHandle", "str", "user32"), A_IsUnicode ? "astr" : "str", "GetWindowInfo")
			,OnMessage(5, A_ThisFunc),	VarSetCapacity(B, 60), NumPut(60, B), adrB := &B
			,hGui := DllCall("GetParent", "uint", hCtrl, "Uint") 

		hParent := DllCall("GetParent", "uint", hCtrl, "Uint") 
		
		if !%hParent%_s
			DllCall(adrWindowInfo, "uint", hParent, "uint", adrB), %hParent%_pw := NumGet(B, 28) - NumGet(B, 20), %hParent%_ph := NumGet(B, 32) - NumGet(B, 24), %hParent%_s := !%hParent%_pw || !%hParent%_ph ? "" : %hParent%_pw " " %hParent%_ph
		
		if InStr(" " aDef " ", "p")
			StringReplace, aDef, aDef, p, xp yp wp hp
		ifEqual, aDef, -, return SubStr(%hCtrl%,1,1) != "-" ? %hCtrl% := "-" %hCtrl% : 
		else if (aDef = "+")
			if SubStr(%hCtrl%,1,1) != "-" 
				 return
			else %hCtrl% := SubStr(%hCtrl%, 2), enable := 1 
		else {
			gosub Attach_GetPos
			%hCtrl% := ""
			loop, parse, aDef, %A_Space%
			{			
				if (l := A_LoopField) = "-"	{
					%hCtrl% := "-" %hCtrl%
					continue
				}
				f := SubStr(l,1,1), k := StrLen(l)=1 ? 1 : SubStr(l,2)
				if (j := InStr(l, "/"))
					k := SubStr(l, 2, j-2) / SubStr(l, j+1)
				%hCtrl% .= f ":" k ":" c%f% " "
			}
 			return %hCtrl% := SubStr(%hCtrl%, 1, -1), %hParent%a .= InStr(%hParent%, hCtrl) ? "" : (%hParent%a = "" ? "" : " ")  hCtrl 
		}
	}
	ifEqual, %hParent%a,, return				;return if nothing to anchor.

	if !reset && !enable {					
		%hParent%_pw := aDef & 0xFFFF, %hParent%_ph := aDef >> 16
		ifEqual, %hParent%_ph, 0, return		;when u create gui without any control, it will send message with height=0 and scramble the controls ....
	} 

	if !%hParent%_s
		%hParent%_s := %hParent%_pw " " %hParent%_ph

	oldCritical := A_IsCritical
	critical, 5000

	StringSplit, s, %hParent%_s, %A_Space%
	loop, parse, %hParent%a, %A_Space%
	{
		hCtrl := A_LoopField, aDef := %hCtrl%, 	uw := uh := ux := uy := r := 0, hCtrl1 := SubStr(%hCtrl%,1,1)
		if (hCtrl1 = "-")
			ifEqual, reset,, continue
			else aDef := SubStr(aDef, 2)	
		
		gosub Attach_GetPos
		loop, parse, aDef, %A_Space%
		{
			StringSplit, z, A_LoopField, :		; opt:coef:initial
			ifEqual, z1, r, SetEnv, r, %z2%
			if z2=p
				 c%z1% := z3 * (z1="x" || z1="w" ?  %hParent%_pw/s1 : %hParent%_ph/s2), u%z1% := true
			else c%z1% := z3 + z2*(z1="x" || z1="w" ?  %hParent%_pw-s1 : %hParent%_ph-s2), 	u%z1% := true
		}
		flag := 4 | (r=1 ? 0x100 : 0) | (uw OR uh ? 0 : 1) | (ux OR uy ? 0 : 2)			; nozorder=4 nocopybits=0x100 SWP_NOSIZE=1 SWP_NOMOVE=2
		;m(hParent, %hParent%a, hCtrl, %hCTRL%)
		DllCall(adrSetWindowPos, "uint", hCtrl, "uint", 0, "uint", cx, "uint", cy, "uint", cw, "uint", ch, "uint", flag)
		r+0=2 ? Attach_redrawDelayed(hCtrl) : 
	}
	critical %oldCritical%
	return Handler != "" ? %Handler%(hParent) : ""

 Attach_GetPos:									;hParent & hCtrl must be set up at this point
		DllCall(adrWindowInfo, "uint", hParent, "uint", adrB), 	lx := NumGet(B, 20), ly := NumGet(B, 24), DllCall(adrWindowInfo, "uint", hCtrl, "uint", adrB)
		,cx :=NumGet(B, 4),	cy := NumGet(B, 8), cw := NumGet(B, 12)-cx, ch := NumGet(B, 16)-cy, cx-=lx, cy-=ly
 return
}

Attach_redrawDelayed(hCtrl){
	static s
	s .= !InStr(s, hCtrl) ? hCtrl " " : ""
	SetTimer, %A_ThisFunc%, -100
	return
 Attach_redrawDelayed:
	loop, parse, s, %A_Space%
		WinSet, Redraw, , ahk_id %A_LoopField%
	s := ""
 return
}
just me
Posts: 9423
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Class_ScrollGUI - updated on 2015-03-13

15 Sep 2015, 09:41

As said, Class_SrollGui is not designed to be used as a child window. But something like this would color the background, though:

Code: Select all

#SingleInstance force
; #Include <attach>
; #Include <Class_ScrollGUI>
#Include attach.ahk
#Include Class_ScrollGUI.ahk

mc := new MyClass()
Return
GuiClose:
ExitApp

class MyClass {
	__New(){
		GuiColor := "FF0000"
		Gui +resize
		Gui, Add, Tab2, hwndhTab w200 h200, Tab A|Tab B
		fn := this.OnSize.Bind(this)
		Gui +HwndhDef
		Gui, Add, Text, hwndhEdit w180 h160
		Gui, New, hwndhChild -Border
		this.hChild := hChild
		Gui, Color, %GuiColor%
		Loop 10 {
			Gui, Add, Edit, xm w300
		}
		this.SG1 := New ScrollGUI(hChild, 160, 140, "-Border +Parent" . hEdit, 3, 4)
		this.SG1.Show("ScrollGUI1", "x0 y0 w180 h160")
	   	Gui, % this.SG1.HWND ":Color", %GuiColor%
		Attach(hTab,"w1 h1")
		Attach(hEdit,"w1 h1")
		Gui, % hDef ":Show"
		OnMessage(0x0005, fn)
	}
	OnSize(wParam, lParam){
		dllcall("MoveWindow", "Ptr", this.SG1.HWND, "int", 0,"int", 0, "int", (lParam & 0xffff) - 50, "int", (lParam >> 16) - 55, "Int", 1)
	}
}
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: Class_ScrollGUI - updated on 2015-03-13

15 Sep 2015, 13:16

Yeah, but that is kind of "faking it".
It's a real pity, as the SG code seems really robust, it's just the way that it disables sizing up of the gui once the scrollbars are no longer needed that makes it not suitable.
Being a child gui doesn't seem to be a problem - the issue exhibits itself in your demo code: Once all the GuiControls are visible, you cannot size up the GUI any more. I had a play with the code and I just cannot work out how you stop the sizing up. If I could, I would happily fork your code rather than writing my own version.
just me
Posts: 9423
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Class_ScrollGUI - updated on 2015-03-13

15 Sep 2015, 15:38

Well, Class_ScrollGui isn't designed to be resized using commands/functions like WinMove or DllCall("MoveWindow", ...), too. If used as intended the maximum size is defined by Gui, %HWND%:+MaxSize%MaxH%x%MaxV%. If you want to resize the ScrollGui using the commands/functions, you can resize both the parent and the child of the Scrollgui like shown here:

Code: Select all

#SingleInstance force
; #Include <attach>
; #Include <Class_ScrollGUI>
#Include attach.ahk
#Include Class_ScrollGUI.ahk

mc := new MyClass()
Return
GuiClose:
ExitApp

class MyClass {
	__New(){
		GuiColor := "FF0000"
		Gui +resize
		Gui, Add, Tab2, hwndhTab w200 h200, Tab A|Tab B
		fn := this.OnSize.Bind(this)
		Gui +HwndhDef
		Gui, Add, Text, hwndhEdit w180 h160
		Gui, New, hwndhChild -Border
		this.hChild := hChild
		Gui, Color, %GuiColor%
		Loop 10 {
			Gui, Add, Edit, xm w300
		}
		this.SG1 := New ScrollGUI(hChild, 160, 140, "-Border +Parent" . hEdit, 3, 4)
	   	this.SG1.Show("ScrollGUI1", "x0 y0 w180 h160")
		Attach(hTab,"w1 h1")
		Attach(hEdit,"w1 h1")
		Gui, % hDef ":Show"
		OnMessage(0x0005, fn)
	}
	OnSize(wParam, lParam){
		W := (lParam & 0xffff) - 50
		H := (lParam >> 16) - 55
		dllcall("MoveWindow", "Ptr", this.SG1.HGUI, "int", 0,"int", 0, "int", W, "int", H, "Int", 1)
		dllcall("MoveWindow", "Ptr", this.SG1.HWND, "int", 0,"int", 0, "int", W, "int", H, "Int", 1)
	}
}
If it's not acceptable for you, you should write your own version.
clintyg
Posts: 3
Joined: 18 Nov 2015, 23:10

Re: Class_ScrollGUI - updated on 2015-03-13

18 Nov 2015, 23:55

First, thanks for this handy scoll GUI class!

How would I toggle the AlwaysOnTop property of a Gui?

As a test, I took (part of) the Sample Script, and changed the g-routine called by the button.

Thanks in advance!

Code: Select all

#NoEnv
#Include Class_ScrollGUI.ahk
SetBatchLines, -1
; ----------------------------------------------------------------------------------------------------------------------
; ChildGUI 2
onTopState = 0
Gui, New, +hwndHGUI2
Gui, Add, Text, xm Border Hidden vTX1, Test
Gui, Add, Text, x+m yp w460 h300 Center 0x200 Border Section, GUI number 2
Gui, Add, Button, xs wp gOnTop, Toggle AlwaysOnTop.
; Create ScrollGUI2 with both horizontal and vertical scrollbars
SG2 := New ScrollGUI(HGUI2, 600, 200, "+Resize +LabelGui2")
; Show ScrollGUI2
SG2.Show("ScrollGUI2", "x0 yCenter")
Return
; ----------------------------------------------------------------------------------------------------------------------
OnTop:
  if (onTopState > 0) {
    Gui %HGUI2%: -AlwaysOnTop
    onTopState = 0
  } else {
    Gui %HGUI2%: +AlwaysOnTop
    onTopState = 1
  }
return
; ----------------------------------------------------------------------------------------------------------------------
Gui2Size:
Return
; ----------------------------------------------------------------------------------------------------------------------
Gui2Close:
Gui2Escape:
   SG2 := ""
Return
clintyg
Posts: 3
Joined: 18 Nov 2015, 23:10

Re: Class_ScrollGUI - updated on 2015-03-13

19 Nov 2015, 00:35

clintyg wrote:How would I toggle the AlwaysOnTop property of a Gui?
For now, I found an acceptable work-around using WinSet, AlwaysOnTop, Toggle

However, I am still curious to learn the correct way to update the properties of the child-Gui (or is it the ScrollGUI instance?). So for example, instead of toggling "AlwaysOnTop", what if I had a button to toggle the visible height of the window, from 500px to 50px?

Again, thanks for your contribution!
just me
Posts: 9423
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Class_ScrollGUI - updated on 2015-03-13

19 Nov 2015, 04:05

Currently there's only one 'correct' way to change properties of the child GUI. You can add or remove/hide controls and call the AdjustToChild() method afterwards to adjust the GUI. Your last request is not supported by class methods, so you have to use some kind of 'work-around':

Code: Select all

#NoEnv
#Include Class_ScrollGUI.ahk
SetBatchLines, -1
; ----------------------------------------------------------------------------------------------------------------------
; ChildGUI 2
Gui, New, +hwndHGUI2
Gui, Add, Button, w460 gResize vResize, Collapse
GuiControlGet, P, Pos, Resize
CollapsedHeight := PY + PH + PY
Gui, Add, Text, wp h300 Center 0x200 Border Section, GUI number 2
; Create ScrollGUI2 with both horizontal and vertical scrollbars
SG2 := New ScrollGUI(HGUI2, 600, 200, "+Resize +LabelGui2")
SG2GUI := SG2.HWND
; Show ScrollGUI2
SG2.Show("ScrollGUI2", "x0 yCenter")
Return
; ----------------------------------------------------------------------------------------------------------------------
Resize:
   GuiControlGet, Resize
   If (Resize = "Collapse") {
      PreviousHeight := SG2.Height
      Height := CollapsedHeight
      GuiControl, , Resize, Restore
   }
   Else {
      Height := PreviousHeight
      GuiControl, , Resize, Collapse
   }
   Gui, %SG2GUI%:Show, h%Height%
Return
; ----------------------------------------------------------------------------------------------------------------------
Gui2Size:
Return
; ----------------------------------------------------------------------------------------------------------------------
Gui2Close:
Gui2Escape:
   SG2 := ""
ExitApp
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: Class_ScrollGUI - updated on 2015-03-13

19 Nov 2015, 04:21

FYI, HotkeyIt and I are working on implementing Scrollbars and Auto-size / Auto-pos / Child Guis as GuiControls (ie auto-resize of child guis to fit parent) in the AHK source.
We currently have a proof-of-concept implementation working, and are seeking people to help test so that we can iron out all the kinks and hopefully get it merged into the main AHK branch.
We have a thread here which contains a test build of AHK_L, or you can use the latest version of AHK_H, which includes this new code.

[Edit] The code currently does not support the use-case that you are talking about here, but it is something we are going to have to implement, so we would be interested to hear any feedback you have as to how it should work. Currently, my thinking is that hidden items should not be considered when calculating scrollbar size.
clintyg
Posts: 3
Joined: 18 Nov 2015, 23:10

Re: Class_ScrollGUI - updated on 2015-03-13

19 Nov 2015, 14:13

just me wrote:... you have to use some kind of 'work-around':
Your example was exactly what I wanted.

Thank you! for the assistance, and again for the class code.
User avatar
bichlepa
Posts: 183
Joined: 15 Aug 2014, 06:44
Location: Germany
Contact:

Re: Class_ScrollGUI - updated on 2015-03-13

29 Feb 2016, 08:05

I'm working on AutoHotFlow now and I have created a class named "instance". I've discovered that the class overwritten as soon as a new ScrollGUI is created. I don't understand how this happens, since you don't use global variables at all.
I've modified your example code which demonstrates the issue:

Code: Select all

#NoEnv
#Include Class_ScrollGUI.ahk
SetBatchLines, -1
 
class instance
{
   static height="nonSenceValue"
}
; ----------------------------------------------------------------------------------------------------------------------
; ChildGUI
MsgBox % "1 " instance.height
Gui, New, +hwndHGUI
Gui, Margin, 20, 20
I := 0
Gui, Add, Text, w370 h20 0x200, % "Edit " . ++I
Gui, Add, Edit, xp y+0 wp r6 vEdit10, 1`n2`n3`n4`n5`n6`n7`n8`n9
Loop, 4 {
   Gui, Add, Text, xp y+0 wp h20 0x200, % "Edit " . ++I
   Gui, Add, Edit, xp y+0 wp r6 vEdit1%A_Index%, 1`n2`n3`n4`n5`n6`n7`n8`n9
}
; Create ScrollGUI1 with vertical scrollbar and scrolling by mouse wheel
MsgBox % "2 " instance.height
Global SG1 := New ScrollGUI(HGUI, 0, 400, "", 2, 4)
MsgBox % "3 " instance.height
; Create the main window (parent)
Gui, Main:New
MsgBox % "4 " instance.height
Gui, Margin, 0, 20
Gui, % SG1.HWND . ": -Caption +ParentMAIN +LastFound"
Gui, % SG1.HWND . ":Show", Hide
WinGetPos, , , W, H,% "ahk_id " SG1.HWND
;~ W := Round(W * (96 / A_ScreenDPI))
;~ H := Round(H * (96 / A_ScreenDPI))
Y := H + 20
WinGetPos, , , Wsettings, Hsettings,% "ahk_id " SG1.HGUI
HParent:=Hsettings+60
;Make resizeable
gui,+minsize%w%x100 ;Ensure contant width.
gui,+maxsize%w%x%HParent%
Gui, Main:Add, Button, vButtonSave x20 y%Y% w100, Save
Gui, Main:Add, Button, vButtonCancel x+20 yp wp, Cancel
Gui, Main:Show, w%W%, Settings
Gui, Main:Show, w%W%, Settings ;Needed twice, otherwise the width is not correct
; Show ScrollGUI1
SG1.Show("", "x0 y0")
MsgBox % "5 " instance.height
Return
; ----------------------------------------------------------------------------------------------------------------------
MainGuiClose:
MainGuiEscape:
MsgBox % "6 " instance.height
ExitApp
During execution the variable instance.height contains the defined value as it should. After creating a new ScrollGUI (strangely some lines after that) its value is empty. Later the value contains 400.
In my code in AutoHotFlow, the variable "instance" contains after creation of the Scrollable GUI following object (I use the function strobj)
Height: 454
HGUI: 0x130e10
HWND: 1049388
LineV: 23
MaxV: 454
PageV: 455
PosV: 0
ScrollV: 1
UseShift: 0
WheelV: 1
Width: 420
Maybe you can correct this or tell why this happens. Otherwise I'll nee to use an other name for the class.
Scripting is too complicated? Try AutoHotFlow, the graphical automation tool! Written in AutoHotkey.
just me
Posts: 9423
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Class_ScrollGUI - updated on 2015-03-13

29 Feb 2016, 10:16

It's a side-effect of the feature that classes are always super-global. It wouldn't make much sense to correct the class for one single name. Everyone is free to create classes named Msg, OnBar, Styles, and so on. It might be easier for you to change just that one name. I'll think about fixing it, but I'd have to fix all of my classes, then. :think:
User avatar
bichlepa
Posts: 183
Joined: 15 Aug 2014, 06:44
Location: Germany
Contact:

Re: Class_ScrollGUI - updated on 2015-03-13

29 Feb 2016, 10:48

Thank you for the explanation. I'll use more unique names for my classes, then.
Scripting is too complicated? Try AutoHotFlow, the graphical automation tool! Written in AutoHotkey.
Tre4shunter
Posts: 139
Joined: 26 Jan 2016, 16:05

Re: Class_ScrollGUI - updated on 2015-03-13

04 Apr 2017, 08:05

First off - Amazing class as always! Thank you!!

Im using it almost to create a 'fake' tab control with each tab scrollable.

Here is my current code:

Code: Select all

#NoEnv
SetBatchLines, -1

;Child GUI 1
Gui, New, +hwndHGUI
Gui, Margin, 10, 10
Loop, 6 {
	Gui,Add,Text, ,First Section
	Gui, Add, combobox, w400
}

;Child GUI 2
Gui, New, +hwndHGUI2
Gui, Margin, 10, 10
Loop, 6 {
	Gui,Add,Text, ,Second Section
	Gui, Add, combobox, w400
}

;Child GUI 3
Gui, New, +hwndHGUI3
Gui, Margin, 10, 10
gui,add,listbox,w150 h500


; Create ScrollGUI's
Global SG1 := New ScrollGUI(HGUI, 0, 175, "", 2, 4)
Global SG2 := New ScrollGUI(HGUI2, 0, 175, "", 2, 4)
Global SG3 := New ScrollGUI(HGUI3, 0, 175, "", 2, 4)

; Create the main window (parent)
Gui, Main:New
Gui, Margin, 0, 0
Gui, % SG1.HWND . ":-Caption +ParentMAIN +LastFound +Resize"
Gui, % SG1.HWND . ":Show", Hide

Gui, % SG2.HWND . ": -Caption +ParentMAIN +LastFound +Resize"
Gui, % SG2.HWND . ":Show", Hide

Gui, % SG3.HWND . ": -Caption +ParentMAIN +LastFound +Resize"
Gui, % SG3.HWND . ":Show", Hide

Gui, Add, Button,gFirstButton		w150		, First
Gui, Add, Button,gSecondbutton	w150	ys	, Second
Gui, Add, Button,gThirdButton		w150	ys	, Third
Gui, Show,w450 h200, Settings
; Show ScrollGUI1
;SG1.Show("", "x0 y50")
Return

FirstButton:
SG2.Hide()
SG3.Hide()
SG1.Show("", "x0 y25")
return

SecondButton:
SG1.Hide()
SG3.Hide()
SG2.Show("", "x0 y25")
return

ThirdButton:
SG1.Hide()
SG2.Hide()
SG3.Show("", "x0 y25")
return

guisize:
return

; ----------------------------------------------------------------------------------------------------------------------
MainGuiClose:
MainGuiEscape:
ExitApp

; ======================================================================================================================
; Namepace:       ScrollGUI
; Function:       Creates a scrollable GUI as a parent for GUI windows.
; Tested with:    AHK 1.1.20.02 (1.1.20+ required)
; Tested on:      Win 8.1 (x64)
; License:        The Unlicense -> http://unlicense.org
; Change log:
;                 1.0.00.00/2015-02-06/just me        -  initial release on ahkscript.org
;                 1.0.01.00/2015-02-08/just me        -  bug fixes
;                 1.1.00.00/2015-02-13/just me        -  bug fixes, mouse wheel handling, AutoSize method
;                 1.2.00.00/2015-03-12/just me        -  mouse wheel handling, resizing, OnMessage, bug fixes
; ======================================================================================================================
Class ScrollGUI {
	Static Instances := []
   ; ===================================================================================================================
   ; __New          Creates a scrollable parent window (ScrollGUI) for the passed GUI.
   ; Parameters:
   ;    HGUI        -  HWND of the GUI child window.
   ;    Width       -  Width of the client area of the ScrollGUI.
   ;                   Pass 0 to set the client area to the width of the child GUI.
   ;    Height      -  Height of the client area of the ScrollGUI.
   ;                   Pass 0 to set the client area to the height of the child GUI.
   ;    ----------- Optional:
   ;    GuiOptions  -  GUI options to be used when creating the ScrollGUI (e.g. +LabelMyLabel).
   ;                   Default: empty (no options)
   ;    ScrollBars  -  Scroll bars to register:
   ;                   1 : horizontal
   ;                   2 : vertical
   ;                   3 : both
   ;                   Default: 3
   ;    Wheel       -  Register WM_MOUSEWHEEL / WM_MOUSEHWHEEL messages:
   ;                   1 : register WM_MOUSEHWHEEL for horizontal scrolling (reqires Win Vista+)
   ;                   2 : register WM_MOUSEWHEEL for vertical scrolling
   ;                   3 : register both
   ;                   4 : register WM_MOUSEWHEEL for vertical and Shift+WM_MOUSEWHEEL for horizontal scrolling
   ;                   Default: 0
   ; Return values:
   ;    On failure:    False
   ; Remarks:
   ;    The dimensions of the child GUI are determined internally according to the visible children.
   ;    The maximum width and height of the parent GUI will be restricted to the dimensions of the child GUI.
   ;    If you register mouse wheel messages, the messages will be passed to the focused control, unless the mouse 
   ;    is hovering on one of the ScrollGUI's scroll bars. If the control doesn't process the message, it will be
   ;    returned back to the ScrollGUI.
   ;    Common controls seem to ignore wheel messages whenever the CTRL is down. So you can use this modifier to 
   ;    scroll the ScrollGUI even if a scrollable control has the focus.
   ; ===================================================================================================================
	__New(HGUI, Width, Height, GuiOptions := "", ScrollBars := 3, Wheel := 0) {
		Static WS_HSCROLL := "0x100000", WS_VSCROLL := "0x200000"
		Static FN_SCROLL := ObjBindMethod(ScrollGui, "On_WM_Scroll")
		Static FN_SIZE := ObjBindMethod(ScrollGui, "On_WM_Size")
		Static FN_WHEEL := ObjBindMethod(ScrollGUI, "On_WM_Wheel")
		ScrollBars &= 3
		Wheel &= 7
		If ((ScrollBars <> 1) && (ScrollBars <> 2) && (ScrollBars <> 3))
      || ((Wheel <> 0) && (Wheel <> 1) && (Wheel <> 2) && (Wheel <> 3) && (Wheel <> 4))
			Return False
		If !DllCall("User32.dll\IsWindow", "Ptr", HGUI, "UInt")
			Return False
		VarSetCapacity(RC, 16, 0)
      ; Child GUI
		If !This.AutoSize(HGUI, GuiW, GuiH)
			Return False
		Gui, %HGUI%:-Caption -Resize
		Gui, %HGUI%:Show, w%GuiW% h%GuiH% Hide
		MaxH := GuiW
		MaxV := GuiH
		LineH := Ceil(MaxH / 20)
		LineV := Ceil(MaxV / 20)
      ; ScrollGUI
		If (Width = 0) || (Width > MaxH)
			Width := MaxH
		If (Height = 0) || (Height > MaxV)
			Height := MaxV
		Styles := (ScrollBars & 1 ? " +" . WS_HSCROLL : "") . (ScrollBars & 2 ? " +" . WS_VSCROLL : "")
		Gui, New, %GuiOptions% %Styles% +hwndHWND
		Gui, %HWND%:Show, w%Width% h%Height% Hide
		Gui, %HWND%:+MaxSize%MaxH%x%MaxV%
		PageH := Width + 1
		PageV := Height + 1
      ; Instance variables
		This.HWND := HWND + 0
		This.HGUI := HGUI
		This.Width := Width
		This.Height := Height
		This.UseShift := False
		If (ScrollBars & 1) {
			This.SetScrollInfo(0, {Max: MaxH, Page: PageH, Pos: 0}) ; SB_HORZ = 0
			OnMessage(0x0114, FN_SCROLL) ; WM_HSCROLL = 0x0114
			If (Wheel & 1)
				OnMessage(0x020E, FN_WHEEL) ; WM_MOUSEHWHEEL = 0x020E
			Else If (Wheel & 4) {
				OnMessage(0x020A, FN_WHEEL) ; WM_MOUSEWHEEL = 0x020A
				This.UseShift := True
			}
			This.MaxH := MaxH
			This.LineH := LineH
			This.PageH := PageH
			This.PosH := 0
			This.ScrollH := True
			If (Wheel & 5)
				This.WheelH := True
		}
		If (ScrollBars & 2) {
			This.SetScrollInfo(1, {Max: MaxV, Page: PageV, Pos: 0}) ; SB_VERT = 1
			OnMessage(0x0115, FN_SCROLL) ; WM_VSCROLL = 0x0115
			If (Wheel & 6)
				OnMessage(0x020A, FN_WHEEL) ; WM_MOUSEWHEEL = 0x020A
			This.MaxV := MaxV
			This.LineV := LineV
			This.PageV := PageV
			This.PosV := 0
			This.ScrollV := True
			If (Wheel & 6)
				This.WheelV := True
		}
      ; Set the position of the child GUI
		Gui, %HGUI%:+Parent%HWND%
		Gui, %HGUI%:Show, x0 y0
      ; Adjust the scroll bars
		This.Instances[This.HWND] := &This
		This.Size()
		OnMessage(0x0005, FN_SIZE) ; WM_SIZE = 0x0005
	}
   ; ===================================================================================================================
   ; __Delete       Destroy the GUIs, if they still exist.
   ; ===================================================================================================================
	__Delete() {
		This.Destroy()
	}
   ; ===================================================================================================================
   ; Show           Shows the ScrollGUI.
   ; Parameters:
   ;    Title       -  Title of the ScrollGUI window
   ;    ShowOptions -  Gui, Show command options, width or height options are ignored
   ; Return values:
   ;    On success: True
   ;    On failure: False
   ; ===================================================================================================================
	Show(Title := "", ShowOptions := "") {
		ShowOptions := RegExReplace(ShowOptions, "i)\+?AutoSize")
		W := This.Width
		H := This.Height
		Gui, % This.HWND . ":Show", %ShowOptions% w%W% h%H%, %Title%
		Return True
	}
   ;Hide Gui From View
   ; ==================
	Hide() {
		If This.Instances.HasKey(This.HWND) {
			Gui, % This.HWND . ":Hide"
			Return True
		}
	}
   ; ===================================================================================================================
   ; Destroy        Destroys the ScrollGUI and the associated child GUI.
   ; Parameters:
   ;    None.
   ; Return values:
   ;    On success: True
   ;    On failure: False
   ; Remarks:
   ;    Use this method instead of 'Gui, Destroy' to remove the ScrollGUI from the 'Instances' object.
   ; ===================================================================================================================
	Destroy() {
		If This.Instances.HasKey(This.HWND) {
			Gui, % This.HWND . ":Destroy"
			This.Instances.Remove(This.HWND, "")
			Return True
		}
	}
   ; ===================================================================================================================
   ; AdjustToChild  Adjust the scroll bars to the new child dimensions.
   ; Parameters:
   ;    None
   ; Return values:
   ;    On success: True
   ;    On failure: False
   ; Remarks:
   ;    Call this method whenever the visible area of the child GUI has to be changed, e.g. after adding, hiding,
   ;    unhiding, resizing, or repositioning controls.
   ;    The dimensions of the child GUI are determined internally according to the visible children.
   ; ===================================================================================================================
	AdjustToChild() {
		VarSetCapacity(RC, 16, 0)
		DllCall("User32.dll\GetWindowRect", "Ptr", This.HGUI, "Ptr", &RC)
		PrevW := NumGet(RC, 8, "Int") - NumGet(RC, 0, "Int")
		PrevH := Numget(RC, 12, "Int") - NumGet(RC, 4, "Int")
		DllCall("User32.dll\ScreenToClient", "Ptr", This.HWND, "Ptr", &RC)
		XC := XN := NumGet(RC, 0, "Int")
		YC := YN := NumGet(RC, 4, "Int")
		If !This.AutoSize(This.HGUI, GuiW, GuiH)
			Return False
		Gui, % This.HGUI . ":Show", x%XC% y%YC% w%GuiW% h%GuiH%
		MaxH := GuiW
		MaxV := GuiH
		Gui, % This.HWND . ":+MaxSize" . MaxH . "x" . MaxV
		If (GuiW < This.Width) || (GuiH < This.Height) {
			Gui, % This.HWND . ":Show", w%GuiW% h%GuiH%
			This.Width := GuiW
			This.SetPage(1, MaxH + 1)
			This.Height := GuiH
			This.SetPage(2, MaxV + 1)
		}
		LineH := Ceil(MaxH / 20)
		LineV := Ceil(MaxV / 20)
		If This.ScrollH {
			This.SetMax(1, MaxH)
			This.LineH := LineH
			If (XC + MaxH) < This.Width {
				XN += This.Width - (XC + MaxH)
				If (XN > 0)
					XN := 0
				This.SetScrollInfo(0, {Pos: XN * -1})
				This.GetScrollInfo(0, SI)
				This.PosH := NumGet(SI, 20, "Int")
			}
		}
		If This.ScrollV {
			This.SetMax(2, MaxV)
			This.LineV := LineV
			If (YC + MaxV) < This.Height {
				YN += This.Height - (YC + MaxV)
				If (YN > 0)
					YN := 0
				This.SetScrollInfo(1, {Pos: YN * -1})
				This.GetScrollInfo(1, SI)
				This.PosV := NumGet(SI, 20, "Int")
			}
		}
		If (XC <> XN) || (YC <> YN)
			DllCall("User32.dll\ScrollWindow", "Ptr", This.HWND, "Int", XN - XC, "Int", YN - YC, "Ptr", 0, "Ptr", 0)
		Return True
	}
   ; ===================================================================================================================
   ; SetMax         Sets the width or height of the scrolling area.
   ; Parameters:
   ;    SB          -  Scroll bar to set the value for:
   ;                   1 = horizontal
   ;                   2 = vertical
   ;    Max         -  Width respectively height of the scrolling area in pixels
   ; Return values:
   ;    On success: True
   ;    On failure: False
   ; ===================================================================================================================
	SetMax(SB, Max) {
      ; SB_HORZ = 0, SB_VERT = 1
		SB--
		If (SB <> 0) && (SB <> 1)
			Return False
		If (SB = 0)
			This.MaxH := Max
		Else
			This.MaxV := Max
		Return This.SetScrollInfo(SB, {Max: Max})
	}
   ; ===================================================================================================================
   ; SetLine        Sets the number of pixels to scroll by line.
   ; Parameters:
   ;    SB          -  Scroll bar to set the value for:
   ;                   1 = horizontal
   ;                   2 = vertical
   ;    Line        -  Number of pixels.
   ; Return values:
   ;    On success: True
   ;    On failure: False
   ; ===================================================================================================================
	SetLine(SB, Line) {
      ; SB_HORZ = 0, SB_VERT = 1
		SB--
		If (SB <> 0) && (SB <> 1)
			Return False
		If (SB = 0)
			This.LineH := Line
		Else
			This.LineV := Line
		Return True
	}
   ; ===================================================================================================================
   ; SetPage        Sets the number of pixels to scroll by page.
   ; Parameters:
   ;    SB          -  Scroll bar to set the value for:
   ;                   1 = horizontal
   ;                   2 = vertical
   ;    Page        -  Number of pixels.
   ; Return values:
   ;    On success: True
   ;    On failure: False
   ; Remarks:
   ;    If the ScrollGUI is resizable, the page size will be recalculated automatically while resizing.
   ; ===================================================================================================================
	SetPage(SB, Page) {
      ; SB_HORZ = 0, SB_VERT = 1
		SB--
		If (SB <> 0) && (SB <> 1)
			Return False
		If (SB = 0)
			This.PageH := Page
		Else
			This.PageV := Page
		Return This.SetScrollInfo(SB, {Page: Page})
	}
   ; ===================================================================================================================
   ; Methods for internal or system use!!!
   ; ===================================================================================================================
	AutoSize(HGUI, ByRef Width, ByRef Height) {
		DHW := A_DetectHiddenWindows
		DetectHiddenWindows, On
		VarSetCapacity(RECT, 16, 0)
		Width := Height := 0
		HWND := HGUI
		CMD := 5 ; GW_CHILD
		L := T := R := B := LH := TH := ""
		While (HWND := DllCall("GetWindow", "Ptr", HWND, "UInt", CMD, "UPtr")) && (CMD := 2) {
			WinGetPos, X, Y, W, H, ahk_id %HWND%
			W += X, H += Y
			WinGet, Styles, Style, ahk_id %HWND%
			If (Styles & 0x10000000) { ; WS_VISIBLE
				If (L = "") || (X < L)
					L := X
				If (T = "") || (Y < T)
					T := Y
				If (R = "") || (W > R)
					R := W
				If (B = "") || (H > B)
					B := H
			}
			Else {
				If (LH = "") || (X < LH)
					LH := X
				If (TH = "") || (Y < TH)
					TH := Y
			}
		}
		DetectHiddenWindows, %DHW%
		If (LH <> "") {
			VarSetCapacity(POINT, 8, 0)
			NumPut(LH, POINT, 0, "Int")
			DllCall("ScreenToClient", "Ptr", HGUI, "Ptr", &POINT)
			LH := NumGet(POINT, 0, "Int")
		}
		If (TH <> "") {
			VarSetCapacity(POINT, 8, 0)
			NumPut(TH, POINT, 4, "Int")
			DllCall("ScreenToClient", "Ptr", HGUI, "Ptr", &POINT)
			TH := NumGet(POINT, 4, "Int")
		}
		NumPut(L, RECT, 0, "Int"), NumPut(T, RECT,  4, "Int")
		NumPut(R, RECT, 8, "Int"), NumPut(B, RECT, 12, "Int")
		DllCall("MapWindowPoints", "Ptr", 0, "Ptr", HGUI, "Ptr", &RECT, "UInt", 2)
		Width := NumGet(RECT, 8, "Int") + (LH <> "" ? LH : NumGet(RECT, 0, "Int"))
		Height := NumGet(RECT, 12, "Int") + (TH <> "" ? TH : NumGet(RECT,  4, "Int"))
		Return True
	}
   ; ===================================================================================================================
	GetScrollInfo(SB, ByRef SI) {
		VarSetCapacity(SI, 28, 0) ; SCROLLINFO
		NumPut(28, SI, 0, "UInt")
		NumPut(0x17, SI, 4, "UInt") ; SIF_ALL = 0x17
		Return DllCall("User32.dll\GetScrollInfo", "Ptr", This.HWND, "Int", SB, "Ptr", &SI, "UInt")
	}
   ; ===================================================================================================================
	SetScrollInfo(SB, Values) {
		Static SIF := {Max: 0x01, Page: 0x02, Pos: 0x04}
		Static Off := {Max: 12, Page: 16, Pos: 20}
		Mask := 0
		VarSetCapacity(SI, 28, 0) ; SCROLLINFO
		NumPut(28, SI, 0, "UInt")
		For Key, Value In Values {
			If SIF.HasKey(Key) {
				Mask |= SIF[Key]
				NumPut(Value, SI, Off[Key], "UInt")
			}
		}
		If (Mask) {
			NumPut(Mask | 0x08, SI, 4, "UInt") ; SIF_DISABLENOSCROLL = 0x08
			Return DllCall("User32.dll\SetScrollInfo", "Ptr", This.HWND, "Int", SB, "Ptr", &SI, "UInt", 1, "UInt")
		}
		Return False
	}
   ; ===================================================================================================================
	On_WM_Scroll(WP, LP, Msg, HWND) {
      ; WM_HSCROLL = 0x0114, WM_VSCROLL = 0x0115
		If (Instance := Object(This.Instances[HWND]))
			If ((Msg = 0x0114) && Instance.ScrollH)
         || ((Msg = 0x0115) && Instance.ScrollV)
				Return Instance.Scroll(WP, LP, Msg, HWND)
	}
   ; ===================================================================================================================
	Scroll(WP, LP, Msg, HWND) {
      ; WM_HSCROLL = 0x0114, WM_VSCROLL = 0x0115
		Static SB_LINEMINUS := 0, SB_LINEPLUS := 1, SB_PAGEMINUS := 2, SB_PAGEPLUS := 3, SB_THUMBTRACK := 5
		If (LP <> 0)
			Return
		SB := (Msg = 0x0114 ? 0 : 1) ; SB_HORZ : SB_VERT
		SC := WP & 0xFFFF
		SD := (Msg = 0x0114 ? This.LineH : This.LineV)
		SI := 0
		If !This.GetScrollInfo(SB, SI)
			Return
		PA := PN := NumGet(SI, 20, "Int")
		PN := (SC = 0) ? PA - SD ; SB_LINEMINUS
          : (SC = 1) ? PA + SD ; SB_LINEPLUS
          : (SC = 2) ? PA - NumGet(SI, 16, "UInt") ; SB_PAGEMINUS
          : (SC = 3) ? PA + NumGet(SI, 16, "UInt") ; SB_PAGEPLUS
          : (SC = 5) ? NumGet(SI, 24, "Int") ; SB_THUMBTRACK
          : PA
		If (PA = PN)
			Return 0
		This.SetScrollInfo(SB, {Pos: PN})
		This.GetScrollInfo(SB, SI)
		PN := NumGet(SI, 20, "Int")
		If (SB = 0)
			This.PosH := PN
		Else
			This.PosV := PN
		If (PA <> PN) {
			HS := (Msg = 0x0114) ? PA - PN : 0
			VS := (Msg = 0x0115) ? PA - PN : 0
			DllCall("User32.dll\ScrollWindow", "Ptr", This.HWND, "Int", HS, "Int", VS, "Ptr", 0, "Ptr", 0)
		}
		Return 0
	}
   ; ===================================================================================================================
	On_WM_Size(WP, LP, Msg, HWND) {
		If ((WP = 0) || (WP = 2)) && (Instance := Object(This.Instances[HWND]))
			Return Instance.Size(LP & 0xFFFF, (LP >> 16) & 0xFFFF)
	}
   ; ===================================================================================================================
	Size(Width := 0, Height := 0) {
		If (Width = 0) || (Height = 0) {
			VarSetCapacity(RC, 16, 0)
			DllCall("User32.dll\GetClientRect", "Ptr", This.HWND, "Ptr", &RC)
			Width := NumGet(RC, 8, "Int")
			Height := Numget(RC, 12, "Int")
		}
		SH := SV := 0
		If This.ScrollH {
			If (Width <> This.Width) {
				This.SetScrollInfo(0, {Page: Width + 1})
				This.Width := Width
				This.GetScrollInfo(0, SI)
				PosH := NumGet(SI, 20, "Int")
				SH := This.PosH - PosH
				This.PosH := PosH
			}
		}
		If This.ScrollV {
			If (Height <> This.Height) {
				This.SetScrollInfo(1, {Page: Height + 1})
				This.Height := Height
				This.GetScrollInfo(1, SI)
				PosV := NumGet(SI, 20, "Int")
				SV := This.PosV - PosV
				This.PosV := PosV
			}
		}
		If (SH) || (SV)
			DllCall("User32.dll\ScrollWindow", "Ptr", This.HWND, "Int", SH, "Int", SV, "Ptr", 0, "Ptr", 0)
		Return 0
	}
   ; ===================================================================================================================
	On_WM_Wheel(WP, LP, Msg, HWND) {
      ; MK_SHIFT = 0x0004, WM_MOUSEWHEEL = 0x020A, WM_MOUSEHWHEEL = 0x020E, WM_NCHITTEST = 0x0084
		HACT := WinActive("A") + 0
		If (HACT <> HWND) && (Instance := Object(This.Instances[HACT])) {
			SendMessage, 0x0084, 0, % (LP & 0xFFFFFFFF), , ahk_id %HACT%
			OnBar := ErrorLevel
			If (OnBar = 6) && Instance.WheelH ; HTHSCROLL = 6
				Return Instance.Wheel(WP, LP, 0x020E, HACT)
			If (OnBar = 7) && Instance.WheelV ; HTVSCROLL = 7
				Return Instance.Wheel(WP, LP, 0x020A, HACT)
		}
		If (Instance := Object(This.Instances[HWND])) {
			If ((Msg = 0x020E) && Instance.WheelH)
         || ((Msg = 0x020A) && (Instance.WheelV || (Instance.WheelH && Instance.UseShift && (WP & 0x0004))))
				Return Instance.Wheel(WP, LP, Msg, HWND)
		}
	}
   ; ===================================================================================================================
	Wheel(WP, LP, Msg, HWND) {
      ; MK_SHIFT = 0x0004, WM_MOUSEWHEEL = 0x020A, WM_MOUSEHWHEEL = 0x020E, WM_HSCROLL = 0x0114, WM_VSCROLL = 0x0115
      ; SB_LINEMINUS = 0, SB_LINEPLUS = 1
		If (Msg = 0x020A) && This.UseShift && (WP & 0x0004)
			Msg := 0x020E
		Msg := (Msg = 0x020A ? 0x0115 : 0x0114)
		SB := ((WP >> 16) > 0x7FFF) || (WP < 0) ? 1 : 0
		Return This.Scroll(SB, 0, Msg, HWND)
	}
}
I added the whole class because i added Hide() to the class. If there is a simpler way to do what i am attempting feel free to slap me then tell me the correct way :P

My problem is that i can't use the scroll wheel on the child gui 'tabs' i think this is mostly due to the fact that scroll gui isnt meant to be used on a child window? But is there a way around this limitation? If not, i understand obviously.

Thanks so much!
just me
Posts: 9423
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Class_ScrollGUI - updated on 2015-03-13

04 Apr 2017, 10:52

Hi Tre4shunter,

your Hide() method is looking good for me.

And yes, the class is not designed to create scrollable Guis as children of other children. That's why no Hide() method exists in the original class.
DRocks
Posts: 565
Joined: 08 May 2018, 10:20

Re: Class_ScrollGUI - updated on 2015-03-13

14 Jan 2020, 09:04

Hello @just me
I've been using this great class of yours in a script for a while without a problem.
Thanks for sharing it its very practical.

This week I started integrating it in a control-heavier project - I mean theres more Gui controls that gets populated onto the scrollable area.
And I've been able to reproduce a bug when scrolling.
It seems that it hangs in an infinite loop for a while (let's say 30 seconds) and even the Windows 10 task manager is unable to show up while this moment is happening.

The worse is when you grab the vertical scrollbar with the mouse, but it can happen in a smaller proportion (3 seconds rather than like 30secs) with the mousewheel scrolling.

I am wondering if you have a clue in what part of the class it could be a possibility ?
Is there some place where a heavy calculation would be on critical mode and maybe hang there to your knowledge ?

If you want I can post my source where it's being used, but its not going to be much helpful because it's basic AHK Gui stuff.
Shortly, this project is the creation of a table of controls from a csv import of a bank statement.
When theres only rows that fit in a single page area, there's no problem.
But as soon as you have the vertical scroll bar appear for larger row counts, it freezes on scrolling.
just me
Posts: 9423
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Class_ScrollGUI - updated on 2015-03-13

15 Jan 2020, 03:34

Hello @DRocks,

this issue has not been reported as yet, so it seems best to show your code. If you don't want to post it here, send me a PM, please.
DRocks
Posts: 565
Joined: 08 May 2018, 10:20

Re: Class_ScrollGUI - updated on 2015-03-13

15 Jan 2020, 09:55

@just me
Thanks for your reply!

Here is the source but sorry for the mess, this is a work in progress and I'm far from polishing the formatting.

ScrollGUI is instantiated on line 517 in AHK Studio (ScrollGUI.ahk is meant to be an #include file that I did not join)
This is a AHK Gui that mimics a Excel Table with 11 columns and undefined rows depending on the amount of rows in the csv.
You'll need my 99_rows_fake_desjardins.csv file to import and populate the gui, here's a OneDrive link so you can download it (downlaod is not direct you can refuse downloading once the OneDrive page opens if you want):
https://1drv.ms/u/s!AnxboyZ6ZdpRhYhl3XxlAJxrq5g3Sw?e=nFOCe1

Note that the problem seems to be worse when row count exceed 60++
Spoiler
User avatar
kczx3
Posts: 1640
Joined: 06 Oct 2015, 21:39

Re: Class_ScrollGUI - updated on 2015-03-13

15 Jan 2020, 20:24

It’s probably just the sheer number of controls based on the fact that you’re mimicking an excel spreadsheet

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: sanmaodo and 104 guests