[Class] ImageButton - 1.5.00.00 - 20201230

Post your working scripts, libraries and tools for AHK v1.1 and older
User avatar
masato
Posts: 21
Joined: 08 Sep 2015, 13:38

Re: [Class] ImageButton

Post by masato » 03 Jun 2018, 09:05

Hey,

Really loving this. But is there some way to make it work with private font added with AddFontResourceEx?
I believe it's a restriction from GDIP so I made some research and found this, but links are dead.

Also found this one. It uses a modified function to work with private fonts: Gdip_PrivateFontToGraphics().
Tried doing some modifications, but obliviously it's not as simple:

Code: Select all

      GDIPFont := 0
      ; HFONT := DllCall("User32.dll\SendMessage", "Ptr", HWND, "UInt", WM_GETFONT, "Ptr", 0, "Ptr", 0, "Ptr") ; Default
      FontName := "Fontin SmallCaps", FontFile := A_ScriptDir "\Fontin-SmallCaps.ttf", FontSize := 20, FontStyle := 0
      DllCall("gdiplus\GdipNewPrivateFontCollection", "uint*", hcollection)
      DllCall("gdiplus\GdipPrivateAddFontFile", "uint", hcollection, "uint", &FontFile)
      DllCall("gdiplus\GdipCreateFontFamilyFromName", "uint", &FontName, "uint", hcollection, "uint*", hFamily)
      hFont := Gdip_FontCreate(hFamily, FontSize, FontStyle)
      MsgBox, 0, %A_ThisFunc%, HFONT: %HFONT% - A_LastError: %A_LastError% ; <<<<< added
      DC := DllCall("User32.dll\GetDC", "Ptr", HWND, "Ptr")
      DllCall("Gdi32.dll\SelectObject", "Ptr", DC, "Ptr", HFONT)
      Status := DllCall("Gdiplus.dll\GdipCreateFontFromDC", "Ptr", DC, "PtrP", PFONT)
      MsgBox, 0, %A_ThisFunc%, PFONT: %PFONT% - A_LastError: %A_LastError% - Status: %Status% ; <<<<< added, changed
      DllCall("User32.dll\ReleaseDC", "Ptr", HWND, "Ptr", DC)
      If !(PFONT)
         Return This.SetError("Couldn't get button's font!")
But it's just sooo complicated. Would it even be possible to work with ImageButton?

__EDIT pre-posting: What the fuck, I just got it working somehow by setting PFONT as hFont

Code: Select all

      ; Get the button's font
      GDIPFont := 0
      ; HFONT := DllCall("User32.dll\SendMessage", "Ptr", HWND, "UInt", WM_GETFONT, "Ptr", 0, "Ptr", 0, "Ptr") ; Default
      FontName := "Broken Heart", FontFile := A_ScriptDir "\Broken Heart.ttf", FontSize := 20, FontStyle := 0
      DllCall("gdiplus\GdipNewPrivateFontCollection", "uint*", hcollection)
      DllCall("gdiplus\GdipPrivateAddFontFile", "uint", hcollection, "uint", &FontFile)
      DllCall("gdiplus\GdipCreateFontFamilyFromName", "uint", &FontName, "uint", hcollection, "uint*", hFamily)
      hFont := Gdip_FontCreate(hFamily, FontSize, FontStyle)
      ; MsgBox, 0, %A_ThisFunc%, HFONT: %HFONT% - A_LastError: %A_LastError% ; <<<<< added
      DC := DllCall("User32.dll\GetDC", "Ptr", HWND, "Ptr")
      DllCall("Gdi32.dll\SelectObject", "Ptr", DC, "Ptr", HFONT)
      ; Status := DllCall("Gdiplus.dll\GdipCreateFontFromDC", "Ptr", DC, "PtrP", PFONT)
      PFONT := hFont
      ; MsgBox, 0, %A_ThisFunc%, PFONT: %PFONT% - A_LastError: %A_LastError% - Status: %Status% ; <<<<< added, changed
      DllCall("User32.dll\ReleaseDC", "Ptr", HWND, "Ptr", DC)
      If !(PFONT)
         Return This.SetError("Couldn't get button's font!")
Now that I think about it, it was logic. We created the font, so there's no need to retrieve it from the GUI (or something). Though, this make us unable to get the button's font parameters.
Could you confirm that this approach is safe?

__EDIT 2: Seems it starts using too much memory when creating many imgbtns, fix:
Comment out DllCall("Gdiplus.dll\GdipDeleteFont", "Ptr", PFONT)
And add this after This.FreeBitmaps()

Code: Select all

      Gdip_DeleteFont(hFont)
      Gdip_DeleteFontFamily(hFamily)
      DllCall("gdiplus\GdipDeletePrivateFontCollection", "uint*", hcollection)

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

Re: [Class] ImageButton

Post by just me » 04 Jun 2018, 06:06

Hi masato,

interestingly, most of the additional functions you use are documented as "Not implemented".

Also, you might want to look at this post and the following.

As is, you cannot pass a font to ImageButton.Create(). But you might try to use the additional functions to create a PrivateFontCollection before you call this method. Maybe GDI+ will be able to handle the font, then.

jamie1992
Posts: 3
Joined: 22 Jul 2018, 11:37

Re: [Class] ImageButton

Post by jamie1992 » 22 Jul 2018, 12:02

Hi just me.
Thank you for this.
Please excuse my noobness.

Im am trying to use your button that uses PIC1.jpg and PIC2.jpg. The issue im having is that the jpg is the same size as the button. Because it is the same size as the button the jpg covers the blue border that indicates that the button is highlighted. Im assuming that option 8 - BorderWidth will allow me to change the size so the blue highlight border will be visible. If im assuming correctly could you please give some example code as I cant seem to get it to work and I have read the whole thread. If im assuming incorrectly, apologies for the inconvenience.

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

Re: [Class] ImageButton

Post by just me » 28 Jul 2018, 01:32

Neither the class nor the button control will add some 'highlighted incicator'. If you want it, you have to pass pictures already containing that border for button states 5 and 6.

jamie1992
Posts: 3
Joined: 22 Jul 2018, 11:37

Re: [Class] ImageButton

Post by jamie1992 » 09 Aug 2018, 07:54

Thank you for the reply.

It says "BorderColor optional, ignored for modes 0" right at the top of your awesome script. I asked the question before understanding the script. Sorry to waste your time.

hotkeyguy
Posts: 170
Joined: 11 Oct 2014, 12:22

Re: [Class] ImageButton

Post by hotkeyguy » 28 Sep 2019, 14:23

Hello,

modifying 60 buttons in my GUI lasts < 3s. Is there a way to accelerate that a little bit (I don't expect miracles)? I tried
GuiControl, -Redraw, %g_hdGUI_Main%
without success, every single button is still redrawn.


Many thanks and greetings
hotkeyguy

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

Re: [Class] ImageButton

Post by just me » 09 Oct 2019, 08:07

Hello,

in January 2018 I wrote a special version of the class script trying to speed up things. Amongst others I added my UseGDIP() function. It's not really tested, but you might want to try it anyway:

Code: Select all

; ======================================================================================================================
; Namespace:         ImageButton
; Function:          Create images and assign them to pushbuttons.
; Tested with:       AHK 1.1.14.03 (A32/U32/U64)
; Tested on:         Win 7 (x64)
; Change history:    1.4.00.00/2014-06-07/just me - fixed bug for button caption = "0", "000", etc.
;                    1.3.00.00/2014-02-28/just me - added support for ARGB colors
;                    1.2.00.00/2014-02-23/just me - added borders
;                    1.1.00.00/2013-12-26/just me - added rounded and bicolored buttons       
;                    1.0.00.00/2013-12-21/just me - initial release
; How to use:
;     1. Create a push button (e.g. "Gui, Add, Button, vMyButton hwndHwndButton, Caption") using the 'Hwnd' option
;        to get its HWND.
;     2. Call ImageButton.Create() passing two parameters:
;        HWND        -  Button's HWND.
;        Options*    -  variadic array containing up to 6 option arrays (see below).
;        ---------------------------------------------------------------------------------------------------------------
;        The index of each option object determines the corresponding button state on which the bitmap will be shown.
;        MSDN defines 6 states (http://msdn.microsoft.com/en-us/windows/bb775975):
;           PBS_NORMAL    = 1
;	         PBS_HOT       = 2
;	         PBS_PRESSED   = 3
;	         PBS_DISABLED  = 4
;	         PBS_DEFAULTED = 5
;	         PBS_STYLUSHOT = 6 <- used only on tablet computers (that's false for Windows Vista and 7, see below)
;        If you don't want the button to be 'animated' on themed GUIs, just pass one option object with index 1.
;        On Windows Vista and 7 themed bottons are 'animated' using the images of states 5 and 6 after clicked.
;        ---------------------------------------------------------------------------------------------------------------
;        Each option array may contain the following values:
;           Index Value
;           1     Mode        mandatory:
;                             0  -  unicolored or bitmap
;                             1  -  vertical bicolored
;                             2  -  horizontal bicolored
;                             3  -  vertical gradient
;                             4  -  horizontal gradient
;                             5  -  vertical gradient using StartColor at both borders and TargetColor at the center
;                             6  -  horizontal gradient using StartColor at both borders and TargetColor at the center
;                             7  -  'raised' style
;           2     StartColor  mandatory for Option[1], higher indices will inherit the value of Option[1], if omitted:
;                             -  ARGB integer value (0xAARRGGBB) or HTML color name ("Red").
;                             -  Path of an image file or HBITMAP handle for mode 0.
;           3     TargetColor mandatory for Option[1] if Mode > 0, ignored if Mode = 0. Higher indcices will inherit
;                             the color of Option[1], if omitted:
;                             -  ARGB integer value (0xAARRGGBB) or HTML color name ("Red").
;           4     TextColor   optional, if omitted, the default text color will be used for Option[1], higher indices 
;                             will inherit the color of Option[1]:
;                             -  ARGB integer value (0xAARRGGBB) or HTML color name ("Red").
;                                Default: 0xFF000000 (black)
;           5     Rounded     optional:
;                             -  Radius of the rounded corners in pixel; the letters 'H' and 'W' may be specified
;                                also to use the half of the button's height or width respectively.
;                                Default: 0 - not rounded
;           6     GuiColor    optional, needed for rounded buttons if you've changed the GUI background color:
;                             -  RGB integer value (0xRRGGBB) or HTML color name ("Red").
;                                Default: AHK default GUI background color
;           7     BorderColor optional, ignored for modes 0 (bitmap) and 7, color of the border:
;                             -  RGB integer value (0xRRGGBB) or HTML color name ("Red").
;           8     BorderWidth optional, ignored for modes 0 (bitmap) and 7, width of the border in pixels:
;                             -  Default: 1
;        ---------------------------------------------------------------------------------------------------------------
;        If the the button has a caption it will be drawn above the bitmap.
; Credits:           THX tic     for GDIP.AHK     : http://www.autohotkey.com/forum/post-198949.html
;                    THX tkoi    for ILBUTTON.AHK : http://www.autohotkey.com/forum/topic40468.html
; ======================================================================================================================
; This software is provided 'as-is', without any express or implied warranty.
; In no event will the authors be held liable for any damages arising from the use of this software.
; ======================================================================================================================
; ======================================================================================================================
; CLASS ImageButton()
; ======================================================================================================================
Class ImageButton {
   ; ===================================================================================================================
   ; PUBLIC PROPERTIES =================================================================================================
   ; ===================================================================================================================
   Static DefGuiColor  := ""        ; default GUI color                             (read/write)
   Static DefTxtColor := "Black"    ; default caption color                         (read/write)
   Static LastError := ""           ; will contain the last error message, if any   (readonly)
   ; ===================================================================================================================
   ; PRIVATE PROPERTIES ================================================================================================
   ; ===================================================================================================================
   Static BitMaps := []
   Static GDIPDll := 0
   Static GDIPToken := 0
   Static MaxOptions := 8
   ; HTML colors
   Static HTML := {BLACK: 0x000000, GRAY: 0x808080, SILVER: 0xC0C0C0, WHITE: 0xFFFFFF, MAROON: 0x800000
                 , PURPLE: 0x800080, FUCHSIA: 0xFF00FF, RED: 0xFF0000, GREEN: 0x008000, OLIVE: 0x808000
                 , YELLOW: 0xFFFF00, LIME: 0x00FF00, NAVY: 0x000080, TEAL: 0x008080, AQUA: 0x00FFFF, BLUE: 0x0000FF}
   ; Initialize
   Static ClassInit := ImageButton.InitClass()
   ; ===================================================================================================================
   ; PRIVATE METHODS ===================================================================================================
   ; ===================================================================================================================
   __New(P*) {
      Return False
   }
   ; ===================================================================================================================
   InitClass() {
      ; ----------------------------------------------------------------------------------------------------------------
      ; Get AHK's default GUI background color
      GuiColor := DllCall("User32.dll\GetSysColor", "Int", 15, "UInt") ; COLOR_3DFACE is used by AHK as default
      This.DefGuiColor := ((GuiColor >> 16) & 0xFF) | (GuiColor & 0x00FF00) | ((GuiColor & 0xFF) << 16)
      Return True
   }
   ; ===================================================================================================================
   GdiplusStartup() {
      This.GDIPDll := This.GDIPToken := 0
      If (This.GDIPDll := DllCall("Kernel32.dll\LoadLibrary", "Str", "Gdiplus.dll", "Ptr")) {
         VarSetCapacity(SI, 24, 0)
         Numput(1, SI, 0, "Int")
         If !DllCall("Gdiplus.dll\GdiplusStartup", "PtrP", GDIPToken, "Ptr", &SI, "Ptr", 0)
            This.GDIPToken := GDIPToken
         Else
            This.GdiplusShutdown()
      }
      Return This.GDIPToken
   }
   ; ===================================================================================================================
   GdiplusShutdown() {
      If This.GDIPToken
         DllCall("Gdiplus.dll\GdiplusShutdown", "Ptr", This.GDIPToken)
      If This.GDIPDll
         DllCall("Kernel32.dll\FreeLibrary", "Ptr", This.GDIPDll)
      This.GDIPDll := This.GDIPToken := 0
   }
   ; ===================================================================================================================
   FreeBitmaps() {
      For I, HBITMAP In This.BitMaps
         DllCall("Gdi32.dll\DeleteObject", "Ptr", HBITMAP)
      This.BitMaps := []
   }
   ; ===================================================================================================================
   GetARGB(RGB) {
      ARGB := This.HTML.HasKey(RGB) ? This.HTML[RGB] : RGB
      Return (ARGB & 0xFF000000) = 0 ? 0xFF000000 | ARGB : ARGB
   }
   ; ===================================================================================================================
   PathAddRectangle(Path, X, Y, W, H) {
      Return DllCall("Gdiplus.dll\GdipAddPathRectangle", "Ptr", Path, "Float", X, "Float", Y, "Float", W, "Float", H)
   }
   ; ===================================================================================================================
   PathAddRoundedRect(Path, X1, Y1, X2, Y2, R) {
      D := (R * 2), X2 -= D, Y2 -= D
      DllCall("Gdiplus.dll\GdipAddPathArc"
            , "Ptr", Path, "Float", X1, "Float", Y1, "Float", D, "Float", D, "Float", 180, "Float", 90)
      DllCall("Gdiplus.dll\GdipAddPathArc"
            , "Ptr", Path, "Float", X2, "Float", Y1, "Float", D, "Float", D, "Float", 270, "Float", 90)
      DllCall("Gdiplus.dll\GdipAddPathArc"
            , "Ptr", Path, "Float", X2, "Float", Y2, "Float", D, "Float", D, "Float", 0, "Float", 90)
      DllCall("Gdiplus.dll\GdipAddPathArc"
            , "Ptr", Path, "Float", X1, "Float", Y2, "Float", D, "Float", D, "Float", 90, "Float", 90)
      Return DllCall("Gdiplus.dll\GdipClosePathFigure", "Ptr", Path)
   }
   ; ===================================================================================================================
   SetRect(ByRef Rect, X1, Y1, X2, Y2) {
      VarSetCapacity(Rect, 16, 0)
      NumPut(X1, Rect, 0, "Int"), NumPut(Y1, Rect, 4, "Int")
      NumPut(X2, Rect, 8, "Int"), NumPut(Y2, Rect, 12, "Int")
      Return True
   }
   ; ===================================================================================================================
   SetRectF(ByRef Rect, X, Y, W, H) {
      VarSetCapacity(Rect, 16, 0)
      NumPut(X, Rect, 0, "Float"), NumPut(Y, Rect, 4, "Float")
      NumPut(W, Rect, 8, "Float"), NumPut(H, Rect, 12, "Float")
      Return True
   }
   ; ===================================================================================================================
   SetError(Msg) {
      If (This.Bitmap)
         DllCall("Gdiplus.dll\GdipDisposeImage", "Ptr", This.Bitmap)
      If (This.Graphics)
         DllCall("Gdiplus.dll\GdipDeleteGraphics", "Ptr", This.Graphics)
      If (This.Font)
         DllCall("Gdiplus.dll\GdipDeleteFont", "Ptr", This.Font)
      This.Delete("Bitmap")
      This.Delete("Graphics")
      This.Delete("Font")
      This.FreeBitmaps()
      ; This.GdiplusShutdown()
      This.LastError := Msg
      Return False
   }
   ; ===================================================================================================================
   ; PUBLIC METHODS ====================================================================================================
   ; ===================================================================================================================
   Create(HWND, Options*) {
      ; Windows constants
      Static BCM_SETIMAGELIST := 0x1602
           , BS_CHECKBOX := 0x02, BS_RADIOBUTTON := 0x04, BS_GROUPBOX := 0x07, BS_AUTORADIOBUTTON := 0x09
           , BS_LEFT := 0x0100, BS_RIGHT := 0x0200, BS_CENTER := 0x0300, BS_TOP := 0x0400, BS_BOTTOM := 0x0800
           , BS_VCENTER := 0x0C00, BS_BITMAP := 0x0080
           , BUTTON_IMAGELIST_ALIGN_LEFT := 0, BUTTON_IMAGELIST_ALIGN_RIGHT := 1, BUTTON_IMAGELIST_ALIGN_CENTER := 4
           , ILC_COLOR32 := 0x20
           , OBJ_BITMAP := 7
           , RCBUTTONS := BS_CHECKBOX | BS_RADIOBUTTON | BS_AUTORADIOBUTTON
           , SA_LEFT := 0x00, SA_CENTER := 0x01, SA_RIGHT := 0x02
           , WM_GETFONT := 0x31
      ; ----------------------------------------------------------------------------------------------------------------
      This.LastError := ""
      ; ----------------------------------------------------------------------------------------------------------------
      ; Check HWND
      If !DllCall("User32.dll\IsWindow", "Ptr", HWND)
         Return This.SetError("Invalid parameter HWND!")
      ; ----------------------------------------------------------------------------------------------------------------
      ; Check Options
      If !(IsObject(Options)) || (Options.MinIndex() <> 1) || (Options.MaxIndex() > This.MaxOptions)
         Return This.SetError("Invalid parameter Options!")
      ; ----------------------------------------------------------------------------------------------------------------
      ; Get and check control's class and styles
      WinGetClass, BtnClass, ahk_id %HWND%
      ControlGet, BtnStyle, Style, , , ahk_id %HWND%
      If (BtnClass != "Button") || ((BtnStyle & 0xF ^ BS_GROUPBOX) = 0) || ((BtnStyle & RCBUTTONS) > 1)
         Return This.SetError("The control must be a pushbutton!")
      ; ----------------------------------------------------------------------------------------------------------------
      ; Load GdiPlus
      ; If !This.GdiplusStartup()
         ; Return This.SetError("GDIPlus could not be started!")
      ; ----------------------------------------------------------------------------------------------------------------
      ; Get the button's font
      GDIPFont := 0
      HFONT := DllCall("User32.dll\SendMessage", "Ptr", HWND, "UInt", WM_GETFONT, "Ptr", 0, "Ptr", 0, "Ptr")
      DC := DllCall("User32.dll\GetDC", "Ptr", HWND, "Ptr")
      DllCall("Gdi32.dll\SelectObject", "Ptr", DC, "Ptr", HFONT)
      DllCall("Gdiplus.dll\GdipCreateFontFromDC", "Ptr", DC, "PtrP", PFONT)
      DllCall("User32.dll\ReleaseDC", "Ptr", HWND, "Ptr", DC)
      If !(This.Font := PFONT)
         Return This.SetError("Couldn't get button's font!")
      ; ----------------------------------------------------------------------------------------------------------------
      ; Get the button's rectangle
      VarSetCapacity(RECT, 16, 0)
      If !DllCall("User32.dll\GetWindowRect", "Ptr", HWND, "Ptr", &RECT)
         Return This.SetError("Couldn't get button's rectangle!")
      BtnW := NumGet(RECT,  8, "Int") - NumGet(RECT, 0, "Int")
      BtnH := NumGet(RECT, 12, "Int") - NumGet(RECT, 4, "Int")
      ; ----------------------------------------------------------------------------------------------------------------
      ; Get the button's caption
      ControlGetText, BtnCaption, , ahk_id %HWND%
      If (ErrorLevel)
         Return This.SetError("Couldn't get button's caption!")
      ; ----------------------------------------------------------------------------------------------------------------
      ; Create a GDI+ bitmap
      PBITMAP := 0
      DllCall("Gdiplus.dll\GdipCreateBitmapFromScan0", "Int", BtnW, "Int", BtnH, "Int", 0
            , "UInt", 0x26200A, "Ptr", 0, "PtrP", PBITMAP)
      If !(This.Bitmap := PBITMAP)
         Return This.SetError("Couldn't create the GDI+ bitmap!")
      ; Get the pointer to its graphics
      PGRAPHICS := 0
      DllCall("Gdiplus.dll\GdipGetImageGraphicsContext", "Ptr", PBITMAP, "PtrP", PGRAPHICS)
      If !(This.Graphics := PGRAPHICS)
         Return This.SetError("Couldn't get the the GDI+ bitmap's graphics!")
      ; Quality settings
      DllCall("Gdiplus.dll\GdipSetSmoothingMode", "Ptr", PGRAPHICS, "UInt", 4)
      DllCall("Gdiplus.dll\GdipSetInterpolationMode", "Ptr", PGRAPHICS, "Int", 7)
      DllCall("Gdiplus.dll\GdipSetCompositingQuality", "Ptr", PGRAPHICS, "UInt", 4)
      DllCall("Gdiplus.dll\GdipSetRenderingOrigin", "Ptr", PGRAPHICS, "Int", 0, "Int", 0)
      DllCall("Gdiplus.dll\GdipSetPixelOffsetMode", "Ptr", PGRAPHICS, "UInt", 4)
      ; ----------------------------------------------------------------------------------------------------------------
      ; Create the bitmap(s)
      This.BitMaps := []
      For Index, Option In Options {
         If !IsObject(Option)
            Continue
         BkgColor1 := BkgColor2 := TxtColor := Mode := Rounded := GuiColor := Image := ""
         ; Replace omitted options with the values of Options.1
         Loop, % This.MaxOptions {
            If (Option[A_Index] = "")
               Option[A_Index] := Options.1[A_Index]
         }
         ; -------------------------------------------------------------------------------------------------------------
         ; Check option values
         ; Mode
         Mode := SubStr(Option.1, 1 ,1)
         If !InStr("0123456789", Mode)
            Return This.SetError("Invalid value for Mode in Options[" . Index . "]!")
         ; StartColor & TargetColor
         If (Mode = 0)
         && (FileExist(Option.2) || (DllCall("Gdi32.dll\GetObjectType", "Ptr", Option.2, "UInt") = OBJ_BITMAP))
            Image := Option.2
         Else {
            If !(Option.2 + 0) && !This.HTML.HasKey(Option.2)
               Return This.SetError("Invalid value for StartColor in Options[" . Index . "]!")
            BkgColor1 := This.GetARGB(Option.2)
            If (Option.3 = "")
               Option.3 := Option.2
            If !(Option.3 + 0) && !This.HTML.HasKey(Option.3)
               Return This.SetError("Invalid value for TargetColor in Options[" . Index . "]!")
            BkgColor2 := This.GetARGB(Option.3)
         }
         ; TextColor
         If (Option.4 = "")
            Option.4 := This.DefTxtColor
         If !(Option.4 + 0) && !This.HTML.HasKey(Option.4)
            Return This.SetError("Invalid value for TxtColor in Options[" . Index . "]!")
         TxtColor := This.GetARGB(Option.4)
         ; Rounded
         Rounded := Option.5
         If (Rounded = "H")
            Rounded := BtnH * 0.5
         If (Rounded = "W")
            Rounded := BtnW * 0.5
         If !(Rounded + 0)
            Rounded := 0
         ; GuiColor
         If (Option.6 = "")
            Option.6 := This.DefGuiColor
         If !(Option.6 + 0) && !This.HTML.HasKey(Option.6)
            Return This.SetError("Invalid value for GuiColor in Options[" . Index . "]!")
         GuiColor := This.GetARGB(Option.6)
         ; BorderColor
         BorderColor := ""
         If (Option.7 <> "") {
            If !(Option.7 + 0) && !This.HTML.HasKey(Option.7)
               Return This.SetError("Invalid value for BorderColor in Options[" . Index . "]!")
            BorderColor := 0xFF000000 | This.GetARGB(Option.7) ; BorderColor must be always opaque
         }
         ; BorderWidth
         BorderWidth := Option.8 ? Option.8 : 1
         ; -------------------------------------------------------------------------------------------------------------
         ; Create a GDI+ bitmap
         ; DllCall("Gdiplus.dll\GdipCreateBitmapFromScan0", "Int", BtnW, "Int", BtnH, "Int", 0
               ; , "UInt", 0x26200A, "Ptr", 0, "PtrP", PBITMAP)
         ; Get the pointer to its graphics
         ; DllCall("Gdiplus.dll\GdipGetImageGraphicsContext", "Ptr", PBITMAP, "PtrP", PGRAPHICS)
         ; Quality settings
         ; DllCall("Gdiplus.dll\GdipSetSmoothingMode", "Ptr", PGRAPHICS, "UInt", 4)
         ; DllCall("Gdiplus.dll\GdipSetInterpolationMode", "Ptr", PGRAPHICS, "Int", 7)
         ; DllCall("Gdiplus.dll\GdipSetCompositingQuality", "Ptr", PGRAPHICS, "UInt", 4)
         ; DllCall("Gdiplus.dll\GdipSetRenderingOrigin", "Ptr", PGRAPHICS, "Int", 0, "Int", 0)
         ; DllCall("Gdiplus.dll\GdipSetPixelOffsetMode", "Ptr", PGRAPHICS, "UInt", 4)
         ; Clear the background
         DllCall("Gdiplus.dll\GdipGraphicsClear", "Ptr", PGRAPHICS, "UInt", GuiColor)
         ; Create the image
         If (Image = "") { ; Create a BitMap based on the specified colors
            PathX := PathY := 0, PathW := BtnW, PathH := BtnH
            ; Create a GraphicsPath
            DllCall("Gdiplus.dll\GdipCreatePath", "UInt", 0, "PtrP", PPATH)
            If (Rounded < 1) ; the path is a rectangular rectangle
               This.PathAddRectangle(PPATH, PathX, PathY, PathW, PathH)
            Else ; the path is a rounded rectangle
               This.PathAddRoundedRect(PPATH, PathX, PathY, PathW, PathH, Rounded)
            ; If BorderColor and BorderWidth are specified, 'draw' the border (not for Mode 7)
            If (BorderColor <> "") && (BorderWidth > 0) && (Mode <> 7) {
               ; Create a SolidBrush
               DllCall("Gdiplus.dll\GdipCreateSolidFill", "UInt", BorderColor, "PtrP", PBRUSH)
               ; Fill the path
               DllCall("Gdiplus.dll\GdipFillPath", "Ptr", PGRAPHICS, "Ptr", PBRUSH, "Ptr", PPATH)
               ; Free the brush
               DllCall("Gdiplus.dll\GdipDeleteBrush", "Ptr", PBRUSH)
               ; Reset the path
               DllCall("Gdiplus.dll\GdipResetPath", "Ptr", PPATH)
               ; Add a new 'inner' path
               PathX := PathY := BorderWidth, PathW -= BorderWidth, PathH -= BorderWidth, Rounded -= BorderWidth
               If (Rounded < 1) ; the path is a rectangular rectangle
                  This.PathAddRectangle(PPATH, PathX, PathY, PathW - PathX, PathH - PathY)
               Else ; the path is a rounded rectangle
                  This.PathAddRoundedRect(PPATH, PathX, PathY, PathW, PathH, Rounded)
               ; If a BorderColor has been drawn, BkgColors must be opaque
               BkgColor1 := 0xFF000000 | BkgColor1
               BkgColor2 := 0xFF000000 | BkgColor2               
            }
            PathW -= PathX
            PathH -= PathY
            If (Mode = 0) { ; the background is unicolored
               ; Create a SolidBrush
               DllCall("Gdiplus.dll\GdipCreateSolidFill", "UInt", BkgColor1, "PtrP", PBRUSH)
               ; Fill the path
               DllCall("Gdiplus.dll\GdipFillPath", "Ptr", PGRAPHICS, "Ptr", PBRUSH, "Ptr", PPATH)
            }
            Else If (Mode = 1) || (Mode = 2) { ; the background is bicolored
               ; Create a LineGradientBrush
               This.SetRectF(RECTF, PathX, PathY, PathW, PathH)
               DllCall("Gdiplus.dll\GdipCreateLineBrushFromRect", "Ptr", &RECTF
                     , "UInt", BkgColor1, "UInt", BkgColor2, "Int", Mode & 1, "Int", 3, "PtrP", PBRUSH)
               DllCall("Gdiplus.dll\GdipSetLineGammaCorrection", "Ptr", PBRUSH, "Int", 1)
               ; Set up colors and positions
               This.SetRect(COLORS, BkgColor1, BkgColor1, BkgColor2, BkgColor2) ; sorry for function misuse
               This.SetRectF(POSITIONS, 0, 0.5, 0.5, 1) ; sorry for function misuse
               DllCall("Gdiplus.dll\GdipSetLinePresetBlend", "Ptr", PBRUSH
                     , "Ptr", &COLORS, "Ptr", &POSITIONS, "Int", 4)
               ; Fill the path
               DllCall("Gdiplus.dll\GdipFillPath", "Ptr", PGRAPHICS, "Ptr", PBRUSH, "Ptr", PPATH)
            }
            Else If (Mode >= 3) && (Mode <= 6) { ; the background is a gradient
               ; Determine the brush's width/height
               W := Mode = 6 ? PathW / 2 : PathW  ; horizontal
               H := Mode = 5 ? PathH / 2 : PathH  ; vertical
               ; Create a LineGradientBrush
               This.SetRectF(RECTF, PathX, PathY, W, H)
               DllCall("Gdiplus.dll\GdipCreateLineBrushFromRect", "Ptr", &RECTF
                     , "UInt", BkgColor1, "UInt", BkgColor2, "Int", Mode & 1, "Int", 3, "PtrP", PBRUSH)
               DllCall("Gdiplus.dll\GdipSetLineGammaCorrection", "Ptr", PBRUSH, "Int", 1)
               ; Fill the path
               DllCall("Gdiplus.dll\GdipFillPath", "Ptr", PGRAPHICS, "Ptr", PBRUSH, "Ptr", PPATH)
            }
            Else { ; raised mode
               DllCall("Gdiplus.dll\GdipCreatePathGradientFromPath", "Ptr", PPATH, "PtrP", PBRUSH)
               ; Set Gamma Correction
               DllCall("Gdiplus.dll\GdipSetPathGradientGammaCorrection", "Ptr", PBRUSH, "UInt", 1)
               ; Set surround and center colors
               VarSetCapacity(ColorArray, 4, 0)
               NumPut(BkgColor1, ColorArray, 0, "UInt")
               DllCall("Gdiplus.dll\GdipSetPathGradientSurroundColorsWithCount", "Ptr", PBRUSH, "Ptr", &ColorArray
                   , "IntP", 1)
               DllCall("Gdiplus.dll\GdipSetPathGradientCenterColor", "Ptr", PBRUSH, "UInt", BkgColor2)
               ; Set the FocusScales
               FS := (BtnH < BtnW ? BtnH : BtnW) / 3
               XScale := (BtnW - FS) / BtnW
               YScale := (BtnH - FS) / BtnH
               DllCall("Gdiplus.dll\GdipSetPathGradientFocusScales", "Ptr", PBRUSH, "Float", XScale, "Float", YScale)
               ; Fill the path
               DllCall("Gdiplus.dll\GdipFillPath", "Ptr", PGRAPHICS, "Ptr", PBRUSH, "Ptr", PPATH)
            }
            ; Free resources
            DllCall("Gdiplus.dll\GdipDeleteBrush", "Ptr", PBRUSH)
            DllCall("Gdiplus.dll\GdipDeletePath", "Ptr", PPATH)
         } Else { ; Create a bitmap from HBITMAP or file
            If (Image + 0)
               DllCall("Gdiplus.dll\GdipCreateBitmapFromHBITMAP", "Ptr", Image, "Ptr", 0, "PtrP", PBM)
            Else
               DllCall("Gdiplus.dll\GdipCreateBitmapFromFile", "WStr", Image, "PtrP", PBM)
            ; Draw the bitmap
            DllCall("Gdiplus.dll\GdipDrawImageRectI", "Ptr", PGRAPHICS, "Ptr", PBM, "Int", 0, "Int", 0
                  , "Int", BtnW, "Int", BtnH)
            ; Free the bitmap
            DllCall("Gdiplus.dll\GdipDisposeImage", "Ptr", PBM)
         }
         ; -------------------------------------------------------------------------------------------------------------
         ; Draw the caption
         If (BtnCaption <> "") {
            ; Create a StringFormat object
            DllCall("Gdiplus.dll\GdipStringFormatGetGenericTypographic", "PtrP", HFORMAT)
            ; Text color
            DllCall("Gdiplus.dll\GdipCreateSolidFill", "UInt", TxtColor, "PtrP", PBRUSH)
            ; Horizontal alignment
            HALIGN := (BtnStyle & BS_CENTER) = BS_CENTER ? SA_CENTER
                    : (BtnStyle & BS_CENTER) = BS_RIGHT  ? SA_RIGHT
                    : (BtnStyle & BS_CENTER) = BS_Left   ? SA_LEFT
                    : SA_CENTER
            DllCall("Gdiplus.dll\GdipSetStringFormatAlign", "Ptr", HFORMAT, "Int", HALIGN)
            ; Vertical alignment
            VALIGN := (BtnStyle & BS_VCENTER) = BS_TOP ? 0
                    : (BtnStyle & BS_VCENTER) = BS_BOTTOM ? 2
                    : 1
            DllCall("Gdiplus.dll\GdipSetStringFormatLineAlign", "Ptr", HFORMAT, "Int", VALIGN)
            ; Set render quality to system default
            DllCall("Gdiplus.dll\GdipSetTextRenderingHint", "Ptr", PGRAPHICS, "Int", 0)
            ; Set the text's rectangle
            VarSetCapacity(RECT, 16, 0)
            NumPut(BtnW, RECT,  8, "Float")
            NumPut(BtnH, RECT, 12, "Float")
            ; Draw the text
            DllCall("Gdiplus.dll\GdipDrawString", "Ptr", PGRAPHICS, "WStr", BtnCaption, "Int", -1
                  , "Ptr", PFONT, "Ptr", &RECT, "Ptr", HFORMAT, "Ptr", PBRUSH)
         }
         ; -------------------------------------------------------------------------------------------------------------
         ; Create a HBITMAP handle from the bitmap and add it to the array
         DllCall("Gdiplus.dll\GdipCreateHBITMAPFromBitmap", "Ptr", PBITMAP, "PtrP", HBITMAP, "UInt", 0X00FFFFFF)
         This.BitMaps[Index] := HBITMAP
         ; Free resources
         ; DllCall("Gdiplus.dll\GdipDisposeImage", "Ptr", PBITMAP)
         DllCall("Gdiplus.dll\GdipDeleteBrush", "Ptr", PBRUSH)
         DllCall("Gdiplus.dll\GdipDeleteStringFormat", "Ptr", HFORMAT)
         ; DllCall("Gdiplus.dll\GdipDeleteGraphics", "Ptr", PGRAPHICS)
      }
      ; Now free remaining the GDI+ objects
      DllCall("Gdiplus.dll\GdipDisposeImage", "Ptr", PBITMAP)
      DllCall("Gdiplus.dll\GdipDeleteGraphics", "Ptr", PGRAPHICS)
      DllCall("Gdiplus.dll\GdipDeleteFont", "Ptr", PFONT)
      This.Delete("Bitmap")
      This.Delete("Graphics")
      This.Delete("Font")
      ; ----------------------------------------------------------------------------------------------------------------
      ; Create the ImageList
      HIL := DllCall("Comctl32.dll\ImageList_Create"
                   , "UInt", BtnW, "UInt", BtnH, "UInt", ILC_COLOR32, "Int", 6, "Int", 0, "Ptr")
      Loop, % (This.BitMaps.MaxIndex() > 1 ? 6 : 1) {
         HBITMAP := This.BitMaps.HasKey(A_Index) ? This.BitMaps[A_Index] : This.BitMaps.1
         DllCall("Comctl32.dll\ImageList_Add", "Ptr", HIL, "Ptr", HBITMAP, "Ptr", 0)
      }
      ; Create a BUTTON_IMAGELIST structure
      VarSetCapacity(BIL, 20 + A_PtrSize, 0)
      NumPut(HIL, BIL, 0, "Ptr")
      Numput(BUTTON_IMAGELIST_ALIGN_CENTER, BIL, A_PtrSize + 16, "UInt")
      ; Hide buttons's caption
      ControlSetText, , , ahk_id %HWND%
      Control, Style, +%BS_BITMAP%, , ahk_id %HWND%
      ; Assign the ImageList to the button
      SendMessage, %BCM_SETIMAGELIST%, 0, 0, , ahk_id %HWND%
      SendMessage, %BCM_SETIMAGELIST%, 0, % &BIL, , ahk_id %HWND%
      ; Free the bitmaps
      This.FreeBitmaps()
      ; ----------------------------------------------------------------------------------------------------------------
      ; All done successfully
      ; This.GdiplusShutdown()
      Return True
   }
   ; ===================================================================================================================
   ; Set the default GUI color
   SetGuiColor(GuiColor) {
      ; GuiColor     -  RGB integer value (0xRRGGBB) or HTML color name ("Red").
      If !(GuiColor + 0) && !This.HTML.HasKey(GuiColor)
         Return False
      This.DefGuiColor := (This.HTML.HasKey(GuiColor) ? This.HTML[GuiColor] : GuiColor) & 0xFFFFFF
      Return True
   }
   ; ===================================================================================================================
   ; Set the default text color
   SetTxtColor(TxtColor) {
      ; TxtColor     -  RGB integer value (0xRRGGBB) or HTML color name ("Red").
      If !(TxtColor + 0) && !This.HTML.HasKey(TxtColor)
         Return False
      This.DefTxtColor := (This.HTML.HasKey(TxtColor) ? This.HTML[TxtColor] : TxtColor) & 0xFFFFFF
      Return True
   }
}
UseGDIP(Params*) {
   Static GdipObject := ""
   Static GdipModule := ""
   Static GdipToken  := ""
   Static Load := UseGDIP()
   If (GdipModule = "") {
      If !(GdipModule := DllCall("LoadLibrary", "Str", "Gdiplus.dll", "UPtr")) {
         MsgBox, 262160, UseGDIP, The Gdiplus.dll could not be loaded!`n`nThe program will exit!
         ExitApp
      }
      Else {
         VarSetCapacity(SI, 24, 0), NumPut(1, SI, 0, "UInt") ; size of 64-bit structure
         If DllCall("Gdiplus.dll\GdiplusStartup", "PtrP", GdipToken, "Ptr", &SI, "Ptr", 0) {
            MsgBox, 262160, UseGDIP, GDI+ could not be startet!`n`nThe program will exit!
            ExitApp
         }
         GdipObject := {Base: {__Delete: Func("UseGDIP").Bind(GdipModule, GdipToken)}}
      }
   }
   Else If (Params[1] = GdipModule) && (Params[2] = GdipToken) {
      DllCall("Gdiplus.dll\GdiplusShutdown", "Ptr", GdipToken)
      DllCall("FreeLibrary", "Ptr", GdipModule)
   }
}

hotkeyguy
Posts: 170
Joined: 11 Oct 2014, 12:22

Re: [Class] ImageButton

Post by hotkeyguy » 09 Oct 2019, 08:51

Hello just me,

many thanks for your modified class script! With your code my script is > 50% faster (today > 2s org. vs. < 1s mod.). That's great, really!


Greetings
hotkeyguy

User avatar
mslonik
Posts: 144
Joined: 21 Feb 2019, 04:38
Location: Poland
Contact:

Re: [Class] ImageButton

Post by mslonik » 22 Apr 2020, 15:17

Hello just me,

thank you for this great piece of AutoHotkey class!

Please help me determine what I'm doing wrong. I get plenty of warnings related to the class, coming from the following section of code:

Code: Select all

Gui, % "Layer" . LayerIndex . ": Add", Button
	, % "x" . ButtonX . " y" . ButtonY . " w" . ButtonW . " h" . ButtonH . " hwnd" . LayerIndex . "_" . A_Index . "hwnd" . " gL_PicturePressed"
	, % LayerIndex . "_" . A_Index 
                         
temp1 	:= LayerIndex . "_" . A_Index . "hwnd"
Opt1 	:= {1: 0, 2: ButtonP, 4: "Blue"}
Opt2 	:= {2: ButtonP, 4: "Yellow"}
Opt5 	:= {2: ButtonP, 4: "Yellow"}

If !ImageButton.Create(%temp1%, Opt1, Opt2, , , Opt5)
	MsgBox, 0, ImageButton Error Btn4, % ImageButton.LastError

The warnings:

Code: Select all

Warning in #include file "C:\Users\macie\Documents\GitHub\Autohotkey-scripts\Otagle2\Class_ImageButton.ahk":
     This variable has not been assigned a value.

Specifically: GDIPToken  (a local variable)
Specifically: PFONT (a local variable)
Specifically: PBITMAP (a local variable)
Specifically: PGRAPHICS (a local variable)
Specifically: PBM (a local variable)
Specifically: HFORMAT (a local variable)
Specifically: PBRUSH (a local variable)
Specifically: HBITMAP (a local variable)
(...)
Kind regards, mslonik

My scripts on this forum: Hotstrings Diacritic O T A G L E
Please become my patreon: Patreon👍
Written in AutoHotkey text replacement tool: Hotstrings.technology
Courses on AutoHotkey :ugeek:

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

Re: [Class] ImageButton

Post by just me » 23 Apr 2020, 05:54

Hello, which version of the class do you use? (I usually don't use #Warn)

User avatar
mslonik
Posts: 144
Joined: 21 Feb 2019, 04:38
Location: Poland
Contact:

Re: [Class] ImageButton

Post by mslonik » 23 Apr 2020, 10:26

Hello just me,

thank you for your feedback.

Code: Select all

; Namespace:         ImageButton
; Function:          Create images and assign them to pushbuttons.
; Tested with:       AHK 1.1.14.03 (A32/U32/U64)
; Tested on:         Win 7 (x64)
; Change history:    1.4.00.00/2014-06-07/just me - fixed bug for button caption = "0", "000", etc.
I assume something wrong is with my code (previous post), not with your class. Why? Because the simple code (based on provided example) which I've prepared in parallel (to the code from my previous code and mimics it as much as possible) works like a charm, without any warning:

Code: Select all

#NoEnv
SetBatchLines, -1
#Include Class_ImageButton.ahk
; ----------------------------------------------------------------------------------------------------------------------
Gui, Margin, 50, 20
Gui, Font, s18, Arial Black

pic3 := "C:\Users\macie\Documents\GitHub\Autohotkey-scripts\Otagle2\Layer1\KeePass_60x60.png"

; Common button --------------------------------------------------------------------------------------------------------

var1 := "HBT"
var2 := 4
Gui, Add, Button, % "x5 y5 w200 h30" . " v" . var1 . "_" . var2 . " gBLabel" . " hwnd" . var1 . "_" . var2 . "HWND", % var1 . "_" . var2
Opt1 := {1: 0, 2: pic3, 4: "Blue"}
Opt2 := {2: pic3, 4: "Yellow"} 
Opt5 := {2: pic3, 4: "Yellow"}

var3 := var1 . "_" . var2 . "HWND"
If !ImageButton.Create(%var3%, Opt1, Opt2, , , Opt5)
   MsgBox, 0, ImageButton Error Btn4, % ImageButton.LastError
Gui, Show, , Image Buttons
Return

BLabel:
return
; ----------------------------------------------------------------------------------------------------------------------
GuiClose:
GuiEscape:
ExitApp
Anyway I can't figure out where the difference is :shock: and what I'm doing wrong... Any help would be appreciated.

The whole project ("full file"): https://github.com/mslonik/Autohotkey-scripts/tree/master/Otagle2

Kind regards, Maciej Słojewski

My scripts on this forum: Hotstrings Diacritic O T A G L E
Please become my patreon: Patreon👍
Written in AutoHotkey text replacement tool: Hotstrings.technology
Courses on AutoHotkey :ugeek:

gregster
Posts: 8988
Joined: 30 Sep 2013, 06:48

Re: [Class] ImageButton

Post by gregster » 23 Apr 2020, 10:37

Well, in your new code, you don't seem to have a #Warn line (you probably had one somewhere in your other code. Edit: line 9 here: https://github.com/mslonik/Autohotkey-scripts/blob/master/Otagle2/Otagle.ahk).
Using #Warn in a script is always optional, it's a tool that people can use for debugging (but don't have to).

Warnings are not equvalent to errors. They'll just warn you about potential problems or things that could be an error.

The warnings you got before just seem to indicate that some local variables were read that hadn't been assigned a value before.
In AHK, many people (me included) don't care about this, because AHK doesn't strictly require variables to be initialized before being read.

User avatar
mslonik
Posts: 144
Joined: 21 Feb 2019, 04:38
Location: Poland
Contact:

Re: [Class] ImageButton

Post by mslonik » 26 Apr 2020, 16:36

Hi gregster,

thank you for explanation. Indeed without #Warn everything works as expected too. It seems that I was too caution this time.

Kind regards, mslonik

My scripts on this forum: Hotstrings Diacritic O T A G L E
Please become my patreon: Patreon👍
Written in AutoHotkey text replacement tool: Hotstrings.technology
Courses on AutoHotkey :ugeek:

tmplinshi
Posts: 1604
Joined: 01 Oct 2013, 14:57

Re: [Class] ImageButton

Post by tmplinshi » 24 Aug 2020, 00:06

I found that ControlSetText, , , ahk_id %HWND% at Class_ImageButton.ahk#L457 is one of the reasons that slows down this class. To fix it, you can add SetControlDelay, -1 before it, or replace it with GuiControl,, %HWND%. Another reason, is the repeated execution of GdiplusStartup/GdiplusShutdown that most of you knows.

Here is a class that you can use to optimize the 2 issues mentioned above, without modifying Class_ImageButton.ahk:

Class_ImageButtonOptimize.ahk:

Code: Select all

/*
	Usage:
	
		SetBatchLines, -1
		#Include Class_ImageButton.ahk
		#Include Class_ImageButtonOptimize.ahk

		ImageButton.OptimizeBegin()

			ImageButton.Create(...)
			ImageButton.Create(...)
			...

		ImageButton.OptimizeEnd()
*/
class ImageButtonOptimize
{
	static _ := ImageButton.base := ImageButtonOptimize

	OptimizeBegin() {
		this.GdiplusStartup()

		this.ctrlDelay := A_ControlDelay
		SetControlDelay, -1

		this.__GdiplusStartup  := this.GdiplusStartup
		this.__GdiplusShutdown := this.GdiplusShutdown

		this.GdiplusStartup    := this.ReturnTrue
		this.GdiplusShutdown   := this.ReturnTrue
	}

	OptimizeEnd() {
		this.GdiplusStartup    := this.__GdiplusStartup
		this.GdiplusShutdown   := this.__GdiplusShutdown
		
		this.__GdiplusStartup  := ""
		this.__GdiplusShutdown := ""

		this.GdiplusShutdown()
		SetControlDelay, % this.ctrlDelay
	}

	ReturnTrue() {
		return true
	}
}
Speed test example:

Code: Select all

SetBatchLines, -1
#Include <Class_ImageButton>
;#Include Class_ImageButtonOptimize.ahk ;Included below

ToolTip, Creating buttons (1)...
{
	StartTime1 := A_TickCount

	Create20Buttons()

	ElapsedTime1 := A_TickCount - StartTime1
}

ToolTip, Creating buttons (2)...
{
	StartTime2 := A_TickCount

	ImageButton.OptimizeBegin()
	Create20Buttons()
	ImageButton.OptimizeEnd()

	ElapsedTime2 := A_TickCount - StartTime2
}

ToolTip
Gui, Show,, ElapsedTime1 = %ElapsedTime1%ms`, ElapsedTime2 = %ElapsedTime2%ms
Return

GuiClose:
GuiEscape:
ExitApp

Create20Buttons()
{
	Options := [ [0, 0xffffff,, "Black", "W",, 0xcccccc, 2]
	           , [0, 0xffffff,, "Red"  , "W",, "Red"   , 2]
	           , [0, "Red"   ,, "White", "W",, 0xcccccc, 2] ]

	Loop, 20
	{
		pos := (A_Index ~= "^(1|11)$") ? "xm" : "x+10"
		Gui, Add, Button, hWndHBT w40 h40 %pos%, % A_Index
		ImageButton.Create(HBT, Options*)
	}
}

/*
	Usage:
	
		SetBatchLines, -1
		#Include Class_ImageButton.ahk
		#Include Class_ImageButtonOptimize.ahk

		ImageButton.OptimizeBegin()

			ImageButton.Create(...)
			ImageButton.Create(...)
			...

		ImageButton.OptimizeEnd()
*/
class ImageButtonOptimize
{
	static _ := ImageButton.base := ImageButtonOptimize

	OptimizeBegin() {
		this.GdiplusStartup()

		this.ctrlDelay := A_ControlDelay
		SetControlDelay, -1

		this.__GdiplusStartup  := this.GdiplusStartup
		this.__GdiplusShutdown := this.GdiplusShutdown

		this.GdiplusStartup    := this.ReturnTrue
		this.GdiplusShutdown   := this.ReturnTrue
	}

	OptimizeEnd() {
		this.GdiplusStartup    := this.__GdiplusStartup
		this.GdiplusShutdown   := this.__GdiplusShutdown
		
		this.__GdiplusStartup  := ""
		this.__GdiplusShutdown := ""

		this.GdiplusShutdown()
		SetControlDelay, % this.ctrlDelay
	}

	ReturnTrue() {
		return true
	}
}
Result: ElapsedTime1 = 625ms, ElapsedTime2 = 47ms

evilmanimani
Posts: 29
Joined: 24 Jun 2020, 16:42

Re: [Class] ImageButton

Post by evilmanimani » 15 Dec 2020, 05:27

I'm attempting to load an image that has transparency, but it's just showing a plain black background rather than the gui background color behind it. I've tested with .png, .ico and also from imageres.dll, so for instance:

Code: Select all

pic1 := LoadPicture("button.ico", "GDI+")
pic2 := LoadPicture("imageres.dll", "GDI+ Icon242")
Gui, Add, Button, hwndButton1 w40 h40 vButton1
Opt1 := [0, pic1]
ImageButton.Create(Button1, Opt1)
Gui, Add, Button, hwndButton2 w40 h40 vButton2
Opt1 := [0, pic2]
ImageButton.Create(Button2, Opt1)
Both images appear, but black where the transparency would be, I've messed around with the different parameters with no luck. Any ideas?

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

Re: [Class] ImageButton

Post by just me » 15 Dec 2020, 07:24

Code: Select all

pic1 := LoadPicture("button.ico", "GDI+")
Converts an icon into a GDI bitmap. Within the class

Code: Select all

DllCall("Gdiplus.dll\GdipCreateBitmapFromHBITMAP", "Ptr", Image, "Ptr", 0, "PtrP", PBM)
converts the GDI bitmap into a GDI+ image.
Apparently, transparency gets lost because of the second conversion.

ATM you cannot pass an icon handle (HICO) to the class. I'll think about it.

evilmanimani
Posts: 29
Joined: 24 Jun 2020, 16:42

Re: [Class] ImageButton

Post by evilmanimani » 15 Dec 2020, 15:48

Appreciate the reply. I initially had used the DummyGui method to load the image, but that had the same result. Is there a workaround for this at the moment to maintain the transparency? If not and it'd require the handle to go to the class, I could try my hand at playing with it on my end, if you would know offhand which method I'd need to mess around with, or if you had a rough idea of what that might look like.

Also, I noticed it isn't possible to GuiControl the button text after it's been passed through the class, is this a limitation? Would I need to remove then create the button with the new text, then pass it through the class again to change the label?

edit:
Figured out the second part, just had to re-run the button through ImageButton.Create, and GuiControl, Show again. Also found the DestroyBtnImgList(HBTN) function you posted a while back to prevent a memory leak. Seems to work well despite a little flickering while it redraws the button, but I can live with that.

edit edit:
For the flickering, I thought I could just call the DestroyBtnImgList function after a number of counts of redrawing with ImageButton.Create, but I'm guessing that it it only applies to the most recent instance because it still leaked memory, albeit a bit more slowly.

Code: Select all

DestroyBtnImgList(HBTN) {
   ; BCM_GETIMAGELIST = 0x1603
   VarSetCapacity(BtnImgList, A_PtrSize + 24, 0)
   SendMessage, 0x1603, 0, % &BtnImgList, , ahk_id %HBTN%
   Return IL_Destroy(NumGet(BtnImgList, "UPtr"))
}
I'm wondering if there's some way to keep track of multiple instances and clear them all at once, or if it even works that way.
Last edited by evilmanimani on 16 Dec 2020, 15:07, edited 1 time in total.

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

Re: [Class] ImageButton

Post by just me » 16 Dec 2020, 04:06

I'll add support for icons soon.

If you want to test the result, change the following within ImageButton.Create():

Code: Select all

         ; StartColor & TargetColor
         If (Mode = 0)
         && (FileExist(Option.2) || (DllCall("Gdi32.dll\GetObjectType", "Ptr", Option.2, "UInt") = OBJ_BITMAP))
            Image := Option.2
; change to >>>>>>>>>>
         ; StartColor & TargetColor
         If (Mode = 0)
         && (FileExist(Option.2) || (Option.2 + 0) ; (DllCall("Gdi32.dll\GetObjectType", "Ptr", Option.2, "UInt") = OBJ_BITMAP))
            Image := Option.2
...
...
...
         } Else { ; Create a bitmap from HBITMAP or file
            If (Image + 0)
               DllCall("Gdiplus.dll\GdipCreateBitmapFromHBITMAP", "Ptr", Image, "Ptr", 0, "PtrP", PBM)
; change to >>>>>>>>>>
         } Else { ; Create a bitmap from HBITMAP or file
            If (Image + 0)
               DllCall("Gdiplus.dll\GdipCreateBitmapFromHICON", "Ptr", Image, "PtrP", PBM)
Note: You cannot pass HBITMAP handles any more after that.

The button's caption becomes part of an image, so it's impossible to change the caption without recreating the image.

ATM you have to call DestroyBtnImgList(HBTN) every time before you recreate the button's image list. I'll add it to the class.

User avatar
iilabs
Posts: 296
Joined: 07 Jun 2020, 16:57

Re: [Class] ImageButton

Post by iilabs » 17 Dec 2020, 10:59

tmplinshi wrote:
22 Dec 2013, 14:43
Nice!
I usually create buttons through http://dabuttonfactory.com/, then use your Image2Include to convert them.
Very nice. Can you give a detailed tutorial how you implement into AHK or does Image2Include do that for you? :thumbup:

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

Re: [Class] ImageButton - 1.5.00.00 - 20201230

Post by just me » 30 Dec 2020, 09:31

Update 1.5.00.00 on 2020-12-30

Post Reply

Return to “Scripts and Functions (v1)”