yet another spinner

Post your working scripts, libraries and tools for AHK v1.1 and older
wolf_II
Posts: 2688
Joined: 08 Feb 2015, 20:55

yet another spinner

Post by wolf_II » 23 Jul 2017, 11:05

Hi, all

I have written a spinner that I would like to share here. Instead of me struggling to present it properly, I will let the spinner present itself.

Enjoy :D

class.Spinner.ahk
Demo 1 - AHK Gui & Notepad.ahk
Demo 2 - Example configs.ahk

Successfully tested on Win10 64bit with AHK v1.1.26.01
Last edited by wolf_II on 23 Jul 2017, 18:24, edited 1 time in total.

wolf_II
Posts: 2688
Joined: 08 Feb 2015, 20:55

Re: yet another spinner

Post by wolf_II » 23 Jul 2017, 11:06

update: Spinner.Stop() now hides the spinner GUI. This is more useful in scripts I think. But the example above still demonstrates the original version.
version 1.03

update: Spinner is easily extendable now. Thanks to Helgef's suggestion.
version 1.04

update: Changed the default value for bClockwise to True.
version 1.05

Demo 3 - Example with SpinnerEx.ahk
Last edited by wolf_II on 31 Jul 2017, 08:31, edited 2 times in total.

Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: yet another spinner

Post by Helgef » 23 Jul 2017, 15:50

Hello. Very nice one! Thanks for sharing.

I have problems with the spinners when they have a parent. I can't get them to show at all. I see them fine when they are parentless though. It is very weird and might be a local issue, I've had problems with parent windows recently.

wolf_II
Posts: 2688
Joined: 08 Feb 2015, 20:55

Re: yet another spinner

Post by wolf_II » 23 Jul 2017, 18:22

Thanks for reporting. I went back to check if there is a difference caused by OS: WinXP 32bit (virtual) is also no good :(
I guess I only tested successfully on Win10 64bit. I will put a remark in the first post.

Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: yet another spinner

Post by Helgef » 23 Jul 2017, 18:24

I tested on win7, AHK 1.1.26.00 32 and 64 bit. I can test on win10 later.

Also, small suggestion, if you make a method; makeDots(),

Code: Select all

	;---------------------------------------------------------------------------
    makeDots() {    ; calculate the dots and colours, get brushes
    ;---------------------------------------------------------------------------
        this.DOT := [], this.ARGB := [], this.BRUSHES := []
        Loop, % this.DotCount {
            this.DOT[A_Index, "x"] := this.MidX + this.Radius * Cos(A_Index * 2 * this.Pi / this.DotCount)
            this.DOT[A_Index, "y"] := this.MidY + this.Radius * Sin(A_Index * 2 * this.Pi / this.DotCount)
            this.ARGB.Push(Format("0x{:x}", A_Index * 255 // this.DotCount) this.Color)
            this.BRUSHES.Push(Gdip_BrushCreateSolid(this.ARGB[A_Index]))
        }
	}
and call it at the end of __new(), it will be very easy, and convenient, to extend the spinner class, like this, (a very simple and ugly example to showcase the idea)

Code: Select all

class SpinnerEx extends Spinner {
	; This is a spinner with another shape.
	; Everything is handled in the Spinner class, we just need to mind the shape.
	width := 5
	makeDots(){
		
		this.DOT := [], this.ARGB := [], this.BRUSHES := []
        Loop, % this.DotCount {
            this.DOT[A_Index, "x"] := this.MidX + (this.width * (-1/2 + (A_Index-1)/this.DotCount))/8
            this.DOT[A_Index, "y"] := this.MidY
            this.ARGB.Push(Format("0x{:x}", A_Index * 255 // this.DotCount) this.Color)
            this.BRUSHES.Push(Gdip_BrushCreateSolid(this.ARGB[A_Index]))
        }
	}
}
where we just need to write the makeDots() function to set up this.DOT := [...], this.ARGB := [...], this.BRUSHES := [...] in some desired way.
EDIT: I can confirm this works perfectly on win 10, AHK 64 bits 1.1.26.00 (and .01).

BoBo
Posts: 6564
Joined: 13 May 2014, 17:15

Re: yet another spinner

Post by BoBo » 24 Jul 2017, 11:44

:shock: :o ... Erm, just for the records, "Spinner" in German means "weirdo" - so 'yet another spinner' is erm, sort of funny :mrgreen: :lol: :thumbup:

wolf_II
Posts: 2688
Joined: 08 Feb 2015, 20:55

Re: yet another spinner

Post by wolf_II » 25 Jul 2017, 11:25

sorry for the late reply! :(

I have tested the SpinnerEx extension, this is awesome. :shock: I did not even realize what you can do with extensions or how you are supposed to do so.
I only knew one could do cool stuff. Thanks for sharing. Brilliant stuff 8-)

And btw, thanks for the feedback that it's working on your Win10 PC. :thumbup:

wolf_II
Posts: 2688
Joined: 08 Feb 2015, 20:55

Re: yet another spinner

Post by wolf_II » 19 Jan 2018, 14:40

Progress, maybe? I found out a way to see the difference between Gdi and Gdi+. It's the support of transparent colours.
How? I've rewritten the class to not require Gdip.ahk and here is the first step towards my goal (to be determined when needed)

Code: Select all

;-------------------------------------------------------------------------------
; class.Spinner.ahk
; by wolf_II
;-------------------------------------------------------------------------------
; version 1.06
;-------------------------------------------------------------------------------



;===============================================================================
class Spinner { ; manages a spinning animation
;===============================================================================


    ; ClassVariables
    static ID := 0                      ; instance IDs derive from here
    static Pi := 4 * ATan(1)            ; trigonometric constant
    static bkCol := 0x00F0F0F0          ; default background color of a Gui

    ; pen and brush for the background
    static Pen := DllCall("CreatePen", "Int", 0, "Int", 1, "UInt", Spinner.bkCol, "UPtr")
    static BackBrush := DllCall("CreateSolidBrush", "UInt", Spinner.bkCol, "UPtr")

/*
    find the system's default Gui background color
*/


    ; default configuration
    DotCount := 9                       ; the number of dots to draw
    DotSize := 12                       ; the size of the dots to draw
    Radius := 20                        ; distance of the dots from the center
    Color:= "000000"                    ; 6 hex digits of RGB, w/o "0x"
    Delay := 250                        ; time in ms between drawings
    bClockwise := True                  ; direction of the spinner


    ;---------------------------------------------------------------------------
    __New(Config, hParent := 0) { ; attach a new instance to hParent
    ;---------------------------------------------------------------------------
        ; Config: an object with some/all the configurable options
        ;-----------------------------------------------------------------------

        If Config.HasKey("DotCount")
            this.DotCount := Config.DotCount

        If Config.HasKey("DotSize")
            this.DotSize := Config.DotSize

        If Config.HasKey("Radius")
            this.Radius := Config.Radius

        If Config.HasKey("Color") ; RGB -> GBR
            this.Color := SubStr(Config.Color, 5, 2)
                        . SubStr(Config.Color, 3, 2)
                        . SubStr(Config.Color, 1, 2)

        If Config.HasKey("Delay")
            this.Delay := Config.Delay

        If Config.HasKey("bClockwise")
            this.bClockwise := Config.bClockwise


        ;---------------------------------------------------
        ; position and size
        ;---------------------------------------------------
        If (hParent = 0) {
            this.X      := 0
            this.Y      := 0
            this.Width  := A_ScreenWidth
            this.Height := A_ScreenHeight
        }

        Else {
            WinGetPos, X, Y, Width, Height, ahk_id %hParent%
            this.X      := X
            this.Y      := Y
            this.Width  := Width
            this.Height := Height
        }

        this.MidX := this.Width  // 2   ; x-coord of the center of the dots
        this.MidY := this.Height // 2   ; y-coord of the center of the dots


        ;---------------------------------------------------
        ; setup Spinners
        ;---------------------------------------------------
        this.ID := Spinner.ID++ ; increment ClassVar, store InstanceVar
        this.hParent := hParent


        ;---------------------------------------------------
        ; Gdip magic with DllCalls
        ;---------------------------------------------------
        this.hDC := DllCall("GetDC", "UPtr", this.hParent, "UPtr")
		this.hMemDC := DllCall("CreateCompatibleDC", "UPtr", this.hDC, "UPtr")
		this.hBitmap := DllCall("CreateCompatibleBitmap", "UPtr", this.hDC, "Int", this.Width, "Int", this.Height, "UPtr")
		this.hOriginalBitmap := DllCall("SelectObject", "UPtr", this.hMemDC, "UPtr", this.hBitmap, "UPtr")

        this.makeDots()
    }


    ;---------------------------------------------------------------------------
    __Delete() { ; clean up
    ;---------------------------------------------------------------------------
		DllCall("SelectObject", "UPtr", this.hMemDC, "UPtr", this.hOriginalBitmap)
		DllCall("DeleteObject", "UPtr", this.hBitmap)
		DllCall("DeleteObject", "UPtr", this.hMemDC)
		DllCall("ReleaseDC", "UPtr", this.hParent, "UPtr", this.hDC)

        Loop, % this.DotCount
            DllCall("DeleteObject", "UPtr", this.BRUSHES[A_Index])
    }


    ;---------------------------------------------------------------------------
    makeDots() { ; calculate the dots and colours, get brushes
    ;---------------------------------------------------------------------------
        this.DOT := []
        this.ARGB := []
        this.BRUSHES := []

        Loop, % this.DotCount {
            this.DOT[A_Index, "x"] := this.MidX + this.Radius * Cos(A_Index * 2 * this.Pi / this.DotCount)
            this.DOT[A_Index, "y"] := this.MidY + this.Radius * Sin(A_Index * 2 * this.Pi / this.DotCount)
            this.ARGB.Push(Format("0x{:x}", A_Index * 255 // this.DotCount) this.Color)
            this.BRUSHES.Push(DllCall("CreateSolidBrush", "UInt", this.ARGB[A_Index], "UPtr"))
        }
    }


    ;---------------------------------------------------------------------------
    Call() { ; timer controlled, private method
    ;---------------------------------------------------------------------------
        ; the timer clears the drawing, then draws all the dots again
        ; when drawing, each dot gets a new brush every time
        ; I keep the index for brushes in range with Mod(index, DotCount) + 1
        ; this.Index is initialized large enough for clockwise direction
        ;-----------------------------------------------------------------------

        If (this.Index++ > 2 * this.DotCount)
            this.Index -= this.DotCount

        DllCall("SelectObject", "UPtr", this.hMemDC, "UPtr", this.Pen, "UPtr")
        DllCall("SelectObject", "UPtr", this.hMemDC, "UPtr", this.BackBrush, "UPtr")
		DllCall("Rectangle", "UPtr", this.hMemDC
            , "Int", 0, "Int", 0, "Int", this.Width, "Int", this.Height)

        Sign := this.bClockwise ? -1 : 1

        Loop, % This.DotCount
        {
            BrushIndex := Mod(this.Index + A_Index * Sign, this.DotCount) + 1

            DllCall( "SelectObject", "UPtr", this.hMemDC
                , "UPtr", this.BRUSHES[BrushIndex]
                , "UPtr" )

            DllCall( "Ellipse", "UPtr", this.hMemDC
                , "Int", this.DOT[A_Index].x
                , "Int", this.DOT[A_Index].y
                , "Int", this.DOT[A_Index].x + this.DotSize
                , "Int", this.DOT[A_Index].y + this.DotSize )
        }

        DllCall( "BitBlt"
            , "UPtr", this.hDC
            , "Int",  0
            , "Int",  0
            , "Int",  this.Width - 2
            , "Int",  this.Height - 2
            , "UPtr", this.hMemDC
            , "Int",  1
            , "Int",  1
            , "UInt", 0xCC0020 ) ; SRCCOPY
    }


    ;---------------------------------------------------------------------------
    Start() { ; start spinning
    ;---------------------------------------------------------------------------
        this.Index := this.DotCount ; initialize
        SetTimer, %this%, % this.Delay ; uses Call()
        this.Call() ; start immediately in case Delay is long
    }


    ;---------------------------------------------------------------------------
    Stop() { ; stop spinning and hide GUI
    ;---------------------------------------------------------------------------
        SetTimer, %this%, Off
    }


} ; end of class
Thanks to RUN1E and to tic who supplied working code that I could study.

User avatar
derz00
Posts: 497
Joined: 02 Feb 2016, 17:54
Location: Middle of the round cube
Contact:

Re: yet another spinner

Post by derz00 » 19 Jan 2018, 20:42

FYI this didn't really work with demo 1. Maybe I'm doing something wrong.
try it and see
...

User avatar
KuroiLight
Posts: 327
Joined: 12 Apr 2015, 20:24
Contact:

Re: yet another spinner

Post by KuroiLight » 19 Jan 2018, 23:42

This is cool, might adapt and use in some of my routine scripts;
I tried something similar just spinning a png via gdip Transforms but It would always bounce the image around.
Wish I had a better understanding of maths so I could do a gradient spinning circle this way.
Do you think its possible with a spinner like this?:
circle.png
circle.png (4.91 KiB) Viewed 6137 times
Windows 10, Ryzen 1600, 16GB G.Skill DDR4, 8GB RX 480 | [MyScripts][MySublimeSettings] [Unlicense][MIT License]
01/24/18
[/color]

wolf_II
Posts: 2688
Joined: 08 Feb 2015, 20:55

Re: yet another spinner

Post by wolf_II » 20 Jan 2018, 04:39

@derz00: Thx for testing, The v1.06 is an intermediate step towards my goal, which I will determine now (with the option to change my mind later as I see necessary.)

my goal:
  • write code to learn stuff (learning by doing)
  • the result will have no dependencies on any library
  • runs on all flavours of AHK (ASNSI, 32bit, 64bit)
  • runs on all (?) OS that are WinXP or newer
  • and display a spinner or more
As of yesterday (v1.05), the spinner does not work on Win7 because of SetParent. Or something along this line, not entirely sure.
My coding challenge is to eliminate the nested GUI's and draw directly to the top-level Gui's controls itself (which are already children, no need to set their parents).

As you can see, I eliminated all the nested Guis, and the dependency from the Gdip-library. Somehow I also lost the use of transparent colours, which is bad.
yada, yada, yada, ...
I only tested with demo #2, It took a while to see anything, and in the end what I saw was clearly undesirable outcome.
(I need to start over, now with new insight). I'm sorry to have posted code that will be thrown out and without a warning of what's wrong with it.

Short answer: try out with demo #2, see how awful the spinner looks without transparency and throw it away.

wolf_II
Posts: 2688
Joined: 08 Feb 2015, 20:55

Re: yet another spinner

Post by wolf_II » 20 Jan 2018, 04:54

@KuroiLight: spinning a png, hmmm. I'll keep that in mind, it might be better to write a separate class for that.
Currently the spinner class takes a [configuration array with 6 members] already.

The answer is: I think it is possible, yes. Obviously I have not done it yet. I want to reach my next step and swap out calls to gdi.dll :( with call to gdip.dll :)

I enjoy the feedback immensely, please keep them coming, I am somewhat vulnerable to get stuck in my own world/ideas.

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

Re: yet another spinner

Post by just me » 20 Jan 2018, 04:57

wolf_II wrote:As of yesterday (v1.05), the spinner does not work on Win7 because of SetParent. Or something along this line, not entirely sure.
[b]WS_EX_LAYERED[/b] wrote:Windows 8: The WS_EX_LAYERED style is supported for top-level windows and child windows. Previous Windows versions support WS_EX_LAYERED only for top-level windows.

wolf_II
Posts: 2688
Joined: 08 Feb 2015, 20:55

Re: yet another spinner

Post by wolf_II » 20 Jan 2018, 05:18

@just me: Thanks for that. Brilliant.
@derz00: If I change the color in demo #1 to something different, the spinner spins visibly: MySpinner := new Spinner({Color: "ff3366"}, hTXT)
What is still broken is the disappearing, ... I'll fix the demos when I have some more working code.

wolf_II
Posts: 2688
Joined: 08 Feb 2015, 20:55

Re: yet another spinner

Post by wolf_II » 12 Feb 2018, 20:59

wolf_II wrote:my goal:
  • write code to learn stuff (learning by doing)
  • the result will have no dependencies on any library
  • runs on all flavours of AHK (ASNSI, 32bit, 64bit)
  • runs on all (?) OS that are WinXP or newer
  • and display a spinner or more
The spinner class:

Code: Select all

;-------------------------------------------------------------------------------
; class Spinner.ahk
; by wolf_II
;-------------------------------------------------------------------------------
; version 1.21



;===============================================================================
class Spinner { ; manages a spinning animation
;===============================================================================


    ; ClassVariables
    static pToken, Count := 0


    ; default configuration
    DotCount    := 9                    ; the number of dots to draw
    DotSize     := 12                   ; the size of the dots to draw
    Radius      := 20                   ; distance of the dots from the center
    Color       := "000000"             ; 6 hex digits of RGB, w/o "0x"
    BkColor     := "FFFF99"             ; 6 hex digits of RGB, w/o "0x"
    Delay       := 50                   ; time in ms between drawings
    bClockwise  := True                 ; direction of the spinner


    ;---------------------------------------------------------------------------
    __New(hParent, Config := "") { ; attach to parent
    ;---------------------------------------------------------------------------

        ; store configuration
        If Config.HasKey("DotCount")
            this.DotCount := Config.DotCount
        If Config.HasKey("DotSize")
            this.DotSize := Config.DotSize
        If Config.HasKey("Radius")
            this.Radius := Config.Radius
        If Config.HasKey("Color")
            this.Color := Config.Color
        If Config.HasKey("BkColor")
            this.BkColor := Config.BkColor
        If Config.HasKey("Delay")
            this.Delay := Config.Delay
        If Config.HasKey("bClockwise")
            this.bClockwise := Config.bClockwise

        If !Spinner.pToken
            Spinner.pToken := Startup()
        Spinner.Count++

        this.hWnd := hParent
        this.hBM := CreateDIBSection(A_ScreenWidth, A_ScreenHeight)
        this.hDC := CreateCompatibleDC()
        this.hDC_win := GetDC(hParent)
        this.oBM := SelectObject(this.hDC, this.hBM)
        this.pGraphics := GraphicsFromHDC(this.hDC)
        this.Shift := this.DotCount
        this.Setup()
    }


    ;---------------------------------------------------------------------------
    __Delete() { ; clean up
    ;---------------------------------------------------------------------------
        WinGetPos,,, W, H, % "ahk_id " this.hWnd
        GraphicsClear(this.pGraphics, "0xFF" this.BkColor)
        BitBlt(this.hDC_win, 0, 0, W, H, this.hDC, 0, 0)

        For each, pBrush in this.BRUSHES
            DeleteBrush(pBrush)

        SelectObject(this.hDC, this.oBM)
        DeleteObject(this.hBM)
        DeleteDC(this.hDC)
        DeleteGraphics(this.pGraphics)

        If (--Spinner.Count = 0)
            ShutdownG(Spinner.pToken)
    }


    ;---------------------------------------------------------------------------
    Setup() { ; gets called for every new spinner
    ;---------------------------------------------------------------------------
        this.BRUSHES := []
        Loop, % this.DotCount {
            Trans := (this.DotCount - A_Index + 1) * 255 / this.DotCount
            _ARGB := Format("0x{:X}{}", Trans, this.Color)
            this.BRUSHES.Push(BrushCreateSolid(_ARGB))
        }
    }


    ;---------------------------------------------------------------------------
    Call() { ; timer controlled
    ;---------------------------------------------------------------------------
        static TWO_PI := ATan(1) * 8

        If (this.Shift++ > 2 * this.DotCount)
            this.Shift -= this.DotCount

        Sign := this.bClockwise ? -1 : 1
        GraphicsClear(this.pGraphics, "0xFF" this.BkColor)
        WinGetPos,,, W, H, % "ahk_id " this.hWnd
        MidX := W / 2, MidY := H / 2

        Loop, % this.DotCount {
            cx := MidX + this.Radius * Cos(A_Index * TWO_PI / this.DotCount)
            cy := MidY + this.Radius * Sin(A_Index * TWO_PI / this.DotCount)
            BrushIndex := Mod(this.Shift + A_Index * Sign, this.DotCount) + 1
            FillEllipse(this.pGraphics
                , this.Brushes[BrushIndex]
                , cx - this.DotSize / 2
                , cy - this.DotSize / 2
                , this.DotSize, this.DotSize)
        }
        BitBlt(this.hDC_win, 0, 0, W, H, this.hDC, 0, 0)
    }


    ;---------------------------------------------------------------------------
    Start() { ; start timer
    ;---------------------------------------------------------------------------
        SetTimer, %this%, % this.Delay
        this.Call() ; start immediately
    }


    ;---------------------------------------------------------------------------
    Stop() { ; stop timer
    ;---------------------------------------------------------------------------
        SetTimer, %this%, Off
    }


} ; ============================================================================


; from Tic's library
Startup() {
	if !DllCall("GetModuleHandle", "str", "gdiplus", "UPtr")
		DllCall("LoadLibrary", "str", "gdiplus")
	VarSetCapacity(si, A_PtrSize = 8 ? 24 : 16, 0), si := Chr(1)
	DllCall("gdiplus\GdiplusStartup", "UPtr*", pToken, "UPtr", &si, "UPtr", 0)
	return pToken
}
ShutdownG(pToken) {
	DllCall("gdiplus\GdiplusShutdown", "UPtr", pToken)
	if hModule := DllCall("GetModuleHandle", "str", "gdiplus", "UPtr")
		DllCall("FreeLibrary", "UPtr", hModule)
	return 0
}
GraphicsClear(pGraphics, ARGB=0x00ffffff) {
    return DllCall("gdiplus\GdipGraphicsClear", "UPtr", pGraphics, "int", ARGB)
}
CreateDIBSection(w, h, hdc="", bpp=32, ByRef ppvBits=0) {
	hdc2 := hdc ? hdc : GetDC(), VarSetCapacity(bi, 40, 0)
    , NumPut(w, bi, 4, "uint"), NumPut(h, bi, 8, "uint")
    , NumPut(40, bi, 0, "uint"), NumPut(1, bi, 12, "ushort")
    , NumPut(0, bi, 16, "uInt"), NumPut(bpp, bi, 14, "ushort")
	hbm := DllCall("CreateDIBSection", "UPtr", hdc2, "UPtr", &bi, "uint", 0
        , "UPtr*", ppvBits, "UPtr", 0, "uint", 0, "UPtr")
	if !hdc
		ReleaseDC(hdc2)
	return hbm
}
CreateCompatibleDC(hdc=0) {
   return DllCall("CreateCompatibleDC", "UPtr", hdc)
}
SelectObject(hdc, hgdiobj) {
	return DllCall("SelectObject", "UPtr", hdc, "UPtr", hgdiobj)
}
GraphicsFromHDC(hdc) {
    DllCall("gdiplus\GdipCreateFromHDC", "UPtr", hdc, "UPtr*", pGraphics)
    return pGraphics
}
DeleteBrush(pBrush) {
   return DllCall("gdiplus\GdipDeleteBrush", "UPtr", pBrush)
}
DeleteObject(hObject) {
   return DllCall("DeleteObject", "UPtr", hObject)
}
DeleteDC(hdc) {
   return DllCall("DeleteDC", "UPtr", hdc)
}
DeleteGraphics(pGraphics) {
   return DllCall("gdiplus\GdipDeleteGraphics", "UPtr", pGraphics)
}
BrushCreateSolid(ARGB=0xff000000) {
	DllCall("gdiplus\GdipCreateSolidFill", "UInt", ARGB, "UPtr*", pBrush)
	return pBrush
}
FillEllipse(pGraphics, pBrush, x, y, w, h) {
	return DllCall("gdiplus\GdipFillEllipse", "UPtr", pGraphics
        , "UPtr", pBrush, "float", x, "float", y, "float", w, "float", h)
}
GetDC(hwnd=0) {
	return DllCall("GetDC", "UPtr", hwnd)
}
ReleaseDC(hdc, hwnd=0) {
	return DllCall("ReleaseDC", "UPtr", hwnd, "UPtr", hdc)
}
BitBlt(ddc, dx, dy, dw, dh, sdc, sx, sy, Raster="") {
	return DllCall("gdi32\BitBlt", "UPtr", dDC
        , "int", dx, "int", dy, "int", dw, "int", dh, "UPtr", sDC
        , "int", sx, "int", sy, "uint", Raster ? Raster : 0x00CC0020)
}
Example 1:

Code: Select all

#Include, class Spinner.ahk

    Gui, +hwndhGui
    Gui, Margin, 20, 20
    Gui, Add, Text, w300 h200 +hwndhTXT Border
    MySpinner := new Spinner(hTXT) ; use default config
    MySpinner.Start()
    Gui, Show, x100 y100, Move me!

    MsgBox,, Demonstrate Spinner, Please move the Gui with your mouse
        , `n`nNotice how the spinner moves along
        , `nand it still spins while being moved
        ! `n`nSometimes at least.

    MySpinner.Stop()
    MsgBox,, Demonstrate Spinner, The spinner stopped!

    MySpinner.__Delete()
    MsgBox,, Demonstrate Spinner, The spinner disappeared!

Return

GuiClose:
GuiEscape:
ExitApp

#If WinActive("ahk_id " hGui)
F5:: Reload
Example 2:

Code: Select all

#Include, class Spinner.ahk

    Gui, +hwndhGui +Resize
    Gui, Margin, 20, 20
    Gui, Add, Text, xm  w250 h250 +hwndhTXT Border
    Spinner1 := new Spinner(hTXT, {Delay: 15})
    Spinner1.Start()
    Gui, Show, x50 y50, Faster & Resize

Return

GuiClose:
GuiEscape:
ExitApp

#If WinActive("ahk_id " hGui)
F5:: Reload

GuiSize:
    GuiControl, Move, %hTXT%, % "w" A_GuiWidth  - 40 " h" A_GuiHeight - 40
Return
Example 3:

Code: Select all

#Include, class Spinner.ahk

    ;---------------------------------------------------------------------------
    cfg1 := { DotCount:     5, DotSize: 20, Radius:     18
            , Color: "FF0000", Delay:  500, bClockwise: True }
    ;---------------------------------------------------------------------------
    cfg2 := { DotCount:     7, DotSize: 15, Radius:     19
            , Color: "0000FF", Delay:  400, bClockwise: False }
    ;---------------------------------------------------------------------------
    cfg3 := { DotCount:     9, DotSize: 12, Radius:     20    ; (default config)
            , Color: "000000", Delay:   50, bClockwise: True }
    ;---------------------------------------------------------------------------
    cfg4 := { DotCount:    11, DotSize: 20, Radius:     40
            , Color: "FF00FF", Delay:  200, bClockwise: False }
    ;---------------------------------------------------------------------------
    cfg5 := { DotCount:    15, DotSize: 20, Radius:     60
            , Color: "00FF00", Delay:  150, bClockwise: True }
    ;---------------------------------------------------------------------------
    cfg6 := { DotCount:    19, DotSize: 20, Radius:     75
            , Color: "00FFFF", Delay:  100, bClockwise: False }
    ;---------------------------------------------------------------------------

    Gui, +hwndhGui +Resize
    Gui, Margin, 20, 20

    Gui, Add, Text, xm  w250 h250 Border +hwndhTXT1
    Gui, Add, Text, x+m w250 h250 Border +hwndhTXT2
    Gui, Add, Text, x+m w250 h250 Border +hwndhTXT3
    Gui, Add, Text, xm  w250 h250 Border +hwndhTXT4
    Gui, Add, Text, x+m w250 h250 Border +hwndhTXT5
    Gui, Add, Text, x+m w250 h250 Border +hwndhTXT6

    Spinner1 := new Spinner(hTXT1, cfg1)
    Spinner2 := new Spinner(hTXT2, cfg2)
    Spinner3 := new Spinner(hTXT3, cfg3)
    Spinner4 := new Spinner(hTXT4, cfg4)
    Spinner5 := new Spinner(hTXT5, cfg5)
    Spinner6 := new Spinner(hTXT6, cfg6)

    Spinner1.Start()
    Spinner2.Start()
    Spinner3.Start()
    Spinner4.Start()
    Spinner5.Start()
    Spinner6.Start()

    Gui, Show, x50 y50, % "6 Configs - " AHK_Flavour()

Return

GuiClose:
GuiEscape:
ExitApp

#If WinActive("ahk_id " hGui)
F5:: Reload



;-------------------------------------------------------------------------------
AHK_Flavour() {
;-------------------------------------------------------------------------------
    Return, (A_IsUnicode ? "Unicode" : "ANSI")
          . (A_PtrSize = 8 ? " x64" : " x32")
}



;-------------------------------------------------------------------------------
GuiSize:
;-------------------------------------------------------------------------------
    newWidth  := (A_GuiWidth  - 80) / 3
    newHeight := (A_GuiHeight - 60) / 2

    X2 := 40 + newWidth
    X3 := 60 + newWidth * 2
    Y  := 40 + newHeight

    GuiControl, Move, %hTXT1%,            w%newWidth% h%newHeight%
    GuiControl, Move, %hTXT2%, x%X2%      w%newWidth% h%newHeight%
    GuiControl, Move, %hTXT3%, x%X3%      w%newWidth% h%newHeight%
    GuiControl, Move, %hTXT4%,       y%Y% w%newWidth% h%newHeight%
    GuiControl, Move, %hTXT5%, x%X2% y%Y% w%newWidth% h%newHeight%
    GuiControl, Move, %hTXT6%, x%X3% y%Y% w%newWidth% h%newHeight%

Return
I have aimed for my goals and along the way I had to stop pre-calculating the DOTS position in order to achieve resizing. (means SpinnerEx broke :( )
I have changed the default delay from 250 to 50ms.
I also added a BkColor that you can configure. (not yet included in the examples)

Issues: when testing on a WinXP 32bit I noticed background painting problems. I started all three examples and I moved the MsgBox around wildly.
On XP => slow repainting, so I reduced the delay. (in terms of FPS from 4fps to 20fps)
On Win10 => perfect repainting with all sorts of FPS. :)

wolf_II
Posts: 2688
Joined: 08 Feb 2015, 20:55

Re: yet another spinner

Post by wolf_II » 12 Feb 2018, 21:23

KuroiLight wrote:This is cool, might adapt and use in some of my routine scripts;
I tried something similar just spinning a png via gdip Transforms but It would always bounce the image around.
Wish I had a better understanding of maths so I could do a gradient spinning circle this way.
Do you think its possible with a spinner like this?: circle.png
Sorry for late answer :oops:
If you want to share your effort where a gdip-trnsformed image bounces around, I would love to see it, please.
After I looked at it, I will at least be able to tell about the maths side of things.
The maths should be within my reach, reading other peoples code ... is not always.

Currently I had not attempted to manipulate images, but that's on my list to learn.
Also on my list to learn is clipping regions and gradient filled shapes.
I would image combining the two we get something similar in appearance.
I have not yet decided what to tackle next. For the images, I need to load them, rotate, and paint them.
I like to look at your example. :D

wolf_II
Posts: 2688
Joined: 08 Feb 2015, 20:55

Re: yet another spinner

Post by wolf_II » 12 Feb 2018, 21:30

I think I should somehow avoid a memory leak with my device contexts, hmmm ... :roll:
Note to self: I appear to have lost anti-alias somewhere along the way.

Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: yet another spinner

Post by Helgef » 13 Feb 2018, 03:45

The examples works fine on win7 (1.1.28.00 64/32 bit U/A) :thumbup:. And they spin while moving. Good job!

Cheers.

User avatar
noname
Posts: 515
Joined: 19 Nov 2013, 09:15

Re: yet another spinner

Post by noname » 13 Feb 2018, 14:38

Image

Nice one Wolf_II .Setting smoothing mode improves the image.( the setting you lost somewhere ? :) )

It is easy to rotate png to get the spinning effect but i only managed to rotate with a square image to keep it 100% centered while rotating.

Code: Select all


    ;---------------------------------------------------------------------------
    cfg1 := { DotCount:     5, DotSize: 20, Radius:     18
            , Color: "FF0000", Delay:  500, bClockwise: True }
    ;---------------------------------------------------------------------------
    cfg2 := { DotCount:     7, DotSize: 15, Radius:     19
            , Color: "0000FF", Delay:  400, bClockwise: False }
    ;---------------------------------------------------------------------------
    cfg3 := { DotCount:     9, DotSize: 12, Radius:     20    ; (default config)
            , Color: "000000", Delay:   50, bClockwise: True }
    ;---------------------------------------------------------------------------
    cfg4 := { DotCount:    11, DotSize: 20, Radius:     40
            , Color: "FF00FF", Delay:  200, bClockwise: False }
    ;---------------------------------------------------------------------------
    cfg5 := { DotCount:    15, DotSize: 20, Radius:     60
            , Color: "00FF00", Delay:  150, bClockwise: True }
    ;---------------------------------------------------------------------------
    cfg6 := { DotCount:    19, DotSize: 20, Radius:     75
            , Color: "00FFFF", Delay:  100, bClockwise: False }
    ;---------------------------------------------------------------------------

    Gui, +hwndhGui +Resize
    Gui, Margin, 20, 20

    Gui, Add, Text, xm  w250 h250 Border +hwndhTXT1
    Gui, Add, Text, x+m w250 h250 Border +hwndhTXT2
    Gui, Add, Text, x+m w250 h250 Border +hwndhTXT3
    Gui, Add, Text, xm  w250 h250 Border +hwndhTXT4
    Gui, Add, Text, x+m w250 h250 Border +hwndhTXT5
    Gui, Add, Text, x+m w250 h250 Border +hwndhTXT6

    Spinner1 := new Spinner(hTXT1, cfg1)
    Spinner2 := new Spinner(hTXT2, cfg2)
    Spinner3 := new Spinner(hTXT3, cfg3)
    Spinner4 := new Spinner(hTXT4, cfg4)
    Spinner5 := new Spinner(hTXT5, cfg5)
    Spinner6 := new Spinner(hTXT6, cfg6)

    Spinner1.Start()
    Spinner2.Start()
    Spinner3.Start()
    Spinner4.Start()
    Spinner5.Start()
    Spinner6.Start()

    Gui, Show, x50 y50, % "6 Configs - " AHK_Flavour()

Return

GuiClose:
GuiEscape:
ExitApp

#If WinActive("ahk_id " hGui)
F5:: Reload



;-------------------------------------------------------------------------------
AHK_Flavour() {
;-------------------------------------------------------------------------------
    Return, (A_IsUnicode ? "Unicode" : "ANSI")
          . (A_PtrSize = 8 ? " x64" : " x32")
}



;-------------------------------------------------------------------------------
GuiSize:
;-------------------------------------------------------------------------------
    newWidth  := (A_GuiWidth  - 80) / 3
    newHeight := (A_GuiHeight - 60) / 2

    X2 := 40 + newWidth
    X3 := 60 + newWidth * 2
    Y  := 40 + newHeight

    GuiControl, Move, %hTXT1%,            w%newWidth% h%newHeight%
    GuiControl, Move, %hTXT2%, x%X2%      w%newWidth% h%newHeight%
    GuiControl, Move, %hTXT3%, x%X3%      w%newWidth% h%newHeight%
    GuiControl, Move, %hTXT4%,       y%Y% w%newWidth% h%newHeight%
    GuiControl, Move, %hTXT5%, x%X2% y%Y% w%newWidth% h%newHeight%
    GuiControl, Move, %hTXT6%, x%X3% y%Y% w%newWidth% h%newHeight%

Return

;-------------------------------------------------------------------------------
; class Spinner.ahk
; by wolf_II
;-------------------------------------------------------------------------------
; version 1.21



;===============================================================================
class Spinner { ; manages a spinning animation
;===============================================================================


    ; ClassVariables
    static pToken, Count := 0


    ; default configuration
    DotCount    := 9                    ; the number of dots to draw
    DotSize     := 12                   ; the size of the dots to draw
    Radius      := 20                   ; distance of the dots from the center
    Color       := "000000"             ; 6 hex digits of RGB, w/o "0x"
    BkColor     := "FFFF99"             ; 6 hex digits of RGB, w/o "0x"
    Delay       := 50                   ; time in ms between drawings
    bClockwise  := True                 ; direction of the spinner


    ;---------------------------------------------------------------------------
    __New(hParent, Config := "") { ; attach to parent
    ;---------------------------------------------------------------------------

        ; store configuration
        If Config.HasKey("DotCount")
            this.DotCount := Config.DotCount
        If Config.HasKey("DotSize")
            this.DotSize := Config.DotSize
        If Config.HasKey("Radius")
            this.Radius := Config.Radius
        If Config.HasKey("Color")
            this.Color := Config.Color
        If Config.HasKey("BkColor")
            this.BkColor := Config.BkColor
        If Config.HasKey("Delay")
            this.Delay := Config.Delay
        If Config.HasKey("bClockwise")
            this.bClockwise := Config.bClockwise

        If !Spinner.pToken
            Spinner.pToken := Startup()
        Spinner.Count++

        this.hWnd := hParent
        this.hBM := CreateDIBSection(A_ScreenWidth, A_ScreenHeight)
        this.hDC := CreateCompatibleDC()
        this.hDC_win := GetDC(hParent)
        this.oBM := SelectObject(this.hDC, this.hBM)
        this.pGraphics := GraphicsFromHDC(this.hDC)
        Gdip_SetInterpolationMode(this.pGraphics, 7)
        ;Gdip_SetSmoothingMode(this.pGraphics, 4)
        this.Shift := this.DotCount
        this.Setup()
    }


    ;---------------------------------------------------------------------------
    __Delete() { ; clean up
    ;---------------------------------------------------------------------------
        WinGetPos,,, W, H, % "ahk_id " this.hWnd
        GraphicsClear(this.pGraphics, "0xFF" this.BkColor)
        BitBlt(this.hDC_win, 0, 0, W, H, this.hDC, 0, 0)

        For each, pBrush in this.BRUSHES
            DeleteBrush(pBrush)

        SelectObject(this.hDC, this.oBM)
        DeleteObject(this.hBM)
        DeleteDC(this.hDC)
        DeleteGraphics(this.pGraphics)

        If (--Spinner.Count = 0)
            ShutdownG(Spinner.pToken)
    }


    ;---------------------------------------------------------------------------
    Setup() { ; gets called for every new spinner
    ;---------------------------------------------------------------------------
        this.BRUSHES := []
        Loop, % this.DotCount {
            Trans := (this.DotCount - A_Index + 1) * 255 / this.DotCount
            _ARGB := Format("0x{:X}{}", Trans, this.Color)
            this.BRUSHES.Push(BrushCreateSolid(_ARGB))
        }
    }


    ;---------------------------------------------------------------------------
    Call() { ; timer controlled
    ;---------------------------------------------------------------------------
        static TWO_PI := ATan(1) * 8

        If (this.Shift++ > 2 * this.DotCount)
            this.Shift -= this.DotCount

        Sign := this.bClockwise ? -1 : 1
        GraphicsClear(this.pGraphics, "0xFF" this.BkColor)
        WinGetPos,,, W, H, % "ahk_id " this.hWnd
        MidX := W / 2, MidY := H / 2

        Loop, % this.DotCount {
            cx := MidX + this.Radius * Cos(A_Index * TWO_PI / this.DotCount)
            cy := MidY + this.Radius * Sin(A_Index * TWO_PI / this.DotCount)
            BrushIndex := Mod(this.Shift + A_Index * Sign, this.DotCount) + 1
            FillEllipse(this.pGraphics
                , this.Brushes[BrushIndex]
                , cx - this.DotSize / 2
                , cy - this.DotSize / 2
                , this.DotSize, this.DotSize)
        }
        BitBlt(this.hDC_win, 0, 0, W, H, this.hDC, 0, 0)
    }


    ;---------------------------------------------------------------------------
    Start() { ; start timer
    ;---------------------------------------------------------------------------
        SetTimer, %this%, % this.Delay
        this.Call() ; start immediately
    }


    ;---------------------------------------------------------------------------
    Stop() { ; stop timer
    ;---------------------------------------------------------------------------
        SetTimer, %this%, Off
    }


} ; ============================================================================


; from Tic's library
Startup() {
	if !DllCall("GetModuleHandle", "str", "gdiplus", "UPtr")
		DllCall("LoadLibrary", "str", "gdiplus")
	VarSetCapacity(si, A_PtrSize = 8 ? 24 : 16, 0), si := Chr(1)
	DllCall("gdiplus\GdiplusStartup", "UPtr*", pToken, "UPtr", &si, "UPtr", 0)
	return pToken
}
ShutdownG(pToken) {
	DllCall("gdiplus\GdiplusShutdown", "UPtr", pToken)
	if hModule := DllCall("GetModuleHandle", "str", "gdiplus", "UPtr")
		DllCall("FreeLibrary", "UPtr", hModule)
	return 0
}
Gdip_SetSmoothingMode(pGraphics, SmoothingMode)
{
   return DllCall("gdiplus\GdipSetSmoothingMode", A_PtrSize ? "UPtr" : "UInt", pGraphics, "int", SmoothingMode)
}
Gdip_SetInterpolationMode(pGraphics, InterpolationMode)
{
   return DllCall("gdiplus\GdipSetInterpolationMode", A_PtrSize ? "UPtr" : "UInt", pGraphics, "int", InterpolationMode)
}
GraphicsClear(pGraphics, ARGB=0x00ffffff) {
    return DllCall("gdiplus\GdipGraphicsClear", "UPtr", pGraphics, "int", ARGB)
}
CreateDIBSection(w, h, hdc="", bpp=32, ByRef ppvBits=0) {
	hdc2 := hdc ? hdc : GetDC(), VarSetCapacity(bi, 40, 0)
    , NumPut(w, bi, 4, "uint"), NumPut(h, bi, 8, "uint")
    , NumPut(40, bi, 0, "uint"), NumPut(1, bi, 12, "ushort")
    , NumPut(0, bi, 16, "uInt"), NumPut(bpp, bi, 14, "ushort")
	hbm := DllCall("CreateDIBSection", "UPtr", hdc2, "UPtr", &bi, "uint", 0
        , "UPtr*", ppvBits, "UPtr", 0, "uint", 0, "UPtr")
	if !hdc
		ReleaseDC(hdc2)
	return hbm
}
CreateCompatibleDC(hdc=0) {
   return DllCall("CreateCompatibleDC", "UPtr", hdc)
}
SelectObject(hdc, hgdiobj) {
	return DllCall("SelectObject", "UPtr", hdc, "UPtr", hgdiobj)
}
GraphicsFromHDC(hdc) {
    DllCall("gdiplus\GdipCreateFromHDC", "UPtr", hdc, "UPtr*", pGraphics)
    return pGraphics
}
DeleteBrush(pBrush) {
   return DllCall("gdiplus\GdipDeleteBrush", "UPtr", pBrush)
}
DeleteObject(hObject) {
   return DllCall("DeleteObject", "UPtr", hObject)
}
DeleteDC(hdc) {
   return DllCall("DeleteDC", "UPtr", hdc)
}
DeleteGraphics(pGraphics) {
   return DllCall("gdiplus\GdipDeleteGraphics", "UPtr", pGraphics)
}
BrushCreateSolid(ARGB=0xff000000) {
	DllCall("gdiplus\GdipCreateSolidFill", "UInt", ARGB, "UPtr*", pBrush)
	return pBrush
}
FillEllipse(pGraphics, pBrush, x, y, w, h) {
	return DllCall("gdiplus\GdipFillEllipse", "UPtr", pGraphics
        , "UPtr", pBrush, "float", x, "float", y, "float", w, "float", h)
}
GetDC(hwnd=0) {
	return DllCall("GetDC", "UPtr", hwnd)
}
ReleaseDC(hdc, hwnd=0) {
	return DllCall("ReleaseDC", "UPtr", hwnd, "UPtr", hdc)
}
BitBlt(ddc, dx, dy, dw, dh, sdc, sx, sy, Raster="") {
	return DllCall("gdi32\BitBlt", "UPtr", dDC
        , "int", dx, "int", dy, "int", dw, "int", dh, "UPtr", sDC
        , "int", sx, "int", sy, "uint", Raster ? Raster : 0x00CC0020)
}


wolf_II
Posts: 2688
Joined: 08 Feb 2015, 20:55

Re: yet another spinner

Post by wolf_II » 13 Feb 2018, 16:41

Hi all, thanks for feeding me back :D

regarding leaking fromn my device context: I added inside of __Delete()

Code: Select all

        DeleteDC(this.hDC_win)
Can anybody please confirm or correct me, please?
I think not deleting all DC's may not be too bad? AHK takes care of things?
I think my fix fixes the problem? I have no idea what I'm doing here, really.

regarding anti-alias: I added inside of __New()

Code: Select all

        SetInterpolationMode(this.pGraphics, 7) ; HighQualityBicubic
        SetSmoothingMode(this.pGraphics, 4) ; AntiAlias
And I added at the end:

Code: Select all

; from Tic's library
; ...

; Default = 0
; LowQuality = 1
; HighQuality = 2
; Bilinear = 3
; Bicubic = 4
; NearestNeighbor = 5
; HighQualityBilinear = 6
; HighQualityBicubic = 7
SetInterpolationMode(pGraphics, InterpolationMode) {
   return DllCall("gdiplus\GdipSetInterpolationMode"
   , A_PtrSize ? "UPtr" : "UInt", pGraphics
   , "int", InterpolationMode)
}

; Default = 0
; HighSpeed = 1
; HighQuality = 2
; None = 3
; AntiAlias = 4
SetSmoothingMode(pGraphics, SmoothingMode) {
   return DllCall("gdiplus\GdipSetSmoothingMode"
   , A_PtrSize ? "UPtr" : "UInt", pGraphics
   , "int", SmoothingMode)
}

Version 1.22

Code: Select all

;-------------------------------------------------------------------------------
; class Spinner.ahk
; by wolf_II
;-------------------------------------------------------------------------------
; version 1.22



;===============================================================================
class Spinner { ; manages a spinning animation
;===============================================================================


    ; ClassVariables
    static pToken, Count := 0


    ; default configuration
    DotCount    := 9                    ; the number of dots to draw
    DotSize     := 12                   ; the size of the dots to draw
    Radius      := 20                   ; distance of the dots from the center
    Color       := "000000"             ; 6 hex digits of RGB, w/o "0x"
    BkColor     := "FFFF99"             ; 6 hex digits of RGB, w/o "0x"
    Delay       := 50                   ; time in ms between drawings
    bClockwise  := True                 ; direction of the spinner


    ;---------------------------------------------------------------------------
    __New(hParent, Config := "") { ; attach to parent
    ;---------------------------------------------------------------------------

        ; store configuration
        If Config.HasKey("DotCount")
            this.DotCount := Config.DotCount
        If Config.HasKey("DotSize")
            this.DotSize := Config.DotSize
        If Config.HasKey("Radius")
            this.Radius := Config.Radius
        If Config.HasKey("Color")
            this.Color := Config.Color
        If Config.HasKey("BkColor")
            this.BkColor := Config.BkColor
        If Config.HasKey("Delay")
            this.Delay := Config.Delay
        If Config.HasKey("bClockwise")
            this.bClockwise := Config.bClockwise

        If !Spinner.pToken
            Spinner.pToken := Startup()
        Spinner.Count++

        this.hWnd := hParent
        this.hBM := CreateDIBSection(A_ScreenWidth, A_ScreenHeight)
        this.hDC := CreateCompatibleDC()
        this.hDC_win := GetDC(hParent)
        this.oBM := SelectObject(this.hDC, this.hBM)
        this.pGraphics := GraphicsFromHDC(this.hDC)
        SetInterpolationMode(this.pGraphics, 7) ; HighQualityBicubic
        SetSmoothingMode(this.pGraphics, 4) ; AntiAlias
        this.Shift := this.DotCount ; init mid-range
        this.Setup() ; hijack Setup() and Call()
    }


    ;---------------------------------------------------------------------------
    __Delete() { ; clean up
    ;---------------------------------------------------------------------------
        WinGetPos,,, W, H, % "ahk_id " this.hWnd
        GraphicsClear(this.pGraphics, "0xFF" this.BkColor)
        BitBlt(this.hDC_win, 0, 0, W, H, this.hDC, 0, 0)

        For each, pBrush in this.BRUSHES
            DeleteBrush(pBrush)

        SelectObject(this.hDC, this.oBM)
        DeleteObject(this.hBM)
        DeleteDC(this.hDC_win)
        DeleteDC(this.hDC)
        DeleteGraphics(this.pGraphics)

        If (--Spinner.Count = 0)
            ShutdownG(Spinner.pToken)
    }


    ;---------------------------------------------------------------------------
    Setup() { ; gets called for every new spinner
    ;---------------------------------------------------------------------------
        this.BRUSHES := []
        Loop, % this.DotCount {
            Trans := (this.DotCount - A_Index + 1) * 255 / this.DotCount
            _ARGB := Format("0x{:X}{}", Trans, this.Color)
            this.BRUSHES.Push(BrushCreateSolid(_ARGB))
        }
    }


    ;---------------------------------------------------------------------------
    Call() { ; timer controlled
    ;---------------------------------------------------------------------------
        static TWO_PI := ATan(1) * 8

        If (this.Shift++ > 2 * this.DotCount)
            this.Shift -= this.DotCount

        Sign := this.bClockwise ? -1 : 1
        GraphicsClear(this.pGraphics, "0xFF" this.BkColor)
        WinGetPos,,, W, H, % "ahk_id " this.hWnd
        MidX := W / 2, MidY := H / 2

        Loop, % this.DotCount {
            cx := MidX + this.Radius * Cos(A_Index * TWO_PI / this.DotCount)
            cy := MidY + this.Radius * Sin(A_Index * TWO_PI / this.DotCount)
            BrushIndex := Mod(this.Shift + A_Index * Sign, this.DotCount) + 1
            FillEllipse(this.pGraphics
                , this.Brushes[BrushIndex]
                , cx - this.DotSize / 2
                , cy - this.DotSize / 2
                , this.DotSize, this.DotSize)
        }
        BitBlt(this.hDC_win, 0, 0, W, H, this.hDC, 0, 0)
    }


    ;---------------------------------------------------------------------------
    Start() { ; start timer
    ;---------------------------------------------------------------------------
        SetTimer, %this%, % this.Delay
        this.Call() ; start immediately
    }


    ;---------------------------------------------------------------------------
    Stop() { ; stop timer
    ;---------------------------------------------------------------------------
        SetTimer, %this%, Off
    }


} ; ============================================================================


; from Tic's library
Startup() {
	if !DllCall("GetModuleHandle", "str", "gdiplus", "UPtr")
		DllCall("LoadLibrary", "str", "gdiplus")
	VarSetCapacity(si, A_PtrSize = 8 ? 24 : 16, 0), si := Chr(1)
	DllCall("gdiplus\GdiplusStartup", "UPtr*", pToken, "UPtr", &si, "UPtr", 0)
	return pToken
}
ShutdownG(pToken) {
	DllCall("gdiplus\GdiplusShutdown", "UPtr", pToken)
	if hModule := DllCall("GetModuleHandle", "str", "gdiplus", "UPtr")
		DllCall("FreeLibrary", "UPtr", hModule)
	return 0
}
GraphicsClear(pGraphics, ARGB=0x00ffffff) {
    return DllCall("gdiplus\GdipGraphicsClear", "UPtr", pGraphics, "int", ARGB)
}
CreateDIBSection(w, h, hdc="", bpp=32, ByRef ppvBits=0) {
	hdc2 := hdc ? hdc : GetDC(), VarSetCapacity(bi, 40, 0)
    , NumPut(w, bi, 4, "uint"), NumPut(h, bi, 8, "uint")
    , NumPut(40, bi, 0, "uint"), NumPut(1, bi, 12, "ushort")
    , NumPut(0, bi, 16, "uInt"), NumPut(bpp, bi, 14, "ushort")
	hbm := DllCall("CreateDIBSection", "UPtr", hdc2, "UPtr", &bi, "uint", 0
        , "UPtr*", ppvBits, "UPtr", 0, "uint", 0, "UPtr")
	if !hdc
		ReleaseDC(hdc2)
	return hbm
}
CreateCompatibleDC(hdc=0) {
   return DllCall("CreateCompatibleDC", "UPtr", hdc)
}
SelectObject(hdc, hgdiobj) {
	return DllCall("SelectObject", "UPtr", hdc, "UPtr", hgdiobj)
}
GraphicsFromHDC(hdc) {
    DllCall("gdiplus\GdipCreateFromHDC", "UPtr", hdc, "UPtr*", pGraphics)
    return pGraphics
}
DeleteBrush(pBrush) {
   return DllCall("gdiplus\GdipDeleteBrush", "UPtr", pBrush)
}
DeleteObject(hObject) {
   return DllCall("DeleteObject", "UPtr", hObject)
}
DeleteDC(hdc) {
   return DllCall("DeleteDC", "UPtr", hdc)
}
DeleteGraphics(pGraphics) {
   return DllCall("gdiplus\GdipDeleteGraphics", "UPtr", pGraphics)
}
BrushCreateSolid(ARGB=0xff000000) {
	DllCall("gdiplus\GdipCreateSolidFill", "UInt", ARGB, "UPtr*", pBrush)
	return pBrush
}
FillEllipse(pGraphics, pBrush, x, y, w, h) {
	return DllCall("gdiplus\GdipFillEllipse", "UPtr", pGraphics
        , "UPtr", pBrush, "float", x, "float", y, "float", w, "float", h)
}
GetDC(hwnd=0) {
	return DllCall("GetDC", "UPtr", hwnd)
}
ReleaseDC(hdc, hwnd=0) {
	return DllCall("ReleaseDC", "UPtr", hwnd, "UPtr", hdc)
}
BitBlt(ddc, dx, dy, dw, dh, sdc, sx, sy, Raster="") {
	return DllCall("gdi32\BitBlt", "UPtr", dDC
        , "int", dx, "int", dy, "int", dw, "int", dh, "UPtr", sDC
        , "int", sx, "int", sy, "uint", Raster ? Raster : 0x00CC0020)
}

; Default = 0
; LowQuality = 1
; HighQuality = 2
; Bilinear = 3
; Bicubic = 4
; NearestNeighbor = 5
; HighQualityBilinear = 6
; HighQualityBicubic = 7
SetInterpolationMode(pGraphics, InterpolationMode) {
   return DllCall("gdiplus\GdipSetInterpolationMode"
   , A_PtrSize ? "UPtr" : "UInt", pGraphics
   , "int", InterpolationMode)
}

; Default = 0
; HighSpeed = 1
; HighQuality = 2
; None = 3
; AntiAlias = 4
SetSmoothingMode(pGraphics, SmoothingMode) {
   return DllCall("gdiplus\GdipSetSmoothingMode"
   , A_PtrSize ? "UPtr" : "UInt", pGraphics
   , "int", SmoothingMode)
}

I have continued to developed the spinner class, to see what I can do for the usual suspects in terms of hijacking my stuff :D .
The result is awesome and exiting, but is no longer called Spinner, it's called Animation and it is maybe too early to work with, but it is matured sufficiently to show to you.
Notice the gradual change here:

Code: Select all

class Spinner { ; manages a spinning animation
class Animation { ; manage animations
Link https://autohotkey.com/boards/viewtopic.php?f=6&t=44198

Post Reply

Return to “Scripts and Functions (v1)”