Other things I am looking to implement are an additional border on inside and outside for possible 3D look. To be able to specify a color scheme for a circle so that it will use a specific color for a given segment or an interpolated color. A more elaborate demo showing a greater range of features.
Code: Select all
;- ProgressCircle
; 2008 by walker
; modifications by ebs
; modifications by Demivec
; modifications by walker
;--------------------------------------------
;-free for everyone and any purposes
;-enhancements made by others
;-must be published and posted in the same PB-Forum ;-)
;-cross plattform ability must be achieved
;--------------------------------------------
EnableExplicit
; [Demivec] added Size parameter for init instead of radius, changed radius to a float,
; added style parameter to select a continous or segmented circle (1=continuos)
;Allows more than one progressCircle to be displayed.
Structure ProgressCircle
;for init: need to set Size,segmentCount,segmentColor,backgroundColor,gx,gy,gadgetNum,imageNum
; all values used for init, excluding segmentColor are considered constants after init is performed
;between updates: can change segmentColor,textcolor, either fontID or newFont/fontNum
Size.l ;size of circle (even #'s only);used during init ;12 is minimum; maximum... your screen ;-)
radius.f ;radius of circle ;(1/2 Size) used internally and not to be changed by the user
segmentCount.l ;#of segments in circle ;any value allowed, working values are usually less than Size (i.e. segments<Size)
;for max segments at various sizes these work (seg,Size) (8,12) (12,16) (16,20) (32,30) (1 to 44,50) (100,120)
segmentColor.l ;color of circle ,during init the entire circle is drawn in this color
backgroundcolor.l ;background color of circle
textcolor.l ;color of text in circle
gx.l ;coordinates to create ImageGadget at (used only during init)
gy.l
;If any of the next two values are set to #PB_any, an init call will set them with their new value,
;if they are not equal to #PB_Any their value will be used for the desired item.
gadgetNum.l ;if =#PB_Any during init, will be equal to gadget# on exit
imageNum.l ;if =#PB_Any during init, will be equal to image# on exit
;A user should either set fontID to a pre-initialized fontID or they should set newFont to the name of font.
;If newFont is used it will have the side-effect of freeing the previous fontID, so for a given
;ProgressCircle only one method should be used during it's existance.
fontID.l ;font handle for displayed text, if not zero it is treated as valid (or pre-initialized)
newFont.s ;name of font to use load during next update, fontID will equal the font handle and this
;will equal "" after LoadFont is attempted
fontNum.l ;if =#PB_Any during init, will be equal to font# on exit or zero, this value can be read
;to determine if a new font was succesfully loaded (it will be non-zero if successful)
style.l ;indicates if using a continuous (=1) or a segmented circle (<>1)
EndStructure
; [Demivec] utilized a structure to hold ProgressCircle data, unified seperate procedures and created
; a command format to specify actions (rather OOP like), allowed font to be specified and changed,
; made it possible to use either #pb_any or custom values for gadget#,image#,fontID.
Procedure ProgressCircle(*pCircle.ProgressCircle, cmd.l, state.l = 1, text.s = "")
Protected x.l,y.l,x2.l,y2.l,m.l,TW2.l,TH2.l,rangeStart.l ;added x2,y2 to help with continuous circle
Protected angle_rad.f ; [Demivec] changed to a float
;Call procedure ProgressCircle with the forms below to accomplish each given task:
; ProgressCircle(@Data.ProgressCircle,10) Setup Data of ProgressCircle with default values
; ProgressCircle(@Data.ProgressCircle,0) Init ProgressCircle
; ProgressCircle(@Data.ProgressCircle,1,state# [,Text]) Draw a specific state# [, display Text]
; ProgressCircle(@Data.ProgressCircle,1,-state# [,Text]) Draw a range of states from 1 to +state# [, display Text]
; ProgressCircle(@Data.ProgressCircle,-1) Free circle elements (image,font,gadget), convenient to
; use if all three items were created by ProgressCircle.
Select cmd ;commands in order of likely use
;the command #'s are more or less arbitrary, may be better to replace with constants in future
;of the form #PC_SetDefaults_cmd or maybe these forms #PCCmd_Init,#PCCmd_Update,#PCCmd_FreeElements
Case 1 ;{draw state#
;check if current font should be replaced with a new font
If *pCircle\newFont <> ""
If *pCircle\fontNum <> #PB_Any
FreeFont(*pCircle\fontNum) ;free font if a previous font was loaded
EndIf
*pCircle\fontNum = LoadFont(*pCircle\fontNum,*pCircle\newFont,*pCircle\radius / 3 - 1,#PB_Font_Bold)
*pCircle\newFont = "" ;reset so repeated attempts will not be made
If IsFont(*pCircle\fontNum) ;if successfully loaded set the fontID
*pCircle\fontID = FontID(*pCircle\fontNum)
EndIf
EndIf
CallDebugger
StartDrawing(ImageOutput(*pCircle\imageNum))
;handle text first
If text <> ""
If *pCircle\fontID <> 0 ;check if fontID is initialized
DrawingFont(*pCircle\fontID) ;use only successfully loaded font
TH2 = TextHeight(text) / 2
TW2 = TextWidth(text) / 2
; [Demivec] adjusted circle function to use new parameter
Circle(*pCircle\radius + 0.5,*pCircle\radius + 0.5,*pCircle\Size / 3,*pCircle\backgroundcolor);clear text area
DrawText(*pCircle\radius - TW2,*pCircle\radius - TH2,text,*pCircle\textcolor,*pCircle\backgroundcolor)
EndIf
EndIf
;check if setting a range or only a single state
; a range will be set (of from 1 to +state) if state is negative,
; only the single state will be set if state>0
If state<0
rangeStart = 1
state * -1
Else
rangeStart = state
EndIf
CallDebugger
;first segment is at top of circle
state - 1
For m= rangeStart - 1 To state ; To clear the circle (set color to initial color and text to 1 space, and state=-segments) [Demivec] changed explanation
angle_rad = #PI * ((2 * m + 1) / *pCircle\segmentCount - 1/2)
; calculate point on radius
x = *pCircle\radius + (*pCircle\radius - 1) * Cos(angle_rad) ; [Demivec] adjusted calculation, finds a point at a set distance from largest radius
y = *pCircle\radius + (*pCircle\radius - 1) * Sin(angle_rad)
;fill segment with segmentColor, if non-continuos, or small variations of segmentColro if a continuous circle
If *pCircle\style = 1
If m = (*pCircle\segmentCount - 1)
FillArea(x,y,-1,*pCircle\segmentColor + 1 - 2 *(*pCircle\segmentCount & 1)) ;handle color choice for last segment
Else
FillArea(x,y,-1,*pCircle\segmentColor + (m & 1)) ;handle color choice for all but last segment
EndIf
Else
FillArea(x,y,*pCircle\backgroundcolor,*pCircle\segmentColor) ;handle non-continuous circle segment
EndIf
Next
StopDrawing()
SetGadgetState(*pCircle\gadgetNum,ImageID(*pCircle\imageNum)) ;}
Case 0 ;{initialize progress circle image and gadget
;
If *pCircle\imageNum = #PB_Any
*pCircle\imageNum = CreateImage(#PB_Any,*pCircle\Size,*pCircle\Size) ;assign a generated image#
Else
;use image# supplied (frees previous image if necessary)
CreateImage(*pCircle\imageNum,*pCircle\Size,*pCircle\Size)
EndIf
*pCircle\radius = (*pCircle\Size - 1) / 2 ; [Demivec] adjusted calculation based on circle function side-effects
StartDrawing(ImageOutput(*pCircle\imageNum))
Box(0,0,*pCircle\Size,*pCircle\Size,*pCircle\backgroundcolor)
; [Demivec] adjusted circle function to use new parameter
Circle(*pCircle\radius + 0.5,*pCircle\radius + 0.5,*pCircle\radius + 0.5,*pCircle\segmentColor+(*pCircle\style & 1) * 2)
Circle(*pCircle\radius + 0.5,*pCircle\radius + 0.5,*pCircle\Size / 3,*pCircle\backgroundcolor)
LineXY(*pCircle\radius,*pCircle\radius,*pCircle\radius,0,*pCircle\segmentColor)
If *pCircle\style = 1 ;continuous
;draw alternating segments using small variations in color
For m = 1 To *pCircle\segmentCount - 1
angle_rad = #PI * ((2 * m) / *pCircle\segmentCount - 1/2)
x = *pCircle\radius * (1 + Cos(angle_rad))
y = *pCircle\radius * (1 + Sin(angle_rad))
LineXY(*pCircle\radius,*pCircle\radius,x,y,*pCircle\segmentColor + (m & 1))
angle_rad = #PI * ((2 * m - 1) / *pCircle\segmentCount - 1/2)
; calculate point on radius
x2 = *pCircle\radius + (*pCircle\radius - 1) * Cos(angle_rad)
y2 = *pCircle\radius + (*pCircle\radius - 1) * Sin(angle_rad)
FillArea(x2,y2,-1,*pCircle\segmentColor + ((m + 1) & 1))
Next
If (*pCircle\segmentCount & 1) = 1 ;for odd segmentCount last segment gets a unique color
LineXY(*pCircle\radius,*pCircle\radius,x,y,*pCircle\segmentColor - (*pCircle\segmentCount & 1))
EndIf
angle_rad = #PI * ((2 * (*pCircle\segmentCount) - 1) / *pCircle\segmentCount - 1/2)
x = *pCircle\radius + (*pCircle\radius - 1) * Cos(angle_rad)
y = *pCircle\radius + (*pCircle\radius - 1) * Sin(angle_rad)
FillArea(x,y,-1,*pCircle\segmentColor + 1 - 2 *(*pCircle\segmentCount & 1))
Else ;non-continuous
;draw radial lines
For m = 1 To *pCircle\segmentCount - 1
angle_rad = #PI * ((2 * m) / *pCircle\segmentCount - 1/2) ;
x = *pCircle\radius * (1 + Cos(angle_rad))
y = *pCircle\radius * (1 + Sin(angle_rad))
LineXY(*pCircle\radius,*pCircle\radius,x,y,*pCircle\backgroundcolor)
Next
EndIf
StopDrawing()
If *pCircle\gadgetNum = #PB_Any
*pCircle\gadgetNum = ImageGadget(#PB_Any,x,y,*pCircle\Size,*pCircle\Size,ImageID(*pCircle\imageNum))
Else
ImageGadget(*pCircle\gadgetNum,*pCircle\gx,*pCircle\gy,*pCircle\Size,*pCircle\Size,ImageID(*pCircle\imageNum))
EndIf ;}
Case -1 ;{free progress circle elements (image, font, gadget)
If IsGadget(*pCircle\gadgetNum)
FreeGadget(*pCircle\gadgetNum)
*pCircle\gadgetNum = 0
EndIf
If IsFont(*pCircle\fontNum)
FreeFont(*pCircle\fontNum)
*pCircle\fontNum = 0
EndIf
If IsImage(*pCircle\imageNum)
FreeImage(*pCircle\imageNum)
*pCircle\imageNum = 0
EndIf ;}
Case 10 ;{setup default progress circle data, suitable for an init cmd
;gx,gy,textColor values are left unchanged
*pCircle\Size = 50 ;
*pCircle\segmentCount = 25
*pCircle\segmentColor = $AAB68F
*pCircle\backgroundcolor = $D2D8C3
*pCircle\gadgetNum = #PB_Any
*pCircle\imageNum = #PB_Any
*pCircle\fontNum = #PB_Any
*pCircle\fontID = 0
CompilerIf #PB_Compiler_OS = #PB_OS_Windows
*pCircle\newFont = "Arial"
CompilerEndIf
CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
*pCircle\newFont = "Arial"
CompilerEndIf
CompilerIf #PB_Compiler_OS = #PB_OS_Linux
*pCircle\newFont = "FreeSans" ;this is only a guess, suggestions welcome
CompilerEndIf ;}
EndSelect
While WindowEvent():Wend; update the gadget
ProcedureReturn 1
EndProcedure
;---- DEMO -----<
Define.l r,g,b,count,m
Define.l event
OpenWindow(0,0,0,310,310,"ProgressCircle DEMO",#PB_Window_SystemMenu | #PB_Window_SizeGadget)
CreateGadgetList(WindowID(0))
Define.ProgressCircle circle_1,circle_2
;setup progressCircle data
ProgressCircle(@circle_1,10) ;set ProgressCircle defaults
circle_1\gadgetNum = 0 ;#PB_Any
circle_1\gx = 70
circle_1\gy = 70
circle_1\style = 1
ProgressCircle(@circle_1,0) ;initialize progressCircle
r = 10
g = 150
b = 10
count = 0
Repeat
circle_1\segmentColor = RGB(r, g, b)
For m = 1 To circle_1\segmentCount
circle_1\textcolor = RGB(236, 200 - (m * 5), 19)
ProgressCircle(@circle_1,1,m,StrF(m / circle_1\segmentCount * 100,0) + "%") ;update ProgressCircle
Delay(100)
event = WindowEvent()
While event ;improved event catcher for demo
If event = #PB_Event_CloseWindow
Break 2
EndIf
event=WindowEvent()
Wend
Next
count+1
r+20
g+20
b+10
If r >240 Or g>240 Or b>240
r=Random(255)
g=Random(255)
b=Random(255)
EndIf
Until event =#PB_Event_CloseWindow Or count=3
circle_1\segmentColor = $AAB68F
ProgressCircle(@circle_1,1,-circle_1\segmentCount) ; clear the ProgressCircle....
Delay(300)
;ProgressCircle(@circle_1,-1) ;free Progress Circle elements, image,gadget,font [Demivec]
End