Re: [Class] ImageButton - 1.5.00.00 - 20201230
Posted: 18 Jul 2021, 05:58
@jNizM, thanks for your 'Bootstrap like Buttons'. I added a link to the OP.
Let's help each other out
https://www.autohotkey.com/boards/
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" )
Which AHK V2 version? Tried it with V2.0-a138 and there were errors ...F4Jonatas wrote: ↑18 Jul 2021, 07:52First 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.
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
just me wrote: ↑17 May 2021, 07:15@c7aesa7r,
because of problems on Win XP (and maybe later versions) the class containsto remove the button's caption. You might try to comment it out.Code: Select all
; Hide buttons's caption ControlSetText, , , ahk_id %HWND%
Is there any way to do hover effect more smoothly? cuz its cool but honestly looks kinda 5 fpsjust me wrote: ↑22 Dec 2013, 10:28Prior 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 HistoryLook at GitHub!
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
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
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>
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
If I create a common button with this style and the caption text "Hello World!!" it results in "!!Hello World".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.