Page 3 of 13

Re: Proposed New GUI API for AutoHotkey v2

Posted: 29 Jul 2014, 13:35
by fincs
You can also use classes with methods to catch events with the new Gui API; and the implicit event handler for buttons is present even in AHK v1.0.

Code: Select all

new MyMainWindow

class MyMainWindow
{
    __New()
    {
        g := GuiCreate("Image Viewer", "Resize", this)
        g.AddButton("Load New Image", "gDifferentEventName") ; BTW, this is one of the design changes I mentioned earlier.
        g.Show()
    }

    DifferentEventName()
    {
        [...]
    }
}

Re: Proposed New GUI API for AutoHotkey v2

Posted: 29 Jul 2014, 14:27
by toralf
You will be able to define your own function names (event) in the gui.AddButton() similar to the option g-label for subroutines
Fincs in the first post wrote:gui.AddCtrl([Value, Options, Event]) where Ctrl is a control type
Adds a new control to the Gui.
In v1.x, some control types such as ListView accept a string with a changeable delimiter (by default |).
In v2, arrays are also supported, and this obviates the need to support changing the delimiter.
The Event parameter specifies the function or method name to call in order to receive
events from the control (equivalent to v1 g-labels). It can also be a function reference.
For Button controls, if Event is omitted, the same v1 transformation rules are applied to
automatically generate a function or method name (expanded to accomodate for stricter identifier
names).
Returns a GuiControl object.

Re: Proposed New GUI API for AutoHotkey v2

Posted: 29 Jul 2014, 14:30
by fincs
That's what was implied in the post above -- I decided to do away with the Event parameter and still specify the name using the 'g' prefix in the options (as opposed to a new parameter).

Re: Proposed New GUI API for AutoHotkey v2

Posted: 31 Jul 2014, 17:12
by fincs
I updated the OP with test binaries (and changes in the proposed API).

Re: Proposed New GUI API for AutoHotkey v2

Posted: 01 Aug 2014, 07:51
by evilC
In the progress bar example, MyText := gui.AddText(, "wp") throws an error - Invalid control type. Label seems to have been added and Text appears to have been removed. Intentional?

Also the picture does not appear - I just get text of the filename.
Looking at script_gui.cpp, there seems to be a comment that it doesn't work, so you maybe know about that.

Also, I am having problems getting scite4ahk with v2 - I currently have the extension .ahk2 associated with the v2 exe in program files\autohotkey2

I tried installing scite4ahk into that folder but no dice. Any suggestions on how best to set up dual v1 / v2 (ideally with scite and debugging for both) would be appreciated, as the thread appears locked.

Re: Proposed New GUI API for AutoHotkey v2

Posted: 01 Aug 2014, 07:59
by Coco
fincs wrote:I decided to do away with the Event parameter and still specify the name using the 'g' prefix in the options (as opposed to a new parameter).
Is it possible to specify a function reference? e.g.: gVarContainingFuncObj

Re: Proposed New GUI API for AutoHotkey v2

Posted: 01 Aug 2014, 08:37
by Coco
Hi fincs, I ran a small test.. SetFont doesn't seem to work. Also, do we need to specify the gui parameter for Gui event handler(s) like OnClose even if it's a class method... How about automatically setting the this parameter to the Gui object that invoked the call.
Test code:

Code: Select all

win := GuiCreate("Test GUI",, App)
win.SetFont("Arial", "s10") ;// <- Not working
win.AddLabel("Hello World")
win.AddButton("Hello", "w100 h30 gButtonClick")
win.Show("w400 h250")
return

class App
{
	ButtonClick() {
		MsgBox Hello World
	}

	OnClose(win) { ;// <- Still need to explicitly specify parameter for Gui object
		win.Destroy()
		MsgBox Gui Destroyed
	}
}

Re: Proposed New GUI API for AutoHotkey v2

Posted: 01 Aug 2014, 08:44
by fincs
evilC wrote:In the progress bar example, MyText := gui.AddText(, "wp") throws an error - Invalid control type.
Oops, forgot to mention that I renamed Text to Label. OP updated.
evilC wrote:Also, I am having problems getting scite4ahk with v2 - I currently have the extension .ahk2 associated with the v2 exe in program files\autohotkey2
See SciTE4AutoHotkey AHK v2 Support.
Coco wrote:Is it possible to specify a function reference? e.g.: gVarContainingFuncObj
There will never be a NameOfVarInTextForm parameter again. What you proposed collides with method names as well as function names. Currently passing function objects is not supported, but reintroducing the Event parameter in AddCtrl() would allow them (however I haven't bothered doing it since I think it's a rare use case).
Coco wrote:Hi fincs, I ran a small test.. SetFont doesn't seem to work.
You're passing the parameters in the wrong order.
Coco wrote:Also, do we need to specify the gui parameter for Gui event handler(s) like OnClose even if it's a class method...
Yes. You can omit them if you don't need to use them, as in all event handlers (including OnMessage, RegisterCallback, ...).
Coco wrote:How about automatically setting the this parameter to the Gui object that invoked the call.
No. That's unintuitive and inflexible behaviour - you get no way to refer back to the object instance that registered itself as the event handler.

Re: Proposed New GUI API for AutoHotkey v2

Posted: 01 Aug 2014, 08:46
by joedf
test binaries... awesome!

Re: Proposed New GUI API for AutoHotkey v2

Posted: 01 Aug 2014, 08:55
by Coco
fincs wrote:You're passing the parameters in the wrong order.
Oops, I swear I saw something like gui.SetFont([FontName, Options]) in the OP. Sorry 'bout that
fincs wrote:No. That's unintuitive and inflexible behaviour - you get no way to refer back to the object instance that registered itself as the event handler.
Fair enough.

Looking good so far :)

Re: Proposed New GUI API for AutoHotkey v2

Posted: 01 Aug 2014, 09:16
by Coco
fincs via OP wrote:In v2, arrays are also supported, and this obviates the need to support changing the delimiter.
Is this already in effect? Doesn't seem to work for both v2.0-a049 and your test binaries.
Gui Add, ListView,, % ["Column 1", "Column 2"] / lv := win.AddListView(["Column 1", "Column 2"]) <- Not working OR am I doing it wrong?

Re: Proposed New GUI API for AutoHotkey v2

Posted: 01 Aug 2014, 09:24
by fincs
Not yet supported, sorry.

Re: Proposed New GUI API for AutoHotkey v2

Posted: 01 Aug 2014, 09:32
by Coco
No worries, I'm fine with the "|" delimited string style. Array support is definitely a plus as it will make life easier especially for dynamic control creation/modification -> Pop, Push, InsertAt, etc and you're good to go... No more string manipulation routines

Re: Proposed New GUI API for AutoHotkey v2

Posted: 01 Aug 2014, 09:45
by evilC
Failing to pass a width or height when doing gui.Show() seems to result in no gui appearing and no error.

gui := GuiCreate("Progress Example",,) seems valid, but gui := GuiCreate("Progress Example",,"") does not (No gui appears).

Re: Proposed New GUI API for AutoHotkey v2

Posted: 01 Aug 2014, 12:16
by evilC
Lexikos' Scrollable window proof of concept ported to this new gui system:

Seems like A_GuiWidth and A_GuiHeight no longer work. Maybe more A_vars too

Code: Select all

#SingleInstance force
OnMessage(0x115, "OnScroll") ; WM_VSCROLL
OnMessage(0x114, "OnScroll") ; WM_HSCROLL
gui := GuiCreate("Test","+Resize +0x300000","myGui_")

GuiControls := []

gui.AddButton("Do absolutely nothing")
Loop 10
	GuiControls.Push(gui.AddEdit("Test"))

gui.Show("x0 y0 w200 h200")

GroupAdd("MyGui", "ahk_id " . gui.Hwnd)


return

#IfWinActive ahk_group MyGui
~WheelUp::
~WheelDown::
~+WheelUp::
~+WheelDown::
	OnScroll(InStr(A_ThisHotkey,"Down") ? 1 : 0, 0, GetKeyState("Shift") ? 0x114 : 0x115, gui.Hwnd)
return
#IfWinActive

MyGui_OnSize(){
	global gui
    ;UpdateScrollBars(A_Gui, A_GuiWidth, A_GuiHeight)
	
	;A_GuiWidth / Height do not seem to work.
	WinGetPos(x,y,w,h)
	UpdateScrollBars(w,h)
	return
}

GuiClose:
ExitApp

UpdateScrollBars(GuiWidth, GuiHeight)
{
    static SIF_RANGE:=0x1, SIF_PAGE:=0x2, SIF_DISABLENOSCROLL:=0x8, SB_HORZ:=0, SB_VERT:=1
	global GuiControls
	global gui

    ; Calculate scrolling area.
    Left := Top := 999999
    Right := Bottom := 0
    Loop GuiControls.Length()
    {
		p := GuiControls[A_Index].Pos()
        if (p.x < Left)
            Left := p.x
        if (p.y < Top)
            Top := p.y
        if (p.x + p.w > Right)
            Right := p.x + p.w
        if (p.y + p.h > Bottom)
            Bottom := p.y + p.h
		
    }
    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)
}

Re: Proposed New GUI API for AutoHotkey v2

Posted: 01 Aug 2014, 12:28
by fincs
The A_Gui variables no longer exist - they are parameters of Gui event handlers.

Re: Proposed New GUI API for AutoHotkey v2

Posted: 01 Aug 2014, 12:32
by Coco
Hi fincs, how do I retrieve the WebBrowser object if I do win.AddActiveX("Shell.Explorer")? Obviously, vWb no longer works.The control object returned by the command is not a ComObject as well. Is it a property of the control object?

Re: Proposed New GUI API for AutoHotkey v2

Posted: 01 Aug 2014, 12:34
by fincs
activeXControl.Value returns a new wrapper object.

Re: Proposed New GUI API for AutoHotkey v2

Posted: 01 Aug 2014, 12:38
by Coco
Got it, thanks!

Re: Proposed New GUI API for AutoHotkey v2

Posted: 01 Aug 2014, 16:40
by evilC
I further extended Lex's code to provide a scrolling child window that contains further child windows (guis inside a scrolling gui inside a gui).

Some thoughts from writing this code:

Would it be possible to implement an OnMove() event for GUIs?

If you add a gui as a child gui using +parent<hwnd>, maybe some built-in method to iterate through child windows would be nice?

Again if a gui is parented to another, it would be nice if gui.Show("x0 y0") were relative to the canvas (ie ignoring where you are scrolled) rather than relative to the current viewport as it is now.

I had to do quite a lot of shennanigans with client rects in order to do the scrollbar calcs - this would be significantly easier if we could get gui positions relative to the client rect of it's parent.

Do you know a way to route the messages directly to the class instance, or do I have to use an external function like I did?
Could OnMessage maybe be updated? Am I missing something?

GitHub page for this code

Code: Select all

#SingleInstance Force

MainWindow := new MainWindow()

; Is it possible to move this inside the class somehow?
OnMessage(0x115, "OnScroll") ; WM_VSCROLL
OnMessage(0x114, "OnScroll") ; WM_HSCROLL
#IfWinActive ahk_group MyGui
~WheelUp::
~WheelDown::
~+WheelUp::
~+WheelDown::
    ; SB_LINEDOWN=1, SB_LINEUP=0, WM_HSCROLL=0x114, WM_VSCROLL=0x115
    MainWindow.OnScroll(InStr(A_ThisHotkey,"Down") ? 1 : 0, 0, GetKeyState("Shift") ? 0x114 : 0x115, MainWindow.ScrollableSubWindow.Hwnd)
return
#IfWinActive

OnScroll(wParam, lParam, msg, hwnd){
	global MainWindow
	MainWindow.ScrollableSubWindow.OnScroll(wParam, lParam, msg, hwnd)
}

class MainWindow extends Window {
	
	__New(){
		this.Gui := GuiCreate("Outer Parent","Resize",this)
		this.Gui.AddButton("Add","gAddClicked")
		this.debug := this.Gui.AddLabel("debug: ","w500")
		
		this.Gui.Show("x0 y0 w600 h500")
		this.Hwnd := this.Gui.Hwnd
		
		; Set up child GUIs
		this.ScrollableSubWindow := new ScrollingSubWindow(this)
		this.ScrollableSubWindow.OnSize()
		
		this.OnSize()
		GroupAdd("MyGui", "ahk_id " . this.Hwnd)

	}

	AddClicked(){
		this.ScrollableSubWindow.AddClicked()
	}
	
	OnSize(){
		; Size Scrollable Child Window
		Critical

		; Lots of hard wired values - would like to eliminate these!
		r := this.GetClientRect(this.Hwnd)
		r.b -= 50	; How far down from the top of the main gui does the child window start?
		; Subtract border widths
		r.b -= r.t + 6
		r.r -= r.l + 6

		; Client rect seems to not include scroll bars - check if they are showing and subtract accordingly
		sbv := this.GetScrollBarVisibility(this.ScrollableSubWindow.Hwnd)
		if (sbv.x){
			r.r -= 16
		}

		if (sbv.y){
			r.b -= 16
		}
		
		this.ScrollableSubWindow.Gui.Show("x0 y50 w" . r.r . " h" . r.b)
	}
	
	OnScroll(wParam, lParam, msg, hwnd){
		this.ScrollableSubWindow.OnScroll(wParam, lParam, msg, hwnd)
	}
}

class ScrollingSubWindow extends Window {
	__New(parent){
		this.parent := parent
		this.ChildWindows := []

		this.Gui := GuiCreate("","-Border 0x300000 Parent" . this.parent.Hwnd, this)
		this.Gui.Show("x0 y50 w10 h10")
		this.Hwnd := this.Gui.Hwnd
		
		;OnMessage(0x115, OnScroll()) ; WM_VSCROLL
		;OnMessage(0x114, OnScroll()) ; WM_HSCROLL

	}
	
	AddClicked(){
		child := new ChildWindow(this)
		this.ChildWindows[child.Hwnd] := child
	}
	
	ChildClosed(hwnd){
		this.ChildWindows.RemoveAt(hwnd)
		this.OnSize()
	}
	
	OnSize(){
		static SIF_RANGE := 0x1, SIF_PAGE := 0x2, SIF_DISABLENOSCROLL := 0x8, SB_HORZ := 0, SB_VERT := 1
		
		scroll_status := this.GetScrollInfos(this.Hwnd)

		viewport := {Top: 0, Left: 0, Right: 0, Bottom: 0}
		ctr := 0
		For key, value in this.ChildWindows {
			pos := this.ChildWindows[key].GetClientPos()
			bot := pos.y + pos.h
			if (pos.y < viewport.Top){
				viewport.Top := pos.y
			}
			if (pos.x < viewport.Left){
				viewport.Left := pos.x
			}
			if (bot > viewport.Bottom){
				viewport.Bottom := bot
			}
			right := pos.x + pos.w
			if (right > viewport.Right){
				viewport.Right := right
			}
			
			this.ChildWindows[key].SetDesc("b: " bot ", y: " pos.y ", h: " pos.h)
			ctr++
		}
		if (!ctr){
			; Update horizontal scroll bar.
			this.SetScrollInfo(this.Hwnd, SB_HORZ, {nMax: 0, nPage: 0, fMask: SIF_RANGE | SIF_PAGE })
			; Update vertical scroll bar.
			this.SetScrollInfo(this.Hwnd, SB_VERT, {nMax: 0, nPage: 0, fMask: SIF_RANGE | SIF_PAGE })
			return
		}
		
		ScrollWidth := viewport.Right - viewport.Left
		ScrollHeight := viewport.Bottom - viewport.Top

		; GuiHeight = size of client area
		g := this.GetClientRect(this.Hwnd)
		GuiWidth := g.r
		GuiHeight := g.b

		this.parent.SetDesc("SUB_GUI DEBUG: Lowest Widget Bottom: " . viewport.Bottom . ", GuiHeight: " . GuiHeight)

		; Update horizontal scroll bar.
		this.SetScrollInfo(this.Hwnd, SB_HORZ, {nMax: ScrollWidth, nPage: GuiWidth, fMask: SIF_RANGE | SIF_PAGE })

		; Update vertical scroll bar.
		this.SetScrollInfo(this.Hwnd, SB_VERT, {nMax: ScrollHeight, nPage: GuiHeight, fMask: SIF_RANGE | SIF_PAGE })
		
		; If being window gets bigger while child items are clipped, drag the child items into view
		if (viewport.Left < 0 && viewport.Right < GuiWidth){
			x := Abs(viewport.Left) > GuiWidth-viewport.Right ? GuiWidth-viewport.Right : Abs(viewport.Left)
		}
		if (viewport.Top < 0 && viewport.Bottom < GuiHeight){
			y := Abs(viewport.Top) > GuiHeight-viewport.Bottom ? GuiHeight-viewport.Bottom : Abs(viewport.Top)
		}
		if (x || y){
			this.ScrollWindow(this.Hwnd, x, y)
		}


	}

	OnScroll(wParam, lParam, msg, hwnd){
		static SCROLL_STEP := 10
		static SIF_ALL := 0x17

		bar := msg - 0x114 ; SB_HORZ=0, SB_VERT=1

		scroll_status := this.GetScrollInfos(this.Hwnd)
		
		; If call returns no info, quit
		if (scroll_status[bar] == 0){
			return
		}
		
		rect := this.GetClientRect(hwnd)
		new_pos := scroll_status[bar].nPos

		action := wParam & 0xFFFF
		if (action = 0){ ; SB_LINEUP
			;tooltip % "NP: " new_pos
			new_pos -= SCROLL_STEP
		} else if (action = 1){ ; SB_LINEDOWN
			; Wheel down
			new_pos += SCROLL_STEP
		} else if (action = 2){ ; SB_PAGEUP
			; Page Up ?
			new_pos -= rect.b - SCROLL_STEP
		} else if (action = 3){ ; SB_PAGEDOWN
			; Page Down ?
			new_pos += rect.b - SCROLL_STEP
		} else if (action = 5 || action = 4){ ; SB_THUMBTRACK || SB_THUMBPOSITION
			; Drag handle
			new_pos := wParam >> 16
		} else if (action = 6){ ; SB_TOP
			; Home?
			new_pos := scroll_status[bar].nMin ; nMin
		} else if (action = 7){ ; SB_BOTTOM
			; End?
			new_pos := scroll_status[bar].nMax ; nMax
		} else {
			return
		}
		
		min := scroll_status[bar].nMin ; nMin
		max := scroll_status[bar].nMax - scroll_status[bar].nPage ; nMax-nPage
		new_pos := new_pos > max ? max : new_pos
		new_pos := new_pos < min ? min : new_pos
		
		old_pos := scroll_status[bar].nPos ; 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.
		this.ScrollWindow(hwnd, x, y)
		
		; Update scroll bar.
		tmp := scroll_status[bar]
		tmp.nPos := new_pos
		tmp.fMask := SIF_ALL

		this.SetScrollInfo(hwnd, bar, tmp)
		return
	}

}

; A Child Window Within the scrolling sub-window
class ChildWindow extends Window {
	__New(parent){
		this.parent := parent
		this.Gui := GuiCreate("Child","+Parent" . this.parent.Hwnd,this)
		this.Gui.AddLabel("I am " . this.Gui.Hwnd)	;this.Gui.Hwnd
		this.debug := this.Gui.AddLabel("debug: ", "w200")	;this.Gui.Hwnd
		this.Gui.Show("x0 y0 w200 h50")
		
		this.Hwnd := this.Gui.Hwnd
	}
	
	GetClientPos(){
		pos := this.GetPos(this.Hwnd)
		offset := this.ScreenToClient(this.parent.Hwnd, x, y)
		pos.x += offset.x
		pos.y += offset.y
		return pos
	}
	
	OnClose(){
		this.parent.ChildClosed(this.Hwnd)
	}
}

; Helper functions
class Window {
	; Wrapper for WinGetPos
	GetPos(hwnd){
		WinGetPos(x, y, w, h, "ahk_id " hwnd)
		return {x: x, y: y, w: w, h: h}
	}
	
	; Wrapper for GetClientRect DllCall
	; Gets "Client" (internal) area of a window
	GetClientRect(hwnd){
		VarSetCapacity(rect, 16, 0)
        DllCall("GetClientRect", "Ptr", hwnd, "Ptr", &rect)
        return {l: NumGet(rect, 0, "Int"), t: NumGet(rect, 4, "Int") , r: NumGet(rect, 8, "Int"), b: NumGet(rect, 12, "Int")}
	}
	
	; Wrapper for GetScrollInfo DllCall
	GetScrollInfo(hwnd, bar){
		static SIF_ALL := 0x17

	    VarSetCapacity(si, 28, 0)
	    NumPut(28, si) ; cbSize
	    NumPut(SIF_ALL, si, 4) ; fMask
	    if (DllCall("GetScrollInfo", "uint", hwnd, "int", bar, "uint", &si)){
			ret := {}
			ret.cbSize := NumGet(si, 0, "uint") ; cbSize
			ret.fMask := NumGet(si, 4, "uint") ; fMask
			ret.nMin := NumGet(si, 8, "int") ; nMin
			ret.nMax := NumGet(si, 12, "int") ; nMax
			ret.nPage := NumGet(si, 16) ; nPage
			ret.nPos := NumGet(si, 20) ; nPos
			ret.nTrackPos := NumGet(si, 24) ; nTrackPos
			return ret
		} else {
			return 0
		}
	}
	
	GetScrollInfos(hwnd){
		ret := []
		ret[0] := this.GetScrollInfo(hwnd, 0)
		ret[1] := this.GetScrollInfo(hwnd, 1)
		return ret
	}


	; Wrapper for SetScrollInfo DllCall
	SetScrollInfo(hwnd, bar, scrollinfo){
		VarSetCapacity(si, 28, 0)
		NumPut(28, si) ; cbSize
		

		if (scrollinfo.fMask){
			NumPut(scrollinfo.fMask, si, 4) ; fMask
		}
		if (scrollinfo.nMin){
			NumPut(scrollinfo.nMin, si, 8) ; nMin
		}
		if (scrollinfo.nMax){
			NumPut(scrollinfo.nMax, si, 12) ; nMax
		}
		if (scrollinfo.nPage){
			NumPut(scrollinfo.nPage, si, 16) ; nPage
		}
		if (scrollinfo.nPos){
			NumPut(scrollinfo.nPos, si, 20, "int") ; nPos
		}
		if (scrollinfo.nTrackPos){
			NumPut(scrollinfo.nTrackPos, si, 24) ; nTrackPos
		}
		return DllCall("SetScrollInfo", "uint", hwnd, "int", bar, "uint", &si, "int", 1)
	}

	; Wrapper for ScrollWindow DllCall
	ScrollWindow(hwnd, x, y){
		DllCall("ScrollWindow", "uint", hwnd, "int", x, "int", y, "uint", 0, "uint", 0)
	}

	; Wrapper for ScreenToClient DllCall
	; returns offset between screen and client coords
	ScreenToClient(hwnd, x, y){
		VarSetCapacity(pt, 16)
		NumPut(x,pt,0)
		NumPut(y,pt,4)
		DllCall("ScreenToClient", "uint", hwnd, "Ptr", &pt)
		x := NumGet(pt, 0, "long")
		y := NumGet(pt, 4, "long")
		
		return {x: x, y: y}
	}

	GetScrollBarVisibility(hwnd){
		static WS_HSCROLL := 0x00100000
		static WS_VSCROLL := 0x00200000

		ret := DllCall("GetWindowLong", "uint", hwnd, "int", -16)
		out := {}
		out.x := (ret & WS_HSCROLL) > 0
		out.y := (ret & WS_VSCROLL) > 0
		return out
	}

	SetDesc(str){
		if (this.debug){
			this.debug.Value := str
		}
	}

}
[Edit: Bugfix-Canvas now dragged into view when window gets bigger]