Class_ScrollGUI - updated on 2015-03-13

Post your working scripts, libraries and tools for AHK v1.1 and older
User avatar
evilC
Posts: 4823
Joined: 27 Feb 2014, 12:30

Re: Class_ScrollGUI - updated on 2015-02-08

09 Feb 2015, 13:40

TBH, I think I am maybe starting to come around to your point of view on this - I was initially hoping to do a variable-height GUI, but even without scrollbars, resizing the "Scrolling Area" (The GUI within which all Child GUIs reside) when you resize the main GUI was overly slow.

My ultimate goal is a fixed-width, vertically scrolling "list" of Child GUis - Do you have any plans to support scrolling lists of GUIs?

Something like this:

Code: Select all

Gui, New, +hwndHGUI
Gui, Margin, 20, 20
Loop 10 {
   Gui, new, % "hwndChild -Border +Parent" HGUI
   Gui, % Child ":Add", Text, % "xm ym", Child %A_Index%
   Gui, % Child ":Show", % "h40 w380 x5 y" (A_Index - 1)*50,
}
; Create ScrollGUI1 with both horizontal and vertical scrollbars and mouse wheel capturing
SG1 := New ScrollGUI(HGUI, 400, 200, "+Resize +MinSize +LabelGui1", 3, 3)
; Show ScrollGUI1
SG1.Show("ScrollGUI1 Title", "y0 xcenter")
Gui, % HGUI ":Show", x0 y0 h400 w500
I guess in order to do this, I guess AdjustToChild would need to be altered to check for Child GUIs?
I would imagine this may slow down cases where there are no child GUIs, but I suppose some flag like "HasChildGuis" could be set to let the class know to take into account Child GUIs when calculating the size of the Scrolling Area.
just me
Posts: 9451
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Class_ScrollGUI - updated on 2015-02-08

10 Feb 2015, 03:19

evilC wrote:Something like this:
I don't see the point. For what reason do you need more than one child GUI for this?
User avatar
evilC
Posts: 4823
Joined: 27 Feb 2014, 12:30

Re: Class_ScrollGUI - updated on 2015-02-08

10 Feb 2015, 10:56

I am writing a plugin based app. Each GUI is a plugin. Each plugin is a derived instance of a class.

Here is an example of what it may vaguely look like. This is my current proof of concept - 3 plugins that work together to let disabled kids with no arms etc play video games via a "OneSwitch" box.

Image

People with different disabilities need different assists. I want a way of modularizing the various components, so their carers can assemble different blocks to cater for their own unique set of disabilities.
I do not want to have to write a bespoke app for each kid - I would not have the time.
Last edited by evilC on 10 Feb 2015, 12:11, edited 1 time in total.
just me
Posts: 9451
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Class_ScrollGUI - updated on 2015-02-08

10 Feb 2015, 12:06

Well, the most important problem is the child GUI sizing using AutoSize. If the GUI contains only other child GUIs, it does not work. I'll think about it.
User avatar
evilC
Posts: 4823
Joined: 27 Feb 2014, 12:30

Re: Class_ScrollGUI - updated on 2015-02-08

10 Feb 2015, 12:16

If it is not in line with the direction you wish to go, please don't overly worry yourself. Surely it's the kind of thing pretty easily changed, as all you have to do is make sure AdjustToChild takes into account the RECT of each child GUI?

It's possibly the kind of thing I would need to do anyway - there would be little point having the code calculate it all from actual positions - you may as well just define the client area's size as you add each child GUI.

ie Each time you add a child, increment the bottom of the rect by the height of the child you are adding.
just me
Posts: 9451
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Class_ScrollGUI - updated on 2015-02-08

11 Feb 2015, 09:54

OK, I've tried to implement my own AutoSize method. I'm not sure whether it will work just as well as the built-in in either case, but I had no issues as yet. Would you please make some testing and tell me if it's closer to your needs?

Code: Select all

Removed test script!
Last edited by just me on 12 Feb 2015, 06:04, edited 1 time in total.
just me
Posts: 9451
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Class_ScrollGUI - updated on 2015-02-08

11 Feb 2015, 11:58

If you mean this code you must not set the size of the ScrollGUI's main child GUI manually as done with Gui, % HGUI ":Show", x0 y0 h400 w500. Just add, move, resize, or hide/destroy controls or child GUIs and call AdjustToChild().
User avatar
evilC
Posts: 4823
Joined: 27 Feb 2014, 12:30

Re: Class_ScrollGUI - updated on 2015-02-08

11 Feb 2015, 12:06

Ah right, yeah that would make sense.

Pixel perfect now :)

I do notice one thing strange, however.

Code: Select all

#SingleInstance force
#NoEnv
;#Include <CScrollGUI>

SetBatchLines, -1
; -------------------------------------------------------------------------------------------------------------------
; ChildGUI 1
Gui, New, +hwndHGUI
Gui, Margin, 20, 20
Loop 10 {
   Gui, new, % "hwndChild -Border +Parent" HGUI
   Gui, % Child ":Add", Text, % "xm ym", Child %A_Index%
   Gui, % Child ":Show", % "h40 w380 x5 y" (A_Index - 1)*50,
}
; Create ScrollGUI1 with both horizontal and vertical scrollbars and mouse wheel capturing
SG1 := New ScrollGUI(HGUI, 400, 200, "+Resize +MinSize +LabelGui1", 3, 3)
; Show ScrollGUI1
SG1.Show("ScrollGUI1 Title", "y0 xcenter")
SG1.AdjustToChild()
; ----------------------------------------------------------------------------------------------------------------------
ShowHide:
   GuiControlGet, V, %HGUI2%:Visible, TX2
   GuiControl, %HGUI2%:Hide%V%, TX2
   GuiControlGet, V, %HGUI2%:Visible, TX3
   GuiControl, %HGUI2%:Hide%V%, TX3
   SG2.AdjustToChild()
Return
; ----------------------------------------------------------------------------------------------------------------------
Esc::
Gui1Close:
Gui1Escape:
ExitApp
; ----------------------------------------------------------------------------------------------------------------------
Gui1Size:
   If (A_EventInfo <> 1)
      SG1.AdjustToParent()
Return
; ----------------------------------------------------------------------------------------------------------------------
Gui2Close:
Gui2Escape:
   SG1 := ""
Return
Grab the left border and try and move it left. The window "wobbles".
just me
Posts: 9451
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Class_ScrollGUI - updated on 2015-02-08

12 Feb 2015, 06:03

Well, it took some time but I think I've got it. It was caused by the additional call off SG1.AdjustToChild() (which I overlooked until this morning) and a wrong recalculation of the MaxSize values within this method. AHK permits to set MaxSize to be less than MinSize, and this causes the "wobbling".

Code: Select all

Removed test script!
Does this new version work for you?
Last edited by just me on 13 Feb 2015, 04:51, edited 1 time in total.
User avatar
evilC
Posts: 4823
Joined: 27 Feb 2014, 12:30

Re: Class_ScrollGUI - updated on 2015-02-08

13 Feb 2015, 02:17

So I am starting to try to work ScrollGui into my current proof-of-concept framework, and noticed that the mouse wheel stopped working as soon as I put it inside a class.
I am guessing funcs are not bound or something?

Code: Select all

#SingleInstance force
#NoEnv
#Include class_scrollgui.ahk

SetBatchLines, -1

mc := new MyClass()

; -------------------------------------------------------------------------------------------------------------------
Class MyClass {
   __New(){
      Gui, New, hwndHGUI
      Gui % HGUI ":show", % "w500 h400"
      this.hMain := HGUI
      Gui, new, % "hwndHGUI -Border +Parent" this.hMain
      this.hChild := HGUI
      Gui, % this.hChild ":show", % "x175 y0 w300 h400"

      Gui, Margin, 20, 20
      Loop 10 {
         Gui, new, % "hwndChild -Border +Parent" this.hChild
         Gui, % Child ":Add", Text, % "xm ym", Child %A_Index%
         Gui, % Child ":Show", % "h40 w380 x5 y" (A_Index - 1)*50,
      }
      ; Create ScrollGUI1 with both horizontal and vertical scrollbars and mouse wheel capturing
      this.SG1 := New ScrollGUI(this.hChild, 300, 400, "-Border +Parent" this.hMain, 3, 3)
      ; Show ScrollGUI1
      this.SG1.Show("ScrollGUI1 Title", "x175 y0")
      this.SG1.AdjustToChild()
   }
}
; ChildGUI 1
; ----------------------------------------------------------------------------------------------------------------------
Esc::
Gui1Close:
Gui1Escape:
ExitApp
; ----------------------------------------------------------------------------------------------------------------------
Gui1Size:
   If (A_EventInfo <> 1)
      SG1.AdjustToParent()
Return
Whilst I love the overall result that has been achieved, I am not so keen on the architecture.
For vanilla v1 as it stands, this is a fine implementation, but with the way the test builds are going, turning GUIs and controls into classes is going to be a much better way to go, and this code as it stands does not sit very well with that.

Here's some code to give you an idea of what I mean:
(Needs test Build from here)

Code: Select all

#SingleInstance force

#include class_scrollgui.ahk

UCR := new UCR()
return

Esc::
GuiClose:
	ExitApp

Class UCR extends _CWindow {
	Plugins := []
	__New(){
		base.__New()
		this.myedit := this.Add("Edit","xm ym w100","ChangeMe")
		this.mybtn := this.Add("Button","xm yp+20 w100","Beep")
		this.Show("w500 h400", "UCR")
		this.GuiControl("+g", this.mybtn, this.Test)
		this.GuiControl("+g", this.myedit, this.EditChanged)
		
		this.ChildWindow := new _CWindow("-Border +Parent" this._hwnd)
		this.ChildWindow.Show("x175 y0 w300 h400")
		Gui, Margin, 20, 20
		Loop 10 {
			this.Plugins.Insert(new _CWindow("-Border +Parent" this.ChildWindow._hwnd))
			this.Plugins[A_Index].Add("Text", "xm ym", "Child" A_Index)
			this.Plugins[A_Index].Show("h40 w380 x5 y" (A_Index - 1)*50 )
		}
		; Create ScrollGUI1 with both horizontal and vertical scrollbars and mouse wheel capturing
		this.SG1 := New ScrollGUI(this.ChildWindow._hwnd, 300, 400, "-Border +Parent" this._hwnd, 3, 3)
		; Show ScrollGUI1
		this.SG1.Show("ScrollGUI1 Title", "x175 y0")
		this.SG1.AdjustToChild()
	}
	
	Test(){
		SoundBeep
	}
	
	EditChanged(){
		this.ToolTip(this.myedit.value, 2000)
	}
}

; Wrap AHK functionality in a standardized, easy to use, syntactically similar class
Class _CWindow {
	; equivalent to Gui, New, <params>
	__New(aParams*){
		Gui, new, % "hwndhwnd " aParams[1], % aParams[2], % aParams[3]
		this._hwnd := hwnd
	}
	
	; Equivalent to Gui, Add, <params>
	Add(aParams*){
		return new this._CGuiControl(this, aParams*)
	}
	
	; Equivalent to Gui, Show, <params>
	Show(aParams*){
		Gui, % this._hwnd ":Show", % aParams[1], % aParams[2], % aParams[3]
	}
	
	; Wraps GuiControl to use hwnds and function binding etc
	GuiControl(aParams*){
		m := SubStr(aParams[1],1,1)
		if (m = "+" || m = "-"){
			; Options
			o := SubStr(aParams[1],2,1)
			if (o = "g"){
				; G-Label
				fn := bind(aParams[3],this)
				GuiControl % aParams[1], % aParams[2]._hwnd, % fn
			}
		} else {
			GuiControl, % aParams[1], % aParams[2]._hwnd, % aParams[3]
		}
	}
	
	; Gui Controls
	Class _CGuiControl {
		; equivalent to Gui, Add, <params>
		; Pass parent as param 1
		__New(aParams*){
			this._parent := aParams[1]
			this._type := aParams[2]
			Gui, % this._parent._hwnd ":Add", % aParams[2], % "hwndhwnd " aParams[3], % aParams[4]
			this._hwnd := hwnd
		}
		
		__Get(aParam){
			if (aParam = "value"){
				; ToDo: What about other types?
				;if (this._type = "listview"){
				GuiControlGet, val, , % this._hwnd
				return val
			}
		}
	}
	
	ToolTip(Text, duration){
		fn := bind(this.ToolTipTimer, this)
		SetTimer, % fn, % "-" duration
		ToolTip % Text
	}
	
	ToolTipTimer(){
		ToolTip
	}
}

bind(fn, args*) {  ; bind v1.2
    try bound := fn.bind(args*)  ; Func.Bind() not yet implemented.
    return bound ? bound : new BoundFunc(fn, args*)
}

class BoundFunc {
    __New(fn, args*) {
        this.fn := IsObject(fn) ? fn : Func(fn)
        this.args := args
    }
    __Call(callee, args*) {
        if (callee = "" || callee = "call" || IsObject(callee)) {  ; IsObject allows use as a method.
            fn := this.fn, args.Insert(1, this.args*)
            return %fn%(args*)
        }
    }
}
Notice how you can just use commands like this.GuiControl("+g", this.mybtn, this.Test) you just pass it the instance of the class control and the method to use as the g-label - the function binding and hwnd lookup are done for you and you get nice, easy to understand syntax (which is still basically the same as AHK)

Also it seems that scrollgui creates an extra gui ? Is there a particular reason for that?
Any thoughts on how to integrate this code with scrollgui, or am I just likely to be better off ripping out the functions and adapting the code?
just me
Posts: 9451
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Class_ScrollGUI - updated on 2015-02-08

13 Feb 2015, 03:20

this.SG1 := New ScrollGUI(this.hChild, 300, 400, "-Border +Parent" this.hMain, 3, 3)
The ScrollGui cannot be a child of another GUI.

Edit: Are you looking for something like MDI?
just me
Posts: 9451
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Class_ScrollGUI - updated on 2015-02-08

13 Feb 2015, 04:52

*Update!*
User avatar
evilC
Posts: 4823
Joined: 27 Feb 2014, 12:30

Re: Class_ScrollGUI - updated on 2015-02-13

13 Feb 2015, 06:20

Hmm, MDI looks interesting.
I take it the word "Document" is just a generic term for a window of any kind, or is it specific to text editors etc?
I did originally want floating windows, but now I think a vertically scrolling list is the way to go, to free the end user from having to lay out windows.
If it would allow me to create a vertically scrolling list of AHK GUIs, and handle all the scrollbars etc in an appropriate manner, then I am very interested.
just me
Posts: 9451
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Class_ScrollGUI - updated on 2015-02-13

13 Feb 2015, 09:09

evilC wrote:If it would allow me to create a vertically scrolling list of AHK GUIs, and handle all the scrollbars etc in an appropriate manner, then I am very interested.
That's not what MDI is designed for.

If you want to use the ScrollGUI as a child of another GUI, the mouse wheel messages handling as is will fail. But I think it might be feasible to solve the problem. I'll try.
User avatar
evilC
Posts: 4823
Joined: 27 Feb 2014, 12:30

Re: Class_ScrollGUI - updated on 2015-02-13

13 Feb 2015, 14:04

cheers man.
I have been playing a bunch more with that GUI code and managed to wrap a bunch of the gui functionality quite nicely, so I think that is the way I want to go, as long as I can solve a problem I am facing at the moment.
I have a thread about it here.

Can use syntax like this.ChildWindow := new CWindow(this, "-Border").GuiOption("+Parent", this) - would be nice to be able to do this with scrollable GUIs :)

this.ChildWindow := new CWindow(this, "-Border").GuiOption("+Parent", this).SetScrollInfo(Blah)? That would be so cool!
User avatar
evilC
Posts: 4823
Joined: 27 Feb 2014, 12:30

Re: Class_ScrollGUI - updated on 2015-02-13

14 Feb 2015, 15:03

just me wrote:
evilC wrote:If it would allow me to create a vertically scrolling list of AHK GUIs, and handle all the scrollbars etc in an appropriate manner, then I am very interested.
That's not what MDI is designed for.

If you want to use the ScrollGUI as a child of another GUI, the mouse wheel messages handling as is will fail. But I think it might be feasible to solve the problem. I'll try.
Are you using the new test build of AHK? All my stuff is targetting this version, and it's changes are very much relevant to this code as you can now bind class methods to OnMessage calls easily.
Jeez, of course, I have known this for a while but I forgot to mention it. Is that the reason for the Instances var etc?
You don't need to bother with any of that any more, just do like this:

Code: Select all

fn := bind(this.OnScrollDown, this)
OnMessage(WM_SCROLL, fn)
User avatar
evilC
Posts: 4823
Joined: 27 Feb 2014, 12:30

Re: Class_ScrollGUI - updated on 2015-02-13

15 Feb 2015, 06:13

Well, I just sat down and spent 8 hours ripping apart your code.
I *kind* of understand how it works, but some of the code still escapes me a bit lol.

I did manage to re-write it though, and it all seems to work. I made the following changes:
  • No global functions / stuff stored in Instances - Everything is properly encapsulated.
  • Messages bound directly to the class.
  • Does not create a child window - applies the scrollbars to the specified GUI.
  • Gui Size event trapped by Message instead of GuiSize:
  • Not implemented Constructor properly yet, but you do not really need it. You no longer have to specify size.
  • Size limiting feature gone for now (I don't know how you did it).
  • A bunch of functions gone (SetPage etc), I am not sure if they were needed - I may be calculating stuff more often than i need to, I dunno.
Note the dependencies at the start of the script, Don't forget to use the special AHK build!

Code: Select all

; REQUIRES AHK TEST BUILD from HERE: http://ahkscript.org/boards/viewtopic.php?f=24&t=5802
; DEPENDENCIES:
; _Struct():  https://raw.githubusercontent.com/HotKeyIt/_Struct/master/_Struct.ahk - docs: http://www.autohotkey.net/~HotKeyIt/AutoHotkey/_Struct.htm
; sizeof(): https://raw.githubusercontent.com/HotKeyIt/_Struct/master/sizeof.ahk - docs: http://www.autohotkey.net/~HotKeyIt/AutoHotkey/sizeof.htm
; WinStructs: https://github.com/ahkscript/WinStructs
#SingleInstance force
#NoEnv

#include <_Struct>
#include <WinStructs>
;#include *i <SkinSharp>

SetBatchLines, -1

ScrollGui := new _CScrollGui()

Esc::
Gui1Close:
Gui1Escape:
ExitApp
; ----------------------------------------------------------------------------------------------------------------------

class _CScrollGui {
	__New(){
		static WM_HSCROLL := 0x0114, WM_VSCROLL := 0x0115
		static WM_MOUSEWHEEL := 0x020A, WM_MOUSEHWHEEL := 0x020E
		
		Gui, new, hwndhParent
		Gui, % hParent ":Show", w400 h400
		
		Gui, new, % "hwndhwnd -Border -Resize +Parent" hParent
		this._Scroll_H := 1
		this._Scroll_V := 1
		this._Scroll_UseShift := False

		this._hwnd := hwnd
		Loop 20 {
			x := (A_Index -1) * 20
			Gui, Add, Text, x%x%, Test %A_Index%
		}  
		Gui, Show, x100 y100 w200 h200
		
		this.AdjustToChild()
		
		fn := bind(this._Wheel, this)
		OnMessage(WM_MOUSEWHEEL, fn)
		
		fn := bind(this._Scroll, this)
		OnMessage(WM_VSCROLL, fn)
		OnMessage(WM_HSCROLL, fn)
		
		fn := bind(this.AdjustToParent, this)
		OnMessage(0x0005, fn)
	}
	
	AdjustToParent(){
		Static SB_HORZ := 0, SB_VERT = 1
		static SIF_PAGE := 0x2
		
		WindowRECT := this._GetClientRect()
		CanvasRECT := this._GetClientSize()
		Width := WindowRECT.Right
		Height := WindowRECT.Bottom
		If (A_EventInfo <> 1) {
			SH := SV := 0
			If This._Scroll_H {
				If (Width <> This._Scroll_Width) {
					lpsi := this._BlankScrollInfo()
					lpsi.fMask := SIF_PAGE
					lpsi.nPage := Width + 1
					This._SetScrollInfo(SB_HORZ, lpsi)

					This._Scroll_Width := Width
					This._GetScrollInfo(SB_HORZ, SI)
					PosH := SI.nPos
					SH := This._Scroll_PosH - PosH
					This._Scroll_PosH := PosH
				}
			}
			If This._Scroll_V {
				If (Height <> This._Scroll_Height) {
					lpsi := this._BlankScrollInfo()
					lpsi.fMask := SIF_PAGE
					lpsi.nPage := Height + 1
					This._SetScrollInfo(SB_VERT, lpsi)
					
					This._Scroll_Height := Height
					This._GetScrollInfo(SB_VERT, SI)
					PosV := SI.nPos
					SV := This._Scroll_PosV - PosV
					This._Scroll_PosV := PosV
				}
			}
			if (SV || SH){
				this._ScrollWindow(SH, SV)
			}
		}
	}
	
	AdjustToChild(){
		Static SB_HORZ := 0, SB_VERT = 1
		static SIF_ALL := 0x17
		
		WindowRECT := this._GetClientRect()
		CanvasRECT := this._GetClientSize()
		if (!this._Scroll_Width || !this._Scroll_Height){
			Width := WindowRECT.Right
			Height := WindowRECT.Bottom
		}
		this.MaxH := WindowRECT.Right
		this.MaxV := WindowRECT.Bottom
		this.LineH := Ceil(this.MaxH / 20)
		this.LineV := Ceil(this.MaxV / 20)
		
		lpsi := this._BlankScrollInfo()
		lpsi.fMask := SIF_ALL
		
		lpsi.nMin := 0
		lpsi.nMax := CanvasRECT.Bottom
		lpsi.nPage := WindowRECT.Bottom
		this._SetScrollInfo(SB_VERT, lpsi)
		
		lpsi.nMax := CanvasRECT.Right
		lpsi.nPage := WindowRECT.Right
		this._SetScrollInfo(SB_HORZ, lpsi)
		
		this._Scroll_Width := Width
		this._Scroll_Height := Height
	}
	
	_GetScrollInfo(fnBar, ByRef lpsi){
		static SIF_ALL := 0x17
		; https://msdn.microsoft.com/en-us/library/windows/desktop/bb787583%28v=vs.85%29.aspx
		lpsi := this._BlankScrollInfo()
		lpsi.fMask := SIF_ALL
		r := DllCall("User32.dll\GetScrollInfo", "Ptr", this._hwnd, "Int", fnBar, "Ptr", lpsi[], "UInt")
		Return r
	}

	_SetScrollInfo(fnBar, ByRef lpsi, fRedraw := 1){
		; https://msdn.microsoft.com/en-us/library/windows/desktop/bb787595%28v=vs.85%29.aspx
		return DllCall("User32.dll\SetScrollInfo", "Ptr", this._hwnd, "Int", fnBar, "Ptr", lpsi[], "UInt", fRedraw, "UInt")
	}
	
	_ScrollWindow(XAmount, YAmount){
		; https://msdn.microsoft.com/en-us/library/windows/desktop/bb787591%28v=vs.85%29.aspx
		return DllCall("User32.dll\ScrollWindow", "Ptr", this._hwnd, "Int", XAmount, "Int", YAmount, "Ptr", 0, "Ptr", 0)
	}

	; Returns a RECT describing the size of the window
	_GetClientRect(){
		lpRect := new _Struct(WinStructs.RECT)
		DllCall("User32.dll\GetClientRect", "Ptr", This._HWND, "Ptr", lpRect[])
		return lpRect
	}

	; Returns a RECT encompassing all GuiControls and GUIs that are a child of this GUI
	_GetClientSize(){
		DHW := A_DetectHiddenWindows
		DetectHiddenWindows, On
		
		Width := Height := 0
		HWND := this._HWND
		L := T := R := B := LH := TH := ""
		CMD := 5 ; GW_CHILD
		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 <> "") {
			POINT := new _Struct(WinStructs.POINT)
			POINT.x := LH
			DllCall("ScreenToClient", "Ptr", this._HWND, "Ptr", POINT[])
			LH := POINT.x
		}
		If (TH <> "") {
			POINT := new _Struct(WinStructs.POINT)
			POINT.y := TH
			DllCall("ScreenToClient", "Ptr", this._HWND, "Ptr", POINT[])
			TH := POINT.y
		}
		RECT := new _Struct(WinStructs.RECT)
		RECT.Left := L
		RECT.Right := R
		RECT.Top := T
		RECT.Bottom := B
		DllCall("MapWindowPoints", "Ptr", 0, "Ptr", this._HWND, "Ptr", RECT[], "UInt", 2)
		Width := RECT.Right + (LH <> "" ? LH : RECT.Left)
		Height := RECT.Bottom + (TH <> "" ? TH : RECT.Top)

		ret := new _Struct(WinStructs.RECT)
		ret.Right := Width
		ret.Bottom := Height
		return ret
	}
	
	
	_Scroll(WP, LP, Msg, HWND) {
		;ToolTip, % "wp: " WP ", lp: " LP ", msg: " msg ", h: " hwnd
		Static SB_LINEMINUS := 0, SB_LINEPLUS := 1, SB_PAGEMINUS := 2, SB_PAGEPLUS := 3, SB_THUMBTRACK := 5
		Static WM_HSCROLL := 0x0114, WM_VSCROLL := 0x0115
		Static SIF_POS := 0x4
		
		If (LP <> 0) {
			Return
		}
		SB := (Msg = WM_HSCROLL ? 0 : 1) ; SB_HORZ : SB_VERT
		SC := WP & 0xFFFF
		SD := (Msg = WM_HSCROLL ? This.LineH : This.LineV)
		SI := 0
		If (!This._GetScrollInfo(SB, SI)){
			Return
		}
		PA := PN := SI.nPos
		If (SC = SB_LINEMINUS) {
			PN := PA - SD
		} Else If (SC = SB_LINEPLUS) {
			PN := PA + SD
		} Else If (SC = SB_PAGEMINUS) {
			PN := PA - SI.nPage
		} Else If (SC = SB_PAGEPLUS) {
			PN := PA + SI.nPage
		} Else If (SC = SB_THUMBTRACK) {
			PN := SI.nTrackPos
		} 
		If (PA = PN) {
			Return 0
		}
		
		lpsi := this._BlankScrollInfo()
		lpsi.fMask := SIF_POS
		lpsi.nPos := PN
		this._SetScrollInfo(SB, lpsi)
		
		This._GetScrollInfo(SB, SI)
		PN := SI.nPos
		If (SB = 0) {
			This._Scroll_PosH := PN
		} Else {
			This._Scroll_PosV := PN
		}
		If (PA <> PN) {
			HS := VS := 0
		}
		If (Msg = WM_HSCROLL) {
			HS := PA - PN
		} Else {
			VS := PA - PN
		}
		this._ScrollWindow(HS,VS)
		Return 0
   }

	_Wheel(WP, LP, Msg, H) {
		Static MK_SHIFT := 0x0004
		Static SB_LINEMINUS := 0, SB_LINEPLUS := 1
		Static WM_MOUSEWHEEL := 0x020A, WM_MOUSEHWHEEL := 0x020E
		Static WM_HSCROLL := 0x0114, WM_VSCROLL := 0x0115
		If (Msg = WM_MOUSEWHEEL) && This._Scroll_UseShift && (WP & MK_SHIFT) {
			Msg := WM_MOUSEHWHEEL
		}
		MSG := (Msg = WM_MOUSEWHEEL ? WM_VSCROLL : WM_HSCROLL)
		SB := ((WP >> 16) > 0x7FFF) || (WP < 0) ? SB_LINEPLUS : SB_LINEMINUS
		Return this._Scroll(sb, 0, MSG, H)
	}
	
	_BlankScrollInfo(){
		lpsi := new _Struct(WinStructs.SCROLLINFO)
		lpsi.cBsize := sizeof(WinStructs.SCROLLINFO)
		return lpsi
	}
}

bind(fn, args*) {  ; bind v1.2
	 try bound := fn.bind(args*)  ; Func.Bind() not yet implemented.
	 return bound ? bound : new BoundFunc(fn, args*)
}

class BoundFunc {
	 __New(fn, args*) {
		  this.fn := IsObject(fn) ? fn : Func(fn)
		  this.args := args
	 }
	 __Call(callee, args*) {
		  if (callee = "" || callee = "call" || IsObject(callee)) {  ; IsObject allows use as a method.
				fn := this.fn, args.Insert(1, this.args*)
				return %fn%(args*)
		  }
	 }
}
Last edited by evilC on 15 Feb 2015, 07:49, edited 3 times in total.
just me
Posts: 9451
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: Class_ScrollGUI - updated on 2015-02-13

15 Feb 2015, 07:08

Hmmm, I didn't test it, but I believe that your script will catch all mouse wheel messages in the first called Wheel() handler.
User avatar
evilC
Posts: 4823
Joined: 27 Feb 2014, 12:30

Re: Class_ScrollGUI - updated on 2015-02-13

15 Feb 2015, 11:13

Yes, currently only one message handler works.

The new test build is meant to allow multiple functions to subscribe to the same message, but I cannot get it working.

If you wish to have a play with the code without having to install the test build, I posted a compiled EXE demonstrating scrollbars in the CGui thread.

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 207 guests