[Class] ImageButton - 1.5.00.00 - 20201230

Post your working scripts, libraries and tools for AHK v1.1 and older
just me
Posts: 9423
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: [Class] ImageButton - 1.5.00.00 - 20201230

Post by just me » 18 Jul 2021, 05:58

@jNizM, thanks for your 'Bootstrap like Buttons'. I added a link to the OP.

User avatar
oldbrother
Posts: 273
Joined: 23 Oct 2013, 05:08

Re: [Class] ImageButton - 1.5.00.00 - 20201230

Post by oldbrother » 18 Jul 2021, 07:10

@jNizM, Thanks! It seems not working with ver. 1.5.

User avatar
F4Jonatas
Posts: 45
Joined: 22 Oct 2015, 06:35
Contact:

Re: [Class] ImageButton - 1.5.00.00 - 20201230

Post by F4Jonatas » 18 Jul 2021, 07:52

Hello everyone!
I'm playing with the "just-me" libs.
First I did it for AHK v2 and I'm adding other things, for example more gradient, support gradient angle and transparency on the edges and background.

Leave a comment on what you think.
I know there must be a lot of mistakes, but you can make fun of it... :lol:

Code: Select all

/*
	Return:
		Errors:
			-1 : Invalid parameter "target".
			-2 : Invalid parameter "properties"
			-3 : GDIPlus could not be started
			-4 : This button does not have the required styles
			-5 : Couldn't get button's font
			-6 : Couldn't get button's rectangle
			-7 : Couldn't create the Gdiplus bitmap
			-8 : Couldn't get the the Gdiplus bitmap's graphics
			-9 : Could not get background from properties
			-10: Could not get color from properties
			-11: Could not get margin from properties
			-12: Could not get bordercolor from properties
			-13: Could not get borderwidth from properties

	supports only hex colors. look like css
		"linear( 45deg, 0xFF0000 0%, 0xFFFF00 25%, 0x00FF00 50%, 0x00FFFF 75%, 0x0000FF, 100% )"
		"linear( 45deg, 0xFF0000, 0xFFFF00, 0x00FF00, 0x00FFFF, 0x0000FF )"
		"#FF0000" / "#FFF" / "#FF0000FF"

	property fill:
		Optional, needed for rounded buttons if you've changed the GUI background color.
		Default: AHK default GUI background color



	button.new(
		[object/required] button gui class
		[object/required] properties
	)


	button.new( bt, {
		"normal", { ; PBS_NORMAL    = 1
			fill: "#fff"
			background: "linear( top, #3A9CFF, #61afff )",
			margin: 0,
			color: "#101010",
			bordercolor: "#000",
			borderwidth: 0
		},

		"hover", { ; PBS_HOT       = 2
			background: "#3398FF",
			margin: 0,
			color: "#101010",
			bordercolor: "#000",
			borderwidth: 0
		},

		"default", { ; PBS_DEFAULTED = 5
			background: "#3398FF",
			margin: 0,
			color: "#101010",
			bordercolor: "#000",
			borderwidth: 0
		},

		"active", { ; PBS_PRESSED   = 3
			background: "#3398FF",
			margin: 0,
			color: "#101010",
			bordercolor: "#000",
			borderwidth: 0
		},

		"disabled", { ; PBS_DISABLED  = 4
			background: "#3398FF",
			margin: 0,
			color: "#101010",
			bordercolor: "#000",
			borderwidth: 0
		}
	})
*/
class button {
	static tokengp := 0

	; Windows constants
	static BCM_GETIMAGELIST              := 0x1603
	static ILC_COLOR32                   := 0x020
	static BS_CHECKBOX                   := 0x2
	static BS_RADIOBUTTON                := 0x4
	static BS_GROUPBOX                   := 0x7
	static BS_AUTORADIOBUTTON            := 0x9
	static BS_LEFT                       := 0x100
	static BS_RIGHT                      := 0x200
	static BS_CENTER                     := 0x300
	static BS_TOP                        := 0x400
	static BS_BOTTOM                     := 0x800
	static BS_VCENTER                    := 0xC00
	static BS_BITMAP                     := 0x0080
	static SA_LEFT                       := 0x0
	static SA_CENTER                     := 0x1
	static SA_RIGHT                      := 0x2
	static WM_GETFONT                    := 0x31
	static IMAGE_BITMAP                  := 0x0
	static BITSPIXEL                     := 0xC
	static BCM_SETIMAGELIST              := 0x1602
	static BUTTON_IMAGELIST_ALIGN_LEFT   := 0
	static BUTTON_IMAGELIST_ALIGN_RIGHT  := 1
	static BUTTON_IMAGELIST_ALIGN_CENTER := 4
	static RCBUTTONS                     := (
		button.BS_CHECKBOX | button.BS_RADIOBUTTON | button.BS_AUTORADIOBUTTON
	)


	static __new() {
		; Get AHK's default GUI background color
		; COLOR_3DFACE is used by AHK as default
		local color := dllcall( "user32\GetSysColor", "int", 15, "uint" )
		this.defguicolor := (
			0xFF000000                |
			(( color >> 16 ) & 0xFF ) |
			( color & 0x00FF00 )      |
			(( color & 0xFF ) << 16 )
		)
	}



	__new( target, properties ) {
		; Check handle
		if not dllcall( "user32\IsWindow", "ptr", target.hwnd ) {
			msgbox( "Invalid parameter `"target`"!" )
			return -1
		}

		; Check Options
		if not isobject( properties ) {
			msgbox( "Invalid parameter `"properties`"!" )
			return -2
		}

		this.gamafix := 0


		this.safe := {}
		this.safe.loadedgp := false
		this.safe.modulegp := dllcall( "kernel32\GetModuleHandle", "str", "gdiplus", "uptr" )

		; If necessary, load the Library
		if this.safe.modulegp == 0
			this.safe.modulegp := dllcall( "kernel32\LoadLibrary", "str", "gdiplus.dll", "ptr" )

		; If the Library is already in use
		else
			this.safe.loadedgp := true


		if button.tokengp == 0 {
			; GdiplusStartupInput structure
			local input := bufferalloc( a_ptrsize == 8 ? 24 : 16, 0 )
			strput( chr( 1 ), input )

			local gdiptoken := 0
			dllcall( "gdiplus\GdiplusStartup", "ptrp", gdiptoken, "ptr", input.ptr, "ptr", 0 )

			button.tokengp := gdiptoken
			varsetcapacity( gdiptoken, 0 )

			; Unload class
			input := "" ; varsetcapacity( input, 0 )
		}

		if button.tokengp == 0 {
			msgbox( "GDIPlus could not be started!" )
			return -3
		}


		; Get and check control's class and styles
		this.safe.style := controlgetstyle( target.hwnd )
		if ( target.type !== "Button" ) or (( this.safe.style & 0xF ^ button.BS_GROUPBOX ) == 0 ) or (( this.safe.style & button.RCBUTTONS ) > 1 ) {
			button.gpshutdown( this )
			msgbox( "This button does not have the required styles!" )
			return -4
		}


		; Get the button's font
		local GDIPFont := 0
		local HFONT := dllcall( "user32\SendMessage", "ptr", target.hwnd, "uint", button.WM_GETFONT, "ptr", 0, "ptr", 0, "ptr" )
		local DC := dllcall( "user32\GetDC", "ptr", target.hwnd, "ptr" )
		dllcall( "gdi32\SelectObject", "ptr", DC, "ptr", HFONT )
		dllcall( "gdiplus\GdipCreateFontFromDC", "ptr", DC, "ptrp", GDIPFont )
		dllcall( "user32\ReleaseDC", "ptr", target.hwnd, "ptr", DC )

		if not GDIPFont {
			dllcall( "gdiplus\GdipDeleteFont", "ptr", GDIPFont )
			button.gpshutdown( this )
			msgbox( "Couldn't get button's font!" )
			return -5
		}

		; else
		this.font := GDIPFont
		GDIPFont := ""
		; varsetcapacity( GDIPFont, 0 )


		; Get the button's rect
		this.rect := bufferalloc( 16, 0 )
		if not dllcall( "user32\GetClientRect", "ptr", target.hwnd, "ptr", this.rect.ptr ) {
			this.rect := ""
			button.gpshutdown( this )
			msgbox( "Couldn't get button's rectangle!" )
			return -6
		}

		this.width  := numget( this.rect,  8, "int" ) - numget( this.rect, 0, "int" )
		this.height := numget( this.rect, 12, "int" ) - numget( this.rect, 4, "int" )


		; Create a GDI+ bitmap
		local PBITMAP := 0
		dllcall( "gdiplus\GdipCreateBitmapFromScan0",
			"int" , this.width,
			"int" , this.height,
			"int" , 0,
			"uint", 0x26200A,
			"ptr" , 0,
			"ptrp", PBITMAP
		)

		if not ( this.bitmap := PBITMAP ) {
			button.gpshutdown( this )
			msgbox( "Couldn't create the Gdiplus bitmap!" )
			return -7
		}


		; Get the pointer to it's graphics
		local PGRAPHICS := 0
		dllcall( "gdiplus\GdipGetImageGraphicsContext", "ptr", PBITMAP, "ptrp", PGRAPHICS )

		if not ( this.graphics := PGRAPHICS ) {
			button.gpshutdown( this )
			msgbox( "Couldn't create the Gdiplus bitmap!" )
			return -8
		}

		; Quality settings
		dllcall( "gdiplus\GdipSetSmoothingMode", "ptr", PGRAPHICS, "uint", 4 )
		; dllcall( "gdiplus\GdipSetInterpolationMode", "ptr", PGRAPHICS, "int", 7 )
		; dllcall( "gdiplus\GdipSetCompositingQuality", "ptr", PGRAPHICS, "uint", 4 )
		; dllcall( "gdiplus\GdipSetRenderingOrigin", "ptr", PGRAPHICS, "int", 0, "int", 0 )
		; dllcall( "gdiplus\GdipSetPixelOffsetMode", "ptr", PGRAPHICS, "uint", 4 )


		; Get the button's caption
		this.caption :=  target.text
		this.hwnd := target.hwnd
		this.rules := array()

		if objownpropcount( properties ) {
			this.rules.push( button.extract( properties, "normal" ))
			this.rules.push( button.extract( properties.hasownprop( "hover" )    ? properties.hover    : properties, "hover" ))
			this.rules.push( button.extract( properties.hasownprop( "active" )   ? properties.active   : properties, "active" ))
			this.rules.push( button.extract( properties.hasownprop( "disabled" ) ? properties.disabled : properties, "disabled" ))
			this.rules.push( button.extract( properties.hasownprop( "default" )  ? properties.default  : properties, "default" ))
			this.rules.push( this.rules[1] )


			this.rules.vars := {
				bordercolor: "0x00FFFFFF",
				boder: max(
					this.rules[1].borderwidth,
					this.rules[2].borderwidth,
					this.rules[3].borderwidth,
					this.rules[4].borderwidth,
					this.rules[5].borderwidth
				)
			}


			button.setattributes( this )
		}
	}



	/*
		PRIVATE
	*/
	static setattributes( self ) {
		local bitmap := []

		; clear button default image
		dllcall( "gdiplus\GdipGraphicsClear", "ptr", self.graphics, "uint", this.defguicolor )

		local path1 := 0 ; body
		local path2 := 0 ; border
		local brush := 0
		for property in self.rules {
			; create a bitmap
			local pathx := 0 + ( self.rules.vars.boder / 2 )
			local pathy := 0 + ( self.rules.vars.boder / 2 )
			local pathw := self.width  - self.rules.vars.boder
			local pathh := self.height - self.rules.vars.boder

			if self.rules.vars.boder {
				local pen := 0
				local border := property.borderwidth || self.rules.vars.boder
				local color :=  property.bordercolor || self.rules.vars.bordercolor

				; UnitPixel = 2
				dllcall( "gdiplus\GdipCreatePen1", "uint", color, "float", border, "int", 2, "uptrp", pen )

				if property.borderradius > 0 {
					dllcall( "gdiplus\GdipCreatePath", "uint", 0, "ptrp", path2 )
					this.pathaddrect( path2, pathx, pathy, pathw, pathh, property.borderradius + ( property.borderradius / 4 ), true )
					dllcall( "gdiplus\GdipDrawPath", "ptr", self.graphics, "uint", pen, "uint", path2 )
					dllcall( "gdiplus\GdipDeletePath", "ptr", path2 )
				}
				else
					dllcall( "gdiplus\GdipDrawRectangle", "ptr", self.graphics, "ptr", pen, "float", pathx, "float", pathy, "float", pathw, "float", pathh )


				pathx += border / 2
				pathy += border / 2
				pathw -= border / 2
				pathh -= border / 2
				dllcall( "gdiplus\GdipDeletePen", "ptr", pen )
				dllcall( "gdiplus\GdipDeletePath", "ptr", path2 )
			}


			if type( property.background ) == "Integer" {
				dllcall( "gdiplus\GdipCreatePath", "uint", 0, "ptrp", path1 )
				this.pathaddrect( path1, pathx, pathy, pathw, pathh, property.borderradius )
				; create a solidbrush
				dllcall( "gdiplus\GdipCreateSolidFill", "uint", property.background, "ptrp", brush )
				; fill the path
				dllcall( "gdiplus\GdipFillPath", "ptr", self.graphics, "ptr", brush, "ptr", path1 )

				; Free resources
				dllcall( "gdiplus\GdipDeleteBrush", "ptr", brush )
				dllcall( "gdiplus\GdipDeletePath", "ptr", path1 )
				/*
				; Create a PathGradientBrush
				local PBRUSH := 0
				varsetcapacity( POINTS, 4 * 8, 0 )
				numput( self.width - 1, POINTS, 8, "uint"  )
				numput( self.width - 1, POINTS, 16, "uint" )
				numput( self.height - 1, POINTS, 20, "uint" )
				numput( self.height - 1, POINTS, 28, "uint" )
				dllcall( "gdiplus\GdipCreatePathGradientI", "ptr", &POINTS, "int", 4, "int", 0, "ptrp", PBRUSH )

				; Start and target colors
				; background := "0xFF" background
				; Color2 := "0x00" . background
				; Set the PresetBlend
				varsetcapacity( COLORS, 12, 0 )
				numput( property.background, COLORS, 0, "uint" )
				numput( property.background, COLORS, 4, "uint" )
				numput( 0, COLORS, 8, "uint" )

				varsetcapacity( RELINT, 12, 0 )
				numput( 0.00, RELINT, 0, "float" )
				numput( 1.00, RELINT, 4, "float" )
				dllcall( "gdiplus\GdipSetPathGradientPresetBlend", "ptr", PBRUSH, "ptr", &COLORS, "ptr", &RELINT, "int", 2 )

				; Set the FocusScales
				local DH := self.height / 2
				local XScale := ( property.mode == 1 ? ( self.width  - DH ) / self.width  : property.mode == 2 ? 1 : 0 )
				local YScale := ( property.mode == 1 ? ( self.height - DH ) / self.height : property.mode == 3 ? 1 : 0 )

				dllcall( "gdiplus\GdipSetPathGradientFocusScales", "ptr", PBRUSH, "float", XScale, "float", YScale )
				; Set the GammaCorrection
				dllcall( "gdiplus\GdipSetPathGradientGammaCorrection", "ptr", PBRUSH, "int", self.gamafix )
				; Fill button's rectangle
				dllcall( "gdiplus\GdipFillRectangleI", "ptr", self.graphics, "ptr", PBRUSH, "int", 0, "int", 0, "int", self.width, "int", self.height )
				; Free the brush
				dllcall( "Gdiplus\GdipDeleteBrush", "ptr", PBRUSH )
				*/
			}

			else if type( property.background ) == "Array" {
				; Fill the bitmap with a linear gradient
				; RECTF structure for line brush
				local rect := bufferalloc( 16, 0 )
				numput( self.width , rect,  8, "float" )
				numput( self.height, rect, 12, "float" )

				; Create a linear gradient brush with angle
				; https://purebasic.developpez.com/tutoriels/gdi/documentation/GdiPlus/LinearGradientBrush/html/GdipCreateLineBrushFromRectWithAngle.html
				dllcall( "gdiplus\GdipCreateLineBrushFromRectWithAngle",
					"uptr" , rect.ptr,
					"uint" , 0,
					"uint" , 0,
					"float", property.background.angle,
					"int"  , 1, ; Angle Scalable
					"uint" , 0,
					"ptrp" , brush
				)


				; https://purebasic.developpez.com/tutoriels/gdi/documentation/GdiPlus/LinearGradientBrush/html/GdipSetLineGammaCorrection.html
				dllcall( "gdiplus\GdipSetLineGammaCorrection", "ptr", brush, "int", 1 )

				local colors := bufferalloc( property.background.length * 4, 0 )
				local positions := bufferalloc( property.background.length * 4, 0 )
				local size := 0

				for val in property.background {
					numput( val.color, colors, size, "uint" )
					numput( val.pos, positions, size, "float" )
					size += 4
				}

				dllcall( "gdiplus\GdipSetLinePresetBlend", "ptr", brush, "ptr", colors.ptr, "ptr", positions.ptr, "int", property.background.length )
				; dllcall( "gdiplus\GdipFillRectangle", "ptr", self.graphics, "ptr", brush, "float", 0, "float", 0, "float", self.width, "float", self.height )
				dllcall( "gdiplus\GdipCreatePath", "uint", 0, "ptrp", path1 )
				this.pathaddrect( path1, pathx, pathy, pathw, pathh, property.borderradius )
				dllcall( "gdiplus\GdipFillPath", "ptr", self.graphics, "ptr", brush, "ptr", path1 )

				; free
				rect := ""
				colors := ""
				positions := ""

				dllcall( "gdiplus\GdipDeleteBrush", "ptr", brush )
				dllcall( "gdiplus\GdipDeletePath", "ptr", path1 )
			}
			; Create a bitmap from HBITMAP or file
			else {
				if type( Image ) == "Integer"
					dllcall( "gdiplus\GdipCreateBitmapFromHBITMAP", "ptr", Image, "ptr", 0, "ptrp", PBM )
				else
					dllcall( "gdiplus\GdipCreateBitmapFromFile", "wstr", Image, "ptrp", PBM )

				; Draw the bitmap
				dllcall( "gdiplus\GdipDrawImageRectI", "ptr", self.graphics, "ptr", PBM, "int", 0, "int", 0, "int", self.width, "int", self.height )
				; Free the bitmap
				dllcall( "Gdiplus\GdipDisposeImage", "ptr", PBM )
			}


			; Draw the caption
			local HFORMAT := 0
			if ( self.caption ) {
				; Create a StringFormat object
				dllcall( "gdiplus\GdipCreateStringFormat", "int", 0x5404, "uint", 0, "ptrp", HFORMAT )
				; Text color
				local PBRUSH := 0
				dllcall( "gdiplus\GdipCreateSolidFill", "uint", property.color, "ptrp", PBRUSH )
				; Horizontal alignment
				local HALIGN := (
					  ( self.safe.style & this.BS_CENTER ) == this.BS_CENTER ? this.SA_CENTER
					: ( self.safe.style & this.BS_CENTER ) == this.BS_RIGHT  ? this.SA_RIGHT
					: ( self.safe.style & this.BS_CENTER ) == this.BS_Left   ? this.SA_LEFT
					: this.SA_CENTER
				)

				dllcall( "gdiplus\GdipSetStringFormatAlign", "ptr", HFORMAT, "int", HALIGN )

				; Vertical alignment
				local VALIGN := (
					  ( self.safe.style & this.BS_VCENTER ) == this.BS_TOP    ? 0
					: ( self.safe.style & this.BS_VCENTER ) == this.BS_BOTTOM ? 2
					: 1
				)

				dllcall( "gdiplus\GdipSetStringFormatLineAlign", "ptr", HFORMAT, "int", VALIGN )

				; Set render quality to system default
				dllcall( "gdiplus\GdipSetTextRenderingHint", "ptr", self.graphics, "int", 0 )

				; Set the text's rectangle
				numput( 0.0, self.rect, 0, "float" )
				numput( 0.0, self.rect, 4, "float" )
				numput( self.width, self.rect, 8  , "float" )
				numput( self.height, self.rect, 12 , "float" )

				; Draw the text
				dllcall( "gdiplus\GdipDrawString",
					"ptr" , self.graphics,
					"wstr", self.caption,
					"int" , -1,
					"ptr" , self.font,
					"ptr" , self.rect.ptr,
					"ptr" , HFORMAT,
					"ptr" , PBRUSH
				)
			}

			; Create a HBITMAP handle from the bitmap
			local HBITMAP := 0
			dllcall( "gdiplus\GdipCreateHBITMAPFromBitmap", "ptr", self.bitmap, "ptrp", HBITMAP, "uint", 0x00FFFFFF )
			bitmap.push( HBITMAP )

			; Free resources
			; dllcall( "gdiplus\GdipDeleteBrush", "ptr", PBRUSH )
			dllcall( "gdiplus\GdipDeleteStringFormat", "ptr", HFORMAT )
		}



		; Now free remaining the GDI+ objects
		dllcall( "gdiplus\GdipDisposeImage", "ptr", self.bitmap )
		dllcall( "gdiplus\GdipDeleteGraphics", "ptr", self.graphics )
		dllcall( "gdiplus\GdipDeleteFont", "ptr", self.font )



		; Create the ImageList
		HIL := dllcall( "comctl32\ImageList_Create", "int", self.width, "int", self.height, "uint", this.ILC_COLOR32, "int", 6, "int", 0, "ptr" )
		loop 6 {
			dllcall( "comctl32\ImageList_Add", "ptr", HIL, "ptr", bitmap[ a_index ], "ptr", 0 )
			; a_lasterror := 0
		}

		; Create a BUTTON_IMAGELIST structure
		varsetcapacity( BIL, 20 + a_ptrsize, 0 )
		; Get the currently assigned image list
		dllcall( "user32\SendMessage", "ptr", self.hwnd, "uint", this.BCM_GETIMAGELIST, "ptr", 0, "ptr", &BIL )
		local imgl := numget( BIL, "uptr" )

		numput( HIL, BIL, 0, "ptr" )
		numput( this.BUTTON_IMAGELIST_ALIGN_CENTER, BIL, a_ptrsize + 16, "uint" )

		; Hide buttons's caption
		controlsettext( "", self.hwnd )
		controlsetstyle( "+" this.BS_BITMAP, self.hwnd )

		; Remove the currently assigned image list, if any
		if imgl
			il_destroy( imgl )

		; Assign the ImageList to the button
		sendmessage( this.BCM_SETIMAGELIST, 0, 0, , self.hwnd )
		sendmessage( this.BCM_SETIMAGELIST, 0, &BIL, , self.hwnd )

		; Free the bitmaps
		for index in bitmap
			dllcall( "gdi32\DeleteObject", "ptr", bitmap[ a_index ])

		; all done successfully
		this.gpshutdown( self )
		return true
	}




	/*
		PRIVATE
	*/
	static gpshutdown( self ) {

	}



	/*
	*/
	static extract( properties, tipe ) {
		local background   := ( tipe == "hover" ? 0xFF43D4FF : 0xFF007FFF )
		local color        := 0xFFFFFFFF
		local bordercolor  := ( tipe == "hover" ? 0xFFCFCFCF : 0xFFBFBFBF )
		local borderwidth  := ( tipe == "hover" ? 2 : 1 )
		local borderradius := 4
		local margin       := [ 0, 0, 0, 0 ]

		; https://stackoverflow.com/questions/20215440/parse-css-gradient-rule-with-javascript-regex
		if properties.hasownprop( "background" ) {
			; check the background if it's just one color
			if ( properties.background ~= "i)^((0x|#)?[0-9a-f]+)$" ) {
				background := integer( this.gethexacolor( properties.background ))
				if background == -1 {
					this.gpshutdown( this )
					msgbox( "Could not get background from properties!" )
					return -9
				}
			}

			; check the background for the gradient
			else if ( properties.background ~= "i)^(linear(-gradient)?)" ) {
				; cleaning up. remove the type from the function and remove the end of it.
				background := regexreplace( properties.background , "i)^(linear(-gradient)?\s*\(\s*)|\s*\)$", "" )

				; convert orientation, from to, degrees, turning or radians.
				local angle := regexreplace( background, "i)^(to\s*)?((top|bottom|left|right)|[\d\.\-]+(turn|deg|rad)?).*", "$2" )
				; cleaning up. remove orientation.
				background := regexreplace( background, "i)^(to\s*)?((top|bottom|left|right)|[\d\.\-]+(turn|deg|rad)?)\s*,\s*", "" )
				if angle ~= "deg"
					angle := ( float( substr( angle, 1, -3 )) - 90 )
				else if angle ~= "turn"
					angle := this.turn2deg( substr( angle, 1, -4 ))
				else if angle ~= "rad"
					angle := this.rad2deg( substr( angle, 1, -3 ))
				else if angle == "right"
					angle := 0.0
				else if angle == "left"
					angle := 180.0
				else if angle == "top"
					angle := -90.0
				else if angle == "bottom"
					angle := 90.0

				local split := strsplit( background, "," )
				background := []
				background.angle := angle

				local safepos := [ 0.0 ]
				loop split.length - 2
					safepos.push(( 1.0 / ( split.length - 1 )) * a_index )
				safepos.push( 1.0 )


				for val in split {
					local find

					regexmatch( val, "i)\s*((#|0x)(?<color>[\da-f]+))((\)|\s+)(?<pos>[\.\d]+)(?<perc>%)?\s*)?", find )
					local pos := ( find.pos ? ( find.pos / ( find.perc ? 100 : 0 )) : safepos[ a_index ])

					if not find.color {
						msgbox( "Something is wrong in the gradient color parameters." )
						exitapp
					}

					background.push({
						color: this.gethexacolor( find.color ),
						pos: pos
					})
				}

				; prevent error when starting gradient with position other than 0
				if background[1].pos !== 0 {
					background.insertat( 1, {
						color: background[1].color,
						pos: 0.0
					})
				}
			}
		}


		if properties.hasownprop( "color" ) {
			if ( properties.color ~= "i)^((0x|#)?[0-9a-f]+)$" ) {
				color := this.gethexacolor( properties.color )
				if color == -1 {
					this.gpshutdown( this )
					msgbox( "Could not get color from properties!" )
					return -10
				}
			}
		}


		if properties.hasownprop( "margin" ) and properties.margin !== 0 {
			; prevent syntax errors
			local match
			regexmatch(
				strreplace( properties.margin, "px", "" ),
				"([\d-]+)\s*([\d-]+)?\s*([\d-]+)?\s*([\d-]+)?\s*",
				match
			)

			; margin := []
			; ( match[1] !== "" ? margin.push( match[1] ) : false )
			; ( match[2] !== "" ? margin.push( match[2] ) : false )
			; ( match[3] !== "" ? margin.push( match[3] ) : false )
			; ( match[4] !== "" ? margin.push( match[4] ) : false )


			if match {
				if match[2] match[3] match[4] == ""
					margin := [ match[1], match[1], match[1], match[1] ]
				else if match[3] match[4] == ""
					margin := [ match[1], match[2], match[1], match[2] ]
				else if match[4] == ""
					margin := [ match[1], match[2], match[3], match[2] ]
				else if match[1] !== ""
					margin := [ match[1], match[2], match[3], match[4] ]

				else {
					this.gpshutdown( this )
					msgbox( "Could not get margin from properties!" )
					return -11
				}
			}
		}


		if properties.hasownprop( "bordercolor" ) {
			if ( bordercolor ~= "i)^((0x|#)?[0-9a-f]+)$" ) {
				bordercolor := this.gethexacolor( bordercolor )
				if bordercolor == -1 {
					this.gpshutdown( this )
					msgbox( "Could not get bordercolor from properties!" )
					return -12
				}
			}
		}


		if properties.hasownprop( "borderwidth" ) and not properties.borderwidth ~= "(none|0)" {
			; prevent syntax errors
			local match
			regexmatch(
				strreplace( properties.borderwidth, "px", "" ),
				"([\d-]+)\s*([\d-]+)?\s*([\d-]+)?\s*([\d-]+)?\s*",
				match
			)

			if match {
				if match[2] match[3] match[4] == ""
					borderwidth := [ match[1], match[1], match[1], match[1] ]
				else if match[3] match[4] == ""
					borderwidth := [ match[1], match[2], match[1], match[2] ]
				else if match[4] == ""
					borderwidth := [ match[1], match[2], match[3], match[2] ]
				else if match[1] !== ""
					borderwidth := [ match[1], match[2], match[3], match[4] ]

				else {
					this.gpshutdown( this )
					msgbox( "Could not get borderwidth from properties!" )
					return -13
				}
			}
		}


		if properties.hasownprop( "borderradius" ) and not properties.borderradius ~= "^(\s*(none|)\s*)$" {
			borderradius := properties.borderradius
		}

		return {
			"background"  : background,
			"bordercolor" : bordercolor ? bordercolor : "",
			"borderwidth" : borderwidth,
			"borderradius": borderradius,
			"margin"      : margin,
			"color"       : color
		}
	}



	/*
		PRIVATE
	*/
	static gethexacolor( color ) {
		color := regexreplace( color, "^#", "" )
		local length := strlen( color )

		if length == 6
			return integer( "0xFF" strupper( color ))

		if length == 8
			return integer( "0x" strupper( substr( color, 7, 2 ) substr( color, 1, 6 )))

		if length == 3 or length == 4
			return integer( "0x" strupper(
				( length == 4 ? substr( color, 4, 1 ) substr( color, 4, 1 ) : "FF" )
				substr( color, 1, 1 ) substr( color, 1, 1 )
				substr( color, 2, 1 ) substr( color, 2, 1 )
				substr( color, 3, 1 ) substr( color, 3, 1 )
			))

		if length == 10
			return color

		; error
		else
			return -1
	}



	/*
		PRIVATE
	*/
	static pathaddrect( path, left, top, width, height, radius := 0, draft := false ) {
		if radius < 1 {
			; for body
			return dllcall( "gdiplus\GdipAddPathRectangle",
				"ptr"  , path,
				"float", left,
				"float", top,
				"float", width,
				"float", height
			)
		}


		local D := ( radius * 2 )
		width -= D
		height -= D

		; top left arc
		dllcall( "gdiplus\GdipAddPathArc",
			"ptr"  , path,
			"float", left,
			"float", top,
			"float", D,
			"float", D,
			"float", 180,
			"float", 90
		)

		; top right arc
		dllcall( "gdiplus\GdipAddPathArc",
			"ptr"  , path,
			"float", width,
			"float", top,
			"float", D,
			"float", D,
			"float", 270,
			"float", 90
		)

		; bottom right line
		dllcall( "gdiplus\GdipAddPathArc",
			"ptr"  , path,
			"float", width,
			"float", height,
			"float", D,
			"float", D,
			"float", 0,
			"float", 90
		)

		; bottom left arc
		dllcall( "gdiplus\GdipAddPathArc",
			"ptr"  , path,
			"float", left,
			"float", height,
			"float", D,
			"float", D,
			"float", 90,
			"float", 90
		)

		return dllcall( "gdiplus\GdipClosePathFigure", "ptr", path )
	}



	/*
		PRIVATE
		PI: 3.14159265359
	*/
	static rad2deg( rad )    => ( round( rad * 180 / 3.14159265359, 2 ) - 90.0 )
	; static rad2turn( rad  )   => round(( rad * 180 / 3.14159265359 ) / 360, 2 )
	; static rad2perc( rad )   => round(( 100 * ( rad * 180 / 3.14159265359 )) / 360, 2 )
	; static deg2rad( deg  )    => deg * ( 3.14159265359 / 180 )
	; static deg2perc( deg  )  => ( 100 * deg ) / 360
	; static deg2turn( deg )    => ( deg / 360 )
	; static turn2rad( turn )   => dllcall( "msvcrt\atan2", "double", turn, "double", 0, "cdecl double" )
	; static turn2perc( turn ) => float( turn * 100 )
	static turn2deg( turn )   => (( 360 / 100 ) * ( turn * 100 ) - 90.0 )
}




fm := guicreate( "", "Image Buttons" )
fm.marginx := 50
fm.marginy := 20
fm.setfont( "s10" )
bt := fm.addbutton( "vBT2 h30 w200", "Button" )
fm.addbutton( "YP h30 w200", "Button" )
bt.setfont( "", "Verdana" )

button.new( bt, {
	background: "linear( top, #3A9CFF, #61afff )",
	hover: { background: "linear( top, #FF0000 10%, #FFFF00 25%, #00FF00 50%, #00FFFF 75%, #0000FF 100% )" },
	active: { background: "#43D4FF" }
})

fm.show( "x10 y10" )

User avatar
hoppfrosch
Posts: 443
Joined: 07 Oct 2013, 04:05
Location: Rhine-Maine-Area, Hesse, Germany
Contact:

Re: [Class] ImageButton - 1.5.00.00 - 20201230

Post by hoppfrosch » 20 Jul 2021, 00:09

F4Jonatas wrote:
18 Jul 2021, 07:52
First I did it for AHK v2 and I'm adding other things, for example more gradient, support gradient angle and transparency on the edges and background.
Which AHK V2 version? Tried it with V2.0-a138 and there were errors ...

just me
Posts: 9423
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: [Class] ImageButton - 1.5.00.00 - 20201230

Post by just me » 20 Jul 2021, 05:19

@F4Jonatas: Move your script to AutoHotkey v2 Scripts and Functions, please. I don't intend do do anything with v2 before a beta will be released.

If you want I'll provide a link in the OP.

User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: [Class] ImageButton - 1.5.00.00 - 20201230

Post by jNizM » 20 Jul 2021, 07:22

oldbrother wrote:
18 Jul 2021, 07:10
@jNizM, Thanks! It seems not working with ver. 1.5.
I added 2 comments in my script

Code: Select all

; for v1.5 add UseGDIP.ahk or Include them into Class_ImageButton.ahk
; #Include UseGDIP.ahk
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile

User avatar
F4Jonatas
Posts: 45
Joined: 22 Oct 2015, 06:35
Contact:

Re: [Class] ImageButton - 1.5.00.00 - 20201230

Post by F4Jonatas » 20 Jul 2021, 20:19

@just me I just wanted to show you that we can add more stuff. But if you want I move my post.

hoppfrosch I'm using a version: 2.0-a108-a2fa0498

CzarOfScripts
Posts: 7
Joined: 09 Apr 2021, 05:57

Re: [Class] ImageButton - 1.5.00.00 - 20201230

Post by CzarOfScripts » 19 Feb 2022, 13:08

If you use the Create method of the ImageButton class again, the text disappears.

just me
Posts: 9423
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: [Class] ImageButton - 1.5.00.00 - 20201230

Post by just me » 20 Feb 2022, 04:20

just me wrote:
17 May 2021, 07:15
@c7aesa7r,
because of problems on Win XP (and maybe later versions) the class contains

Code: Select all

      ; Hide buttons's caption
      ControlSetText, , , ahk_id %HWND%
to remove the button's caption. You might try to comment it out.

flightlex_
Posts: 2
Joined: 13 Apr 2022, 13:14

Re: [Class] ImageButton - 1.5.00.00 - 20201230

Post by flightlex_ » 16 May 2022, 06:50

just me wrote:
22 Dec 2013, 10:28
Prior version at old forum -> /board/topic/75064-function-createimagebutton-ahk-l/

Caution: Update 1.5.00.00 contains script breaking changes! Please read the change history.
Also, it is barely tested and may contain bugs!


Well, here's a new version of an old script of mine. It has become more up-to-date than ever, because it can be 'misused' for somewhat 'Metro styled' buttons.

ImageButton_1_3.png
Change History
:arrow: Look at GitHub!
:arrow: Download from GitHub!

Note: To run the sample script, download both script files from the Sources folder and both pic files from Resources and store them all together into one folder. The script files should be saved as UTF-8 with BOM.

Other Samples
Is there any way to do hover effect more smoothly? cuz its cool but honestly looks kinda 5 fps

jly
Posts: 89
Joined: 30 Sep 2020, 06:06

Re: [Class] ImageButton - 1.5.00.00 - 20201230

Post by jly » 02 Oct 2022, 04:04

hello, just me:

Is it possible to make the button highlighted immediately when the mouse is over it. It is now about 100ms before the button is highlighted.

just me
Posts: 9423
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: [Class] ImageButton - 1.5.00.00 - 20201230

Post by just me » 02 Oct 2022, 07:16

All timings are controlled by the OS.

jly
Posts: 89
Joined: 30 Sep 2020, 06:06

Re: [Class] ImageButton - 1.5.00.00 - 20201230

Post by jly » 01 Nov 2022, 20:40

just me wrote:
02 Oct 2022, 07:16
All timings are controlled by the OS.

@just me Thanks for your reply, I found the solution:
viewtopic.php?t=27743

Code: Select all

#SingleInstance, Force
#NoEnv
DisableFadeEffect()
gui, font, s10
gui, add, Button, w200, 确定
gui, show, w300 center, test01

DisableFadeEffect() {
	; SPI_GETCLIENTAREAANIMATION = 0x1042
	DllCall("SystemParametersInfo", "UInt", 0x1042, "UInt", 0, "UInt*", isEnabled, "UInt", 0)

	if isEnabled {
		; SPI_SETCLIENTAREAANIMATION = 0x1043
		DllCall("SystemParametersInfo", "UInt", 0x1043, "UInt", 0, "UInt", 0, "UInt", 0)
		Progress, 10:P100 Hide
		Progress, 10:Off
		DllCall("SystemParametersInfo", "UInt", 0x1043, "UInt", 0, "UInt", 1, "UInt", 0)
	}
}
esc::ExitApp


User avatar
HiSoKa
Posts: 480
Joined: 27 Jan 2020, 15:43

Re: [Class] ImageButton - 1.5.00.00 - 20201230

Post by HiSoKa » 30 Nov 2022, 13:01

Hello just me,
How i can increase the blue color in this button.
gra btn.png
gra btn.png (8.14 KiB) Viewed 2490 times
I do not want the gradient to be 50% between the two colors.
I want to specify which color should be bigger than the other color, Is this possible?

Code: Select all

#NoEnv
SetBatchLines, -1

Gradients := [["0xFFFFFF", "0x009dff"], ["0xFFD8F029", "0xFFAABD20"], ["0xFFF011AA", "0xFFBD0D85"]
            , ["0xFF29F08D", "0xFF20BD70"], ["0xFFA027F5", "0xFF7A1EBD"]]

Buttons := [1]

NumOfBtns := Buttons.Length()
BtnsPerRow := 4
BtnW := "w160"
BtnH := "h45"
GuiColor := "White"
TxtColor := "White"
Gui, Margin, 3, 3
Gui, Font, s12
Gui, Color, %GuiColor%
ImageButton.SetGuiColor(GuiColor)

BtnNum := 0
While (BtnNum < NumOfBtns) {
   Loop, %BtnsPerRow% {
      BtnNum++
      Gradient := Gradients[Buttons[BtnNum]]
      Opt1 := [3, Gradient[1], Gradient[2], "White", 5]
      BtnX := A_Index = 1 ? "xm" : "x+m"
      Gui, Add, Button, %BtnX% %BtnW% %BtnH% vBtn%BtnNum% gBtnClicked hwndHBTN, Button %BtnNum%
      If !ImageButton.Create(HBTn, Opt1) {
      }
   } Until (BtnNum >= NumOfBtns)
}
Gui, Show, , Gradient Buttons
Return

GuiClose:
GuiEscape:
ExitApp

BtnClicked:
   Gui, +OwnDialogs
   MsgBox, 0, Click!, You clicked on button %A_GuiControl%!
Return
#Include <UseGDIP>
#Include <Class_ImageButton>

User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: [Class] ImageButton - 1.5.00.00 - 20201230

Post by jNizM » 01 Dec 2022, 06:07

As far as I know, it is not currently possible with the class. You have only one start and one finish color here.
@just me would have to adapt the class so that more than 2 colors can be passed. Similar to https://github.com/AHK-just-me/LinearGradient/blob/master/Sources/LinearGradient.ahk
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile

just me
Posts: 9423
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: [Class] ImageButton - 1.5.00.00 - 20201230

Post by just me » 01 Dec 2022, 06:47

jNizM wrote:
01 Dec 2022, 06:07
...
That's right! ;)

User avatar
boiler
Posts: 16767
Joined: 21 Dec 2014, 02:44

Re: [Class] ImageButton - 1.5.00.00 - 20201230

Post by boiler » 01 Dec 2022, 09:21

@HiSoKa - Perhaps this will give you a result close to what you want. Run the script with your current gradient and use the Window Spy tool to grab a color part-way through the gradient displayed on the button, then use that as the new start end of the gradient instead of 0xFFFFFF. So you would get a resulting gradient to use something like this: ["0xD7E9FF", "0x009dff"]

button gradients.png
button gradients.png (4.03 KiB) Viewed 2401 times

User avatar
HiSoKa
Posts: 480
Joined: 27 Jan 2020, 15:43

Re: [Class] ImageButton - 1.5.00.00 - 20201230

Post by HiSoKa » 01 Dec 2022, 11:31

Thanks @jNizM and @just me I will take a look there..

Thanks You @boiler This is exactly what I intended to do, And now I got the desired result :) ,
Thank you so much to point it out

User avatar
HiSoKa
Posts: 480
Joined: 27 Jan 2020, 15:43

Re: [Class] ImageButton - 1.5.00.00 - 20201230

Post by HiSoKa » 27 Dec 2022, 01:32

Hi, @just me .
Sorry for the inconvenience again,
But I'm having a problem and I'm wondering if you have a solution.
My problem is that I want to use WS_EX_LAYOUTRTL for writing style, Because the Arabic language is from right to left That's way sometimes some problems appear when writing in Arabic, and this style solves this problem.
My problem is when I apply it to [Class] ImageButton, it completely reverses the characters.
hello world.png
hello world.png (12.25 KiB) Viewed 2257 times
Is there a solution to this problem?
And thank you in advance

Code: Select all

#NoEnv
SetBatchLines, -1
Gui, Font, s12


   Loop, 1 {
      Gradient := ["0xA020F0", "0xD7A1F9"]
      Opt1 := [3, Gradient[1], Gradient[2], "White", 5]
      Gui, Add, Button,  w160 h45 hwndHBTN +E0x00400000, Hello World!!
      If !ImageButton.Create(HBTn, Opt1) {
      }
}
Gui, Show, , Gradient Buttons
Return

GuiClose:
GuiEscape:
ExitApp

#Include <UseGDIP>
#Include <Class_ImageButton>

ESC::ExitApp

just me
Posts: 9423
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: [Class] ImageButton - 1.5.00.00 - 20201230

Post by just me » 27 Dec 2022, 06:05

Hi, I cannot test it.
WS_EX_LAYOUTRTL wrote:If the shell language is Hebrew, Arabic, or another language that supports reading order alignment, the horizontal origin of the window is on the right edge. Increasing horizontal values advance to the left.
If I create a common button with this style and the caption text "Hello World!!" it results in "!!Hello World".

The image button doesn't have a caption. Apparently, the bitmap is just mirrored.

Post Reply

Return to “Scripts and Functions (v1)”