Proposed New GUI API for AutoHotkey v2

Discuss the future of the AutoHotkey language
User avatar
fincs
Posts: 527
Joined: 30 Sep 2013, 14:17
Location: Seville, Spain
Contact:

Re: Proposed New GUI API for AutoHotkey v2

29 Jul 2014, 13:35

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()
    {
        [...]
    }
}
fincs
Windows 11 Pro (Version 22H2) | AMD Ryzen 7 3700X with 32 GB of RAM | AutoHotkey v2.0.0 + v1.1.36.02
Get SciTE4AutoHotkey v3.1.0 - [My project list]
toralf
Posts: 868
Joined: 27 Apr 2014, 21:08
Location: Germany

Re: Proposed New GUI API for AutoHotkey v2

29 Jul 2014, 14:27

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.
Last edited by toralf on 29 Jul 2014, 14:31, edited 1 time in total.
ciao
toralf
User avatar
fincs
Posts: 527
Joined: 30 Sep 2013, 14:17
Location: Seville, Spain
Contact:

Re: Proposed New GUI API for AutoHotkey v2

29 Jul 2014, 14:30

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).
fincs
Windows 11 Pro (Version 22H2) | AMD Ryzen 7 3700X with 32 GB of RAM | AutoHotkey v2.0.0 + v1.1.36.02
Get SciTE4AutoHotkey v3.1.0 - [My project list]
User avatar
fincs
Posts: 527
Joined: 30 Sep 2013, 14:17
Location: Seville, Spain
Contact:

Re: Proposed New GUI API for AutoHotkey v2

31 Jul 2014, 17:12

I updated the OP with test binaries (and changes in the proposed API).
fincs
Windows 11 Pro (Version 22H2) | AMD Ryzen 7 3700X with 32 GB of RAM | AutoHotkey v2.0.0 + v1.1.36.02
Get SciTE4AutoHotkey v3.1.0 - [My project list]
User avatar
evilC
Posts: 4823
Joined: 27 Feb 2014, 12:30

Re: Proposed New GUI API for AutoHotkey v2

01 Aug 2014, 07:51

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.
Coco
Posts: 771
Joined: 29 Sep 2013, 20:37
Contact:

Re: Proposed New GUI API for AutoHotkey v2

01 Aug 2014, 07:59

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
Coco
Posts: 771
Joined: 29 Sep 2013, 20:37
Contact:

Re: Proposed New GUI API for AutoHotkey v2

01 Aug 2014, 08:37

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
	}
}
User avatar
fincs
Posts: 527
Joined: 30 Sep 2013, 14:17
Location: Seville, Spain
Contact:

Re: Proposed New GUI API for AutoHotkey v2

01 Aug 2014, 08:44

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.
fincs
Windows 11 Pro (Version 22H2) | AMD Ryzen 7 3700X with 32 GB of RAM | AutoHotkey v2.0.0 + v1.1.36.02
Get SciTE4AutoHotkey v3.1.0 - [My project list]
User avatar
joedf
Posts: 8959
Joined: 29 Sep 2013, 17:08
Location: Canada
Contact:

Re: Proposed New GUI API for AutoHotkey v2

01 Aug 2014, 08:46

test binaries... awesome!
Image Image Image Image Image
Windows 10 x64 Professional, Intel i5-8500, NVIDIA GTX 1060 6GB, 2x16GB Kingston FURY Beast - DDR4 3200 MHz | [About Me] | [About the AHK Foundation] | [Courses on AutoHotkey]
[ASPDM - StdLib Distribution] | [Qonsole - Quake-like console emulator] | [LibCon - Autohotkey Console Library]
Coco
Posts: 771
Joined: 29 Sep 2013, 20:37
Contact:

Re: Proposed New GUI API for AutoHotkey v2

01 Aug 2014, 08:55

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 :)
Coco
Posts: 771
Joined: 29 Sep 2013, 20:37
Contact:

Re: Proposed New GUI API for AutoHotkey v2

01 Aug 2014, 09:16

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?
User avatar
fincs
Posts: 527
Joined: 30 Sep 2013, 14:17
Location: Seville, Spain
Contact:

Re: Proposed New GUI API for AutoHotkey v2

01 Aug 2014, 09:24

Not yet supported, sorry.
fincs
Windows 11 Pro (Version 22H2) | AMD Ryzen 7 3700X with 32 GB of RAM | AutoHotkey v2.0.0 + v1.1.36.02
Get SciTE4AutoHotkey v3.1.0 - [My project list]
Coco
Posts: 771
Joined: 29 Sep 2013, 20:37
Contact:

Re: Proposed New GUI API for AutoHotkey v2

01 Aug 2014, 09:32

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
User avatar
evilC
Posts: 4823
Joined: 27 Feb 2014, 12:30

Re: Proposed New GUI API for AutoHotkey v2

01 Aug 2014, 09:45

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).
User avatar
evilC
Posts: 4823
Joined: 27 Feb 2014, 12:30

Re: Proposed New GUI API for AutoHotkey v2

01 Aug 2014, 12:16

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)
}
User avatar
fincs
Posts: 527
Joined: 30 Sep 2013, 14:17
Location: Seville, Spain
Contact:

Re: Proposed New GUI API for AutoHotkey v2

01 Aug 2014, 12:28

The A_Gui variables no longer exist - they are parameters of Gui event handlers.
fincs
Windows 11 Pro (Version 22H2) | AMD Ryzen 7 3700X with 32 GB of RAM | AutoHotkey v2.0.0 + v1.1.36.02
Get SciTE4AutoHotkey v3.1.0 - [My project list]
Coco
Posts: 771
Joined: 29 Sep 2013, 20:37
Contact:

Re: Proposed New GUI API for AutoHotkey v2

01 Aug 2014, 12:32

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?
User avatar
fincs
Posts: 527
Joined: 30 Sep 2013, 14:17
Location: Seville, Spain
Contact:

Re: Proposed New GUI API for AutoHotkey v2

01 Aug 2014, 12:34

activeXControl.Value returns a new wrapper object.
fincs
Windows 11 Pro (Version 22H2) | AMD Ryzen 7 3700X with 32 GB of RAM | AutoHotkey v2.0.0 + v1.1.36.02
Get SciTE4AutoHotkey v3.1.0 - [My project list]
Coco
Posts: 771
Joined: 29 Sep 2013, 20:37
Contact:

Re: Proposed New GUI API for AutoHotkey v2

01 Aug 2014, 12:38

Got it, thanks!
User avatar
evilC
Posts: 4823
Joined: 27 Feb 2014, 12:30

Re: Proposed New GUI API for AutoHotkey v2

01 Aug 2014, 16:40

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]
Last edited by evilC on 02 Aug 2014, 07:44, edited 5 times in total.

Return to “AutoHotkey Development”

Who is online

Users browsing this forum: No registered users and 56 guests