Bubble Text / Funky Colorful Text

Post your working scripts, libraries and tools for AHK v1.1 and older
User avatar
Hellbent
Posts: 2109
Joined: 23 Sep 2017, 13:34

Bubble Text / Funky Colorful Text

29 Mar 2024, 05:37

This is pretty much just a first draft / POC class.
If you have any improvements that you can make feel free to post your code.
Special thanks to @jNizM and the others in this thread for their help in making rendering faster. viewtopic.php?f=76&t=112861

This class creates bordered text in a few different styles.

.
Image
.
Image
.

Here are two examples of how to create the text bitmaps using two different versions of the script.

.
bubble text demo 1.gif
bubble text demo 1.gif (512.25 KiB) Viewed 164 times
.

This first example uses an older version of the class but is a bit simpler to use and understand.

Code: Select all

;****************************************************************************************************************************************************************************
#Include <GDIP_ALL> ;GDIP:  https://www.autohotkey.com/boards/viewtopic.php?f=6&t=6517
;~ #Include <PopUpWindow_V2> ; At the bottom of the script 
;****************************************************************************************************************************************************************************
#SingleInstance, Force
SetBatchLines, -1
;********************

;load gdip

global gdiplus := LoadLibrary("gdiplus") ;For faster speeds rendering the bitmap. https://www.autohotkey.com/boards/viewtopic.php?f=76&t=112861#p502736
Gdip_Startup()
;********************

;Create the color array. 
;Each color requires a index in the size array.
;You can add as many colors/sizes as you want.

;~ ColorArray := [ "0x33FFFF80" , "0x33FF8040" , "0x33804040" , "0x33FFFF00" ]
;~ ColorArray := [ "0x3300FF80" , "0x33008080" , "0x33004040" , "0x33004000" ]
;~ ColorArray := [ "0x338C3535" , "0x33008080" , "0x33004040" , "0x33004000" ]
ColorArray := [ "0x33004444" , "0x44FFFFFF" , "0x33aaaaaa" , "0x6622262a"  ]
;********************

;Create the size array. 
;Each size is paired with a color from the ColorArray
;The sizes are in decending order ( from highest to lowest )

;~ SizeArray := [ 9 , 6 , 5 , 3 ]
SizeArray := [ 18 , 12 , 9 , 5 ]


;********************

text =
(
This 
is 
sample 
text
)
;********************

;Create the Bubble text object.
;The object has a number of key / values that you can access 
;The important keys are [ Width , Height , Bitmap ]
;FullWarp: 1 = Fully wrap the text in the bubbles, 0 = Wrap the bottom and right side only (something like a shadow)

;Once this object is created the bitmap for it can be saved as a .png to file rather than displaying it as I have it here in this example. [ i.e. Gdip_SaveBitmapToFile(pBitmap, sOutput, Quality=100) ]


MyString := New BubbleText( { Text: text , FontColor: "0xFFFFFFFF" , FontSize: 55 , FontType: "Impact" , FullWrap: 1 } , ColorArray , SizeArray )

;********************

;Example of drawing the bubble text on a layered window.
Gui1 := New PopUpWindow( { AutoShow: 1 , X: 300 , Y: 280 , W: MyString.Width , H: MyString.Height , Options: " -DPIScale +AlwaysOnTop +E0x20" } )
Gui1.PaintBackground( { Color: "0x99000000" , X: 2 , Y: 2 , W: Gui1.W - 4 , H: Gui1.H - 4 , Round: 30 } , AutoUpdate := 0 ) 
Gui1.DrawBitmap( MyString.Bitmap , { X: 0 , Y: 0 , W: MyString.Width , H: MyString.Height } , dispose := 1 , AutoUpdate := 1 )

;********************


return
GuiClose:
GuiContextMenu:
*ESC::ExitApp

RALT::PopUpWindow.Helper()




;####################################################################################################################################################################################
;####################################################################################################################################################################################
;####################################################################################################################################################################################
;####################################################################################################################################################################################
;Color Text class
;Written By: Hellbent
;Date: Jan 18th , 2023
;Last Edit: Jan 20th , 2023
;Change: Can now set to use a pie shape to do a drop shadow. [ Key: FullWrap | Values: 0/1 ]
;Thanks To: jNizM (  https://www.autohotkey.com/boards/viewtopic.php?f=76&t=112861#p502745 )
class BubbleText	{
	__New( obj := "" , Colors := "0xFF000000" , Sizes := 2 ){
		This._SetDefaults()
		This._UpdateDefaults( obj , Colors , Sizes )
		This._CreateBitmaps()
		This._LiquidatePixels()
		return This.Output
	}
	
	_SetDefaults(){
		This.Text := "Hello World"
		This.FontType := "Comic Sans MS"
		This.FontSize := 16
		This.FontColor := "0xFFFFFFFF"
		This.FontOptions := " Center vCenter "
		This.BackgroundColor := ""
		This.StringHeight := ""
		This.StringWidth := ""
		This.TextBitmap := ""
		This.Bitmaps := []
		This.Colors := []
		This.SizeInterval := 1
		This.Smoothing := 4
		This.RedRange := 128
		This.GreenRange := 128
		This.BlueRange := 128
		This.FullWrap := 1
	}
	
	_UpdateDefaults( obj := "" , Colors := "0xFF000000" , Sizes := 2 ){ 
		for k , v in obj	
			This[ k ] := obj[ k ]
		if( IsObject( Colors ) ){
			for k , v in Colors
				This.Colors[ A_Index ] := Colors[ A_Index ]
		}else if( colors != "" ){
			This.Colors[ 1 ] := colors
		}
		if( IsObject( Sizes ) ){
			for k , v in Sizes
				This.Sizes[ A_Index ] := Sizes[ A_Index ]
		}else if( Sizes != "" ){
			This.Sizes[ 1 ] := Sizes
		}
		if( ( dif := This.Sizes.Length() - This.Colors.Length() ) < 0 ){
			index := This.Sizes.Length()
			loop, % dif	{
				This.Sizes[ index + A_Index ] := This.Sizes[ index + A_Index - 1 ] - This.SizeInterval
			}
		}else if( ( dif := This.Colors.Length() - This.Sizes.Length() ) < 0 ){
			index := This.Colors.Length()
			loop, % dif	{
				This.Colors[ index + A_Index ] := This.Colors[ index + A_Index - 1 ] 
			}
		}
	}
	
	_CreateBitmaps(){
		This.StringWidth := This._GetTextSize( 1 , This.Text ) 
		This.StringHeight := This._GetTextSize( 2 , This.Text ) / 1.05
		This.TextBitmap := Gdip_CreateBitmap( This.StringWidth , This.StringHeight )
		This.TextBitmapGraphics := Gdip_GraphicsFromImage( This.TextBitmap )
		Gdip_SetSmoothingMode( This.TextBitmapGraphics , This.Smoothing )
		if( This.BackgroundColor != ""){
			Brush := Gdip_BrushCreateSolid( This.BackgroundColor )
			Gdip_FillRectangle( This.TextBitmapGraphics , Brush, -10 , -10 , This.StringWidth + 20 , This.StringHeight + 20 )
			Gdip_DeleteBrush( Brush )
		}
		Brush := Gdip_BrushCreateSolid( This.FontColor )
		Gdip_TextToGraphics( This.TextBitmapGraphics , This.Text , " s" This.Fontsize " c" Brush " " This.FontOptions " x" 0 " y" 0 , This.FontType , This.StringWidth, This.StringHeight  )
		Gdip_DeleteBrush( Brush )
		This.MaxSize := 0
		Loop, % This.Sizes.Length()	{
			if( This.Sizes[ A_Index ] > This.MaxSize ){
				This.MaxSize := This.Sizes[ A_Index ]
			}
		}
		Loop, % This.Colors.Length()	{
			This.Bitmaps[ A_Index ] := {}
			This.Bitmaps[ A_Index ].Bitmap := Gdip_CreateBitmap( This.StringWidth + 2 * This.MaxSize + 4 , This.StringHeight + 2 * This.MaxSize + 4 )
			This.Bitmaps[ A_Index ].Graphics := Gdip_GraphicsFromImage( This.Bitmaps[ A_Index ].Bitmap )
			Gdip_SetSmoothingMode( This.Bitmaps[ A_Index ].Graphics , This.Smoothing )
		}
		This.Output := {}
		This.Output.Width := This.StringWidth + 2 * This.MaxSize + 4
		This.Output.Height := This.StringHeight + 2 * This.MaxSize + 4
		This.Output.Bitmap := Gdip_CreateBitmap( This.Output.Width , This.Output.Height )
		This.Output.Graphics := Gdip_GraphicsFromImage( This.Output.Bitmap )
	}
	
	_GetTextSize( outputType := 0 , String := "" ){ ; 0 = all , 1 = width , 2 = height 
		local pBitmap, G, Brush, temparr 
		pBitmap := Gdip_CreateBitmap( 10,10), G := Gdip_GraphicsFromImage( pBitmap ), Gdip_SetSmoothingMode( G , 2 )
		Brush := Gdip_BrushCreateSolid( "0xFF000000")
		temparr := StrSplit( Gdip_TextToGraphics( G , ( String != "" ) ? ( String ) : ( "T" ) , " s" This.Fontsize " c" Brush " " This.FontOptions " x" 0 " y" 0 , This.FontType , 10000, 10000  ),"|","|"  )
		Gdip_DeleteBrush( Brush ), Gdip_DeleteGraphics( G ), Gdip_DisposeImage( pBitmap )
		if( outputType = 0 )
			return temparr
		else if( outputType = 1 )
			return temparr[ 3 ]
		else if( outputType = 2 )
			return temparr[ 4 ]
	}
	
	_GetGray( OUTPUTCOLOR ){ ;From HB color picker v2
		StringTrimLeft,OUTPUTCOLOR,OUTPUTCOLOR,2
		StringLeft , r ,  OUTPUTCOLOR , 2
		StringTrimLeft,OUTPUTCOLOR,OUTPUTCOLOR,2
		StringLeft , g ,  OUTPUTCOLOR , 2
		StringTrimLeft,OUTPUTCOLOR,OUTPUTCOLOR,2
		StringLeft , b ,  OUTPUTCOLOR , 2
		r := "0x" r , g := "0x" g , b := "0x" b
		REDSLIDERVALUE:=r+0,GreenSLIDERVALUE:=g+0,BlueSLIDERVALUE:=b+0
		GreyScaleSLIDERVALUE:=Round((REDSLIDERVALUE+GreenSLIDERVALUE+BlueSLIDERVALUE)/3)
		return GreyScaleSLIDERVALUE
	}
	_LiquidatePixels(){
		local x , y , brushes := [] , Ptr := A_PtrSize ? "UPtr" : "UInt" 
		Loop, % This.Colors.Length()	
			Brushes[ A_Index ] := Gdip_BrushCreateSolid( This.Colors[ A_Index ] )
		y := 0
		Loop, % This.StringHeight 	{
			x := 0
			Loop, % This.StringWidth	{
				SetFormat, IntegerFast, hex
				DllCall( gdiplus.GdipBitmapGetPixel , Ptr , This.TextBitmap, "int", x, "int", y, "uint*", col )
				if( col != 0x0 ){
					Loop, % This.Bitmaps.Length()	{
						if( This.FullWrap ){
							DllCall( gdiplus.GdipFillEllipse , Ptr, This.Bitmaps[ A_Index ].Graphics, Ptr, Brushes[ A_Index ] , "float", x + This.MaxSize + 2 - This.Sizes[ A_Index ] , "float", y + This.MaxSize + 2 - This.Sizes[ A_Index ] , "float", 2 * This.Sizes[ A_Index ] , "float", 2 * This.Sizes[ A_Index ] )
						}else{
							DllCall( gdiplus.GdipFillPie , Ptr, This.Bitmaps[ A_Index ].Graphics , Ptr, Brushes[ A_Index ] , "float", x + This.MaxSize + 2 - This.Sizes[ A_Index ] , "float", y + This.MaxSize + 2 - This.Sizes[ A_Index ] , "float", 2 * This.Sizes[ A_Index ] , "float", 2 * This.Sizes[ A_Index ] , "float", 0 , "float", 90)
						}
					}
				}
				SetFormat, IntegerFast, dec
				x++
			}
			y++
		}
		Loop, % This.Bitmaps.Length()	{
			Gdip_DeleteGraphics( This.Bitmaps[ A_Index ].Graphics )
			Gdip_DrawImage( This.Output.Graphics , This.Bitmaps[ A_Index ].Bitmap , 0 , 0 , This.Output.Width , This.Output.Height )
		}
		if( This.BackgroundColor = "" )
			Gdip_DrawImage( This.Output.Graphics , This.TextBitmap , This.MaxSize + 2 , This.MaxSize + 2 , This.StringWidth , This.StringHeight )
		else{
			Gdip_DeleteGraphics( This.TextBitmapGraphics )
			Gdip_DisposeImage( This.TextBitmap )
			This.TextBitmap := Gdip_CreateBitmap( This.StringWidth , This.StringHeight )
			This.TextBitmapGraphics := Gdip_GraphicsFromImage( This.TextBitmap )
			Gdip_SetSmoothingMode( This.TextBitmapGraphics , This.Smoothing )
			Brush := Gdip_BrushCreateSolid( This.FontColor )
			Gdip_TextToGraphics( This.TextBitmapGraphics , This.Text , " s" This.Fontsize " c" Brush " " This.FontOptions " x" 0 " y" 0 , This.FontType , This.StringWidth, This.StringHeight  )
			Gdip_DeleteBrush( Brush )
			Gdip_DrawImage( This.Output.Graphics , This.TextBitmap , This.MaxSize + 2 , This.MaxSize + 2 , This.StringWidth , This.StringHeight )
		}
		Gdip_DeleteGraphics( This.Output.Graphics )
		Loop, % This.Colors.Length()	{
			Gdip_DeleteBrush( Brushes[ A_Index ] )
		}
		Gdip_DeleteGraphics( This.TextBitmapGraphics )
	}
	_hex2rgb( CR , ByRef R , ByRef G , ByRef B , ByRef A := "NA" ){ ;https://www.autohotkey.com/boards/viewtopic.php?t=3925&p=312862
		H := InStr(CR, "0x") ? CR : ( InStr(CR, "#" ) ? "0x" SubStr( CR , 2 ) : "0x" CR )
		R := (H & 0xFF0000) >> 16 , G := (H & 0xFF00) >> 8 , B := (H & 0xFF) , ( A != "NA" ) ? ( A := (H & 0xFF000000) >> 24 ) 
	}
}


LoadLibrary(filename){ ;https://www.autohotkey.com/boards/viewtopic.php?p=48392#p48392
    static ref := {}
    if (!(ptr := p := DllCall("LoadLibrary", "str", filename, "ptr")))
        return 0
    ref[ptr,"count"] := (ref[ptr]) ? ref[ptr,"count"]+1 : 1
    p += NumGet(p+0, 0x3c, "int")+24
    o := {_ptr:ptr, __delete:func("FreeLibrary"), _ref:ref[ptr]}
    if (NumGet(p+0, (A_PtrSize=4) ? 92 : 108, "uint")<1 || (ts := NumGet(p+0, (A_PtrSize=4) ? 96 : 112, "uint")+ptr)=ptr || (te := NumGet(p+0, (A_PtrSize=4) ? 100 : 116, "uint")+ts)=ts)
        return o
    n := ptr+NumGet(ts+0, 32, "uint")
    loop % NumGet(ts+0, 24, "uint"){
        if (p := NumGet(n+0, (A_Index-1)*4, "uint")){
            o[f := StrGet(ptr+p, "cp0")] := DllCall("GetProcAddress", "ptr", ptr, "astr", f, "ptr")
            if (Substr(f, 0)==((A_IsUnicode) ? "W" : "A"))
                o[Substr(f, 1, -1)] := o[f]
        }
    }
    return o
}

FreeLibrary(lib){
    if (lib._ref.count>=1)
        lib._ref.count -= 1
    if (lib._ref.count<1)
        DllCall("FreeLibrary", "ptr", lib._ptr)
}













;####################################################################################################################################################################################
;####################################################################################################################################################################################
;####################################################################################################################################################################################
;####################################################################################################################################################################################
class PopUpWindow	{
;PopUpWindow v2.2
;Date Written: Oct 28th, 2021
;Last Edit: Feb 7th, 2022 :Changed the trigger method.
;Written By: Hellbent aka CivReborn
;SpcThanks: teadrinker , malcev 
	static Index := 0 , Windows := [] , Handles := [] , EditHwnd , HelperHwnd
	__New( obj := "" ){
		This._SetDefaults()
		This.UpdateSettings( obj )
		This._CreateWindow()
		This._CreateWindowGraphics()
		if( This.AutoShow )
			This.ShowWindow( This.Title )
	}
	_SetDefaults(){
		This.X := 10
		This.Y := 10
		This.W := 10
		This.H := 10
		This.Smoothing := 2
		This.Options := " -DPIScale +AlwaysOnTop "
		This.AutoShow := 0
		This.GdipStartUp := 0
		This.Title := ""
		
		This.Controls := []
		This.Handles := []
		This.Index := 0 
	}
	AddTrigger( obj ){
		local k , v , cc , bd
		
		This.Controls[ ++This.Index ] := { 	X:		10
										,	Y:		10
										,	W:		10
										,	H:		10	}
		for k, v in obj
			This.Controls[ This.Index ][ k ] := obj[ k ] 
		cc := This.Controls[ This.Index ]
		Gui, % This.Hwnd ":Add", Text, % "x" cc.X " y" cc.Y " w" cc.W " h" cc.H " hwndhwnd"
		This.Handles[ hwnd ] := This.Index
		This.Controls[ This.Index ].Hwnd := hwnd
		
		if( IsObject( cc.Label ) ){
			bd := cc.Label
			GuiControl, % This.Hwnd ":+G" , % hwnd , % bd
		}else{
			bd := This._TriggerCall.Bind( This )
			GuiControl, % This.Hwnd ":+G" , % hwnd , % bd
		}
		return hwnd
		
	}
	_TriggerCall(){
		MouseGetPos,,,, ctrl, 2
		Try
			;~ SetTimer, % This.Controls[ This.Handles[ ctrl ] ].Label, -0
			gosub, % This.Controls[ This.Handles[ ctrl ] ].Label
		
				
	}
	DrawTriggers( color := "0xFFFF0000" , AutoUpdate := 0 ){
		local brush , cc 
		Brush := Gdip_BrushCreateSolid( color ) 
		Gdip_SetSmoothingMode( This.G , 3 )
		loop, % This.Controls.Length()	{
			cc := This.Controls[ A_Index ]
			Gdip_FillRectangle( This.G , Brush , cc.x , cc.y , cc.w , cc.h )
		
		}
		Gdip_DeleteBrush( Brush )
		Gdip_SetSmoothingMode( This.G , This.Smoothing )
		if( AutoUpdate )
			This.UpdateWindow()
	}
	UpdateSettings( obj := "" , UpdateGraphics := 0 ){
		local k , v
		if( IsObject( obj ) )
			for k, v in obj
				This[ k ] := obj[ k ]
		( This.X = "Center" ) ? ( This.X := ( A_ScreenWidth - This.W ) / 2 ) 	
		( This.Y = "Center" ) ? ( This.Y := ( A_ScreenHeight - This.H ) / 2 ) 	
		if( UpdateGraphics ){
			This._DestroyWindowsGraphics()
			This._CreateWindowGraphics()
		}
	}
	_CreateWindow(){
		local hwnd
		Gui , New, % " +LastFound +E0x80000 hwndhwnd -Caption  " This.Options
		PopUpWindow.Index++
		This.Index := PopUpWindow.Index
		PopUpWindow.Windows[ PopUpWindow.Index ] := This
		This.Hwnd := hwnd
		PopUpWindow.Handles[ hwnd ] := PopUpWindow.Index
		if( This.GdipStartUp && !PopUpWindow.pToken )
			PopUpWindow.pToken := GDIP_STARTUP()
	}
	_DestroyWindowsGraphics(){
		Gdip_DeleteGraphics( This.G )
		SelectObject( This.hdc , This.obm )
		DeleteObject( This.hbm )
		DeleteDC( This.hdc )
	}
	_CreateWindowGraphics(){
		This.hbm := CreateDIBSection( This.W , This.H )
		This.hdc := CreateCompatibleDC()
		This.obm := SelectObject( This.hdc , This.hbm )
		This.G := Gdip_GraphicsFromHDC( This.hdc )
		Gdip_SetSmoothingMode( This.G , This.Smoothing )
	}
	ShowWindow( Title := "" ){
		Gui , % This.Hwnd ":Show", % "x" This.X " y" This.Y " w" This.W " h" This.H " NA", % Title
	}
	HideWindow(){
		Gui , % This.Hwnd ":Hide",
	}
	UpdateWindow( alpha := 255 ){
		UpdateLayeredWindow( This.hwnd , This.hdc , This.X , This.Y , This.W , This.H , alpha )
	}
	ClearWindow( AutoUpdate := 0 , Color := "" ){
		if( color != "" )
			Gdip_GraphicsClear( This.G , color )
		else
			Gdip_GraphicsClear( This.G )
		if( Autoupdate )
			This.UpdateWindow()
	}
	DrawBitmap( pBitmap , obj , dispose := 1 , AutoUpdate := 0 ){
		Gdip_DrawImage( This.G , pBitmap , obj.X , obj.Y , obj.W , obj.H )
		if( dispose )
			Gdip_DisposeImage( pBitmap )
		if( Autoupdate )
			This.UpdateWindow()
	}
	PaintBackground( color := "0xFF000000" , AutoUpdate := 0 ){
		if( isObject( color ) ){
			Brush := Gdip_BrushCreateSolid( ( color.HasKey( "Color" ) ) ? ( color.Color ) : ( "0xFF000000" ) ) 
			if( color.Haskey( "Round" ) )
				Gdip_FillRoundedRectangle( This.G , Brush , color.X , color.Y , color.W , color.H , color.Round )
			else
				Gdip_FillRectangle( This.G , Brush , color.X , color.Y , color.W , color.H ) 
		}else{
			Brush := Gdip_BrushCreateSolid( color ) 
			Gdip_FillRectangle( This.G , Brush , -1 , -1 , This.W + 2 , This.H + 2 ) 
		}
		Gdip_DeleteBrush( Brush )
		if( AutoUpdate )
			This.UpdateWindow()
	}
	DeleteWindow( GDIPShutdown := 0 ){
		Gui, % This.Hwnd ":Destroy"
		SelectObject( This.hdc , This.obm )
		DeleteObject( This.hbm )
		DeleteDC( This.hdc )
		Gdip_DeleteGraphics( This.G )
		hwnd := This.Hwnd
		for k, v in PopUpWindow.Windows[ Hwnd ]
			This[k] := ""
		PopUpWindow.Windows[ Hwnd ] := ""
		if( GDIPShutdown ){
			Gdip_Shutdown( PopUpWindow.pToken )
			PopUpWindow.pToken := ""
		}
	}
	_OnClose( wParam ){
		if( wParam = 0xF060 ){	;SC_CLOSE ;[ clicking on the gui close button ]
			Try{
				Gui, % PopUpWindow.HelperHwnd ":Destroy"
				SoundBeep, 555
			}
		}
	}
	CreateCachedBitmap( pBitmap , Dispose := 0 ){
		local pCachedBitmap
		if( This.CachedBitmap )
			This.DisposeCachedbitmap()
		DllCall( "gdiplus\GdipCreateCachedBitmap" , "Ptr" , pBitmap , "Ptr" , this.G , "PtrP" , pCachedBitmap )
		This.CachedBitmap := pCachedBitmap
		if( Dispose )
			Gdip_DisposeImage( pBitmap )
	}
	DrawCachedBitmap( AutoUpdate := 0 ){
		DllCall( "gdiplus\GdipDrawCachedBitmap" , "Ptr" , this.G , "Ptr" , This.CachedBitmap , "Int" , 0 , "Int" , 0 )
		if( AutoUpdate )
			This.UpdateWindow()
	}
	DisposeCachedbitmap(){
		DllCall( "gdiplus\GdipDeleteCachedBitmap" , "Ptr" , This.CachedBitmap )
	}
	Helper(){
		local hwnd , MethodList := ["__New","UpdateSettings","ShowWindow","HideWindow","UpdateWindow","ClearWindow","DrawBitmap","PaintBackground","DeleteWindow" , "AddTrigger" , "DrawTriggers", "CreateCachedBitmap" , "DrawCachedBitmap" , "DisposeCachedbitmap" ]
		Gui, New, +AlwaysOnTop +ToolWindow +HwndHwnd
		PopUpWindow.HelperHwnd := hwnd
		Gui, Add, Edit, xm ym w250 r1 Center hwndhwnd, Gui1
		PopUpWindow.EditHwnd := hwnd
		loop, % MethodList.Length()	
			Gui, Add, Button, xm y+1 w250 r1 gPopUpWindow._HelperClip, % MethodList[ A_Index ]
		Gui, Show,,
		OnMessage( 0x112 , This._OnClose.Bind( hwnd ) )
	}
	_HelperClip(){
		local ClipList 
		
		GuiControlGet, out, % PopUpWindow.HelperHwnd ":", % PopUpWindow.EditHwnd	
		
		ClipList := 		{ 	__New: 					" := New PopUpWindow( { AutoShow: 1 , X: 0 , Y: 0 , W: A_ScreenWidth , H: A_ScreenHeight , Options: "" -DPIScale +AlwaysOnTop "" } )"
							,	UpdateSettings:			".UpdateSettings( { X: """" , Y: """" , W: """" , H: """" } , UpdateGraphics := 0 )"
							,	ShowWindow:				".ShowWindow( Title := """" )"
							,	HideWindow:				".HideWindow()"
							,	UpdateWindow:			".UpdateWindow()"
							,	ClearWindow:			".ClearWindow( AutoUpdate := 0 )"
							,	DrawBitmap:				".DrawBitmap( pBitmap := """" , { X: 0 , Y: 0 , W: " Out ".W , H: " Out ".H } , dispose := 1 , AutoUpdate := 0 )"
							,	PaintBackground:		".PaintBackground( color := ""0xFF000000"" , AutoUpdate := 0 )  "  ";{ Color: ""0xFF000000"" , X: 2 , Y: 2 , W: " Out ".W - 4 , H: " Out ".H - 4 , Round: 10 }"
							,	DeleteWindow:			".DeleteWindow( GDIPShutdown := 0 )"
							,	AddTrigger:				".AddTrigger( { X: """" , Y: """" , W: """" , H: """" , Value: """" , Label: """" } )"	
							,	DrawTriggers:			".DrawTriggers( color := ""0xFFFF0000"" , AutoUpdate := 0 )"	
							,	CreateCachedBitmap:		".CreateCachedBitmap( pBitmap , Dispose := 0 )"	
							,	DrawCachedBitmap: 		".DrawCachedBitmap( AutoUpdate := 0 )"	
							,	DisposeCachedbitmap:	".DisposeCachedbitmap()"	}
							
		clipboard := Out ClipList[ A_GuiControl ]
		
	}
}
This second example uses the newer version of the class but is a bit more complicated (has more features)

Code: Select all

;****************************************************************************************************************************************************************************
#Include <GDIP_ALL> ;GDIP:  https://www.autohotkey.com/boards/viewtopic.php?f=6&t=6517
;~ #Include <PopUpWindow_V2> ; At the bottom of the script 
;~ #Include <HB Vectors v2>  ; At the bottom of the script
;****************************************************************************************************************************************************************************
#SingleInstance, Force
SetBatchLines, -1
Gdip_Startup()
global gdiplus := LoadLibrary("gdiplus")

;~ Start := A_TickCount
global Gui1 := New PopUpWindow( { AutoShow: 1 , X: 0 , Y: 130 , W: A_ScreenWidth , H: A_ScreenHeight , Options: " -DPIScale +AlwaysOnTop +OwnDialogs" } )

;Create the points map for some text
MyText := new BubbleText( { Text: "##~AutoHotkey~##" , FontSize: 66 , FontOptions: "  Center vCenter " , FontType: "Segoe UI" , FontColor: "0xFF22262a" } )

;extra params ( all that is normally required is a set of color / radius pairs. [ see ColorsArray and SizesArray ] )
UseGradient := 1
GradientColor := "0x33ffffff" 
LinearGradientMode := 2
SwapColors := 0
UseStartText := 1

AngleLineProjection := 1
AngleLineX := -10
AngleLineY := -10
AngleLineThickness := 2
AngleLineColor := "0x62777777"
AngleSize := 3

;Render the points map. ( get a bitmap of the rendered text ) 
MyTextRenders := []
MyTextRenders.Push( MyText.RenderString( ColorsArray  := [ "0xFFEC999F" , "0xFF2C1020" , "0xFF4E1C4F" , "0xFF722A80" , "0xFF8E2B64" , "0xFF922D4B" , "0xFFA8334B" , "0xFFB93D4D" , "0xFFE16267" , "0xFFE16269" , "0xFFF6777C" , "0xFFEC999F" ] , SizesArray := [ 43 , 40 , 34 , 30 , 26 , 20 , 15 , 10 , 7 , 4 , 2 ] , UseGradient , GradientColor , LinearGradientMode , SwapColors , UseStartText ) ) ;, AngleLineProjection , AngleLineX , AngleLineY , AngleLineThickness , AngleLineColor , AngleSize ) )
MyTextRenders.Push( MyText.RenderString(  "0xFFFFFFFF" , , UseGradient , GradientColor , LinearGradientMode , SwapColors , UseStartText , AngleLineProjection , AngleLineX , AngleLineY , AngleLineThickness , AngleLineColor , AngleSize ) )
MyTextRenders.Push( MyText.RenderString( ColorsArray   := [ "0xFF8FCAE6" , "0xFF229EBD" , "0xFF023048" , "0xFFFEB704" , "0xFFFEB706" ] , SizesArray := [ 18 , 14 , 10 , 5 , 2 ] , UseGradient , GradientColor , LinearGradientMode , SwapColors , UseStartText ) )
MyTextRenders.Push( MyText.RenderString( [ "0x2200FFFF" , "0x66002222" , "0x66005555" , "0x66008888" , "0x6600aaaa" ] , [ 24 , 20 , 16 , 8 , 4 ] , UseGradient , GradientColor , LinearGradientMode , SwapColors , UseStartText ) )

AngleLineX := +5
AngleLineY := +5
MyTextRenders.Push( MyText.RenderString( "0xFF88aa00"  ,  , UseGradient , GradientColor , LinearGradientMode , SwapColors , UseStartText , AngleLineProjection , AngleLineX , AngleLineY , AngleLineThickness , AngleLineColor , AngleSize ) )

;Draw the rendered text bitmaps.
;There is one bitmap in the class that would need to be deleted for full clean up. ( This.SetupBitmaps[ 3 ].Bitmap )
;That is on top of the bitmap being drawn here in this next step.
Loop, % MyTextRenders.Length()	{
	if( A_Index = 1 )
		Gui1.DrawBitmap( MyTextRenders[ A_Index ].pBitmap , { X: x := 0 , Y: y := 0 , W: MyTextRenders[ A_Index ].W , H: MyTextRenders[ A_Index ].H } , dispose := 0 , AutoUpdate := 0 )
	else if( A_Index = 5 )
		Gui1.DrawBitmap( MyTextRenders[ A_Index ].pBitmap , { X: x += MyTextRenders[ 1 ].W , Y: y := 0 , W: MyTextRenders[ A_Index ].W , H: MyTextRenders[ A_Index ].H } , dispose := 0 , AutoUpdate := 0 )
	else	
		Gui1.DrawBitmap( MyTextRenders[ A_Index ].pBitmap , { X: x , Y: y += MyTextRenders[ A_Index - 1 ].H * .9 , W: MyTextRenders[ A_Index ].W , H: MyTextRenders[ A_Index ].H } , dispose := 0 , AutoUpdate := 0 )
}


;Show the updated graphics.
Gui1.UpdateWindow()

;~ ToolTip, % ( A_TickCount - Start ) / 1000
;~ sleep, 1000
;~ ToolTip, 

return

;~ GuiClose:
GuiContextMenu:
*ESC::ExitApp

;************
;BubbleText Class
;**************************************************************************************************************************************************************************
;00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 
;00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 
;00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 
;**************************************************************************************************************************************************************************
class BubbleText	{
	__New( obj := "" ){
		
		This._SetDefaults()
		This._UpdateDefaults( obj )
		This._GetTextSizeBitmap()
		This._GetAbsoluteTextSizeBitmap()
		This._MapVectors()
		
	}
	
	_SetDefaults(){
		
		This.Ptr := A_PtrSize ? "UPtr" : "UInt"
		
		This.Text := "AutoHotkey"
		This.FontType := "Comic Sans MS"
		This.FontSize := 36
		This.FontOptions := " Center vCenter Bold "
		This.FontColor := "0xFF000000"
		
		This.SetupBitmaps := []
		This.GetTextSizeArray := []
		
		This.W := 10
		This.H := 10
		This.Left := 0
		This.Right := 0
		This.Top := 0
		This.Bottom := 0
		This.Margin := 1
		
		This.Points := []
		This.BorderVectors := []
		
		This.ColorsArray := []
		This.SizesArray := []
		This.MaxSize := 0
		
		This.Layers := []
		
	}
	
	_UpdateDefaults( obj := "" ){
		local k , v 
		for k , v in obj 
			This[ k ] := v
	}
	
	_GetTextSizeBitmap(){ 
		
		This.SetupBitmaps[ 1 ] 				:= { Bitmap: "" , Graphics: "" , Smoothing: 2 , Brush: "" }
		This.SetupBitmaps[ 1 ].Bitmap 		:= Gdip_CreateBitmap( This.W , This.H ) 
		This.SetupBitmaps[ 1 ].Graphics 	:= Gdip_GraphicsFromImage( This.SetupBitmaps[ 1 ].Bitmap ) 
		This.SetupBitmaps[ 1 ].Brush 		:= Gdip_BrushCreateSolid( This.FontColor )
		
		Gdip_SetSmoothingMode( This.SetupBitmaps[ 1 ].Graphics , This.SetupBitmaps[ 1 ].Smoothing )
		This.GetTextSizeArray := StrSplit( Gdip_TextToGraphics( This.SetupBitmaps[ 1 ].Graphics , This.Text , " s" This.Fontsize " c" This.SetupBitmaps[ 1 ].Brush  " " This.FontOptions " x" 0 " y" 0 , This.FontType , 10000, 10000  ),"|","|"  )
		
		This.W := This.GetTextSizeArray[ 3 ]
		This.H := This.GetTextSizeArray[ 4 ]
		
		
		This.SetupBitmaps[ 2 ] 				:= { Bitmap: "" , Graphics: "" , Smoothing: 2 , Brush: "" }
		This.SetupBitmaps[ 2 ].Bitmap 		:= Gdip_CreateBitmap( This.W , This.H ) 
		This.SetupBitmaps[ 2 ].Graphics 	:= Gdip_GraphicsFromImage( This.SetupBitmaps[ 2 ].Bitmap ) 
		This.SetupBitmaps[ 2 ].Brush 		:= Gdip_BrushCreateSolid( This.FontColor )
		
		Gdip_SetSmoothingMode( This.SetupBitmaps[ 2 ].Graphics , This.SetupBitmaps[ 2 ].Smoothing )
		Gdip_TextToGraphics( This.SetupBitmaps[ 2 ].Graphics , This.Text , " s" This.Fontsize " c" This.SetupBitmaps[ 2 ].Brush " " This.FontOptions " x" 0 " y" 0 , This.FontType , This.W , This.H  )
	
	}
	_GetAbsoluteTextSizeBitmap(){
		
		local color , x , y 
		
		;Get Top
		;-----------------------------
		y := 1
		Loop, % This.H	{
			x := 1
			Loop, % This.W	{
				DllCall( gdiplus.GdipBitmapGetPixel , This.Ptr , This.SetupBitmaps[ 2 ].Bitmap , "int", x, "int", y, "uint*", color )
				if( color ){
					This.Top := y - This.Margin
					break 2
				}
				x++
			}
			y++
		}
		
		;Get Bottom
		;-----------------------------
		y := Floor( This.H )
		Loop, % This.H	{
			x := 1
			Loop, % This.W	{
				DllCall( gdiplus.GdipBitmapGetPixel , This.Ptr , This.SetupBitmaps[ 2 ].Bitmap , "int", x, "int", y, "uint*", color )
				if( color ){
					This.Bottom := y + This.Margin
					break 2
				}
				x++
			}
			y--
		}
		
		;Get Left
		;-----------------------------
		x := 1
		Loop, % This.W	{
			y := 1
			Loop, % This.H	{
				DllCall( gdiplus.GdipBitmapGetPixel , This.Ptr , This.SetupBitmaps[ 2 ].Bitmap , "int", x, "int", y, "uint*", color )
				if( color ){
					This.Left := x - This.Margin - 2
					break 2
				}
				y++
			}
			x++
		}
		
		;Get Right
		;-----------------------------
		x := Floor( This.W )
		Loop, % This.W	{
			y := 1
			Loop, % This.H	{
				DllCall( gdiplus.GdipBitmapGetPixel , This.Ptr , This.SetupBitmaps[ 2 ].Bitmap , "int", x, "int", y, "uint*", color )
				if( color ){
					This.Right := x + This.Margin
					break 2
				}
				y++
			}
			x--
		}
		
		This.SetupBitmaps[ 3 ] 				:= { Bitmap: "" , Graphics: "" , Smoothing: 2 , Brush: "" }
		This.SetupBitmaps[ 3 ].Bitmap 		:= Gdip_CreateBitmap( This.Right - This.Left , This.Bottom - This.Top ) 
		This.SetupBitmaps[ 3 ].Graphics 	:= Gdip_GraphicsFromImage( This.SetupBitmaps[ 3 ].Bitmap ) 
		This.SetupBitmaps[ 3 ].Brush 		:= Gdip_BrushCreateSolid( This.FontColor )
		
		Gdip_SetSmoothingMode( This.SetupBitmaps[ 3 ].Graphics , This.SetupBitmaps[ 3 ].Smoothing )
		Gdip_TextToGraphics( This.SetupBitmaps[ 3 ].Graphics , This.Text , " s" This.Fontsize " c" This.SetupBitmaps[ 3 ].Brush " " This.FontOptions " x" -This.Left " y" -This.Top , This.FontType , This.W , This.H  )
		This.W := This.Right - This.Left
		This.H := This.Bottom - This.Top
		
	}
	
	_MapVectors(){
		
		local x , y 
		
		y := 1
		
		Loop, % This.H	{
			
			This.Points[ Index := A_Index ] := []
			x := 1
			
			Loop, % This.W	{
				
				DllCall( gdiplus.GdipBitmapGetPixel , This.Ptr , This.SetupBitmaps[ 3 ].Bitmap , "int", x, "int", y, "uint*", color )
				( color ) ? ( This.Points[ Index ].Push( 1 ) ) : ( This.Points[ Index ].Push( 0 ) )
				x++
			}
			y++
		}
		;**********************************************************
		;**********************************************************
		y := 1
		
		Loop, % This.H	{
			
			Points[ Index := A_Index ] := []
			x := 1
			
			Loop, % This.W  {
				
				if( 		This.Points[ Index , A_Index ] 
					&& (	This.Points[ Index - 1 , A_Index ] = 0 
					|| 		This.Points[ Index + 1 , A_Index ] = 0 
					|| 		This.Points[ Index , A_Index - 1 ] = 0 
					|| 		This.Points[ Index , A_Index + 1 ] = 0 ) 
					&& 		x <= This.Right 
					&& 		y <= This.Bottom - 1 ){
					
					This.BorderVectors.Push( New Vector( x , y ) )
	
				}
				x++
			}
			y++
		}
		
		Loop, 2	{
			
			Gdip_DisposeImage( This.SetupBitmaps[ A_Index ].Bitmap ) 
			Gdip_DeleteGraphics( This.SetupBitmaps[ A_Index ].Graphics )
			Gdip_DeleteBrush( This.SetupBitmaps[ A_Index ].Brush )
			
		}
		
	}
	
	RenderString( ColorsArray := "" , SizesArray := "" , UseGradient := 0 , GradientColor := "0xFF000000" , LinearGradientMode := 1 , SwapColors := 0 , UseStartText := 1 , AngleLineProjection := 0 , AngleLineX := 10 , AngleLineY := 40 , AngleLineThickness := 6 , AngleLineColor := "0xFF000000" , AngleSize := 3 ){
		
		local removeAmount

		This.ColorsArray := []
		This.SizesArray := []
		
		if( ColorsArray != "" && !IsObject( ColorsArray ) ){
			
			This.ColorsArray[ 1 ] := ColorsArray
			
		}else if( IsObject( ColorsArray ) ){
			
			loop, % ColorsArray.Length()	{
				
				This.ColorsArray[ A_Index ] := ColorsArray[ A_Index ]
				
			}
			
		}else{
			
			This.ColorsArray[ 1 ] := "0xFF000000"
			
		}
		
		if( SizesArray != "" && !IsObject( SizesArray ) ){
			
			This.SizesArray[ 1 ] := SizesArray
			
		}else if( IsObject( SizesArray ) ){
			
			loop, % SizesArray.Length()	{
				
				This.SizesArray[ A_Index ] := SizesArray[ A_Index ]
				
			}
			
		}else{
			
			This.SizesArray[ 1 ] := 6
		}
		
		if( This.SizesArray.Length() != This.ColorsArray.Length() ){
			
			if( This.SizesArray.Length() < This.ColorsArray.Length() ){
				
				Loop, % This.ColorsArray.Length() - This.SizesArray.Length() {
					This.ColorsArray.Pop()
					
				}
				
			}else{
				
				Loop, % This.SizesArray.Length() - This.ColorsArray.Length()	{
					This.SizesArray.Pop()
				
				}
				
			}
		}
		
		loop, % This.SizesArray.Length()	{
			
			This.Layers[ A_Index ] 				:= { Bitmap: "" , Graphics: "" , Smoothing: 2 , Brush: "" }
			This.Layers[ A_Index ].Bitmap 		:= Gdip_CreateBitmap( This.W + ( This.SizesArray[ 1 ] * 2 ) , This.H + ( This.SizesArray[ 1 ] * 2 ) ) 
			This.Layers[ A_Index ].Graphics 	:= Gdip_GraphicsFromImage( This.Layers[ A_Index ].Bitmap ) 
			
			if( UseGradient ){
				
				This.Layers[ A_Index ].Brush 	:= Gdip_CreateLineBrushFromRect( 0 , 0 , This.W + ( This.SizesArray[ 1 ] * 2 ) , This.H + ( This.SizesArray[ 1 ] * 2 ) , ( !SwapColors ) ? ( This.ColorsArray[ A_Index ] ) : ( GradientColor ) , ( SwapColors ) ? ( This.ColorsArray[ A_Index ] ) : ( GradientColor ) , LinearGradientMode , 1 )
			
			}else{
				
				This.Layers[ A_Index ].Brush 	:= Gdip_BrushCreateSolid( This.ColorsArray[ A_Index ] )
				
			}
			Gdip_SetSmoothingMode( This.Layers[ A_Index ].Graphics , This.Layers[ A_Index ].Smoothing )
		}
		
		if( AngleLineProjection ){
			
			Pen := Gdip_CreatePen( AngleLineColor , AngleLineThickness )
			
			if( UseStartText ){
				
				Gdip_DrawImage( This.Layers[ 1 ].Graphics , This.SetupBitmaps[ 3 ].Bitmap , This.SizesArray[ 1 ] , This.SizesArray[ 1 ] , This.W , This.H )
			
			}
			Loop, % This.BorderVectors.Length()	{
				
				Gdip_DrawLine( This.Layers[ 1 ].Graphics , Pen , This.BorderVectors[ A_Index ].X + This.SizesArray[ 1 ] , This.BorderVectors[ A_Index ].Y + This.SizesArray[ 1 ] , This.BorderVectors[ A_Index ].X + AngleLineX + This.SizesArray[ 1 ] , This.BorderVectors[ A_Index ].Y + AngleLineY + This.SizesArray[ 1 ] )
					
			}
			
			Gdip_DeletePen( Pen )
			
			Loop, % This.BorderVectors.Length()	{
				
				DllCall( gdiplus.GdipFillEllipse , This.Ptr , This.Layers[ 1 ].Graphics , This.Ptr , This.Layers[ 1 ].Brush , "float", This.BorderVectors[ A_Index ].X + AngleLineX + This.SizesArray[ 1 ] , "float", This.BorderVectors[ A_Index ].Y + AngleLineY + This.SizesArray[ 1 ] , "float", AngleSize , "float", AngleSize )
					
			}
			
			
		}else{
			
			Loop, % This.BorderVectors.Length()	{
				Index := A_Index
			
				loop, % This.SizesArray.Length()	{
					
					DllCall( gdiplus.GdipFillEllipse , This.Ptr , This.Layers[ A_Index ].Graphics , This.Ptr , This.Layers[ A_Index ].Brush , "float", This.BorderVectors[ Index ].X + ( This.SizesArray[ 1 ] - This.SizesArray[ A_Index ] ) , "float", This.BorderVectors[ Index ].Y + ( This.SizesArray[ 1 ] - This.SizesArray[ A_Index ] ) , "float", This.SizesArray[ A_Index ] * 2 , "float", This.SizesArray[ A_Index ] * 2 )
							
				}
			}
		}
		
		
		Output					:= { pBitmap: "" , Graphics: "" , Smoothing: 2 , W: This.W + ( This.SizesArray[ 1 ] * 2 ) , H: This.H + ( This.SizesArray[ 1 ] * 2 ) }
		Output.pBitmap			:= Gdip_CreateBitmap( Output.W , Output.H ) 
		Output.Graphics			:= Gdip_GraphicsFromImage( Output.pBitmap ) 
		Gdip_SetSmoothingMode( Output.Graphics , Output.Smoothing )
		
		Loop, % This.SizesArray.Length()	{
			
			Gdip_DrawImage( Output.Graphics , This.Layers[ A_Index ].Bitmap , 0 , 0 , Output.W , Output.H )
			Gdip_DisposeImage( This.Layers[ A_Index ].Bitmap ) 
			Gdip_DeleteGraphics( This.Layers[ A_Index ].Graphics )
			Gdip_DeleteBrush( This.Layers[ A_Index ].Brush )
			
		}
		
		if( UseStartText && !AngleLineProjection ){
			
			Gdip_DrawImage( Output.Graphics , This.SetupBitmaps[ 3 ].Bitmap , This.SizesArray[ 1 ] , This.SizesArray[ 1 ] , This.W , This.H )
		}
		
		Gdip_DeleteGraphics( Output.Graphics )
		return output
		
	}
	
}


LoadLibrary(filename){ ;https://www.autohotkey.com/boards/viewtopic.php?p=48392#p48392
    static ref := {}
    if (!(ptr := p := DllCall("LoadLibrary", "str", filename, "ptr")))
        return 0
    ref[ptr,"count"] := (ref[ptr]) ? ref[ptr,"count"]+1 : 1
    p += NumGet(p+0, 0x3c, "int")+24
    o := {_ptr:ptr, __delete:func("FreeLibrary"), _ref:ref[ptr]}
    if (NumGet(p+0, (A_PtrSize=4) ? 92 : 108, "uint")<1 || (ts := NumGet(p+0, (A_PtrSize=4) ? 96 : 112, "uint")+ptr)=ptr || (te := NumGet(p+0, (A_PtrSize=4) ? 100 : 116, "uint")+ts)=ts)
        return o
    n := ptr+NumGet(ts+0, 32, "uint")
    loop % NumGet(ts+0, 24, "uint"){
        if (p := NumGet(n+0, (A_Index-1)*4, "uint")){
            o[f := StrGet(ptr+p, "cp0")] := DllCall("GetProcAddress", "ptr", ptr, "astr", f, "ptr")
            if (Substr(f, 0)==((A_IsUnicode) ? "W" : "A"))
                o[Substr(f, 1, -1)] := o[f]
        }
    }
    return o
}

FreeLibrary(lib){
    if (lib._ref.count>=1)
        lib._ref.count -= 1
    if (lib._ref.count<1)
        DllCall("FreeLibrary", "ptr", lib._ptr)
}
;####################################################################################################################################################################################
;####################################################################################################################################################################################
;####################################################################################################################################################################################
;####################################################################################################################################################################################
class PopUpWindow	{
;PopUpWindow v2.2
;Date Written: Oct 28th, 2021
;Last Edit: Feb 7th, 2022 :Changed the trigger method.
;Written By: Hellbent aka CivReborn
;SpcThanks: teadrinker , malcev 
	static Index := 0 , Windows := [] , Handles := [] , EditHwnd , HelperHwnd
	__New( obj := "" ){
		This._SetDefaults()
		This.UpdateSettings( obj )
		This._CreateWindow()
		This._CreateWindowGraphics()
		if( This.AutoShow )
			This.ShowWindow( This.Title )
	}
	_SetDefaults(){
		This.X := 10
		This.Y := 10
		This.W := 10
		This.H := 10
		This.Smoothing := 2
		This.Options := " -DPIScale +AlwaysOnTop "
		This.AutoShow := 0
		This.GdipStartUp := 0
		This.Title := ""
		
		This.Controls := []
		This.Handles := []
		This.Index := 0 
	}
	AddTrigger( obj ){
		local k , v , cc , bd
		
		This.Controls[ ++This.Index ] := { 	X:		10
										,	Y:		10
										,	W:		10
										,	H:		10	}
		for k, v in obj
			This.Controls[ This.Index ][ k ] := obj[ k ] 
		cc := This.Controls[ This.Index ]
		Gui, % This.Hwnd ":Add", Text, % "x" cc.X " y" cc.Y " w" cc.W " h" cc.H " hwndhwnd"
		This.Handles[ hwnd ] := This.Index
		This.Controls[ This.Index ].Hwnd := hwnd
		
		if( IsObject( cc.Label ) ){
			bd := cc.Label
			GuiControl, % This.Hwnd ":+G" , % hwnd , % bd
		}else{
			bd := This._TriggerCall.Bind( This )
			GuiControl, % This.Hwnd ":+G" , % hwnd , % bd
		}
		return hwnd
		
	}
	_TriggerCall(){
		MouseGetPos,,,, ctrl, 2
		Try
			;~ SetTimer, % This.Controls[ This.Handles[ ctrl ] ].Label, -0
			gosub, % This.Controls[ This.Handles[ ctrl ] ].Label
		
				
	}
	DrawTriggers( color := "0xFFFF0000" , AutoUpdate := 0 ){
		local brush , cc 
		Brush := Gdip_BrushCreateSolid( color ) 
		Gdip_SetSmoothingMode( This.G , 3 )
		loop, % This.Controls.Length()	{
			cc := This.Controls[ A_Index ]
			Gdip_FillRectangle( This.G , Brush , cc.x , cc.y , cc.w , cc.h )
		
		}
		Gdip_DeleteBrush( Brush )
		Gdip_SetSmoothingMode( This.G , This.Smoothing )
		if( AutoUpdate )
			This.UpdateWindow()
	}
	UpdateSettings( obj := "" , UpdateGraphics := 0 ){
		local k , v
		if( IsObject( obj ) )
			for k, v in obj
				This[ k ] := obj[ k ]
		( This.X = "Center" ) ? ( This.X := ( A_ScreenWidth - This.W ) / 2 ) 	
		( This.Y = "Center" ) ? ( This.Y := ( A_ScreenHeight - This.H ) / 2 ) 	
		if( UpdateGraphics ){
			This._DestroyWindowsGraphics()
			This._CreateWindowGraphics()
		}
	}
	_CreateWindow(){
		local hwnd
		Gui , New, % " +LastFound +E0x80000 hwndhwnd -Caption  " This.Options
		PopUpWindow.Index++
		This.Index := PopUpWindow.Index
		PopUpWindow.Windows[ PopUpWindow.Index ] := This
		This.Hwnd := hwnd
		PopUpWindow.Handles[ hwnd ] := PopUpWindow.Index
		if( This.GdipStartUp && !PopUpWindow.pToken )
			PopUpWindow.pToken := GDIP_STARTUP()
	}
	_DestroyWindowsGraphics(){
		Gdip_DeleteGraphics( This.G )
		SelectObject( This.hdc , This.obm )
		DeleteObject( This.hbm )
		DeleteDC( This.hdc )
	}
	_CreateWindowGraphics(){
		This.hbm := CreateDIBSection( This.W , This.H )
		This.hdc := CreateCompatibleDC()
		This.obm := SelectObject( This.hdc , This.hbm )
		This.G := Gdip_GraphicsFromHDC( This.hdc )
		Gdip_SetSmoothingMode( This.G , This.Smoothing )
	}
	ShowWindow( Title := "" ){
		Gui , % This.Hwnd ":Show", % "x" This.X " y" This.Y " w" This.W " h" This.H " NA", % Title
	}
	HideWindow(){
		Gui , % This.Hwnd ":Hide",
	}
	UpdateWindow( alpha := 255 ){
		UpdateLayeredWindow( This.hwnd , This.hdc , This.X , This.Y , This.W , This.H , alpha )
	}
	ClearWindow( AutoUpdate := 0 ){
		Gdip_GraphicsClear( This.G )
		if( Autoupdate )
			This.UpdateWindow()
	}
	DrawBitmap( pBitmap , obj , dispose := 1 , AutoUpdate := 0 ){
		Gdip_DrawImage( This.G , pBitmap , obj.X , obj.Y , obj.W , obj.H )
		if( dispose )
			Gdip_DisposeImage( pBitmap )
		if( Autoupdate )
			This.UpdateWindow()
	}
	PaintBackground( color := "0xFF000000" , AutoUpdate := 0 ){
		if( isObject( color ) ){
			Brush := Gdip_BrushCreateSolid( ( color.HasKey( "Color" ) ) ? ( color.Color ) : ( "0xFF000000" ) ) 
			if( color.Haskey( "Round" ) )
				Gdip_FillRoundedRectangle( This.G , Brush , color.X , color.Y , color.W , color.H , color.Round )
			else
				Gdip_FillRectangle( This.G , Brush , color.X , color.Y , color.W , color.H ) 
		}else{
			Brush := Gdip_BrushCreateSolid( color ) 
			Gdip_FillRectangle( This.G , Brush , -1 , -1 , This.W + 2 , This.H + 2 ) 
		}
		Gdip_DeleteBrush( Brush )
		if( AutoUpdate )
			This.UpdateWindow()
	}
	DeleteWindow( GDIPShutdown := 0 ){
		Gui, % This.Hwnd ":Destroy"
		SelectObject( This.hdc , This.obm )
		DeleteObject( This.hbm )
		DeleteDC( This.hdc )
		Gdip_DeleteGraphics( This.G )
		hwnd := This.Hwnd
		for k, v in PopUpWindow.Windows[ Hwnd ]
			This[k] := ""
		PopUpWindow.Windows[ Hwnd ] := ""
		if( GDIPShutdown ){
			Gdip_Shutdown( PopUpWindow.pToken )
			PopUpWindow.pToken := ""
		}
	}
	_OnClose( wParam ){
		if( wParam = 0xF060 ){	;SC_CLOSE ;[ clicking on the gui close button ]
			Try{
				Gui, % PopUpWindow.HelperHwnd ":Destroy"
				SoundBeep, 555
			}
		}
	}
	CreateCachedBitmap( pBitmap , Dispose := 0 ){
		local pCachedBitmap
		if( This.CachedBitmap )
			This.DisposeCachedbitmap()
		DllCall( "gdiplus\GdipCreateCachedBitmap" , "Ptr" , pBitmap , "Ptr" , this.G , "PtrP" , pCachedBitmap )
		This.CachedBitmap := pCachedBitmap
		if( Dispose )
			Gdip_DisposeImage( pBitmap )
	}
	DrawCachedBitmap( AutoUpdate := 0 ){
		DllCall( "gdiplus\GdipDrawCachedBitmap" , "Ptr" , this.G , "Ptr" , This.CachedBitmap , "Int" , 0 , "Int" , 0 )
		if( AutoUpdate )
			This.UpdateWindow()
	}
	DisposeCachedbitmap(){
		DllCall( "gdiplus\GdipDeleteCachedBitmap" , "Ptr" , This.CachedBitmap )
	}
	Helper(){
		local hwnd , MethodList := ["__New","UpdateSettings","ShowWindow","HideWindow","UpdateWindow","ClearWindow","DrawBitmap","PaintBackground","DeleteWindow" , "AddTrigger" , "DrawTriggers", "CreateCachedBitmap" , "DrawCachedBitmap" , "DisposeCachedbitmap" ]
		Gui, New, +AlwaysOnTop +ToolWindow +HwndHwnd
		PopUpWindow.HelperHwnd := hwnd
		Gui, Add, Edit, xm ym w250 r1 Center hwndhwnd, Gui1
		PopUpWindow.EditHwnd := hwnd
		loop, % MethodList.Length()	
			Gui, Add, Button, xm y+1 w250 r1 gPopUpWindow._HelperClip, % MethodList[ A_Index ]
		Gui, Show,,
		OnMessage( 0x112 , This._OnClose.Bind( hwnd ) )
	}
	_HelperClip(){
		local ClipList 
		
		GuiControlGet, out, % PopUpWindow.HelperHwnd ":", % PopUpWindow.EditHwnd	
		
		ClipList := 		{ 	__New: 					" := New PopUpWindow( { AutoShow: 1 , X: 0 , Y: 0 , W: A_ScreenWidth , H: A_ScreenHeight , Options: "" -DPIScale +AlwaysOnTop "" } )"
							,	UpdateSettings:			".UpdateSettings( { X: """" , Y: """" , W: """" , H: """" } , UpdateGraphics := 0 )"
							,	ShowWindow:				".ShowWindow( Title := """" )"
							,	HideWindow:				".HideWindow()"
							,	UpdateWindow:			".UpdateWindow()"
							,	ClearWindow:			".ClearWindow( AutoUpdate := 0 )"
							,	DrawBitmap:				".DrawBitmap( pBitmap := """" , { X: 0 , Y: 0 , W: " Out ".W , H: " Out ".H } , dispose := 1 , AutoUpdate := 0 )"
							,	PaintBackground:		".PaintBackground( color := ""0xFF000000"" , AutoUpdate := 0 )  "  ";{ Color: ""0xFF000000"" , X: 2 , Y: 2 , W: " Out ".W - 4 , H: " Out ".H - 4 , Round: 10 }"
							,	DeleteWindow:			".DeleteWindow( GDIPShutdown := 0 )"
							,	AddTrigger:				".AddTrigger( { X: """" , Y: """" , W: """" , H: """" , Value: """" , Label: """" } )"	
							,	DrawTriggers:			".DrawTriggers( color := ""0xFFFF0000"" , AutoUpdate := 0 )"	
							,	CreateCachedBitmap:		".CreateCachedBitmap( pBitmap , Dispose := 0 )"	
							,	DrawCachedBitmap: 		".DrawCachedBitmap( AutoUpdate := 0 )"	
							,	DisposeCachedbitmap:	".DisposeCachedbitmap()"	}
							
		clipboard := Out ClipList[ A_GuiControl ]
		
	}
}
;************
;Vector Class
;**************************************************************************************************************************************************************************
;00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 
;00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 
;00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 
;**************************************************************************************************************************************************************************
Class Vector	{
	;Written By: HB
	;Date: Sept 23rd, 2022
	;Last Edit: Sept 24th, 2022
	;Purpose: Vector math class 
	;Credit: Rohwedder 
	;Resources: 
		;Line intercept concepts and code: https://www.autohotkey.com/boards/viewtopic.php?f=76&t=37175
		;Create an Arrow: https://www.autohotkey.com/boards/viewtopic.php?f=76&t=92039&p=479129#p478944
		;Getting an angle: https://www.autohotkey.com/boards/viewtopic.php?f=76&t=108760&p=483661#p483678
		;Setting an Angle: https://www.autohotkey.com/boards/viewtopic.php?f=76&t=108760&p=483786#p483811
		;
		
	static RadToDeg := 45 / ATan( 1 ) 
		, DegToRad := ATan( 1 ) / 45 
		
	__New( x := 0 , y := 0 , rotate := 0 ){ 
		if( IsObject( x ) ){
			if( rotate = 3 ){
				This.X := x.X * -1
				,This.Y := x.Y * -1
			}else if( rotate = 2 ){
				This.X := x.Y 
				,This.Y := x.X * -1
			}else if( rotate = 1 ){
				This.X := x.Y * -1
				,This.Y := x.X 
			}else{
				This.X := x.X
				,This.Y := x.Y
			}
		}else{
			if( rotate = 3 ){
				This.X := X * -1
				,This.Y := Y * -1
			}else if( rotate = 2 ){
				This.X := Y 
				,This.Y := X * -1
			}else if( rotate = 1 ){
				This.X := Y * -1
				,This.Y := X 
			}else{
				This.X := X
				,This.Y := Y
			}
		}
	}
	Add( x , y := "" ){
		if( IsObject( x ) ){
			This.X += x.X
			,This.Y += x.Y
		}else if( y = "" ){
			This.X += x 
			,This.Y += x
		}else{
			This.X += x 
			,This.Y += y 
		}
	}
	Sub( x , y := "" ){
		if( IsObject( x ) ){
			This.X -= x.X
			,This.Y -= x.Y
		}else if( y = "" ){
			This.X -= X
			,This.Y -= X
		}else{
			This.X -= X
			,This.Y -= Y
		}
	}
	Div( x , y := "" ){
		if( IsObject( x ) ){
			This.X /= x.X
			,This.Y /= x.Y
		}else if( x && y = "" ){
			This.X /= x 
			,This.Y /= x 
		}else{
			This.X /= X
			,This.Y /= Y
		}
	}
	Mult( x , y := "" ){
		if( IsObject( x ) ){
			This.X *= x.X
			,This.Y *= x.Y
		}else if( x && y = "" ){
			This.X *= x 
			,This.Y *= x 
		}else{
			This.X *= X
			,This.Y *= Y
		}
	}
	Dist( x , y := "" ){
		if( IsObject( x ) )
			return Sqrt( ( ( This.X - x.X ) **2 ) + ( ( This.Y - x.Y ) **2 ) )
		else 
			return Sqrt( ( ( This.X - X ) **2 ) + ( ( This.Y - Y ) **2 ) )
	}
	GetMag(){
		return Sqrt( This.X * This.X + This.Y * This.Y )
	}
	SetMag( magnitude ){
		local m := This.GetMag()
		This.X := This.X * magnitude / m
		,This.Y := This.Y * magnitude / m
	}
	MagSq(){
		return This.GetMag()**2
	}	
	Dot( x , y := "" ){
		if( IsObject( x ) )
			return ( This.X * x.X ) + ( This.Y * x.Y )
		else
			return ( This.X * X ) + ( This.Y * Y )
	}
	Cross( x , y := "" ){
		if( IsObject( x ) )
			return This.X * x.Y - This.Y * x.X
		else
			return This.X * Y - This.Y * X
		
	}
	Norm(){
		local m := This.GetMag()
		This.X /= m
		This.Y /= m
	}
	GetAngle(){ 
		local angle 
		( (  angle := Vector.RadToDeg * DllCall( "msvcrt\atan2" , "Double" , This.Y , "Double" , This.X , "CDECL Double" ) ) < 0 ) ? ( angle += 360 )
		return angle
	}
	SetAngle( newAngle := 0 , NewVector := 0 ){
		local Angle := This.GetAngle()
		, ChangeAngle := newAngle - Angle 
		, Co := Cos( Vector.DegToRad * ChangeAngle )
		, Si := Sin( Vector.DegToRad * ChangeAngle )
		, X := This.X 
		, Y := This.Y
		, X2 := X * Co - Y * Si 
		, Y2 := X * Si + Y * Co 
		
		if( !NewVector )
			This.X := X2 , This.Y := Y2
		else 
			return New Vector( X2 , Y2 )
	}
	RotateAngle( rotationAmount := 90 , NewVector := 0 ){
		local Co := Cos( Vector.DegToRad * rotationAmount )
		, Si := Sin( Vector.DegToRad * rotationAmount )
		, X := This.X 
		, Y := This.Y
		, X2 := X * Co - Y * Si 
		, Y2 := X * Si + Y * Co 
		
		if( !NewVector )
			This.X := X2 , This.Y := Y2
		else 
			return New Vector( X2 , Y2 )
	}
	;********************************************
	;class methods
	TestLineInterceptPoint( interceptPoint , Line1 , Line2 ){ ; Line = { Start: { X: , Y: } , End: { X: , Y: } } , interceptPoint = { X: , Y: }
		local
		for k , v in [ "X" , "Y" ]	
			M%v%_Min := min( Line1.Start[ v ] , Line1.End[ v ] )
			,M%v%_Max := max( Line1.Start[ v ] , Line1.End[ v ] )
			,L%v%_Min := min( Line2.Start[ v ] , Line2.End[ v ] )
			,L%v%_Max := max( Line2.Start[ v ] , Line2.End[ v ] )
		if( !( interceptPoint.X < Mx_Min || interceptPoint.X > Mx_Max || interceptPoint.X < Lx_Min || interceptPoint.X > Lx_Max ) && !( interceptPoint.Y < My_Min || interceptPoint.Y > My_Max || interceptPoint.Y < Ly_Min || interceptPoint.Y > Ly_Max ) )
			return 1
		return 0
	}
	GetLineInterceptPoint( Line1 , Line2 ){ ; Line = { Start: { X: , Y: } , End: { X: , Y: } }
		local A1 := Line1.End.Y - Line1.Start.Y
		,B1 := Line1.Start.X - Line1.End.X
		,C1 := A1 * Line1.Start.X + B1 * Line1.Start.Y
		,A2 := Line2.End.Y - Line2.Start.Y
		,B2 := Line2.Start.X - Line2.End.X
		,C2 := A2 * Line2.Start.X + B2 * Line2.Start.Y
		,Denominator := A1 * B2 - A2 * B1 
		return New Vector( { X: ( ( B2 * C1 - B1 * C2 ) / Denominator )  , Y: ( ( A1 * C2 - A2 * C1 ) / Denominator ) } )
	}
	;********************************************
}
;**************************************************************************************************************************************************************************
;00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 
;00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 
;00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 00000 <<<>>> 00000 
;**************************************************************************************************************************************************************************

Use case examples:

.
Image
.

.
bubble text demo 2.gif
bubble text demo 2.gif (923.26 KiB) Viewed 164 times
.

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: fiendhunter and 44 guests