Proposed New GUI API for AutoHotkey v2

Discuss the future of the AutoHotkey language

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

Re: Proposed New GUI API for AutoHotkey v2

Post by evilC » 02 Aug 2014, 06:55

To enable this feature, %AutoHotkeyDir%\v2-alpha\x86\AutoHotkey.exe (or x64) must exist [...] and .ahk files will now be detected as AutoHotkey v2 files
But that would then mean I have to choose AHK2 OR AHK1, to be associated with .ahk
Would it be possible to do the same but use the extension .ahk2 ?

User avatar
fincs
Posts: 527
Joined: 30 Sep 2013, 14:17
Location: Seville, Spain
Contact:

Re: Proposed New GUI API for AutoHotkey v2

Post by fincs » 02 Aug 2014, 07:00

No, sorry. Currently there's no standardised AHK v2 file extension and .ahk is used.

It is very easy to tell S4AHK whether to use v1.1 or v2 binaries; use the platform selection menu Image.
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

Post by evilC » 02 Aug 2014, 07:01

fincs wrote:No, sorry. Currently there's no standardised AHK v2 file extension and .ahk is used.

It is very easy to tell S4AHK whether to use v1.1 or v2 binaries; use the platform selection menu Image.
Ah I see, so I could have ahk associated with v1, but when I hit F5 in SciTe, it will pass the v2 code to the v2 binary?

User avatar
fincs
Posts: 527
Joined: 30 Sep 2013, 14:17
Location: Seville, Spain
Contact:

Re: Proposed New GUI API for AutoHotkey v2

Post by fincs » 02 Aug 2014, 07:19

Yes.
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

Post by evilC » 02 Aug 2014, 07:22

Yeah, looks to be working but it is unstable.

If I put a breakpoint in the OnSize method, it seems to stop at the breakpoint OK, but as soon as I hit F5 to continue, the script stops responding.
Last edited by evilC on 02 Aug 2014, 07:43, edited 1 time in total.

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

Re: Proposed New GUI API for AutoHotkey v2

Post by evilC » 02 Aug 2014, 07:37

Update: It does not seem to like it when you declare Critical
I did not need it in my code anyway, so I removed it.
When I debug with critical in the code and it hangs, it seems to leave processes (but no tray icon) - I had to reboot to fix it all as just killing the processes didn't seem to fix it.

I have seen one crash since removing critical though, so it doesn't seem quite 100% (Or I am doing something wrong...).

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

Re: Proposed New GUI API for AutoHotkey v2

Post by evilC » 02 Aug 2014, 20:08

Really starting to make some progress now :)

Two scrolling windows within a parent window - the left one holds the Child windows, the right one is going to be a "Task Bar" (Where Child Windows go when you minimize them). I decided against leaving the child windows on the main canvas when you minimize them, as if you resize the window they can go out of view. You could arrange them, but moving more than one or two is too slow, so I decided to pack all the minimized window into another "Task Bar", so resizing the main window will just cause scroll bars to be generated for the task bar.

I rigged the mouse wheel to act somewhat intelligently as described here.

video of it in action: http://screencast-o-matic.com/watch/c2jec3nK4Z

I still need to make the minimized windows tile neatly in the taskbar, but overall I am increasing happy with the result.

An OnMove() event for GUIs would be really nice though - that way I could have the scrollbars update when you drag windows around.

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

Re: Proposed New GUI API for AutoHotkey v2

Post by evilC » 03 Aug 2014, 15:14

A virtual "Windows" environment comlpete with TaskBar and independently scrolling sub-windows (With mousewheel input redirected accordingly).
You can also click TaskBar items to minimize / restore child windows.
Scrollbars updated as you drag stuff around also :)

http://screencast-o-matic.com/watch/c2jeYjnKgC

I will be using it for my Universal Control Remapper (UCR) project (A GUIfied version of AHK's remapping functionality) but I would love to make it so that anyone can use it as a base for their own applications - just extend the CChildWindow class to give the child windows whatever capability you desire!

https://github.com/evilC/UCR

User avatar
joedf
Posts: 8953
Joined: 29 Sep 2013, 17:08
Location: Canada
Contact:

Re: Proposed New GUI API for AutoHotkey v2

Post by joedf » 03 Aug 2014, 19:18

Really cool! :D
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]

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

Re: Proposed New GUI API for AutoHotkey v2

Post by evilC » 05 Aug 2014, 13:05

I am trying to get my head around something, and could do with a little help.

Setting a GUI's position like so: Gui.Show("x0 y0 w200 h50") results in the GUI being positioned relative to the current viewport, not the canvas.
In other words, if you have one GUI parented to another via +Parent<hwnd>, and the parent is currently scrolled down (or right), then the Show() command positions the item relative to the current view.

This is not the problem, I am quite capable of dealing with that.

What I would like to work out how to do is override the GUI's Show() method and adjust for the offset accordingly.

Something along these lines:

Code: Select all

#SingleInstance force
myWin := new CWindow
Class CWindow {

	__New(title := "", options := "", parent := 0){
		this._Gui := GuiCreate(title, options, this)
		this.parent := parent
		
		
		this._Gui.Show("x0 y0 w600 h200")
		; I want to be able to do this
		;this._Gui.Show({x: 0, y: 0, w: 600, h: 200})
		
		; ... and possibly this - ie "merge" GUI onto "this"
		; this.AddButton(blah)
	}
	
	; Like Gui.Show, but relative to the viewport (ie 0,0 is top left of canvas, not top left of current view)
	; Also uses assoc array (ie {x: 0, y: 0} instead of a string
	Show(options := 0){
		msgbox("Custom Show being used")
		if (!options){
			options := {x: 0, y: 0, w: 200, h: 50}
		}
		if (this.Parent){
			offset := this.GetWindowOffSet(this.Parent.Hwnd)
		} else {
			offset := {x: 0, y: 0}
		}
		str := ""
		ctr := 0
		For key, value in options {
			if (key = "x"){
				value += offset.x
			} else if (key = "y"){
				value += offset.y
			}
			if (ctr){
				str .= " "
			}
			str .= key . value
			ctr++
		}
		this._Gui.Show(str)
	}

	; Get the offset of the canvas of a window due to scrollbar position
	GetWindowOffSet(hwnd){
		ret := {x: 0, y: 0}
		info := this.GetScrollInfos(hwnd)
		if (info[0] == 0){
			; No x scroll bar
			ret.x := 0
		} else {
			ret.x := info[0].nPos * -1
		}
		
		if (info[1] == 0){
			; No y scroll bar
			ret.y := 0
		} else {
			ret.y := info[1].nPos * -1
		}
		
		return ret
	}

	GetScrollInfos(hwnd){
		ret := []
		ret[0] := this.GetScrollInfo(hwnd, 0)
		ret[1] := this.GetScrollInfo(hwnd, 1)
		return ret
	}
	
	; 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
		}
	}
}	
I know there are other ways to achieve this, but I just wondered if there were a way to "extend" the gui objects like you could in AFC. I also preferred in AFC how the class and the gui object were one and the same - so instead of me having to do this._Gui.Show() I would like to be able to just do this.Show().

I am guessing maybe the solution may be __Call ? I am having some trouble understanding that, so if someone could help me out, that would be great.

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

Re: Proposed New GUI API for AutoHotkey v2

Post by evilC » 05 Aug 2014, 18:03

Hmm, I think I worked it out, I used __Call to route Sets, Gets and Calls to the relevant places.

I ended up with something like this (working code):

Code: Select all

#SingleInstance force

test := new MyClass
Class MyClass {
	__New(){
		this.Gui := GuiCreate("First Title")
		; Try some stuff with the regular this.Gui method...
		this.Gui.Show("x0 y0 w500 h100")	; Start off in top left

		msgbox("Pre Move Margin: " this.Gui.MarginX)
		
		; Try to call Gui methods via "this"
		this.Title := "Second Title"
		this.BgColor := "Red"
		this.Show("x100 y100")	; this._Show() is present, so overrides Gui.Show()

		this.MarginX := 0		; Set on this
		msgbox("Post Move Margin: " this.Gui.MarginX)	; Margin was set on this, applied to this.Gui
		
		this.Hide()		; _Hide not declared in class, so this routes direct to Gui's hide() method
		msgbox("check margins match: " this.MarginX)
	}

	__Get(aName){
		static GuiGets := {Title: 1, MarginX: 1, MarginY: 1, Hwnd: 1, Control: 1}
		if (GuiGets[aName]){
			return this["Gui"][aName]
		}
	}
	
	__Set(aName, aValue){
		static GuiSets := {Title: 1, MarginX: 1, MarginY: 1 , BgColor: 1, CtrlColor: 1, Menu: 1}
		if (GuiSets[aName]){
			this["Gui"][aName] := avalue
			return
		}
	}
	
	__Call(Method, p*) {
		if (!IsObject(this[method])){
			; If method not found on this class...
			mf := "_" . method
			if (IsObject(this[mf])){
				; If underscore prefixed version exists - execute that
				return this[mf](p*)
			} else {
				; Else try the Gui item
				return this["Gui"][method](p*)
			}
		}
	}
	
	_Show(options){
		; Do something fancy with the options, eg correct for scrolled parent
		options .= " w200"
		this.Gui.Show(options)
	}
	
}
Any comments on what i have done here? Bad practice? Better way to do it?

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

Re: Proposed New GUI API for AutoHotkey v2

Post by evilC » 11 Aug 2014, 12:28

I have a question regarding the OnClose() event of GUIs.

This event happens before the Gui is removed from view, but after the Gui object is counted as destroyed (ie calling MyGui.Destroy() throws an "The Gui is destroyed" error)

This is a bit of a problem for me at the moment.

I am working on a windows desktop style GUI in AHK - each "Child Window" GUI has a corresponding "Task Bar" Gui that you can click on to minimize / restore the GUI that it represents. See here for a video if you don't get what I mean: http://screencast-o-matic.com/watch/c2jeYjnKgC

OK, so when I close one of the main child windows (in the right pane), I must also remove the corresponding task bar entry (in the left pane).
Removing the task bar gui in the left pane means that tasks below it must be shuffled up to fill the space.
Doing this takes time.

So when you close one of the windows in the right pane, OnClose is called, but the child window in that pane has not disappeared yet.
It must then shuffle all of the taskbar items up ("pack") before that thread completes and the gui that you clicked "X" on disappears.

Is there maybe a way to avoid this?
You can hide the gui, but that just means that the program locks up until the pack completes.

I guess any solution would still mean that the pack completing would lock the program up?
I was gonna look into subscribing to the WM_PARENTNOTIFY message, so I can separate the threads out, dunno if that will help.

Maybe what I really need is a quicker way of moving multiple GUIs together. Whatever happens, deleting one task item will mean all tasks below it need to be moved up by the same amount, so I suppose if they could all be moved in one go, that may accelerate the whole thing considerably. Does anyone know of a way to move multiple HWNDs by the same amount all in one go?

Suggestions welcomed...

lexikos
Posts: 9583
Joined: 30 Sep 2013, 04:07
Contact:

Re: Proposed New GUI API for AutoHotkey v2

Post by lexikos » 11 Aug 2014, 18:39

ScrollWindow moves multiple child windows by the same amount in one go.

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

Re: Proposed New GUI API for AutoHotkey v2

Post by evilC » 11 Aug 2014, 19:04

Yeah, I thought about that, but that would only work if you closed the first task surely?

If you had 10 tasks and closed the 3rd task, you only want to move tasks 4-10 up, not 1-2

lexikos
Posts: 9583
Joined: 30 Sep 2013, 04:07
Contact:

Re: Proposed New GUI API for AutoHotkey v2

Post by lexikos » 11 Aug 2014, 21:53

Right. Perhaps BeginDeferWindowPos and related functions, then.

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

Re: Proposed New GUI API for AutoHotkey v2

Post by evilC » 12 Aug 2014, 07:30

Thankyou Lex, that sounds exactly like what I need.

[Update] Well that was surprisingly simple to implement.

For future reference, here is some test code (v1 code!) demonstrating use of DeferWindowPos

Code: Select all

#SingleInstance force

Gui, New, +hwndMainHwnd
Gui, Show, x0 y0 w500 h500

y := 100
numchildren := 10

Loop %numchildren% {
	Gui, New, +hwndChildHwnd%A_Index% +Parent%MainHwnd% -Border
	Gui, Add, Text,, % "Hwnd " ChildHwnd%A_Index%
	Gui, Show, x0 y%y% w500 h50
	y += 50
}

HDWP := DllCall("BeginDeferWindowPos", "int", numchildren)
return

F2::
	y := 0
	Loop %numchildren% {
		hwnd := ChildHwnd%A_Index%
		WinMove, ahk_id %hwnd%,, 0, %y%
		y += 50
	}
	return
	
F3::
	y := 0
	Loop %numchildren% {
		hwnd := ChildHwnd%A_Index%
		;WinMove, ahk_id %hwnd%,, 0, %y%
		HDWP := DllCall("DeferWindowPos", "Ptr", HDWP, "Ptr", hwnd, "Ptr", , "int", 0, "int", y, "int", 500, "int", 50, "uint", 0)
		y += 50
	}
	DllCall("EndDeferWindowPos", "Ptr", HDWP)
	
	return
F2 uses normal WinMove, F3 uses DeferWindowPos.
DeferWindowPos is indeed effectively instant!

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

Re: Proposed New GUI API for AutoHotkey v2

Post by evilC » 12 Aug 2014, 10:10

Could we maybe get a "Canvas Relative" coord mode for Child GUIs in V2?

As it stands, GUI coords for child GUIs (And controls I think) are relative to the parent's viewport, not to the parent's canvas.

So lets say you have a parent window that is scrolled down 100px
Doing a Gui, Show, x0 y0 +ParentHwnd on a child window positions the child window in the top left of the parent's viewport, not the top left of the parent's canvas. WinMove etc also seem to be susceptible also.

This issue permeates through all kinds of stuff...

For example:
Again, you have a parent window scrolled down 100px.
A Child window is placed at 0,0 - it appears at 0,100 relative to the canvas as mentioned before.

You then minimize the child window.
You then scroll the parent window back up to 0 offset.
Restore the child window - it will appear at 0,0 instead of 0,100 where it was when it was minimized!

lexikos
Posts: 9583
Joined: 30 Sep 2013, 04:07
Contact:

Re: Proposed New GUI API for AutoHotkey v2

Post by lexikos » 12 Aug 2014, 16:28

There are no such things as window viewports and canvases, just the client area. You implemented the scrolling, so you need to do your own coordinate translations.

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

Re: Proposed New GUI API for AutoHotkey v2

Post by evilC » 13 Aug 2014, 06:33

Oh don't get me wrong, I have done it, I just thought it would make a nice addition to AHK.

I am a little confused though (Sorry, the various flavors of windows GUIs - WPF, Forms etc are a bit beyond me atm) as from doing a little googling it does appear that canvases do exist in some form in some windows APIs. I guess just not in whatever AHK was written using?

It seems to me though that it would be relatively simple to make life easier for people implementing such things if AHK's GUI manipulation commands (Show, WinMove etc) were updated to check if the item being manipulated had a parent (GetParent API call) and if so checked the scroll bar status of the parent (GetScrollInfo API call) and if set, subtract the value for each scrollbar from the value passed to the x or y coordinate of the command. Yes, this is possible to do yourself, but requires extra steps that I guess you are already doing in your code - for example with Gui Show, if you pass a string like "x0 y0 w100 h50" then your code already has to parse the string and extract the variables (x,y etc) - it just seems wasteful for the user to be having to do that, only to concatenate them back into a string to pass to Show(), which does it all again. My current implementation works around this by having the user pass an associative array for the parameters, but this then breaks parameter compatibility which is kind of the opposite direction to where I thought we were going with v2. TBH, I would really love to see all AHK v2 GUI related commands accept associative arrays and do away with the old string system.

As a side note, is there any way that AHK's built-in commands can be "overridden"?
For example, declaring a function WinMove() seems to override the built in WinMove(), but I can find no equivalent of base.WinMove() to call the underlying function.

I can understand that many of the stuff that I am talking about is either not on the drawing board or so far off that it would be worth writing something in the meantime, so I have started up a project page here.

Post Reply

Return to “AutoHotkey Development”