Bug: GuiCtrl.Pos[xyhw]

Discuss the future of the AutoHotkey language
Ursi
Posts: 18
Joined: 16 Oct 2018, 14:11
Contact:

Bug: GuiCtrl.Pos[xyhw]

20 Oct 2018, 16:57

You can only access the values in GuiCtrl.Pos with a ., as in GuiCtrl.Pos.x. GuiCtrl.Pos['x'] gives an error.
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: Bug: GuiCtrl.Pos[xyhw]

20 Oct 2018, 18:53

indeed. idk if thats by design. Pos is of type Object, so id assume it should work

Code: Select all

G := GuiCreate()
Ctrl := G.Add("Text")
G.Show("w100 h100")

MsgBox(Ctrl.Pos.x) ;works
try MsgBox(Ctrl.Pos['x']) ;throws, 'invalid usage'
finally
{
	Arr := Ctrl.Pos
	MsgBox(Arr['x']) ;works	
	ExitApp()
}
Ursi
Posts: 18
Joined: 16 Oct 2018, 14:11
Contact:

Re: Bug: GuiCtrl.Pos[xyhw]

20 Oct 2018, 22:28

Best work-around for now is probably to use GuiCtrl.pos.clone()[xywh].
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Bug: GuiCtrl.Pos[xyhw]

21 Oct 2018, 02:55

x.y[z] is the same as x[y,z], which is not the same as x.y.z which is the same as (x.y).z. Basically, it means that when you do GuiCtrl.Pos['x'] you are treating the GuiCtrl object as if it was multidimensional, which it isn't, so there is an error. Example for custom properties,

Code: Select all

gc := new guictrl

msgbox 'get attempt 1:`t' . (gc.pos.x)
msgbox 'get attempt 2:`t' . (gc['pos','x', 'y'])
msgbox 'get attempt 3:`t' . (gc.pos['x','y','z'].x)

class guictrl {
	_pos := {x:100}
	pos[p*] {
		get {
			static ctr := 1
			local
			; show which parameters were passed
			for k, v in p
				s.= v '`n'
			msgbox 'parameters passed, ' . 'attempt ' . (ctr++) .  '`n`n' . s
			return this._pos
		}
	}
}
For the custom guictrl object, you would need to implement the property get like this, to support both access styles,

Code: Select all

pos[p*] {
	get {
		if p.length()
			return this._pos[p.1]
		return this._pos
	}
}

Ursi wrote:
20 Oct 2018, 22:28
Best work-around for now is probably to use GuiCtrl.pos.clone()[xywh].
No, you do (GuiCtrl.pos)[xywh], or as swagfag showed.
Cheers.
Last edited by Helgef on 21 Oct 2018, 02:58, edited 1 time in total.
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: Bug: GuiCtrl.Pos[xyhw]

21 Oct 2018, 02:57

x.y[z] is the same as x[y,z]
This clearly is not the case. I can only see this as a bug or bad language design.
Recommends AHK Studio
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Bug: GuiCtrl.Pos[xyhw]

21 Oct 2018, 03:00

This clearly is not the case.
Clearly it is not literaly the same, but it is treated as if it was.
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: Bug: GuiCtrl.Pos[xyhw]

21 Oct 2018, 03:06

Yeah but I would use x.y[z] when I want to avoid the behavior of x[y,z].
At least that's how I would intuitively use it.
But now you have to use (x.y)[z] in order to make the last parameter dynamic.
This feels like a sort of trap that people would fall in regularily.
Recommends AHK Studio
coffee
Posts: 133
Joined: 01 Apr 2017, 07:55

Re: Bug: GuiCtrl.Pos[xyhw]

21 Oct 2018, 20:56

Helgef wrote:
21 Oct 2018, 02:55
x.y[z] is the same as x[y,z], which is not the same as x.y.z which is the same as (x.y).z. Basically, it means that when you do GuiCtrl.Pos['x'] you are treating the GuiCtrl object as if it was multidimensional, which it isn't, so there is an error. Example for custom properties,
It is multidimensional, it may not be a multidimensional linear array, but if it's going to behave like an object, then it should be accessed like one.
x.y.z is as multidimensional as x[y, z] for the purposes of the "simulation".

The reason this fails is not because of multidimensionalism, but because of how x.y[z] is interpreted. It's not even because of how x[y, z] works, but because x.y[z] is converted to x[y, z], when it shouldn't, and only regular script objects are able to handle this. i.e only Object::invoke does. Gui objects don't support this and gui objects do not derive from Object, but from the common one.

The docs mention this limitation somewhere (x.y[z] being treated as x[y, z]), it also explains more or less how the x[y][z] vs x[y,z] thing works and also mentions it's for regular objects, not COM, and now not gui.

x[y, z] seems to work by cascading the remaining keys through the leftmost's "getter code". y and z are passed to x, x uses y, then y is given z to do stuff with. Like with x[a,b,c,d,e] where x uses a, checks stuff, then b,c,d,e are forwarded to a, a consumes b, then c,d,e are forwarded again and so on. This only works with regular objects.
x[y][z] and x.y.z is simply x.y, then .z on that result - z is not forwarded by x.
x.y[z] should behave like x[y][z].
and along the same lines, x[y, z] shouldn't even be a thing.
nnnik wrote:
21 Oct 2018, 03:06
Yeah but I would use x.y[z] when I want to avoid the behavior of x[y,z].
At least that's how I would intuitively use it.
But now you have to use (x.y)[z] in order to make the last parameter dynamic.
This feels like a sort of trap that people would fall in regularily.
or x["y"]["z"] in place of x.y["z"] until this is changed, if ever. But x.y[z] typically works for regular objects "seamlessly" until you start messing with the meta functions and it goes to shit.
I would assume this is a "known limitation" because it requires more than a quick in and out.

On swagfag snippet

Code: Select all

Msgbox(Ctrl["pos"]["x"]) ; also works

; and let the record know that

MsgBox(Ctrl['pos'].x) ; works 
Msgbox(Ctrl.pos['x']) ; doesn't work.

edit:
It seems that removing this block of code:

Code: Select all

					if (infix_count && infix[infix_count - 1].symbol == SYM_DOT // obj.x[ ...
						&& *omit_leading_whitespace(cp + 1) != ']') // not obj.x[]
					{
						// L36: This is something like obj.x[y] or obj.x[y]:=z, which should be treated
						//		as a single operation such as ObjGet(obj,"x",y) or ObjSet(obj,"x",y,z).
						--infix_count;
						// Below will change the SYM_DOT token into SYM_OBRACKET, keeping the existing deref struct.
					}
and pulling the block inside its else out of it, puts us onto something. Ctrl.pos["x"] works (and a couple other scripts i have where x.y["z"] type of access failed due to custom metas), but it's probably better to error out on obj.x[]

The idea is to avoid the "which should be treated as a single operation such as ObjGet(obj,"x",y) or ObjSet(obj,"x",y,z)."

God knows how many dominos it tips over somewhere else. I don't have a 1000 testing scripts :(
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Bug: GuiCtrl.Pos[xyhw]

22 Oct 2018, 11:53

Hello coffee :wave: .
It is multidimensional, it may not be a multidimensional linear array
Multidimensional objects is more a way of thinking, which is helpful sometimes, i.e, when we do x[y, z] we think that we access the value at [y, z] in x. It should be clear the ctrlobj doesn't have a value at [y, z] so ctrlobj[y, z] doesn't work. Now, x.y[z] is the syntax for passing parameters to properties, but the pos property of ctrlobj doesn't accept parameters, which can be deduced by the absence of documentation stating it does. So this fails too, as it should. My example demonstrates this.

On a side note, guiobj.control.somename fails, but both guiobj.control['somename'] and guiobj['control', 'somename'] works, and it should be clear why.

Is it perfect? No, but it is no bug.

Cheers
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: Bug: GuiCtrl.Pos[xyhw]

22 Oct 2018, 12:44

No I think this is just bad language design. This is potentially worse than a bug.
Recommends AHK Studio
iseahound
Posts: 1434
Joined: 13 Aug 2016, 21:04
Contact:

Re: Bug: GuiCtrl.Pos[xyhw]

26 Oct 2018, 23:10

Helgef wrote: x.y.z is the same as (x.y).z
Yes, resolution of subobjects is left associative. Makes sense since it is like browsing through a folder hierarchy.
Helgef wrote: x.y[z] is the same as x[y,z]
Could you give a motivating example of the usefulness of this weird right-associativity?

This is bad language design, since the period operator is being overwritten by the open brackets, while not preserving associativity rules. However if the usefulness of this format can be shown then it will be ok.
iseahound
Posts: 1434
Joined: 13 Aug 2016, 21:04
Contact:

Re: Bug: GuiCtrl.Pos[xyhw]

26 Oct 2018, 23:33

I thought about it and it is useless. I use square brackets when I want to evaluate the variable inside. If I am not using square brackets it means I already know the name of my sub object. Why are you evaluating my y in x.y[z]? This is extra work.

I've encountered this issue before and the solution to put the x and y in parenthesis, (x.y)[z] makes for more readable code. But what about in a JSON? Stuff like a.b[1].c[2] can be written as a.b.1.c.2 but becomes a['b',1,'c',2]?

I understand that accessing [y,z] in x, as an ordered pair of y and z, but I do not understand why syntax such as x.y[z] would force y and z into an ordered pair.

While forcing right associativity in the presence of square brackets makes no difference to JSON, it detracts from the intuition of left associativity in normal coding. The only time right associativity matters is in functions with multiple arguments so they can be partially evaluated.


Why should the presence of a square bracket change the function of a period?
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Bug: GuiCtrl.Pos[xyhw]

27 Oct 2018, 09:21

Could you give a motivating example
I believe I already did.
I thought about it and it is useless.
So would you like gui.controls[name] to be interpreted as (gui.controls)[name]? It would mean that gui.controls would need to (create and) return an array of all controls. That is potentally a lot of unnecessary work. Shall we not have properties with parameters? That would work ofc, we just use methods instead.

You can implement properties to accept parameters when they return objects, as I exemplified, but this requires more code and documentation (and work). If the pos property accepts parameters, you could also avoid creating the object holding all the data.

Cheers.
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: Bug: GuiCtrl.Pos[xyhw]

27 Oct 2018, 12:54

Thats exactly what the .controls property should do.
Adding bad practice because there already is more bad practice in place will not lead the language down a good road.
Recommends AHK Studio
lexikos
Posts: 9554
Joined: 30 Sep 2013, 04:07
Contact:

Re: Bug: GuiCtrl.Pos[xyhw]

12 May 2019, 21:18

It isn't particularly meaningful to return Controls itself, unless you want to use it like a generic array, which it isn't. The separation of properties and …[index]/array elements in recent builds would allow Gui[name] instead, which would remove the perceived irregularity.

The way I envisioned (and have begun to clarify in the implementation), the [args] in obj[args] and gui.Control[args] is a set of parameters for a property; it's just that the former invokes the default property. For instances of Object, that's __Item, but for Gui it could be Control (or that could be renamed to __Item, or become anonymous). Even if c := Gui.Control, c[args] was allowed, it's still just invoking a property of an object and passing args. The only difference is that there's an additional object which has no purpose on its own (unless it's a copy of the control list).

Inconsistencies such as GuiCtrl.Pos["x"] not being supported will be fixed. As per Object.ahk, the dispatch implementation should automatically apply parameters to the return value of the property if the property does not accept parameters. This wasn't done before because each object basically implements dispatch from scratch. (At the beginning, there was only Object. File and COM objects were added later, by different authors, and others since then.)

However, I'll probably change Pos to GetPos(), to make it less likely that users will:
  • …fall into the trap of GuiCtrl.Pos.X += 10, which is valid but does nothing because Pos is just a generic object, in no way linked to the control.
  • …use something like gc.Pos.X, gc.Pos.Y, gc.Pos.W, gc.Pos.H, retrieving the control's position and creating a new object two (X,Y) or four times.
I'm also not fond of pos := GuiCtrl.Pos … pos.X, pos.Y; I generally want to use variables and ByRef for this sort of thing, as with WinGetPos, MouseGetPos, etc.

Return to “AutoHotkey Development”

Who is online

Users browsing this forum: No registered users and 36 guests