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
class.Spinner.ahk
Code: Select all
;-------------------------------------------------------------------------------
; class.Spinner.ahk
; by wolf_II
;-------------------------------------------------------------------------------
#Include, <Gdip>
;===============================================================================
class Spinner { ; manages a spinning animation
;===============================================================================
; ClassVariables
static ID := 0 ; instance IDs derive from here
static Pi := 4 * ATan(1) ; trigonometric constant
; 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, w/o "0x"
Delay := 250 ; time in ms between drawings
bClockwise := False ; direction of the spinner
;---------------------------------------------------------------------------
__New(oConfig, hParent := 0) { ; attach a new instance to hParent
;---------------------------------------------------------------------------
; oConfig: an object with some/all the configurable options
;-----------------------------------------------------------------------
If oConfig.HasKey("DotCount")
this.DotCount := oConfig.DotCount
If oConfig.HasKey("DotSize")
this.DotSize := oConfig.DotSize
If oConfig.HasKey("Radius")
this.Radius := oConfig.Radius
If oConfig.HasKey("Color")
this.Color := oConfig.Color
If oConfig.HasKey("Delay")
this.Delay := oConfig.Delay
If oConfig.HasKey("bClockwise")
this.bClockwise := oConfig.bClockwise
;---------------------------------------------------
; position and size
;---------------------------------------------------
If (hParent = 0)
this.Width := A_ScreenWidth, this.Height := A_ScreenHeight
Else {
WinGetPos,,, Width, Height, ahk_id %hParent%
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 Gdip
;---------------------------------------------------
this.pToken := Gdip_Startup()
this.hDC := CreateCompatibleDC()
this.hBM := CreateDIBSection(this.Width, this.Height)
this.oBM := SelectObject(this.hDC, this.hBM)
this.pGraphics := Gdip_GraphicsFromHDC(this.hDC)
Gdip_SetSmoothingMode(this.pGraphics, 4) ; **** tip from Helgef
;---------------------------------------------------
; setup SpinnerGUI
;---------------------------------------------------
this.ID := Spinner.ID++ ; increment ClassVar, store InstanceVar
Gui, % "Spinner" this.ID ": New", +AlwaysOnTop +hWndhSpin
Gui, +E0x80020 ; WS_EX_LAYERED | WS_EX_TRANSPARENT (click through)
Gui, +0x40000000 -0x80000000 ; +WS_CHILD -WS_POPUP
DllCall("SetParent", "Ptr", this.hSpin := hSpin, "Ptr", hParent)
;---------------------------------------------------
; 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]))
}
}
;---------------------------------------------------------------------------
__Delete() { ; clean up
;---------------------------------------------------------------------------
Gui, % "Spinner" this.ID ": Destroy"
SelectObject(this.hDC, this.oBM)
DeleteObject(this.hBM)
DeleteDC(this.hDC)
Gdip_DeleteGraphics(this.pGraphics)
Loop, % this.DotCount
Gdip_DeleteBrush(this.BRUSHES[A_Index])
Gdip_Shutdown(this.pToken)
}
;---------------------------------------------------------------------------
Call() { ; timer controlled, private method
;---------------------------------------------------------------------------
; the timer clears the drawing, the 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
Gdip_GraphicsClear(this.pGraphics)
Sign := this.bClockwise ? -1 : 1
Loop, % This.DotCount {
Gdip_FillEllipse(this.pGraphics
, this.BRUSHES[Mod(this.Index + A_Index * Sign, this.DotCount) + 1]
, this.DOT[A_Index].x, this.DOT[A_Index].y, this.DotSize, this.DotSize)
UpdateLayeredWindow(this.hSpin, this.hDC, 0, 0, this.Width, this.Height)
}
}
;---------------------------------------------------------------------------
Start() { ; start spinning
;---------------------------------------------------------------------------
Gui, % "Spinner" this.ID ": Show", % "NA x0 y0 w" this.Width " h" this.Height
this.Index := this.DotCount ; initialize
SetTimer, %this%, % this.Delay ; uses Call()
this.Call() ; start immediately in case Delay is large
}
;---------------------------------------------------------------------------
Stop() { ; stop spinning
;---------------------------------------------------------------------------
SetTimer, %this%, Off
}
} ; end of class
Demo 1 - AHK Gui & Notepad.ahk
Code: Select all
#NoEnv
#SingleInstance, Force
#Include, class.Spinner.ahk
MsgBox, 4, Demonstrate Spinner, Show demo with AHK Gui as parent?
IfMsgBox, Yes
Gosub, Demo_With_Gui
Else ExitApp
MsgBox, 4, Demonstrate Spinner, Show demo with Notepad as parent?
IfMsgBox, Yes
Gosub, Demo_With_Notepad
ExitApp
;-------------------------------------------------------------------------------
Demo_With_Gui: ; demo with a control in a AHK Gui
;-------------------------------------------------------------------------------
Gui, Margin, 30, 18
Gui, Add, Text, w300 h200 +HWNDhTXT Border
Gui, Show, x100 y100, Move me!
MySpinner := new Spinner([], hTXT)
MySpinner.Start()
Sleep, 3000
MsgBox,, Demonstrate Spinner, Please move the Gui with your mouse!
MySpinner.Stop()
Sleep, 3000
MsgBox,, Demonstrate Spinner, The spinner stopped!
MySpinner.__Delete()
Sleep, 3000
MsgBox,, Demonstrate Spinner, The spinner disappeared!
Return
;-------------------------------------------------------------------------------
Demo_With_Notepad: ; Edit1 in Notepad as parent
;-------------------------------------------------------------------------------
;---------------------------------------------------------------------------
Configuration := { DotCount: 15, DotSize: 20, Radius: 50
, Delay: 100, bClockwise: True}
;---------------------------------------------------------------------------
Run, notepad,,, PID
WinWaitActive, ahk_pid%PID%
ControlGet, hParent, Hwnd,, Edit1
MySpinner := new Spinner(Configuration, hParent)
MySpinner.Start()
Sleep, 3000
MsgBox,, Demonstrate Spinner, End of Demonnstration.
WinClose, ahk_pid%PID%
Return
Demo 2 - Example configs.ahk
Code: Select all
#NoEnv
#SingleInstance, Force
#Include, class.Spinner.ahk
;---------------------------------------------------------------------------
cfg1 := { DotCount: 5, DotSize: 20, Radius: 18
, Color: "FF0000" , Delay: 500, bClockwise: False}
;---------------------------------------------------------------------------
cfg2 := { DotCount: 7, DotSize: 15, Radius: 19
, Color: "0000FF" , Delay: 400, bClockwise: True}
;---------------------------------------------------------------------------
cfg3 := { DotCount: 9, DotSize: 12, Radius: 20 ; (default config)
, Color: "000000" , Delay: 250, bClockwise: False}
;---------------------------------------------------------------------------
cfg4 := { DotCount: 11, DotSize: 20, Radius: 40
, Color: "FF00FF" , Delay: 200, bClockwise: True}
;---------------------------------------------------------------------------
cfg5 := { DotCount: 15, DotSize: 20, Radius: 60
, Color: "00FF00" , Delay: 150, bClockwise: False}
;---------------------------------------------------------------------------
cfg6 := { DotCount: 19, DotSize: 20, Radius: 75
, Color: "C0FEFE" , Delay: 100, bClockwise: True}
;---------------------------------------------------------------------------
Gui, Margin, 40, 30
Gui, Color, CCCCCC
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
Gui, Show, x20 y50, 6-fold Demo
Spinner1 := new Spinner(cfg1, hTXT1)
Spinner2 := new Spinner(cfg2, hTXT2)
Spinner3 := new Spinner(cfg3, hTXT3)
Spinner4 := new Spinner(cfg4, hTXT4)
Spinner5 := new Spinner(cfg5, hTXT5)
Spinner6 := new Spinner(cfg6, hTXT6)
Spinner1.Start()
Spinner2.Start()
Spinner3.Start()
Spinner4.Start()
Spinner5.Start()
Spinner6.Start()
Return
GuiClose:
ExitApp
Successfully tested on Win10 64bit with AHK v1.1.26.01