﻿B4A=true
Group=Default Group
ModulesStructureVersion=1
Type=Class
Version=7.3
@EndOfDesignText@
'Custom View class

'version: 2.03 by Ivan Aldaz 2021/08/02
'- Added 'Animation type' property. At this moment Linear and Ease-In Ease-Out, but you can add more. Visit the web http://gizma.com/easing/ for help in formulas
'- Added 'SetLayout' method, to change size and position by code
'- Can change label Typeface, TextSize and TextColor by code
' - v 2.02, 2021/08/01
'- Added 'MaxValue' property in Designer and code
'- Can change colors, stroke and duration by code

'version: 2.01 by DV 2017/10/22
'- fixed radius
'- fixed label (value inside the circle) position and dimension
'- Added visible property
'- Added setValueUnit property to set a text after the value (i.e. %)
'- Added a reset method to immediately clear the circular progressbar

'version: 2.00 by Erel

#DesignerProperty: Key: ColorFull, DisplayName: Color Full, FieldType: Color, DefaultValue: 0xFF06F96B, Description:
#DesignerProperty: Key: ColorEmpty, DisplayName: Color Empty, FieldType: Color, DefaultValue: 0xFF868686, Description:
#DesignerProperty: Key: StrokeWidth, DisplayName: Stroke Width, FieldType: Int, DefaultValue: 10, Description: dip	
#DesignerProperty: Key: MaxValue, DisplayName: Maximum value, FieldType: Float, DefaultValue: 100, Description: Maximum value to show	
#DesignerProperty: Key: Duration, DisplayName: Duration From 0 To Max, FieldType: Float, DefaultValue: 3000, Description: Milliseconds
#DesignerProperty: Key: AnimationType, DisplayName: Animation type, FieldType: String, DefaultValue: Ease-In Ease-Out, List: Ease-In Ease-Out|Linear



Sub Class_Globals
	
	Private mEventName As String 'ignore
	Private mCallBack As Object 'ignore
	Private cvs As B4XCanvas
	Private xui As XUI
	Private mLbl As B4XView
	Private cx, cy, radius As Float
	Private currentValue As Float = 0 
	Private mBase As B4XView
	Private mUnit As String = ""
	
	Public DurationFromZeroToMax As Float
	Public AnimationType As String
	Public ColorFull    As Long
	Public ColorEmpty   As Long
	Public Stroke       As Float
	Public MaxValue     As Float
	
	Public Const ANIMATIONTYPE_LINEAR As String = "Linear"
	Public Const ANIMATIONTYPE_EASEINOUT As String = "Ease-in Ease-out"

End Sub

Public Sub Initialize (Callback As Object, EventName As String)
	mEventName = EventName
	mCallBack = Callback
End Sub

Public Sub DesignerCreateView (Base As Object, Lbl As Label, Props As Map)
	mBase = Base
	mBase.SetLayoutAnimated(0, mBase.Left, mBase.Top,  Min(mBase.Width, mBase.Height), Min(mBase.Width, mBase.Height))
	ColorFull = xui.PaintOrColorToColor(Props.Get("ColorFull"))
	ColorEmpty = xui.PaintOrColorToColor(Props.Get("ColorEmpty"))
	Stroke = DipToCurrent(Props.Get("StrokeWidth"))
	DurationFromZeroToMax = Props.Get("Duration")
	MaxValue = Props.Get("MaxValue")
	AnimationType = Props.Get("AnimationType")
	
	mLbl = Lbl
	cx = mBase.Width / 2
	cy = mBase.Height / 2
	
	'2017/10/22 radius is based on stroke width
	radius = cx - Stroke / 2 
	cvs.Initialize(mBase)
	mLbl.SetTextAlignment("CENTER", "CENTER")
	
	'2017/10/22 center text
	mBase.AddView(mLbl, Stroke, Stroke, mBase.Width - 2 * Stroke, mBase.Height - 2 * Stroke)
		
	cvs.Initialize(mBase)
	DrawValue(currentValue)
	
End Sub


Private Sub Base_Resize (Width As Double, Height As Double)
	cx = Width / 2
	cy = Height / 2
	radius = cx - 10dip
	mBase.SetLayoutAnimated(0, mBase.Left, mBase.Top,  Min(Width,Height), Min(Width, Height))
	cvs.Resize(Width, Height)
	mLbl.SetLayoutAnimated(0, 0, cy - 20dip, Width, 40dip)
	DrawValue(currentValue)
End Sub

Public Sub setValue(NewValue As Float)
	AnimateValueTo(NewValue)
End Sub

Public Sub getValue As Float
	Return currentValue
End Sub

'get or set the circular progressbar visibility
public Sub setVisible(Visible As Boolean)
	mBase.Visible=Visible
End Sub

public Sub getVisible As Boolean
	Return mBase.Visible
End Sub

'Set the value measure unit as string (use short names like %, mV, A)
public Sub setValueUnit (Unit As String)
	mUnit=Unit
	DrawValue(currentValue)
End Sub


'rest the circular progressbar
public Sub Reset
	currentValue=0
	DrawValue(currentValue)
End Sub

Private Sub AnimateValueTo(NewValue As Float)
	
	Dim n As Long = DateTime.Now
	Dim duration As Int = Abs(currentValue - NewValue) / MaxValue  * DurationFromZeroToMax + 1000
	Dim start As Float = currentValue
	
	currentValue = NewValue
	
	Dim tempValue As Float
	
	Do While DateTime.Now < n + duration
		
		Select AnimationType
			Case ANIMATIONTYPE_LINEAR
				tempValue = ValueLinear(n)
			Case ANIMATIONTYPE_EASEINOUT	
				tempValue = ValueFromTimeEaseInOut(DateTime.Now - n, start, NewValue - start, duration)
		   'Case...
		   						
		End Select
		
		DrawValue(tempValue)
				
		Sleep(10)
		If NewValue <> currentValue Then Return 'will happen if another update has started
		
	Loop
	
	DrawValue(currentValue)
End Sub


'quartic easing in/out from http://gizma.com/easing/
Private Sub ValueFromTimeEaseInOut(Time As Float, Start As Float, ChangeInValue As Float, Duration As Int) As Float
	Time = Time / (Duration / 2)
	If Time < 1 Then
		Return ChangeInValue / 2 * Time * Time * Time * Time + Start
	Else
		Time = Time - 2
		Return -ChangeInValue / 2 * (Time * Time * Time * Time - 2) + Start
	End If
End Sub


Private Sub DrawValue(Value As Float)
	'Log("Cx,Cy,Radius,Stroke: " & cx & "," & cy & "," & radius & "," & stroke)
	cvs.ClearRect(cvs.TargetRect)
	cvs.DrawCircle(cx, cy, radius, ColorEmpty, False, Stroke)

	mLbl.Text = $"$1.0{Value}"$ & mUnit
	Dim startAngle = -90, sweepAngle = Value / MaxValue * 360 As Float

	If Value < MaxValue Then
		
		Dim p As B4XPath
			p.InitializeArc(cx, cy, radius + Stroke + 1dip, startAngle, sweepAngle)
		
		cvs.ClipPath(p)
		cvs.DrawCircle(cx, cy, radius - 0.5dip, ColorFull, False, Stroke + 1dip)
		cvs.RemoveClip
		
	Else
		
		cvs.DrawCircle(cx, cy, radius - 0.5dip, ColorFull, False, Stroke + 1dip)
		
	End If
	
	cvs.Invalidate
	
End Sub

'same as Base_Resize, with 'left' and 'top' values
Public Sub SetLayout(left As Int, top As Int, width As Int, height As Int)
	cx = width / 2
	cy = height / 2
	radius = cx - Stroke/2
	mBase.SetLayoutAnimated(0, left, top,  Min(width,height), Min(width, height))
	cvs.Resize(width, height)
	mLbl.SetLayoutAnimated(0, Stroke, Stroke, mBase.Width - 2 * Stroke, mBase.Height - 2 * Stroke)
	mLbl.Color = Colors.ARGB(120, 200,200,200)
	DrawValue(currentValue)	
End Sub

Public Sub setLabelTextSize (size As Int)
	mLbl.TextSize = size	
End Sub

Public Sub setLabelTypeface (font As B4XFont)
	mLbl.Font = font
End Sub

Public Sub setLabelTextColor (clr As Long)
	mLbl.TextColor = clr
End Sub

Private Sub ValueLinear(iniTime As Long) As Float
	Dim timeElapsed As Long = DateTime.Now - iniTime
	Return MaxValue * timeElapsed / DurationFromZeroToMax		
End Sub
