Gui: How to draw a small circle Topic is solved

Get help with using AutoHotkey (v2 or newer) and its commands and hotkeys
songdg
Posts: 630
Joined: 04 Oct 2017, 20:04

Gui: How to draw a small circle

Post by songdg » 25 May 2024, 22:22

I want to draw a small circle similar to the square of the Color Picker that fill with color represent the choosen color of the button.

Code: Select all

; AHK v2
; originally posted by maestrith
; https://autohotkey.com/board/topic/94083-ahk-11-font-and-color-dialogs/

; ===============================================================
; Example
; ===============================================================

global cc, defColor
cc := 0x00FF00 ; green
defColor := [0xAA0000,0x00AA00,0x0000AA,0x000BBB,0xFFFFAA]

oGui := Gui("-MinimizeBox -MaximizeBox","Choose Color")
oGui.OnEvent("close",close_event)
oGui.OnEvent("escape",close_event)
oGui.Add('text', 'vtxtCtrl1', "My Form")
choose_bnt := oGui.AddButton("w150","Choose Color").OnEvent("click",choose_event.Bind(oGui['txtCtrl1']))
oGui.Show("")
return

choose_event(ctl,*) {
    ;Global cc, defColor
    Global defColor

    hwnd := ctl.gui.hwnd ; grab hwnd
    cc := ColorSelect(0,hwnd,&defColor,0)

    If (cc = -1)
        return

    colorList := ""
    For k, v in defColor ; if user changes Custom Colors, they will be stored in defColor array
        If v
            colorList .= "Index: " k " / Color: " Format("0x{:06X}",v) "`r`n"

    ctl.Opt('c' cc)
    ;MsgBox cc

    ;ctl.Opt('cred')
}

close_event(guiObj) {
    ExitApp
}

; ===============================================================
; END Example
; ===============================================================

; =============================================================================================
; Parameters
; =============================================================================================
; Color           = Start color (0 = black) - Format = 0xRRGGBB
; hwnd            = Parent window
; custColorObj    = Array() to load/save custom colors, must be &VarRef
; disp            = 1=full / 0=basic ... full displays custom colors panel, basic does not
; =============================================================================================
; All params are optional.  With no hwnd the dialog will show at top left of screen.  Use an
; object serializer (like JSON) to save/load custom colors to/from disk.
; =============================================================================================

ColorSelect(Color := 0, hwnd := 0, &custColorObj := "",disp:=false) {
    Static p := A_PtrSize
    disp := disp ? 0x3 : 0x1 ; init disp / 0x3 = full panel / 0x1 = basic panel

    If (custColorObj.Length > 16)
        throw Error("Too many custom colors.  The maximum allowed values is 16.")

    Loop (16 - custColorObj.Length)
        custColorObj.Push(0) ; fill out custColorObj to 16 values

    CUSTOM := Buffer(16 * 4, 0) ; init custom colors obj
    CHOOSECOLOR := Buffer((p=4)?36:72,0) ; init dialog

    If (IsObject(custColorObj)) {
        Loop 16 {
            custColor := RGB_BGR(custColorObj[A_Index])
            NumPut "UInt", custColor, CUSTOM, (A_Index-1) * 4
        }
    }

    NumPut "UInt", CHOOSECOLOR.size, CHOOSECOLOR, 0             ; lStructSize
    NumPut "UPtr", hwnd,             CHOOSECOLOR, p             ; hwndOwner
    NumPut "UInt", RGB_BGR(color),   CHOOSECOLOR, 3 * p         ; rgbResult
    NumPut "UPtr", CUSTOM.ptr,       CHOOSECOLOR, 4 * p         ; lpCustColors
    NumPut "UInt", disp,             CHOOSECOLOR, 5 * p         ; Flags

    if !DllCall("comdlg32\ChooseColor", "UPtr", CHOOSECOLOR.ptr, "UInt")
        return -1

    custColorObj := []
    Loop 16 {
        newCustCol := NumGet(CUSTOM, (A_Index-1) * 4, "UInt")
        custColorObj.InsertAt(A_Index, RGB_BGR(newCustCol))
    }

    Color := NumGet(CHOOSECOLOR, 3 * A_PtrSize, "UInt")
    return Format("0x{:06X}",RGB_BGR(color))

    RGB_BGR(c) {
        return ((c & 0xFF) << 16 | c & 0xFF00 | c >> 16)
    }
}

; typedef struct tagCHOOSECOLORW {  offset      size    (x86/x64)
  ; DWORD        lStructSize;       |0      |   4
  ; HWND         hwndOwner;         |4 / 8  |   8 /16
  ; HWND         hInstance;         |8 /16  |   12/24
  ; COLORREF     rgbResult;         |12/24  |   16/28
  ; COLORREF     *lpCustColors;     |16/28  |   20/32
  ; DWORD        Flags;             |20/32  |   24/36
  ; LPARAM       lCustData;         |24/40  |   28/48 <-- padding for x64
  ; LPCCHOOKPROC lpfnHook;          |28/48  |   32/56
  ; LPCWSTR      lpTemplateName;    |32/56  |   36/64
  ; LPEDITMENU   lpEditInfo;        |36/64  |   40/72
; } CHOOSECOLORW, *LPCHOOSECOLORW;
Attachments
circle.png
circle.png (16.83 KiB) Viewed 1073 times

ntepa
Posts: 439
Joined: 19 Oct 2022, 20:52

Re: Gui: How to draw a small circle

Post by ntepa » 25 May 2024, 23:53

Try this:

Code: Select all

choose_event(ctl,*) {
    Global defColor

    hwnd := ctl.gui.hwnd ; grab hwnd
    cc := ColorSelect(0,hwnd,&defColor,0)

    If (cc = -1)
        return

    colorList := ""
    For k, v in defColor ; if user changes Custom Colors, they will be stored in defColor array
        If v
            colorList .= "Index: " k " / Color: " Format("0x{:06X}",v) "`r`n"

    ctl.Opt('c' cc)

    ctl.GetPos(&X, &Y, &W, &H)
    size := 20 ; size of circle
    XStart := X + W + 10
    YStart := 2

    DrawCircle(ctl.gui.hwnd, XStart, YStart, size, cc)
}

DrawCircle(hwnd, x, y, size, color, alpha := 255) {
    ARGB := (alpha << 24) | color
    hdc := DllCall("GetDC", "UPtr", hwnd)

    hModule := DllCall("LoadLibrary", "Str", "gdiplus", "UPtr")
    si := Buffer(A_PtrSize = 8 ? 24 : 16, 0), NumPut("UInt", 1, si, 0)
    DllCall("gdiplus\GdiplusStartup", "UPtr*", &pToken := 0, "Ptr", si, "UPtr", 0)
    DllCall("gdiplus\GdipCreateFromHDC", "UPtr", hdc, "UPtr*", &pGraphics := 0)
    DllCall("gdiplus\GdipCreateSolidFill", "UInt", ARGB, "UPtr*", &pBrush := 0)
    DllCall("gdiplus\GdipSetSmoothingMode", "UPtr", pGraphics, "Int", 4) ; antialias
    DllCall("gdiplus\GdipFillEllipse", "UPtr", pGraphics, "UPtr", pBrush, "Float", x, "Float", y, "Float", size, "Float", size)

    DllCall("gdiplus\GdipDeleteBrush", "UPtr", pBrush)
    DllCall("gdiplus\GdipDeleteGraphics", "UPtr", pGraphics)
    DllCall("gdiplus\GdiplusShutdown", "UPtr", pToken)
    DllCall("ReleaseDC", "UPtr", hwnd, "UPtr", hdc)
    DllCall("FreeLibrary", "UPtr", hModule)
}


teadrinker
Posts: 4412
Joined: 29 Mar 2015, 09:41
Contact:

Re: Gui: How to draw a small circle

Post by teadrinker » 26 May 2024, 00:56

@ntepa
Such a circle would be short-lived! :)
Should be:

Code: Select all

#Requires AutoHotkey v2

circleDiam := 25
circleColor := 0xFF0000

wnd := Gui()
wnd.SetFont('s12', 'Calibri')
text := wnd.AddText('xm y50', 'This is a small circle:')
ControlGetPos(&textX, &textY, &textW, &textH, text)
wnd.Show('h130 w' . textX + textW + 10 + circleDiam + wnd.MarginX)
WM_PAINT('', '', '', wnd.hwnd)
OnMessage(0xF, WM_PAINT)

WM_PAINT(wp, lp, msg, hwnd) {
    if hwnd = wnd.hwnd {
        DrawCircle(hwnd, textX + textW + 10, textY - (circleDiam - textH) / 2, circleDiam, circleColor)
    }
}

DrawCircle(hwnd, x, y, size, color, alpha := 255) {
    ARGB := (alpha << 24) | color
    hdc := DllCall("GetDC", "UPtr", hwnd)

    hModule := DllCall("LoadLibrary", "Str", "gdiplus", "UPtr")
    si := Buffer(A_PtrSize = 8 ? 24 : 16, 0), NumPut("UInt", 1, si, 0)
    DllCall("gdiplus\GdiplusStartup", "UPtr*", &pToken := 0, "Ptr", si, "UPtr", 0)
    DllCall("gdiplus\GdipCreateFromHDC", "UPtr", hdc, "UPtr*", &pGraphics := 0)
    DllCall("gdiplus\GdipCreateSolidFill", "UInt", ARGB, "UPtr*", &pBrush := 0)
    DllCall("gdiplus\GdipSetSmoothingMode", "UPtr", pGraphics, "Int", 4) ; antialias
    DllCall("gdiplus\GdipFillEllipse", "UPtr", pGraphics, "UPtr", pBrush, "Float", x, "Float", y, "Float", size, "Float", size)

    DllCall("gdiplus\GdipDeleteBrush", "UPtr", pBrush)
    DllCall("gdiplus\GdipDeleteGraphics", "UPtr", pGraphics)
    DllCall("gdiplus\GdiplusShutdown", "UPtr", pToken)
    DllCall("ReleaseDC", "UPtr", hwnd, "UPtr", hdc)
    DllCall("FreeLibrary", "UPtr", hModule)
}

Rohwedder
Posts: 7776
Joined: 04 Jun 2014, 08:33
Location: Germany

Re: Gui: How to draw a small circle

Post by Rohwedder » 26 May 2024, 01:38

Hallo,
replace:

Code: Select all

oGui.Add('text', 'vtxtCtrl1', "My Form")
choose_bnt := oGui.AddButton("w150","Choose Color").OnEvent("click",choose_event.Bind(oGui['txtCtrl1']))
by:

Code: Select all

oGui.Add('text',, "My Form")
oGui.Add('text', 'vtxtCtrl1 ys-12 h30', ' ' Chr(0x2B24) '`t').SetFont("S22") ; circle
choose_bnt := oGui.AddButton("xm ys+20 w150","Choose Color").OnEvent("click",choose_event.Bind(oGui['txtCtrl1']))

ntepa
Posts: 439
Joined: 19 Oct 2022, 20:52

Re: Gui: How to draw a small circle

Post by ntepa » 26 May 2024, 02:22

@teadrinker thanks for the correction.
Rohwedder's version might be better. I didn't think about using a circle character.

teadrinker
Posts: 4412
Joined: 29 Mar 2015, 09:41
Contact:

Re: Gui: How to draw a small circle

Post by teadrinker » 26 May 2024, 04:40

ntepa wrote: Rohwedder's version might be better
Yes, this option is certainly easier, but you have to guess the coordinates.

songdg
Posts: 630
Joined: 04 Oct 2017, 20:04

Re: Gui: How to draw a small circle

Post by songdg » 26 May 2024, 21:56

Rohwedder wrote:
26 May 2024, 01:38
Hallo,
replace:

Code: Select all

oGui.Add('text', 'vtxtCtrl1', "My Form")
choose_bnt := oGui.AddButton("w150","Choose Color").OnEvent("click",choose_event.Bind(oGui['txtCtrl1']))
by:

Code: Select all

oGui.Add('text',, "My Form")
oGui.Add('text', 'vtxtCtrl1 ys-12 h30', ' ' Chr(0x2B24) '`t').SetFont("S22") ; circle
choose_bnt := oGui.AddButton("xm ys+20 w150","Choose Color").OnEvent("click",choose_event.Bind(oGui['txtCtrl1']))
@ntepa
@Rohwedder
Thanks both of you, there's a issue if I place the "Choose Color" button at the right side of the circle, something like that happen when choose another color.

Code: Select all

choose_bnt := oGui.AddButton("x+10 ys-5 w150","Choose Color").OnEvent("click",choose_event.Bind(oGui['txtCtrl1']))
Attachments
issue1.png
issue1.png (3.26 KiB) Viewed 829 times

songdg
Posts: 630
Joined: 04 Oct 2017, 20:04

Re: Gui: How to draw a small circle

Post by songdg » 26 May 2024, 22:35

@teadrinker
Thank you very much, your option surely a better one, but how to use a button with Color Picker to update the color, also if there's several text with several circles and buttons in the Gui :?:
Attachments
lineup.png
lineup.png (8.71 KiB) Viewed 805 times

teadrinker
Posts: 4412
Joined: 29 Mar 2015, 09:41
Contact:

Re: Gui: How to draw a small circle

Post by teadrinker » 27 May 2024, 19:30

It's better like this:

Code: Select all

#Requires AutoHotkey v2

smallCircleDiam := 20
mediumCircleDiam := 35
largeCircleDiam := 50

wnd := Gui()
wnd.MarginX := wnd.MarginY := 25
wnd.SetFont('s12', 'Calibri')

getPicY := (size, h) => (y := size/2 - h/2, y > 0 ? '-' . y : '+' . Abs(y))
for size, str in Map( smallCircleDiam , 'This is a small circle:',
                      mediumCircleDiam, 'This is a medium circle:',
                      largeCircleDiam , 'This is a large circle:' )
{
    wnd.AddText('xm', str).GetPos(, &textY,, &textH)
    pic := wnd.AddPicture('x+10 yp' . getPicY(size, textH) . ' w' . size + 1 . ' h' . size + 1) ; size with a small allowance
    wnd.AddButton('x+10 h30 y' . textY - 4, 'Choose Color').OnEvent('Click', SetImage.Bind(pic))
    wnd['Button' . A_Index].GetPos(&buttX)
    (maxButtX ?? 0) < buttX && maxButtX := buttX
}
; vertical align the buttons
Loop 3 {
    wnd['Button' . A_Index].Move(maxButtX)
}
wnd.Show()

SetImage(pic, *) {
    static clr := 0x7f7f7f
    if (res := ChooseColor(clr, pic.Gui.hwnd, 1)) != -1 {
        clr := res
        WinGetPos(,, &w,, pic.hwnd)
        pic.Value := 'HBITMAP: ' . DrawCircle(w, clr)
    }
}

ChooseColor(initColor := 0, hWnd := 0, flags := 3) { ; flags: CC_RGBINIT = 1, CC_FULLOPEN = 2, CC_PREVENTFULLOPEN = 4
    static customColors := Buffer(64, 0xAA), CHOOSECOLOR := Buffer(A_PtrSize * 9)
         , _ := NumPut('Ptr', customColors.ptr, NumPut('Ptr', CHOOSECOLOR.size, CHOOSECOLOR) + A_PtrSize * 3)
         , RGB_BGR := (color) => (color & 0xFF) << 16 | color & 0xFF00 | color >> 16

    NumPut('Ptr', hWnd, CHOOSECOLOR, A_PtrSize)
    NumPut('UInt', RGB_BGR(initColor), CHOOSECOLOR, A_PtrSize * 3)
    NumPut('UInt', flags, CHOOSECOLOR, A_PtrSize * 5)
    return DllCall('Comdlg32\ChooseColor', 'Ptr', CHOOSECOLOR) ? RGB_BGR(NumGet(CHOOSECOLOR, A_PtrSize * 3, 'UInt')) : -1
}

DrawCircle(size, color, alpha := 255) {
    #DllLoad gdiplus
    static pixFormat := PixelFormat32bppARGB := 0x26200A
    si := Buffer(A_PtrSize = 8 ? 24 : 16, 0), NumPut('UInt', 1, si, 0)
    gdiplus := (fn, params*) => DllCall('gdiplus\' . fn, params*)
    gdiplus('GdiplusStartup', 'Ptr*', &pToken := 0, 'Ptr', si, 'Ptr', 0)
    gdiplus('GdipCreateBitmapFromScan0', 'int', size, 'int', size, 'int', 0, 'int', pixFormat, 'Ptr', 0, 'Ptr*', &pBitmap := 0)
    gdiplus('GdipGetImageGraphicsContext', 'Ptr', pBitmap, 'Ptr*', &pGraphics := 0)
    gdiplus('GdipSetSmoothingMode', 'Ptr', pGraphics, 'Int', 4) ; antialias
    gdiplus('GdipCreateSolidFill', 'UInt', alpha << 24 | color, 'Ptr*', &pBrush := 0)
    gdiplus('GdipFillEllipse', 'Ptr', pGraphics, 'Ptr', pBrush, 'Float', 0, 'Float', 0, 'Float', size - 1, 'Float', size - 1)
    gdiplus('GdipCreateHBITMAPFromBitmap', 'Ptr', pBitmap, 'Ptr*', &hBitmap := 0, 'int', 0xFFFFFFFF)

    gdiplus('GdipDeleteBrush', 'Ptr', pBrush)
    gdiplus('GdipDeleteGraphics', 'Ptr', pGraphics)
    gdiplus('GdipDisposeImage', 'Ptr', pBitmap)
    gdiplus('GdiplusShutdown', 'Ptr', pToken)
    return hBitmap
}

songdg
Posts: 630
Joined: 04 Oct 2017, 20:04

Re: Gui: How to draw a small circle

Post by songdg » 28 May 2024, 03:52

teadrinker wrote:
27 May 2024, 19:30
It's better like this:

Code: Select all

#Requires AutoHotkey v2

smallCircleDiam := 20
mediumCircleDiam := 35
largeCircleDiam := 50

wnd := Gui()
wnd.MarginX := wnd.MarginY := 25
wnd.SetFont('s12', 'Calibri')

getPicY := (size, h) => (y := size/2 - h/2, y > 0 ? '-' . y : '+' . Abs(y))
for size, str in Map( smallCircleDiam , 'This is a small circle:',
                      mediumCircleDiam, 'This is a medium circle:',
                      largeCircleDiam , 'This is a large circle:' )
{
    wnd.AddText('xm', str).GetPos(, &textY,, &textH)
    pic := wnd.AddPicture('x+10 yp' . getPicY(size, textH) . ' w' . size + 1 . ' h' . size + 1) ; size with a small allowance
    wnd.AddButton('x+10 h30 y' . textY - 4, 'Choose Color').OnEvent('Click', SetImage.Bind(pic))
    wnd['Button' . A_Index].GetPos(&buttX)
    (maxButtX ?? 0) < buttX && maxButtX := buttX
}
; vertical align the buttons
Loop 3 {
    wnd['Button' . A_Index].Move(maxButtX)
}
wnd.Show()

SetImage(pic, *) {
    static clr := 0x7f7f7f
    if (res := ChooseColor(clr, pic.Gui.hwnd, 1)) != -1 {
        clr := res
        WinGetPos(,, &w,, pic.hwnd)
        pic.Value := 'HBITMAP: ' . DrawCircle(w, clr)
    }
}

ChooseColor(initColor := 0, hWnd := 0, flags := 3) { ; flags: CC_RGBINIT = 1, CC_FULLOPEN = 2, CC_PREVENTFULLOPEN = 4
    static customColors := Buffer(64, 0xAA), CHOOSECOLOR := Buffer(A_PtrSize * 9)
         , _ := NumPut('Ptr', customColors.ptr, NumPut('Ptr', CHOOSECOLOR.size, CHOOSECOLOR) + A_PtrSize * 3)
         , RGB_BGR := (color) => (color & 0xFF) << 16 | color & 0xFF00 | color >> 16

    NumPut('Ptr', hWnd, CHOOSECOLOR, A_PtrSize)
    NumPut('UInt', RGB_BGR(initColor), CHOOSECOLOR, A_PtrSize * 3)
    NumPut('UInt', flags, CHOOSECOLOR, A_PtrSize * 5)
    return DllCall('Comdlg32\ChooseColor', 'Ptr', CHOOSECOLOR) ? RGB_BGR(NumGet(CHOOSECOLOR, A_PtrSize * 3, 'UInt')) : -1
}

DrawCircle(size, color, alpha := 255) {
    #DllLoad gdiplus
    static pixFormat := PixelFormat32bppARGB := 0x26200A
    si := Buffer(A_PtrSize = 8 ? 24 : 16, 0), NumPut('UInt', 1, si, 0)
    gdiplus := (fn, params*) => DllCall('gdiplus\' . fn, params*)
    gdiplus('GdiplusStartup', 'Ptr*', &pToken := 0, 'Ptr', si, 'Ptr', 0)
    gdiplus('GdipCreateBitmapFromScan0', 'int', size, 'int', size, 'int', 0, 'int', pixFormat, 'Ptr', 0, 'Ptr*', &pBitmap := 0)
    gdiplus('GdipGetImageGraphicsContext', 'Ptr', pBitmap, 'Ptr*', &pGraphics := 0)
    gdiplus('GdipSetSmoothingMode', 'Ptr', pGraphics, 'Int', 4) ; antialias
    gdiplus('GdipCreateSolidFill', 'UInt', alpha << 24 | color, 'Ptr*', &pBrush := 0)
    gdiplus('GdipFillEllipse', 'Ptr', pGraphics, 'Ptr', pBrush, 'Float', 0, 'Float', 0, 'Float', size - 1, 'Float', size - 1)
    gdiplus('GdipCreateHBITMAPFromBitmap', 'Ptr', pBitmap, 'Ptr*', &hBitmap := 0, 'int', 0xFFFFFFFF)

    gdiplus('GdipDeleteBrush', 'Ptr', pBrush)
    gdiplus('GdipDeleteGraphics', 'Ptr', pGraphics)
    gdiplus('GdipDisposeImage', 'Ptr', pBitmap)
    gdiplus('GdiplusShutdown', 'Ptr', pToken)
    return hBitmap
}
Thank you so much, You've been a great help :thumbup: There are two more problems, I want to provide SetImage(pic) with another parameter of initial color to the circles by InitialColor := Map("small", 0xFF0080, "large", 0xFF8000), if InitialColor has a key of the circle then draw it directly with that color, otherwise till the color has been choosen. Also how to load/save the custom colors, unlike someone else's code use an array to store, can you explain a bit more customColors := Buffer(64)

teadrinker
Posts: 4412
Joined: 29 Mar 2015, 09:41
Contact:

Re: Gui: How to draw a small circle

Post by teadrinker » 28 May 2024, 04:36

songdg wrote: Also how to load/save the custom colors
What do you mean, load/save from/to a file?

songdg
Posts: 630
Joined: 04 Oct 2017, 20:04

Re: Gui: How to draw a small circle

Post by songdg » 28 May 2024, 05:06

@teadrinker
like defColor in my first post, if user changes Custom Colors, they will be stored in the defColor array, I can save the value of defColor into
an ini file, so next time run the script all the Custom Colors can be restored.

Code: Select all

choose_event(ctl,*) {
    ;Global cc, defColor
    Global defColor
    hwnd := ctl.gui.hwnd ; grab hwnd
    cc := ColorSelect(0,hwnd,&defColor,0)
    If (cc = -1)
        return
    colorList := ""
    For k, v in defColor ;if user changes Custom Colors, they will be stored in defColor array
        If v
            colorList .= "Index: " k " / Color: " Format("0x{:06X}",v) "`r`n"
    ctl.Opt('c' cc)
}

teadrinker
Posts: 4412
Joined: 29 Mar 2015, 09:41
Contact:

Re: Gui: How to draw a small circle  Topic is solved

Post by teadrinker » 28 May 2024, 14:25

You can modify the code as follows:

Code: Select all

#Requires AutoHotkey v2

customColors := [0xAA0000, 0x00AA00, 0x0000AA, 0x000BBB, 0xFFFFAA]

items := [
    Map(
        'text', 'This is a small circle:',
        'color', 0xFF0080,
        'size', 20
    ),
    Map(
        'text', 'This is a medium circle:',
        'size', 35
    ),
    Map(
        'text', 'This is a large circle:',
        'color', 0xFF8000,
        'size', 50
    )
]

wnd := Gui()
wnd.MarginX := wnd.MarginY := 25
wnd.SetFont('s12', 'Calibri')

getPicY := (size, h) => (y := size/2 - h/2, y > 0 ? '-' . y : '+' . Abs(y))
for item in items {
    wnd.AddText('xm', item['text']).GetPos(, &textY,, &textH)
    pic := wnd.AddPicture('x+10 yp' . getPicY(item['size'], textH) . ' w' . item['size'] + 1 . ' h' . item['size'] + 1,
                                        item.Has('color') ? 'HBITMAP:' . DrawCircle(item['size'], item['color']) : '')
    wnd.AddButton('x+10 h30 y' . textY - 4, 'Choose Color').OnEvent('Click', SetImage.Bind(pic, customColors))
    wnd['Button' . A_Index].GetPos(&buttX)
    (maxButtX ?? 0) < buttX && maxButtX := buttX
}
; vertical align the buttons
Loop 3 {
    wnd['Button' . A_Index].Move(maxButtX)
}
wnd.Show()

SetImage(pic, customColors, *) {
    static clr := 0x7f7f7f, flag := CC_RGBINIT := 1
    if (res := ChooseColor(clr, pic.Gui.hwnd, customColors, flag)) != -1 {
        clr := res
        WinGetPos(,, &w,, pic.hwnd)
        pic.Value := 'HBITMAP: ' . DrawCircle(w, clr)
    }
}

ChooseColor(initColor := 0, hWnd := 0, customColorsArr := '', flags := 3) { ; flags: CC_RGBINIT = 1, CC_FULLOPEN = 2, CC_PREVENTFULLOPEN = 4
    static init := false, customColors := '', CHOOSECOLOR := ''
         , RGB_BGR := color => (color & 0xFF) << 16 | color & 0xFF00 | color >> 16
    if !init {
        init := true
        if !(customColorsArr is Array) {
            customColorsArr := []
        }
        customColorsArr.Length := 16
        customColors := Buffer(64)
        Loop 16 {
            clr := customColorsArr.Has(A_Index) && IsInteger(customColorsArr[A_Index])
                ? RGB_BGR(customColorsArr[A_Index] & 0xFFFFFF) : 0xFFFFFF
            NumPut('UInt', clr, customColors, (A_Index - 1) * 4)
        }
        CHOOSECOLOR := Buffer(A_PtrSize * 9)
        NumPut('Ptr', customColors.ptr, NumPut('Ptr', CHOOSECOLOR.size, CHOOSECOLOR) + A_PtrSize * 3)
    }
    NumPut('Ptr', hWnd, CHOOSECOLOR, A_PtrSize)
    NumPut('UInt', RGB_BGR(initColor), CHOOSECOLOR, A_PtrSize * 3)
    NumPut('UInt', flags, CHOOSECOLOR, A_PtrSize * 5)
    res := DllCall('Comdlg32\ChooseColor', 'Ptr', CHOOSECOLOR)
    Loop 16 {
        customColorsArr[A_Index] := RGB_BGR(NumGet(customColors, (A_Index - 1) * 4, 'UInt'))
    }
    return res ? RGB_BGR(NumGet(CHOOSECOLOR, A_PtrSize * 3, 'UInt')) : -1
}

DrawCircle(size, color, alpha := 255) {
    #DllLoad gdiplus
    static pixFormat := PixelFormat32bppARGB := 0x26200A
    si := Buffer(A_PtrSize = 8 ? 24 : 16, 0), NumPut('UInt', 1, si, 0)
    gdiplus := (fn, params*) => DllCall('gdiplus\' . fn, params*)
    gdiplus('GdiplusStartup', 'Ptr*', &pToken := 0, 'Ptr', si, 'Ptr', 0)
    gdiplus('GdipCreateBitmapFromScan0', 'int', size, 'int', size, 'int', 0, 'int', pixFormat, 'Ptr', 0, 'Ptr*', &pBitmap := 0)
    gdiplus('GdipGetImageGraphicsContext', 'Ptr', pBitmap, 'Ptr*', &pGraphics := 0)
    gdiplus('GdipSetSmoothingMode', 'Ptr', pGraphics, 'Int', 4) ; antialias
    gdiplus('GdipCreateSolidFill', 'UInt', alpha << 24 | color, 'Ptr*', &pBrush := 0)
    gdiplus('GdipFillEllipse', 'Ptr', pGraphics, 'Ptr', pBrush, 'Float', 0, 'Float', 0, 'Float', size - 1, 'Float', size - 1)
    gdiplus('GdipCreateHBITMAPFromBitmap', 'Ptr', pBitmap, 'Ptr*', &hBitmap := 0, 'int', 0xFFFFFFFF)

    gdiplus('GdipDeleteBrush', 'Ptr', pBrush)
    gdiplus('GdipDeleteGraphics', 'Ptr', pGraphics)
    gdiplus('GdipDisposeImage', 'Ptr', pBitmap)
    gdiplus('GdiplusShutdown', 'Ptr', pToken)
    return hBitmap
}

songdg
Posts: 630
Joined: 04 Oct 2017, 20:04

Re: Gui: How to draw a small circle

Post by songdg » 29 May 2024, 02:13

teadrinker wrote:
28 May 2024, 14:25
You can modify the code as follows:

Code: Select all

#Requires AutoHotkey v2

customColors := [0xAA0000, 0x00AA00, 0x0000AA, 0x000BBB, 0xFFFFAA]

items := [
    Map(
        'text', 'This is a small circle:',
        'color', 0xFF0080,
        'size', 20
    ),
    Map(
        'text', 'This is a medium circle:',
        'size', 35
    ),
    Map(
        'text', 'This is a large circle:',
        'color', 0xFF8000,
        'size', 50
    )
]

wnd := Gui()
wnd.MarginX := wnd.MarginY := 25
wnd.SetFont('s12', 'Calibri')

getPicY := (size, h) => (y := size/2 - h/2, y > 0 ? '-' . y : '+' . Abs(y))
for item in items {
    wnd.AddText('xm', item['text']).GetPos(, &textY,, &textH)
    pic := wnd.AddPicture('x+10 yp' . getPicY(item['size'], textH) . ' w' . item['size'] + 1 . ' h' . item['size'] + 1,
                                        item.Has('color') ? 'HBITMAP:' . DrawCircle(item['size'], item['color']) : '')
    wnd.AddButton('x+10 h30 y' . textY - 4, 'Choose Color').OnEvent('Click', SetImage.Bind(pic, customColors))
    wnd['Button' . A_Index].GetPos(&buttX)
    (maxButtX ?? 0) < buttX && maxButtX := buttX
}
; vertical align the buttons
Loop 3 {
    wnd['Button' . A_Index].Move(maxButtX)
}
wnd.Show()

SetImage(pic, customColors, *) {
    static clr := 0x7f7f7f, flag := CC_RGBINIT := 1
    if (res := ChooseColor(clr, pic.Gui.hwnd, customColors, flag)) != -1 {
        clr := res
        WinGetPos(,, &w,, pic.hwnd)
        pic.Value := 'HBITMAP: ' . DrawCircle(w, clr)
    }
}

ChooseColor(initColor := 0, hWnd := 0, customColorsArr := '', flags := 3) { ; flags: CC_RGBINIT = 1, CC_FULLOPEN = 2, CC_PREVENTFULLOPEN = 4
    static init := false, customColors := '', CHOOSECOLOR := ''
         , RGB_BGR := color => (color & 0xFF) << 16 | color & 0xFF00 | color >> 16
    if !init {
        init := true
        if !(customColorsArr is Array) {
            customColorsArr := []
        }
        customColorsArr.Length := 16
        customColors := Buffer(64)
        Loop 16 {
            clr := customColorsArr.Has(A_Index) && IsInteger(customColorsArr[A_Index])
                ? RGB_BGR(customColorsArr[A_Index] & 0xFFFFFF) : 0xFFFFFF
            NumPut('UInt', clr, customColors, (A_Index - 1) * 4)
        }
        CHOOSECOLOR := Buffer(A_PtrSize * 9)
        NumPut('Ptr', customColors.ptr, NumPut('Ptr', CHOOSECOLOR.size, CHOOSECOLOR) + A_PtrSize * 3)
    }
    NumPut('Ptr', hWnd, CHOOSECOLOR, A_PtrSize)
    NumPut('UInt', RGB_BGR(initColor), CHOOSECOLOR, A_PtrSize * 3)
    NumPut('UInt', flags, CHOOSECOLOR, A_PtrSize * 5)
    res := DllCall('Comdlg32\ChooseColor', 'Ptr', CHOOSECOLOR)
    Loop 16 {
        customColorsArr[A_Index] := RGB_BGR(NumGet(customColors, (A_Index - 1) * 4, 'UInt'))
    }
    return res ? RGB_BGR(NumGet(CHOOSECOLOR, A_PtrSize * 3, 'UInt')) : -1
}

DrawCircle(size, color, alpha := 255) {
    #DllLoad gdiplus
    static pixFormat := PixelFormat32bppARGB := 0x26200A
    si := Buffer(A_PtrSize = 8 ? 24 : 16, 0), NumPut('UInt', 1, si, 0)
    gdiplus := (fn, params*) => DllCall('gdiplus\' . fn, params*)
    gdiplus('GdiplusStartup', 'Ptr*', &pToken := 0, 'Ptr', si, 'Ptr', 0)
    gdiplus('GdipCreateBitmapFromScan0', 'int', size, 'int', size, 'int', 0, 'int', pixFormat, 'Ptr', 0, 'Ptr*', &pBitmap := 0)
    gdiplus('GdipGetImageGraphicsContext', 'Ptr', pBitmap, 'Ptr*', &pGraphics := 0)
    gdiplus('GdipSetSmoothingMode', 'Ptr', pGraphics, 'Int', 4) ; antialias
    gdiplus('GdipCreateSolidFill', 'UInt', alpha << 24 | color, 'Ptr*', &pBrush := 0)
    gdiplus('GdipFillEllipse', 'Ptr', pGraphics, 'Ptr', pBrush, 'Float', 0, 'Float', 0, 'Float', size - 1, 'Float', size - 1)
    gdiplus('GdipCreateHBITMAPFromBitmap', 'Ptr', pBitmap, 'Ptr*', &hBitmap := 0, 'int', 0xFFFFFFFF)

    gdiplus('GdipDeleteBrush', 'Ptr', pBrush)
    gdiplus('GdipDeleteGraphics', 'Ptr', pGraphics)
    gdiplus('GdipDisposeImage', 'Ptr', pBitmap)
    gdiplus('GdiplusShutdown', 'Ptr', pToken)
    return hBitmap
}
Wow :bravo: , Thanks a bunch!

Post Reply

Return to “Ask for Help (v2)”