Differentiating between a label and a function.

Get help with using AutoHotkey and its commands and hotkeys
User avatar
Hellbent
Posts: 1057
Joined: 23 Sep 2017, 13:34

Differentiating between a label and a function.

08 Jun 2019, 08:58

I am working on a new evolution for the way I write a button class.
Before now I would just use the glabel option in a new control and then in the label or function I would have it call a method.
This way works fine, but there are 2 things about it that don't sit well.

1. I would either have to
(a) Associate a variable with the control based on the index of an array of button instances. (This is bad for many reasons) .Or
(b) Loop through the array of buttons and try to match the hwnd of the control that launched the thread to one of the buttons hwnds.

2. The above code would have to be added to every Label / Function attached to the buttons.

That brings me to the new approach.
In the new approach, I have bound the control directly to a internal method, and from there it will go to the label or function.
This removes the need to add any of the functionality of the control to the external labels/functions, it also removes the need to associate a variable to the control or to loop through the array to try to match the hwnd.

What I have below works exactly as I expect it to, but before I proceed I thought that I would ask those that have more experience with these sorts of things if there is something that I might be over looking, or if there is a better way to tell if I want it to call a function or gosub a label.

Here is a simple example of the structure.

Code: Select all

#SingleInstance,Force

Gui,+AlwaysOnTop

SomeButton:=[]
SomeButton.Push(New SomeClass(Label:="testFunction"))
SomeButton.Push(New SomeClass(Label:="testLabel"))

Gui,Show,

return
GuiClose:
GuiContextMenu:
*Esc::
	ExitApp

testLabel:
	ToolTip, in label
	return
	
testFunction(){
	ToolTip, in function
}

class SomeClass	{
	__New(Label){
		This.Label:=Label
		This.AddButton()
	}
	AddButton(){
		Gui,Add,Button,w200 h30 hwndhwnd
		This.Hwnd:=hwnd
		IBM:=This.InternalMethod.Bind(This)
		GuiControl,+G,% This.Hwnd,% IBM
		(IsFunc(This.Label))?(This.Function:=Func(This.Label))
	}
	InternalMethod(){
		if(This.Function)
			This.Function.Call()
		else
			gosub,% This.Label
	}
}
Thanks.
A_AhkUser
Posts: 1076
Joined: 06 Mar 2017, 16:18
GitHub: AAhkUser
Location: France

Re: Differentiating between a label and a function.

08 Jun 2019, 10:27

Hi Hellbent,


As I see it, your implementation is consistent and cogent. However, you missed at least two things in the current one: if the label is a function it can optionally accept parameter(s); also, in this case, the return value can be significant. Some other suggestions: the user-defined label can be a function object, as an example. In this case, depending on whether or not these objects handle the __Delete event, you might want to properly dispose them:

Code: Select all

#NoEnv
#SingleInstance force
#Warn

#SingleInstance,Force

Gui,+AlwaysOnTop
new SomeClass(ObjBindMethod(new C, "m"))
new SomeClass("testFunction")
new SomeClass("testLabel")
Gui, Show,
OnExit, handleExit
return

GuiClose:
GuiContextMenu:
*Esc::
	ExitApp
handleExit:
	SomeClass.disposeAll()
ExitApp
	
Class C {
	m(params*) {
	ToolTip % A_ThisFunc "," params.1
	}
	; __Delete() {
		; MsgBox % A_ThisFunc
	; }
}
	
testLabel:
	ToolTip % A_ThisLabel
return
	
testFunction(params*){
	ToolTip % A_ThisFunc "," params.1
}

Class SomeClass {
	__New(label) {
		local hwnd
		Gui, Add, Button, w200 h30 hwndhwnd
		This.Hwnd := hwnd
		this.setLabel(label)
	return this, SomeClass.instances[ hwnd+0 ] := this
	}
	setLabel(label) {
		local fn
		if not (type:=(IsObject(label) ? "Object" : (IsFunc(label) && label:=Func(label)) ? "Function" : IsLabel(label) ? "Label" : ""))
			throw
		this.dispose()
		fn := this._internalMethod.bind(this, type, label)
		GuiControl, +G, % This.Hwnd, % fn
	}
	disposeAll() {
		local hwnd, instance
		for hwnd, instance in ObjClone(SomeClass.instances)
			instance.dispose(), SomeClass.instances.delete(hwnd)
	}
	dispose() {
		GuiControl, -G, % This.Hwnd,
	}
	; __Delete() {
		; MsgBox % A_ThisFunc
	; }
	_internalMethod(type, label, params*) { ; if the label is a function it can optionally accept parameter(s)
		if (type = "label") {
			gosub, % label
		return
		} else return %label%(params*) ; the return value can be significant such as with the GuiCLose label
	}
}
Hope this helps. Cheers
User avatar
Hellbent
Posts: 1057
Joined: 23 Sep 2017, 13:34

Re: Differentiating between a label and a function.

08 Jun 2019, 15:59

@A_AhkUser

Thanks for the reply.
I have a few questions for you or anyone else that is able to answer them.

To begin with, I went and designed a new control and created a class for it to make ensure that we are on the same page and so that there is something with the proper context. Perhaps I should have started with this, perhaps not, I just wanted to make my first example as simple and straight forward as I could. Hence the minimal class with just a normal gui button playing stand in.

Here is a very simple button design and the class for making new instances of it.

Code: Select all

#SingleInstance,Force

GDIP_StartUp()

Gui,1:+AlwaysOnTop
Gui,1:Color,% Button_Background_Color:= "F0F0F0"

global HB_Button:=[]

; -----------------------------------------------------------------------------------------------------------------------
HB_Button.Push(New HB_White_Button_Type_1( x := 10
										 , y := 10
										 , w := 100
										 , h := 30
										 , Window := "1"
										 , Label := "TestButton_Label"
										 , Text := "Button"
										 , Font := "Arial" 
										 , Font_Size := "12 Bold" 
										 , Font_Color_Top := "0067D9" ;000000 F36700
										 , Font_Color_Bottom := "000000" ;f0f0f0
										 , Button_Background_Color
										 , Roundness:=2 ))
; -----------------------------------------------------------------------------------------------------------------------


HB_Button.Push(New HB_White_Button_Type_1( x := 10, y += h+10, w := 150, h := 40, Window, Label := "TestButton_Label", Text := "Button",  Font := "Impact", Font_Size := "24 Bold" , Font_Color_Top, Font_Color_Bottom, Button_Background_Color, Roundness:=5 ))
HB_Button.Push(New HB_White_Button_Type_1( x := 10, y += h+10, w := 280, h := 25, Window, Label := "TestButton_Label", Text := "Button",  Font := "Courier New", Font_Size := "12 Bold" , Font_Color_Top, Font_Color_Bottom, Button_Background_Color, Roundness:=0 ))
HB_Button.Push(New HB_White_Button_Type_1( x := 10, y += h+10, w := 50, h := 50, Window, Label := "TestButton_Function", Text := "Button",  Font := "Segoe UI", Font_Size := "12 Bold" , Font_Color_Top, Font_Color_Bottom, Button_Background_Color, Roundness:=0 ))
HB_Button.Push(New HB_White_Button_Type_1( x += w+5, y, w := 50, h := 50, Window, Label := "TestButton_Function", Text := "Button",  Font := "Segoe UI", Font_Size := "12 Bold" , Font_Color_Top, Font_Color_Bottom, Button_Background_Color, Roundness += 3 ))
HB_Button.Push(New HB_White_Button_Type_1( x += w+5, y, w := 50, h := 50, Window, Label := "TestButton_Function", Text := "Button",  Font := "Segoe UI", Font_Size := "12 Bold" , Font_Color_Top, Font_Color_Bottom, Button_Background_Color, Roundness += 3 ))
HB_Button.Push(New HB_White_Button_Type_1( x += w+5, y, w := 50, h := 50, Window, Label := "TestButton_Function", Text := "Button",  Font := "Segoe UI", Font_Size := "12 Bold" , Font_Color_Top, Font_Color_Bottom, Button_Background_Color, Roundness += 3 ))
HB_Button.Push(New HB_White_Button_Type_1( x += w+5, y, w := 50, h := 50, Window, Label := "TestButton_Function", Text := "Button",  Font := "Segoe UI", Font_Size := "12 Bold" , Font_Color_Top, Font_Color_Bottom, Button_Background_Color, Roundness += 3 ))
HB_Button.Push(New HB_White_Button_Type_1( x += w+5, y, w := 50, h := 50, Window, Label := "TestButton_Function", Text := "Button",  Font := "Segoe UI", Font_Size := "12 Bold" , Font_Color_Top, Font_Color_Bottom, Button_Background_Color, Roundness += 3 ))

Gui,1:Show,% "x" A_ScreenWidth-400 " y100 w350 h200 NA",New Button Class Structure

SetTimer,HB_Button_Hover,50
return
GuiClose:
GuiContextMenu:
*ESC::
	ExitApp
	
TestButton_Label:
	SoundBeep,500
	TrayTip,,In Label
	return

TestButton_Function(){
	Loop 2
		SoundBeep,600
	TrayTip,,In Function
}

; Button Hover Function 
; (Will replace sooner or later with a new approach, but for now this does exactly what I want it to do so I'm not in a rush to replace it)
;-----------------------------------------------------------------------------
HB_Button_Hover(){
	Static Index , Hover_On
	MouseGetPos,,,, ctrl , 2
	if( ! Hover_On && ctrl ){
		loop , % HB_Button.Length()
			if( ctrl = HB_Button[ A_Index ].hwnd )
				HB_Button[ A_Index ].Draw_Hover() , Index := A_Index , Hover_On := 1 , break
	}else if( Hover_On = 1 )
		if( ctrl != HB_Button[ Index ].Hwnd )
			HB_Button[ Index ].Draw_Default() , Hover_On := 0
}
;-----------------------------------------------------------------------------

; New Button Class Structure
;#########################################################################################
class HB_White_Button_Type_1	{
	__New( x := 10, y := 10, w := 150, h := 40, Window := "1", Label := "", Text := "Button",  Font := "Arial" , Font_Size := "16 Bold" , Font_Color_Top := "000000" , Font_Color_Bottom := "FFFFFF", Button_Background_Color := "aaaaaa", Roundness:=5 ){
		This.X:=x
		This.Y:=y
		This.W := w
		This.H := h 
		This.Window := Window
		This.Label := Label 
		This.Text := Text
		This.Font := Font 
		This.Font_Size := Font_Size
		This.Text_Color_Top := "0xFF" Font_Color_Top
		This.Text_Color_Bottom := "0xFF" Font_Color_Bottom
		This.Button_Background_Color := "0xFF" Button_Background_Color
		This.Roundness:=Roundness
		This.Create_Default_Bitmap()
		This.Create_Hover_Bitmap()
		This.Create_Pressed_Bitmap()
		This.Create_Trigger()
		sleep, 20
		This.Draw_Default()
	}
	Create_Trigger(){
		Gui , % This.Window ": Add" , Picture , % "x" This.X " y" This.Y " w" This.W " h" This.H " hwndHwnd 0xE"
		This.Hwnd := Hwnd
		fn:=This.Draw_Pressed.Bind(This)
		GuiControl,+G,% This.Hwnd,% fn
		if(This.Label)
			(IsFunc(This.Label))?(This.Function:=Func(This.Label))
	}
	Create_Default_Bitmap(){
		;Bitmap Created Using: HB Bitmap Maker
		pBitmap:=Gdip_CreateBitmap( This.W, This.H )
		G := Gdip_GraphicsFromImage( pBitmap )
		Gdip_SetSmoothingMode( G , 4 )
		Brush := Gdip_BrushCreateSolid( This.Button_Background_Color )
		Gdip_FillRectangle( G , Brush , -1 , -1 , This.W+4 , This.H+3 )
		Gdip_DeleteBrush( Brush )
		Brush := Gdip_CreateLineBrushFromRect( 0 , -1 , This.W-2 , This.H , "0xFFF4F5F6" , "0xFFD5D5D5" , 1 , 1 )
		Gdip_FillRoundedRectangle( G , Brush , 1 , 1 , This.W-2 , This.H-3 , This.Roundness )
		Gdip_DeleteBrush( Brush )
		Brush := Gdip_CreateLineBrushFromRect( 5 , 2 , This.W-10 , This.H-6 , "0xFFE8E8E8" , "0xFFAEAEAE" , 1 , 1 )
		Gdip_FillRoundedRectangle( G , Brush , 1 , 1 , This.W-3 , This.H-4 , This.Roundness )
		Gdip_DeleteBrush( Brush )
		Brush := Gdip_CreateLineBrushFromRect( 1 , 0 , This.W-2 , This.H-3 , "0xFF222222" , "0xFF222222" , 1 , 1 )
		Pen := Gdip_CreatePenFromBrush( Brush , 1 )
		Gdip_DeleteBrush( Brush )
		Gdip_DrawRoundedRectangle( G , Pen , 0 , 0 , This.W-1 , This.H-2 , This.Roundness )
		Gdip_DeletePen( Pen )
		Brush := Gdip_BrushCreateSolid( This.Text_Color_Bottom )
		Gdip_TextToGraphics( G , This.Text , "s" This.Font_Size " Center vCenter c" Brush " x1 y2" , This.Font , This.W , This.H )
		Gdip_DeleteBrush( Brush )
		Brush := Gdip_BrushCreateSolid( This.Text_Color_Top )
		Gdip_TextToGraphics( G , This.Text , "s" This.Font_Size " Center vCenter c" Brush " x0 y1" , This.Font , This.W , This.H )
		Gdip_DeleteBrush( Brush )
		Gdip_DeleteGraphics( G )
		This.Default_Bitmap := Gdip_CreateHBITMAPFromBitmap(pBitmap)
		Gdip_DisposeImage(pBitmap)
	}
	Create_Hover_Bitmap(){
		;Bitmap Created Using: HB Bitmap Maker
		pBitmap:=Gdip_CreateBitmap( This.W, This.H )
		 G := Gdip_GraphicsFromImage( pBitmap )
		Gdip_SetSmoothingMode( G , 4 )
		Brush := Gdip_BrushCreateSolid( This.Button_Background_Color )
		Gdip_FillRectangle( G , Brush , -1 , -1 , This.W+4 , This.H+3 )
		Gdip_DeleteBrush( Brush )
		Brush := Gdip_CreateLineBrushFromRect( 0 , -1 , This.W-2 , This.H , "0xFFF4F5F6" , "0xFFD4E0E5" , 1 , 1 )
		Gdip_FillRoundedRectangle( G , Brush , 1 , 1 , This.W-2 , This.H-3 , This.Roundness )
		Gdip_DeleteBrush( Brush )
		Brush := Gdip_CreateLineBrushFromRect( 1 , 0 , This.W-2 , This.H-3 , "0xFFE7EEF1" , "0xFFADC3CD" , 1 , 1 )
		Gdip_FillRoundedRectangle( G , Brush , 1 , 1 , This.W-3 , This.H-4 , This.Roundness )
		Gdip_DeleteBrush( Brush )
		Brush := Gdip_CreateLineBrushFromRect( 1 , 0 , This.W-2 , This.H-1 , "0xFF494949" , "0xFF494949" , 1 , 1 )
		Pen := Gdip_CreatePenFromBrush( Brush , 1 )
		Gdip_DeleteBrush( Brush )
		Gdip_DrawRoundedRectangle( G , Pen , 0 , 0 , This.W-1 , This.H-2 , This.Roundness )
		Gdip_DeletePen( Pen )
		Brush := Gdip_BrushCreateSolid( This.Text_Color_Bottom )
		Gdip_TextToGraphics( G , This.Text , "s" This.Font_Size " Center vCenter c" Brush " x1 y2" , This.Font , This.W , This.H )
		Gdip_DeleteBrush( Brush )
		Brush := Gdip_BrushCreateSolid( This.Text_Color_Top )
		Gdip_TextToGraphics( G , This.Text , "s" This.Font_Size " Center vCenter c" Brush " x0 y1" , This.Font , This.W , This.H )
		Gdip_DeleteBrush( Brush )
		Gdip_DeleteGraphics( G )
		This.Hover_Bitmap := Gdip_CreateHBITMAPFromBitmap( pBitmap )
		Gdip_DisposeImage( pBitmap )
	}
	Create_Pressed_Bitmap(){
		;Bitmap Created Using: HB Bitmap Maker
		pBitmap:=Gdip_CreateBitmap( This.W, This.H)
		G := Gdip_GraphicsFromImage( pBitmap )
		Gdip_SetSmoothingMode( G , 4 )
		Brush := Gdip_BrushCreateSolid( This.Button_Background_Color )
		Gdip_FillRectangle( G , Brush , -1 , -1 , This.W+4 , This.H+3 )
		Gdip_DeleteBrush( Brush )
		Brush := Gdip_CreateLineBrushFromRect( 0 , -1 , This.W-2 , This.H , "0xFFF4F5F6" , "0xFFD5D5D5" , 1 , 1 )
		Gdip_FillRoundedRectangle( G , Brush , 1 , 1 , This.W-2 , This.H-3 , This.Roundness )
		Gdip_DeleteBrush( Brush )
		Brush := Gdip_CreateLineBrushFromRect( 0 , -1 , This.W-2 , This.H , "0xFF727575" , "0xFFA9B8C0" , 1 , 1 )
		Gdip_FillRoundedRectangle( G , Brush , 0 , 0 , This.W , This.H-2 , This.Roundness )
		Gdip_DeleteBrush( Brush )
		Brush := Gdip_CreateLineBrushFromRect( 5 , 2 , This.W-10 , This.H-6 , "0xFF85898B" , "0xFF9EB2BC" , 1 , 1 )
		Gdip_FillRoundedRectangle( G , Brush , 3 , 2 , This.W-7 , This.H-6 , This.Roundness )
		Gdip_DeleteBrush( Brush )
		Brush := Gdip_CreateLineBrushFromRect( 5 , 2 , This.W-10 , This.H-6 , "0xFF9BA1A4" , "0xFF9EB2BC" , 1 , 1 )
		Gdip_FillRoundedRectangle( G , Brush , 4 , 4 , This.W-9 , This.H-6 , This.Roundness )
		Gdip_DeleteBrush( Brush )
		Brush := Gdip_CreateLineBrushFromRect( 1 , 0 , This.W-2 , This.H-1 , "0xFF495459" , "0xFF495459" , 1 , 1 )
		Pen := Gdip_CreatePenFromBrush( Brush , 1 )
		Gdip_DeleteBrush( Brush )
		Gdip_DrawRoundedRectangle( G , Pen , 0 , 0 , This.W-1 , This.H-2 , This.Roundness )
		Gdip_DeletePen( Pen )
		Brush := Gdip_BrushCreateSolid( This.Text_Color_Bottom )
		Gdip_TextToGraphics( G , This.Text , "s" This.Font_Size " Center vCenter c" Brush " x1 y3" , This.Font , This.W , This.H )
		Gdip_DeleteBrush( Brush )
		Brush := Gdip_BrushCreateSolid( This.Text_Color_Top )
		Gdip_TextToGraphics( G , This.Text , "s" This.Font_Size " Center vCenter c" Brush " x0 y2" , This.Font , This.W , This.H )
		Gdip_DeleteBrush( Brush )
		Gdip_DeleteGraphics( G )
		This.Pressed_Bitmap := Gdip_CreateHBITMAPFromBitmap( pBitmap )
		Gdip_DisposeImage( pBitmap )
	}
	Draw_Pressed(){
		SetTimer,HB_Button_Hover,Off
		SetImage( This.Hwnd , This.Pressed_Bitmap )
		While(GetKeyState("LButton"))
			Sleep, 10
		SetTimer,HB_Button_Hover,On
		MouseGetPos,,,,ctrl,2
		if(ctrl=This.Hwnd){
			This.Draw_Hover()
			;***********************************************************************
			if(This.Label){
				if(This.Function)
					This.Function.Call()    ; If the cursor is still over the button when the mouse is released, execute the buttons function or label (subroutine)
				else
					gosub,% This.Label
			}
			;***********************************************************************
		}else	{
			This.Draw_Default()
		}
	}
	Draw_Hover(){
		SetImage( This.Hwnd , This.Hover_Bitmap )
	}
	Draw_Default(){
		SetImage( This.Hwnd , This.Default_Bitmap )
	}


}
;#########################################################################################


;######################################################################################################################################
;#####################################################   					    #######################################################
;#####################################################  	  Gdip LITE		    #######################################################
;#####################################################  					    #######################################################
;######################################################################################################################################
; Gdip standard library v1.45 by tic (Tariq Porter) 07/09/11
; Modifed by Rseding91 using fincs 64 bit compatible Gdip library 5/1/2013
BitBlt(ddc, dx, dy, dw, dh, sdc, sx, sy, Raster=""){
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	return DllCall("gdi32\BitBlt", Ptr, dDC, "int", dx, "int", dy, "int", dw, "int", dh, Ptr, sDC, "int", sx, "int", sy, "uint", Raster ? Raster : 0x00CC0020)
}
Gdip_DrawImage(pGraphics, pBitmap, dx="", dy="", dw="", dh="", sx="", sy="", sw="", sh="", Matrix=1){
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	if (Matrix&1 = "")
		ImageAttr := Gdip_SetImageAttributesColorMatrix(Matrix)
	else if (Matrix != 1)
		ImageAttr := Gdip_SetImageAttributesColorMatrix("1|0|0|0|0|0|1|0|0|0|0|0|1|0|0|0|0|0|" Matrix "|0|0|0|0|0|1")
	if(sx = "" && sy = "" && sw = "" && sh = ""){
		if(dx = "" && dy = "" && dw = "" && dh = ""){
			sx := dx := 0, sy := dy := 0
			sw := dw := Gdip_GetImageWidth(pBitmap)
			sh := dh := Gdip_GetImageHeight(pBitmap)
		}else	{
			sx := sy := 0,sw := Gdip_GetImageWidth(pBitmap),sh := Gdip_GetImageHeight(pBitmap)
		}
	}
	E := DllCall("gdiplus\GdipDrawImageRectRect", Ptr, pGraphics, Ptr, pBitmap, "float", dx, "float", dy, "float", dw, "float", dh, "float", sx, "float", sy, "float", sw, "float", sh, "int", 2, Ptr, ImageAttr, Ptr, 0, Ptr, 0)
	if ImageAttr
		Gdip_DisposeImageAttributes(ImageAttr)
	return E
}
Gdip_SetImageAttributesColorMatrix(Matrix){
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	VarSetCapacity(ColourMatrix, 100, 0)
	Matrix := RegExReplace(RegExReplace(Matrix, "^[^\d-\.]+([\d\.])", "$1", "", 1), "[^\d-\.]+", "|")
	StringSplit, Matrix, Matrix, |
	Loop, 25
	{
		Matrix := (Matrix%A_Index% != "") ? Matrix%A_Index% : Mod(A_Index-1, 6) ? 0 : 1
		NumPut(Matrix, ColourMatrix, (A_Index-1)*4, "float")
	}
	DllCall("gdiplus\GdipCreateImageAttributes", A_PtrSize ? "UPtr*" : "uint*", ImageAttr)
	DllCall("gdiplus\GdipSetImageAttributesColorMatrix", Ptr, ImageAttr, "int", 1, "int", 1, Ptr, &ColourMatrix, Ptr, 0, "int", 0)
	return ImageAttr
}
Gdip_GetImageWidth(pBitmap){
   DllCall("gdiplus\GdipGetImageWidth", A_PtrSize ? "UPtr" : "UInt", pBitmap, "uint*", Width)
   return Width
}
Gdip_GetImageHeight(pBitmap){
   DllCall("gdiplus\GdipGetImageHeight", A_PtrSize ? "UPtr" : "UInt", pBitmap, "uint*", Height)
   return Height
}
Gdip_DeletePen(pPen){
   return DllCall("gdiplus\GdipDeletePen", A_PtrSize ? "UPtr" : "UInt", pPen)
}
Gdip_DeleteBrush(pBrush){
   return DllCall("gdiplus\GdipDeleteBrush", A_PtrSize ? "UPtr" : "UInt", pBrush)
}
Gdip_DisposeImage(pBitmap){
   return DllCall("gdiplus\GdipDisposeImage", A_PtrSize ? "UPtr" : "UInt", pBitmap)
}
Gdip_DeleteGraphics(pGraphics){
   return DllCall("gdiplus\GdipDeleteGraphics", A_PtrSize ? "UPtr" : "UInt", pGraphics)
}
Gdip_DisposeImageAttributes(ImageAttr){
	return DllCall("gdiplus\GdipDisposeImageAttributes", A_PtrSize ? "UPtr" : "UInt", ImageAttr)
}
Gdip_DeleteFont(hFont){
   return DllCall("gdiplus\GdipDeleteFont", A_PtrSize ? "UPtr" : "UInt", hFont)
}
Gdip_DeleteStringFormat(hFormat){
   return DllCall("gdiplus\GdipDeleteStringFormat", A_PtrSize ? "UPtr" : "UInt", hFormat)
}
Gdip_DeleteFontFamily(hFamily){
   return DllCall("gdiplus\GdipDeleteFontFamily", A_PtrSize ? "UPtr" : "UInt", hFamily)
}
CreateCompatibleDC(hdc=0){
   return DllCall("CreateCompatibleDC", A_PtrSize ? "UPtr" : "UInt", hdc)
}
SelectObject(hdc, hgdiobj){
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	return DllCall("SelectObject", Ptr, hdc, Ptr, hgdiobj)
}
DeleteObject(hObject){
   return DllCall("DeleteObject", A_PtrSize ? "UPtr" : "UInt", hObject)
}
GetDC(hwnd=0){
	return DllCall("GetDC", A_PtrSize ? "UPtr" : "UInt", hwnd)
}
GetDCEx(hwnd, flags=0, hrgnClip=0){
	Ptr := A_PtrSize ? "UPtr" : "UInt"
    return DllCall("GetDCEx", Ptr, hwnd, Ptr, hrgnClip, "int", flags)
}
ReleaseDC(hdc, hwnd=0){
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	return DllCall("ReleaseDC", Ptr, hwnd, Ptr, hdc)
}
DeleteDC(hdc){
   return DllCall("DeleteDC", A_PtrSize ? "UPtr" : "UInt", hdc)
}
Gdip_SetClipRegion(pGraphics, Region, CombineMode=0){
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	return DllCall("gdiplus\GdipSetClipRegion", Ptr, pGraphics, Ptr, Region, "int", CombineMode)
}
CreateDIBSection(w, h, hdc="", bpp=32, ByRef ppvBits=0){
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	hdc2 := hdc ? hdc : GetDC()
	VarSetCapacity(bi, 40, 0)
	NumPut(w, bi, 4, "uint"), NumPut(h, bi, 8, "uint"), NumPut(40, bi, 0, "uint"), NumPut(1, bi, 12, "ushort"), NumPut(0, bi, 16, "uInt"), NumPut(bpp, bi, 14, "ushort")
	hbm := DllCall("CreateDIBSection", Ptr, hdc2, Ptr, &bi, "uint", 0, A_PtrSize ? "UPtr*" : "uint*", ppvBits, Ptr, 0, "uint", 0, Ptr)
	if !hdc
		ReleaseDC(hdc2)
	return hbm
}
Gdip_GraphicsFromImage(pBitmap){
	DllCall("gdiplus\GdipGetImageGraphicsContext", A_PtrSize ? "UPtr" : "UInt", pBitmap, A_PtrSize ? "UPtr*" : "UInt*", pGraphics)
	return pGraphics
}
Gdip_GraphicsFromHDC(hdc){
    DllCall("gdiplus\GdipCreateFromHDC", A_PtrSize ? "UPtr" : "UInt", hdc, A_PtrSize ? "UPtr*" : "UInt*", pGraphics)
    return pGraphics
}
Gdip_GetDC(pGraphics){
	DllCall("gdiplus\GdipGetDC", A_PtrSize ? "UPtr" : "UInt", pGraphics, A_PtrSize ? "UPtr*" : "UInt*", hdc)
	return hdc
}


Gdip_Startup(){
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	if !DllCall("GetModuleHandle", "str", "gdiplus", Ptr)
		DllCall("LoadLibrary", "str", "gdiplus")
	VarSetCapacity(si, A_PtrSize = 8 ? 24 : 16, 0), si := Chr(1)
	DllCall("gdiplus\GdiplusStartup", A_PtrSize ? "UPtr*" : "uint*", pToken, Ptr, &si, Ptr, 0)
	return pToken
}
Gdip_TextToGraphics(pGraphics, Text, Options, Font="Arial", Width="", Height="", Measure=0){
	IWidth := Width, IHeight:= Height
	RegExMatch(Options, "i)X([\-\d\.]+)(p*)", xpos)
	RegExMatch(Options, "i)Y([\-\d\.]+)(p*)", ypos)
	RegExMatch(Options, "i)W([\-\d\.]+)(p*)", Width)
	RegExMatch(Options, "i)H([\-\d\.]+)(p*)", Height)
	RegExMatch(Options, "i)C(?!(entre|enter))([a-f\d]+)", Colour)
	RegExMatch(Options, "i)Top|Up|Bottom|Down|vCentre|vCenter", vPos)
	RegExMatch(Options, "i)NoWrap", NoWrap)
	RegExMatch(Options, "i)R(\d)", Rendering)
	RegExMatch(Options, "i)S(\d+)(p*)", Size)
	if !Gdip_DeleteBrush(Gdip_CloneBrush(Colour2))
		PassBrush := 1, pBrush := Colour2
	if !(IWidth && IHeight) && (xpos2 || ypos2 || Width2 || Height2 || Size2)
		return -1
	Style := 0, Styles := "Regular|Bold|Italic|BoldItalic|Underline|Strikeout"
	Loop, Parse, Styles, |
	{
		if RegExMatch(Options, "\b" A_loopField)
		Style |= (A_LoopField != "StrikeOut") ? (A_Index-1) : 8
	}
	Align := 0, Alignments := "Near|Left|Centre|Center|Far|Right"
	Loop, Parse, Alignments, |
	{
		if RegExMatch(Options, "\b" A_loopField)
			Align |= A_Index//2.1      ; 0|0|1|1|2|2
	}
	xpos := (xpos1 != "") ? xpos2 ? IWidth*(xpos1/100) : xpos1 : 0
	ypos := (ypos1 != "") ? ypos2 ? IHeight*(ypos1/100) : ypos1 : 0
	Width := Width1 ? Width2 ? IWidth*(Width1/100) : Width1 : IWidth
	Height := Height1 ? Height2 ? IHeight*(Height1/100) : Height1 : IHeight
	if !PassBrush
		Colour := "0x" (Colour2 ? Colour2 : "ff000000")
	Rendering := ((Rendering1 >= 0) && (Rendering1 <= 5)) ? Rendering1 : 4
	Size := (Size1 > 0) ? Size2 ? IHeight*(Size1/100) : Size1 : 12
	hFamily := Gdip_FontFamilyCreate(Font)
	hFont := Gdip_FontCreate(hFamily, Size, Style)
	FormatStyle := NoWrap ? 0x4000 | 0x1000 : 0x4000
	hFormat := Gdip_StringFormatCreate(FormatStyle)
	pBrush := PassBrush ? pBrush : Gdip_BrushCreateSolid(Colour)
	if !(hFamily && hFont && hFormat && pBrush && pGraphics)
		return !pGraphics ? -2 : !hFamily ? -3 : !hFont ? -4 : !hFormat ? -5 : !pBrush ? -6 : 0
	CreateRectF(RC, xpos, ypos, Width, Height)
	Gdip_SetStringFormatAlign(hFormat, Align)
	Gdip_SetTextRenderingHint(pGraphics, Rendering)
	ReturnRC := Gdip_MeasureString(pGraphics, Text, hFont, hFormat, RC)
	if vPos
	{
		StringSplit, ReturnRC, ReturnRC, |
		if (vPos = "vCentre") || (vPos = "vCenter")
			ypos += (Height-ReturnRC4)//2
		else if (vPos = "Top") || (vPos = "Up")
			ypos := 0
		else if (vPos = "Bottom") || (vPos = "Down")
			ypos := Height-ReturnRC4
		CreateRectF(RC, xpos, ypos, Width, ReturnRC4)
		ReturnRC := Gdip_MeasureString(pGraphics, Text, hFont, hFormat, RC)
	}
	if !Measure
		E := Gdip_DrawString(pGraphics, Text, hFont, hFormat, pBrush, RC)
	if !PassBrush
		Gdip_DeleteBrush(pBrush)
	Gdip_DeleteStringFormat(hFormat)
	Gdip_DeleteFont(hFont)
	Gdip_DeleteFontFamily(hFamily)
	return E ? E : ReturnRC
}
Gdip_DrawString(pGraphics, sString, hFont, hFormat, pBrush, ByRef RectF){
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	if (!A_IsUnicode)
	{
		nSize := DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &sString, "int", -1, Ptr, 0, "int", 0)
		VarSetCapacity(wString, nSize*2)
		DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &sString, "int", -1, Ptr, &wString, "int", nSize)
	}
	return DllCall("gdiplus\GdipDrawString", Ptr, pGraphics, Ptr, A_IsUnicode ? &sString : &wString, "int", -1, Ptr, hFont, Ptr, &RectF, Ptr, hFormat, Ptr, pBrush)
}
Gdip_CreateLineBrush(x1, y1, x2, y2, ARGB1, ARGB2, WrapMode=1){
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	CreatePointF(PointF1, x1, y1), CreatePointF(PointF2, x2, y2)
	DllCall("gdiplus\GdipCreateLineBrush", Ptr, &PointF1, Ptr, &PointF2, "Uint", ARGB1, "Uint", ARGB2, "int", WrapMode, A_PtrSize ? "UPtr*" : "UInt*", LGpBrush)
	return LGpBrush
}
Gdip_CreateLineBrushFromRect(x, y, w, h, ARGB1, ARGB2, LinearGradientMode=1, WrapMode=1){
	CreateRectF(RectF, x, y, w, h)
	DllCall("gdiplus\GdipCreateLineBrushFromRect", A_PtrSize ? "UPtr" : "UInt", &RectF, "int", ARGB1, "int", ARGB2, "int", LinearGradientMode, "int", WrapMode, A_PtrSize ? "UPtr*" : "UInt*", LGpBrush)
	return LGpBrush
}
Gdip_CloneBrush(pBrush){
	DllCall("gdiplus\GdipCloneBrush", A_PtrSize ? "UPtr" : "UInt", pBrush, A_PtrSize ? "UPtr*" : "UInt*", pBrushClone)
	return pBrushClone
}
Gdip_FontFamilyCreate(Font){
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	if (!A_IsUnicode)
	{
		nSize := DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &Font, "int", -1, "uint", 0, "int", 0)
		VarSetCapacity(wFont, nSize*2)
		DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &Font, "int", -1, Ptr, &wFont, "int", nSize)
	}
	DllCall("gdiplus\GdipCreateFontFamilyFromName", Ptr, A_IsUnicode ? &Font : &wFont, "uint", 0, A_PtrSize ? "UPtr*" : "UInt*", hFamily)
	return hFamily
}
Gdip_SetStringFormatAlign(hFormat, Align){
   return DllCall("gdiplus\GdipSetStringFormatAlign", A_PtrSize ? "UPtr" : "UInt", hFormat, "int", Align)
}
Gdip_StringFormatCreate(Format=0, Lang=0){
   DllCall("gdiplus\GdipCreateStringFormat", "int", Format, "int", Lang, A_PtrSize ? "UPtr*" : "UInt*", hFormat)
   return hFormat
}
Gdip_FontCreate(hFamily, Size, Style=0){
   DllCall("gdiplus\GdipCreateFont", A_PtrSize ? "UPtr" : "UInt", hFamily, "float", Size, "int", Style, "int", 0, A_PtrSize ? "UPtr*" : "UInt*", hFont)
   return hFont
}
Gdip_CreatePen(ARGB, w){
   DllCall("gdiplus\GdipCreatePen1", "UInt", ARGB, "float", w, "int", 2, A_PtrSize ? "UPtr*" : "UInt*", pPen)
   return pPen
}
Gdip_CreatePenFromBrush(pBrush, w){
	DllCall("gdiplus\GdipCreatePen2", A_PtrSize ? "UPtr" : "UInt", pBrush, "float", w, "int", 2, A_PtrSize ? "UPtr*" : "UInt*", pPen)
	return pPen
}
Gdip_BrushCreateSolid(ARGB=0xff000000){
	DllCall("gdiplus\GdipCreateSolidFill", "UInt", ARGB, A_PtrSize ? "UPtr*" : "UInt*", pBrush)
	return pBrush
}
Gdip_BrushCreateHatch(ARGBfront, ARGBback, HatchStyle=0){
	DllCall("gdiplus\GdipCreateHatchBrush", "int", HatchStyle, "UInt", ARGBfront, "UInt", ARGBback, A_PtrSize ? "UPtr*" : "UInt*", pBrush)
	return pBrush
}
CreateRectF(ByRef RectF, x, y, w, h){
   VarSetCapacity(RectF, 16)
   NumPut(x, RectF, 0, "float"), NumPut(y, RectF, 4, "float"), NumPut(w, RectF, 8, "float"), NumPut(h, RectF, 12, "float")
}
Gdip_SetTextRenderingHint(pGraphics, RenderingHint){
	return DllCall("gdiplus\GdipSetTextRenderingHint", A_PtrSize ? "UPtr" : "UInt", pGraphics, "int", RenderingHint)
}
Gdip_MeasureString(pGraphics, sString, hFont, hFormat, ByRef RectF){
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	VarSetCapacity(RC, 16)
	if !A_IsUnicode
	{
		nSize := DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &sString, "int", -1, "uint", 0, "int", 0)
		VarSetCapacity(wString, nSize*2)
		DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, Ptr, &sString, "int", -1, Ptr, &wString, "int", nSize)
	}
	DllCall("gdiplus\GdipMeasureString", Ptr, pGraphics, Ptr, A_IsUnicode ? &sString : &wString, "int", -1, Ptr, hFont, Ptr, &RectF, Ptr, hFormat, Ptr, &RC, "uint*", Chars, "uint*", Lines)
	return &RC ? NumGet(RC, 0, "float") "|" NumGet(RC, 4, "float") "|" NumGet(RC, 8, "float") "|" NumGet(RC, 12, "float") "|" Chars "|" Lines : 0
}
CreateRect(ByRef Rect, x, y, w, h){
	VarSetCapacity(Rect, 16)
	NumPut(x, Rect, 0, "uint"), NumPut(y, Rect, 4, "uint"), NumPut(w, Rect, 8, "uint"), NumPut(h, Rect, 12, "uint")
}
CreateSizeF(ByRef SizeF, w, h){
   VarSetCapacity(SizeF, 8)
   NumPut(w, SizeF, 0, "float"), NumPut(h, SizeF, 4, "float")
}
CreatePointF(ByRef PointF, x, y){
   VarSetCapacity(PointF, 8)
   NumPut(x, PointF, 0, "float"), NumPut(y, PointF, 4, "float")
}
Gdip_DrawArc(pGraphics, pPen, x, y, w, h, StartAngle, SweepAngle){
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	return DllCall("gdiplus\GdipDrawArc", Ptr, pGraphics, Ptr, pPen, "float", x, "float", y, "float", w, "float", h, "float", StartAngle, "float", SweepAngle)
}
Gdip_DrawPie(pGraphics, pPen, x, y, w, h, StartAngle, SweepAngle){
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	return DllCall("gdiplus\GdipDrawPie", Ptr, pGraphics, Ptr, pPen, "float", x, "float", y, "float", w, "float", h, "float", StartAngle, "float", SweepAngle)
}
Gdip_DrawLine(pGraphics, pPen, x1, y1, x2, y2){
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	return DllCall("gdiplus\GdipDrawLine", Ptr, pGraphics, Ptr, pPen, "float", x1, "float", y1, "float", x2, "float", y2)
}
Gdip_DrawLines(pGraphics, pPen, Points){
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	StringSplit, Points, Points, |
	VarSetCapacity(PointF, 8*Points0)
	Loop, %Points0%
	{
		StringSplit, Coord, Points%A_Index%, `,
		NumPut(Coord1, PointF, 8*(A_Index-1), "float"), NumPut(Coord2, PointF, (8*(A_Index-1))+4, "float")
	}
	return DllCall("gdiplus\GdipDrawLines", Ptr, pGraphics, Ptr, pPen, Ptr, &PointF, "int", Points0)
}
Gdip_FillRectangle(pGraphics, pBrush, x, y, w, h){
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	return DllCall("gdiplus\GdipFillRectangle", Ptr, pGraphics, Ptr, pBrush, "float", x, "float", y, "float", w, "float", h)
}
Gdip_FillRoundedRectangle(pGraphics, pBrush, x, y, w, h, r){
	Region := Gdip_GetClipRegion(pGraphics)
	Gdip_SetClipRect(pGraphics, x-r, y-r, 2*r, 2*r, 4)
	Gdip_SetClipRect(pGraphics, x+w-r, y-r, 2*r, 2*r, 4)
	Gdip_SetClipRect(pGraphics, x-r, y+h-r, 2*r, 2*r, 4)
	Gdip_SetClipRect(pGraphics, x+w-r, y+h-r, 2*r, 2*r, 4)
	E := Gdip_FillRectangle(pGraphics, pBrush, x, y, w, h)
	Gdip_SetClipRegion(pGraphics, Region, 0)
	Gdip_SetClipRect(pGraphics, x-(2*r), y+r, w+(4*r), h-(2*r), 4)
	Gdip_SetClipRect(pGraphics, x+r, y-(2*r), w-(2*r), h+(4*r), 4)
	Gdip_FillEllipse(pGraphics, pBrush, x, y, 2*r, 2*r)
	Gdip_FillEllipse(pGraphics, pBrush, x+w-(2*r), y, 2*r, 2*r)
	Gdip_FillEllipse(pGraphics, pBrush, x, y+h-(2*r), 2*r, 2*r)
	Gdip_FillEllipse(pGraphics, pBrush, x+w-(2*r), y+h-(2*r), 2*r, 2*r)
	Gdip_SetClipRegion(pGraphics, Region, 0)
	Gdip_DeleteRegion(Region)
	return E
}
Gdip_GetClipRegion(pGraphics){
	Region := Gdip_CreateRegion()
	DllCall("gdiplus\GdipGetClip", A_PtrSize ? "UPtr" : "UInt", pGraphics, "UInt*", Region)
	return Region
}
Gdip_SetClipRect(pGraphics, x, y, w, h, CombineMode=0){
   return DllCall("gdiplus\GdipSetClipRect",  A_PtrSize ? "UPtr" : "UInt", pGraphics, "float", x, "float", y, "float", w, "float", h, "int", CombineMode)
}
Gdip_SetClipPath(pGraphics, Path, CombineMode=0){
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	return DllCall("gdiplus\GdipSetClipPath", Ptr, pGraphics, Ptr, Path, "int", CombineMode)
}
Gdip_ResetClip(pGraphics){
   return DllCall("gdiplus\GdipResetClip", A_PtrSize ? "UPtr" : "UInt", pGraphics)
}
Gdip_FillEllipse(pGraphics, pBrush, x, y, w, h){
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	return DllCall("gdiplus\GdipFillEllipse", Ptr, pGraphics, Ptr, pBrush, "float", x, "float", y, "float", w, "float", h)
}
Gdip_FillRegion(pGraphics, pBrush, Region){
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	return DllCall("gdiplus\GdipFillRegion", Ptr, pGraphics, Ptr, pBrush, Ptr, Region)
}
Gdip_FillPath(pGraphics, pBrush, Path){
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	return DllCall("gdiplus\GdipFillPath", Ptr, pGraphics, Ptr, pBrush, Ptr, Path)
}
Gdip_CreateRegion(){
	DllCall("gdiplus\GdipCreateRegion", "UInt*", Region)
	return Region
}
Gdip_DeleteRegion(Region){
	return DllCall("gdiplus\GdipDeleteRegion", A_PtrSize ? "UPtr" : "UInt", Region)
}
Gdip_CreateBitmap(Width, Height, Format=0x26200A){
    DllCall("gdiplus\GdipCreateBitmapFromScan0", "int", Width, "int", Height, "int", 0, "int", Format, A_PtrSize ? "UPtr" : "UInt", 0, A_PtrSize ? "UPtr*" : "uint*", pBitmap)
    Return pBitmap
}
Gdip_SetSmoothingMode(pGraphics, SmoothingMode){
   return DllCall("gdiplus\GdipSetSmoothingMode", A_PtrSize ? "UPtr" : "UInt", pGraphics, "int", SmoothingMode)
}
Gdip_DrawRectangle(pGraphics, pPen, x, y, w, h){
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	return DllCall("gdiplus\GdipDrawRectangle", Ptr, pGraphics, Ptr, pPen, "float", x, "float", y, "float", w, "float", h)
}
Gdip_DrawRoundedRectangle(pGraphics, pPen, x, y, w, h, r){
	Gdip_SetClipRect(pGraphics, x-r, y-r, 2*r, 2*r, 4)
	Gdip_SetClipRect(pGraphics, x+w-r, y-r, 2*r, 2*r, 4)
	Gdip_SetClipRect(pGraphics, x-r, y+h-r, 2*r, 2*r, 4)
	Gdip_SetClipRect(pGraphics, x+w-r, y+h-r, 2*r, 2*r, 4)
	E := Gdip_DrawRectangle(pGraphics, pPen, x, y, w, h)
	Gdip_ResetClip(pGraphics)
	Gdip_SetClipRect(pGraphics, x-(2*r), y+r, w+(4*r), h-(2*r), 4)
	Gdip_SetClipRect(pGraphics, x+r, y-(2*r), w-(2*r), h+(4*r), 4)
	Gdip_DrawEllipse(pGraphics, pPen, x, y, 2*r, 2*r)
	Gdip_DrawEllipse(pGraphics, pPen, x+w-(2*r), y, 2*r, 2*r)
	Gdip_DrawEllipse(pGraphics, pPen, x, y+h-(2*r), 2*r, 2*r)
	Gdip_DrawEllipse(pGraphics, pPen, x+w-(2*r), y+h-(2*r), 2*r, 2*r)
	Gdip_ResetClip(pGraphics)
	return E
}
Gdip_DrawEllipse(pGraphics, pPen, x, y, w, h){
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	return DllCall("gdiplus\GdipDrawEllipse", Ptr, pGraphics, Ptr, pPen, "float", x, "float", y, "float", w, "float", h)
}
Gdip_CreateHBITMAPFromBitmap(pBitmap, Background=0xffffffff){
	DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", A_PtrSize ? "UPtr" : "UInt", pBitmap, A_PtrSize ? "UPtr*" : "uint*", hbm, "int", Background)
	return hbm
}
SetImage(hwnd, hBitmap){
	SendMessage, 0x172, 0x0, hBitmap,, ahk_id %hwnd%
	E := ErrorLevel
	DeleteObject(E)
	return E
}
Gdip_FillPolygon(pGraphics, pBrush, Points, FillMode=0){
	Ptr := A_PtrSize ? "UPtr" : "UInt"
	StringSplit, Points, Points, |
	VarSetCapacity(PointF, 8*Points0)
	Loop, %Points0%
	{
		StringSplit, Coord, Points%A_Index%, `,
		NumPut(Coord1, PointF, 8*(A_Index-1), "float"), NumPut(Coord2, PointF, (8*(A_Index-1))+4, "float")
	}
	return DllCall("gdiplus\GdipFillPolygon", Ptr, pGraphics, Ptr, pBrush, Ptr, &PointF, "int", Points0, "int", FillMode)
}
I have included a reduced copy of the gdip lib into the bottom of the script so it is run ready.



Here are a few questions related to your code.

1.
I notice that you are using Local I had assumed that even without adding that they would be local. So, are they not normally local?
I have the feeling that this will create a local variable even if there is a super global declared elsewhere.
If so, i'll be sure to start using it in my functions and classes.

2.
I'm unsure of the purpose of this. Solved

Code: Select all

new SomeClass(ObjBindMethod(new C, "m"))

Class C {
	m(params*) {
	ToolTip % A_ThisFunc "," params.1
	}
}
3.
a) Solved

b) Why are you setting the value of "label" here

Code: Select all

(IsFunc(label) && label:=Func(label))
If "Label" is a function, wouldn't label:=Func(label) always evaluate as true anyway?


c) Solved (sort of)


4.
**Edit**
**********************************************************************************
I think that I have a handle on this one.
Is this deleting every instance of the class or just the one that calls this method?
I'm pretty sure it's all of them, so
What would be a reason to delete every instance and not just a individual one?
I have to admit, this whole "SomeClass.Instance[]" thing still has me at a bit of a loss.
And
Is the name of a class also a Object?

I'm going to leave my original question 4 unedited, but it doesn't really reflect my current questions about this part anymore.
******************************************************************************
I'm not sure about this.

Code: Select all

disposeAll() {
		local hwnd, instance
		for hwnd, instance in ObjClone(SomeClass.instances)
			instance.dispose(), SomeClass.instances.delete(hwnd)
	}
Clicking on "ObjClone" it brings this up in the docs

https://www.autohotkey.com/docs/objects/Object.htm#Clone
Returns a shallow copy of the object.
No clue what a "Shallow copy" is, but what is the purpose of creating a clone?

It looks like this is some sort of removal of the button objects hwnd?, but why do it on a clone and not the actual object (This).
Also, if you the hwnd is being deleted, wouldn't/ shouldn't the rest of the button object be cleared?
At second glace it looks like "hwnd" is being used for every key in the button object clone. Still not sure why it's being done on a clone or why a clone/copy was ever made
i.e.

Code: Select all

return this, SomeClass.instances[ hwnd+0 ] := this


Thanks.
A_AhkUser
Posts: 1076
Joined: 06 Mar 2017, 16:18
GitHub: AAhkUser
Location: France

Re: Differentiating between a label and a function.

09 Jun 2019, 17:53

Hellbent wrote:
08 Jun 2019, 15:59
I notice that you are using Local I had assumed that even without adding that they would be local. [...]
I have the feeling that this will create a local variable even if there is a super global declared elsewhere.
You're right about that - see also assume-global mode and force-local mode for more details.
c) Solved (sort of)
If the c) still somehow raises a question please let me know - my code are always highly questionable... :headwall: :terms:
b) Why are you setting the value of "label" here

Code: Select all

(IsFunc(label) && label:=Func(label))
If "Label" is a function, wouldn't label:=Func(label) always evaluate as true anyway?
Yeah this should have been rather something like:

Code: Select all

if not (type:=(IsObject(label) ? "Object/FuncObject" : IsFunc(label) ? ("Object/FuncObject", label:=Func(label)) : IsLabel(label) ? "Label" : ""))
Might not be ideal either. Anyhow, at the root, this is to stress that control's g-label can also be objects.
It is especially within this framework that object construction and destruction makes sense - we get to the fourth point:
4. [...]

Code: Select all

disposeAll() {
		local hwnd, instance
		for hwnd, instance in ObjClone(SomeClass.instances)
			instance.dispose(), SomeClass.instances.delete(hwnd)
	}
So, we are in assume-global mode: thus, SomeClass refers to the super-global base object. We are accessing the class variable instances, which belong to the class itself. By "shallow copy" (vs deep copy) of the object we have to understand that the structure of the object is copied, not its content. In particular, here, this means that what is stored as value of the instances [EDIT] clone associative array respective keys (unique control identifiers - hwnd) are not the instance itself, but each time a reference to each instance: a new reference to the same object. In the for...in loop the dispose method first removes instance's own control's g-label - as I said this is especially usefull if a function object, bound func object, an internal method etc. is set as label: so that the __Delete method, if implemented by the object, can be called properly ([EDIT] see also: freeing objects). Finally, the delete built-in method, called upon the instances class variable object actually removes keys: but this means that indexes are shifted somehow; ObjClone allows to bypassthis potential issue - compare:

Code: Select all

a := ["a", "b", "c", "d"]
for key, value in clone:=ObjClone(a) ; does not create a new reference to the same object (b := a would do - see below)
	MsgBox % "1 > " a.delete(key)
b := a
for key, value in b
	MsgBox % "2 > " a.delete(key) ; never called: a has been cleared
for key, value in clone ; shows how indexe are shifted
	MsgBox % "3 > " clone.delete(key)
a := ["a", "b", "c", "d"]
for key, value in a  ; shows how indexe are shifted
	MsgBox % "3 > " a.delete(key)
if you the hwnd is being deleted, wouldn't/ shouldn't the rest of the button object be cleared


The approach in this exemple consists in deleting the interface to the button reality, not the button itself. This supposes that once you call dispose, you're done with the interface, that is the access to the button through this interface (might still exist other ones). Sure you can implement it in another way. But, either way, as documented, the delete GuiControl sub-command is not yet implemented.

Cheers.
User avatar
Hellbent
Posts: 1057
Joined: 23 Sep 2017, 13:34

Re: Differentiating between a label and a function.

11 Jun 2019, 00:30

@A_AhkUser

Thank you.

I started this reply with about 20 paragraphs of questions, but typing them out helped me think through them, so now i'm down to just this one.

in your code

Code: Select all

for k, v in ObjClone(SomeClass.instances)
	instance.dispose(), SomeClass.instances.delete(k)
k would be a instances of the class, and v would be the keys (right?). How would I get the keys and values to be represented by k and v?



***Edit***
There was one other thing.


What is the meaning behind naming some methods with a (single) underscore?
A_AhkUser
Posts: 1076
Joined: 06 Mar 2017, 16:18
GitHub: AAhkUser
Location: France

Re: Differentiating between a label and a function.

11 Jun 2019, 10:46

Hellbent wrote:
11 Jun 2019, 00:30

Code: Select all

for k, v in ObjClone(SomeClass.instances)
	instance.dispose(), SomeClass.instances.delete(k)
k would be a instances of the class, and v would be the keys (right?). How would I get the keys and values to be represented by k and v?
This is how SomeClass.instances is populated:

Code: Select all

SomeClass.instances[ hwnd+0 ] := this
In any event, in the for k, v in expression iteration mean, "k" always stores the current key and "v" the value associated with the current key "k".
The delete built-in method called with only one parameter accepts any key as parameter. Thus, Object.Delete(k) means "removes the key-value pairs from an object whose key is 'k'. In the example, keys are hwnd converted as integer (they have the advantage of allowing for identification as such, as unique ID) and each of them is associated with an instance. Btw, since we call delete with exactly one parameter, the removed value is returned (that is, the instance); thus, you can even write it like this:

Code: Select all

for k ObjClone(SomeClass.instances) {
	instance := SomeClass.instances.delete(k)
	instance.dispose()
}
or even (more cryptic, admittedly):

Code: Select all

for k in ObjClone(SomeClass.instances)
	SomeClass.instances.delete(k).dispose()
All this as counterparty of both:

Code: Select all

SomeClass.instances[ hwnd+0 ] := this
but also this:

Code: Select all

IBM:=This.InternalMethod.Bind(This)
GuiControl,+G,% This.Hwnd,% IBM
call each time an instance is created.Actually, __Delete is called when the last known references of the object is freed; this is the main purpose of disposeall: it removes 'stucked' instance references (this): one or more this are still 'stucked' in the instances associative array and in the g-label since it as been bound to the internal method set as g-label's callback.

I'm going to go back to this question:
if you the hwnd is being deleted, wouldn't/ shouldn't the rest of the button object be cleared
It seems I hadn't correctly addressing this question by not wanting to see the "object" alongside the "button". If you meant delete all values somehow bound to an instance, this is generally not necessary unless they take some significant memory - as an example let's take registercallback. registercallback can be used in the same purpose as your internal method "IBM", set as g-label. Your IBM - a bound func object - is an object that can be called by the script as soon as the g-label triggers. Registercallback returns a machine code adress instead, its meaning here lies in the fact that it may be called by DllCall(), in particular: for example, if you use the SetTimer API function you must pass a registercallback instead of This.InternalMethod.Bind(This) aka IBM) - pseudo-codes:

Code: Select all

; ...
adress := RegisterCallback("myObject._internalMethod", "F", 4, &this)
DllCall("SetTimer", "Ptr", 0, "UInt", 0, "UInt", 1000, "Ptr", adress)
; ...
vs:

Code: Select all

; ...
IBM := this._internalMethod.bind(this)
SetTimer % IBM, 1000
; ...
Yet, as per the documentation:
RegisterCallback - memory wrote:a script that calls RegisterCallback() an indefinite/unlimited number of times should explicitly call the following on any unused callbacks
DllCall("GlobalFree", "Ptr", Address, "Ptr")
so if we have let's say this pseudo-code:

Code: Select all

new C
new C
new C
; indefinite/unlimited number of instances
Class C {
	__New() {
	this._cb := RegisterCallback("C._internalMethod") ; let's say it will be used by some win event hook for example
	}
	_internalMethod(params*) {
		; some event hook handling
	}
	__Delete() {
		DllCall("GlobalFree", "Ptr", this._cb, "Ptr")
		; MsgBox % A_LastError
	}
}
It is relevant at some point to handle the __Delete event (> when the last known references of the object is freed) but also to explicitly call GlobalFree to free memory.
Hellbent wrote:
11 Jun 2019, 00:30
What is the meaning behind naming some methods with a (single) underscore?
I use this to distinguish between internal and interfaced methods. This is not idiosyncrasic. As an exemple, in ahk itself, instead of the for...in iteration mean you can use the _NewEnum method, upon the object to be iterated. The main purpose of the (single) underscore is, I think, to avoid at some extent this method to be used by inadvertance, as result of a typo etc. and also to indicate that the use of it should be restricted to thoses cases where you know what your are doing or be considered as a one-time workaround:

Code: Select all

o := [ Func("f"), 1, 2, 3, 4 ]

enum := o._NewEnum()
enum.next(key, func), integer := func.call()
Loop {
	MsgBox % "Integer is " . integer
} Until !(enum[ key, integer ])

f() {
return 0
}
User avatar
Hellbent
Posts: 1057
Joined: 23 Sep 2017, 13:34

Re: Differentiating between a label and a function.

19 Jun 2019, 16:26

@A_AhkUser

Thank you for your time on this topic.

I have been over your last reply twice so far in the last week. Some things are clear, others just don't have relateable context to me yet (nothing to be done about that). I have this page saved and will re-read it a few more times to let it sink in.

Again, thank you very much for your time.

Return to “Ask For Help”

Who is online

Users browsing this forum: AHKStudent, Bing [Bot], boiler, c7aesa7r, dagiccross, EEEEE, Ian and 344 guests