AngleGadget - circular trackbar + user-defined labels
Posted: Mon Jul 11, 2011 12:08 pm
features:
- orientation
- open or closed scalar
- keyboard and mouse functions already integrated (focus, tab, up, down, scroll wheel, ...)
- user-definable labels
- automatic alignment (with caption) for the optimal use of space
Code: Select all
EnableExplicit
;- Include
Prototype.s AngleGadgetLabel(State.i)
Structure AngleGadgetData
State.i
Min.i
Max.i
Flags.i
Ticks.i
NullTick.f
Range.f
MinorTicks.i
MajorTicks.i
Change.i
CenterX.i
CenterY.i
Radius.i
MajorLabel.AngleGadgetLabel
EndStructure
Structure AngleGadgetConstant
FaceColor.l
TextColor.l
ShadowColor.l
HighlightColor.l
FontID.i
EndStructure
Global AngleGadgetConstant.AngleGadgetConstant
With AngleGadgetConstant
CompilerIf #PB_Compiler_OS = #PB_OS_Windows
\FaceColor = GetSysColor_(#COLOR_3DFACE)|$FF<<24
\TextColor = GetSysColor_(#COLOR_WINDOWTEXT)|$FF<<24
\ShadowColor = GetSysColor_(#COLOR_3DSHADOW)|$FF<<24
\HighlightColor = GetSysColor_(#COLOR_3DHIGHLIGHT)|$FF<<24
CompilerElse
\FaceColor = $FFD0D0D0
\TextColor = $FF000000
\ShadowColor = $FF808080
\HighlightColor = $FFFFFFFF
CompilerEndIf
\FontID = FontID(LoadFont(#PB_Any, "Arial", 8))
EndWith
CompilerIf Defined(PB_DefaultColor, #PB_Constant) = #False
#PB_DefaultColor = -$100000000
CompilerEndIf
CompilerIf Defined(TwoPI, #PB_Constant) = #False
#TwoPI = #PI*2.0
CompilerEndIf
CompilerIf Defined(HalfPI, #PB_Constant) = #False
#HalfPI = #PI*0.5
CompilerEndIf
Enumeration #True
#AngleGadget_MinorTicks
#AngleGadget_MajorTicks
#AngleGadget_Orientation
#AngleGadget_Length
#AngleGadget_MajorLabel
EndEnumeration
Procedure.i AngleGadget_Normalize(Value.i, Min.i, Max.i)
Value - Min : Value % (Max-Min)
If Value < 0 : ProcedureReturn Value+Max : Else : ProcedureReturn Value+Min : EndIf
EndProcedure
Procedure.f AngleGadget_NormalizeF(Value.f, Max.f)
Value = Mod(Value, Max)
If Value < 0 : ProcedureReturn Value+Max : Else : ProcedureReturn Value : EndIf
EndProcedure
Procedure AngleGadget_LineRA(X.i, Y.i, Radius1.f, Angle1.f, Radius2.f, Angle2.f, Color.q=#PB_DefaultColor)
If Color = #PB_DefaultColor
LineXY(Cos(Angle1)*Radius1+X, Sin(Angle1)*Radius1+Y, Cos(Angle2)*Radius2+X, Sin(Angle2)*Radius2+Y)
Else
LineXY(Cos(Angle1)*Radius1+X, Sin(Angle1)*Radius1+Y, Cos(Angle2)*Radius2+X, Sin(Angle2)*Radius2+Y, Color)
EndIf
EndProcedure
Procedure DrawAngleGadget(Gadget.i)
Protected *AngleGadgetData.AngleGadgetData = GetGadgetData(Gadget)
Protected Tick.i, Angle.f, Radius.i, Text.s, X.i, Y.i, State.i
Protected DeltaAngle.f = #PI*0.75, Color.i, MinorTicks.i
Protected TextX.i, TextWidth.i, MaxTextWidth.i, MinTextWidth.i
Protected TextY.i, TextHeight.i, MaxTextHeight.i, MinTextHeight.i
Protected MinRadiusX.f=Infinity(), MaxRadiusX.f=-Infinity(), RadiusX.f
Protected MinRadiusY.f=Infinity(), MaxRadiusY.f=-Infinity(), RadiusY.f
With *AngleGadgetData
If StartDrawing(CanvasOutput(Gadget))
DrawingMode(#PB_2DDrawing_Transparent)
DrawingFont(AngleGadgetConstant\FontID)
If \Range = #TwoPI
MinorTicks = \MinorTicks-1
Else
MinorTicks = \MinorTicks
EndIf
For Tick = 0 To MinorTicks
Angle = Tick / \MinorTicks * \Range + \NullTick
If \MajorLabel
State = Tick*\Ticks/\MinorTicks+\Min
If (Tick*\MajorTicks)%\MinorTicks = 0
TextWidth = TextWidth(\MajorLabel(State))
TextHeight = TextHeight(\MajorLabel(State))
TextX = -TextWidth*(1-Cos(Angle))*0.5
TextY = -TextHeight*(1-Sin(Angle))*0.5
If TextX+TextWidth > MaxTextWidth
MaxTextWidth = TextX+TextWidth
EndIf
If TextX < MinTextWidth
MinTextWidth = TextX
EndIf
If TextY+TextHeight > MaxTextHeight
MaxTextHeight = TextY+TextHeight
EndIf
If TextY < MinTextHeight
MinTextHeight = TextY
EndIf
EndIf
EndIf
RadiusX = Cos(Angle)
RadiusY = Sin(Angle)
If RadiusX > MaxRadiusX
MaxRadiusX = RadiusX
EndIf
If RadiusX < MinRadiusX
MinRadiusX = RadiusX
EndIf
If RadiusY > MaxRadiusY
MaxRadiusY = RadiusY
EndIf
If RadiusY < MinRadiusY
MinRadiusY = RadiusY
EndIf
Next
RadiusX = (OutputWidth()-8+MinTextWidth-MaxTextWidth)/(MaxRadiusX-MinRadiusX)
RadiusY = (OutputHeight()-8+MinTextHeight-MaxTextHeight)/(MaxRadiusY-MinRadiusY)
If RadiusX < RadiusY : Radius = RadiusX : Else : Radius = RadiusY : EndIf
\Radius = Radius
\CenterX = (OutputWidth()-MinTextWidth-MaxTextWidth-(MinRadiusX+MaxRadiusX)*Radius)/2
\CenterY = (OutputHeight()-MinTextHeight-MaxTextHeight-(MinRadiusY+MaxRadiusY)*Radius)/2
Box(0, 0, OutputWidth(), OutputHeight(), AngleGadgetConstant\FaceColor)
For Tick = 0 To MinorTicks
Angle = Tick / \MinorTicks * \Range + \NullTick
State = Tick*\Ticks/\MinorTicks+\Min
If (Tick*\MajorTicks)%\MinorTicks = 0
AngleGadget_LineRA(\CenterX, \CenterY, Radius-2, Angle, Radius-11, Angle, AngleGadgetConstant\TextColor)
If \MajorLabel
Text = \MajorLabel(State)
X = \CenterX + Cos(Angle)*Radius - TextWidth(Text)*(1-Cos(Angle))*0.5
Y = \CenterY + Sin(Angle)*Radius - TextHeight(Text)*(1-Sin(Angle))*0.5
DrawText(X, Y, Text, AngleGadgetConstant\TextColor)
EndIf
Else
AngleGadget_LineRA(\CenterX, \CenterY, Radius-2, Angle, Radius-5, Angle, AngleGadgetConstant\TextColor)
EndIf
Next
Angle = (\State-\Min) / \Ticks * \Range + \NullTick
If Angle < 0 Or Angle => #PI
Color = AngleGadgetConstant\ShadowColor
Else
Color = AngleGadgetConstant\HighlightColor
EndIf
AngleGadget_LineRA(\CenterX, \CenterY, Radius*0.3, Angle-DeltaAngle, Radius*0.3, Angle+DeltaAngle, Color)
If Angle < #HalfPI Or Angle => #PI+#HalfPI
Color = AngleGadgetConstant\ShadowColor
Else
Color = AngleGadgetConstant\HighlightColor
EndIf
AngleGadget_LineRA(\CenterX, \CenterY, Radius-14, Angle, Radius*0.3, Angle+DeltaAngle, Color)
If Angle < #TwoPI-#HalfPI And Angle => #PI-#HalfPI
Color = AngleGadgetConstant\ShadowColor
Else
Color = AngleGadgetConstant\HighlightColor
EndIf
AngleGadget_LineRA(\CenterX, \CenterY, Radius-14, Angle, Radius*0.3, Angle-DeltaAngle, Color)
StopDrawing()
EndIf
EndWith
EndProcedure
Procedure AngleGadget(Gadget.i, X.i, Y.i, Width.i, Height.i, Min.i, Max.i)
Protected GadgetFlags = #PB_Canvas_GrabMouse|#PB_Canvas_Keyboard|#PB_Canvas_DrawFocus
Protected *AngleGadgetData.AngleGadgetData = AllocateMemory(SizeOf(AngleGadgetData))
If IsGadget(Gadget) And GetGadgetData(Gadget)
FreeMemory(GetGadgetData(Gadget))
EndIf
If Gadget = #PB_Any
Gadget = CanvasGadget(Gadget, X, Y, Width, Height, GadgetFlags)
Else
CanvasGadget(Gadget, X, Y, Width, Height, GadgetFlags)
EndIf
SetGadgetData(Gadget, *AngleGadgetData)
With *AngleGadgetData
\Min = Min
\Max = Max
\Ticks = \Max-\Min
\Range = #TwoPI
\NullTick = \Min/(\Max-\Min)*\Range
\MinorTicks = \Ticks
\MajorTicks = 1
EndWith
DrawAngleGadget(Gadget)
ProcedureReturn Gadget
EndProcedure
Procedure.i AngleGadgetEvent(Gadget.i)
Protected *AngleGadgetData.AngleGadgetData = GetGadgetData(Gadget)
Protected X.i, Y.i, Angle.f, State.i
Protected EventType = EventType()
With *AngleGadgetData
State = \State
Select EventType
Case #PB_EventType_MouseWheel
If \Range = #TwoPI
\State = AngleGadget_Normalize(\State+GetGadgetAttribute(Gadget, #PB_Canvas_WheelDelta), \Min, \Max)
Else
\State + GetGadgetAttribute(Gadget, #PB_Canvas_WheelDelta)
If \State < \Min : \State = \Min : ElseIf \State > \Max : \State = \Max : EndIf
EndIf
DrawAngleGadget(Gadget)
Case #PB_EventType_KeyDown
Select GetGadgetAttribute(Gadget, #PB_Canvas_Key)
Case #PB_Shortcut_Up, #PB_Shortcut_Right
If \Range = #TwoPI
\State = AngleGadget_Normalize(\State+1, \Min, \Max)
Else
\State + 1
If \State > \Max : \State = \Max : EndIf
EndIf
DrawAngleGadget(Gadget)
Case #PB_Shortcut_Down, #PB_Shortcut_Left
If \Range = #TwoPI
\State = AngleGadget_Normalize(\State-1, \Min, \Max)
Else
\State - 1
If \State < \Min : \State = \Min : EndIf
EndIf
DrawAngleGadget(Gadget)
EndSelect
Case #PB_EventType_LeftButtonDown
X = GetGadgetAttribute(Gadget, #PB_Canvas_MouseX) - \CenterX
Y = GetGadgetAttribute(Gadget, #PB_Canvas_MouseY) - \CenterY
If Sqr(X*X+Y*Y) < \Radius
\Change = #True
EndIf
Case #PB_EventType_LeftButtonUp
\Change = #False
EndSelect
If \Change
Select EventType
Case #PB_EventType_LeftButtonDown, #PB_EventType_MouseMove
X = GetGadgetAttribute(Gadget, #PB_Canvas_MouseX) - \CenterX
Y = GetGadgetAttribute(Gadget, #PB_Canvas_MouseY) - \CenterY
Angle = ATan2(X, Y)
If \Range = #TwoPI
\State = AngleGadget_Normalize((Angle-\NullTick)*\Ticks/\Range+\Min, \Min, \Max)
Else
Angle = AngleGadget_NormalizeF(Angle-\NullTick+(#TwoPI-\Range)*0.5, #TwoPI)-(#TwoPI-\Range)*0.5
\State = Angle*\Ticks/\Range + \Min
If \State < \Min : \State = \Min : ElseIf \State > \Max : \State = \Max : EndIf
EndIf
EndSelect
EndIf
If State <> \State
DrawAngleGadget(Gadget)
ProcedureReturn #True
EndIf
EndWith
EndProcedure
Procedure SetAngleGadgetAttribute(Gadget.i, Attribute.i, Value.i)
Protected *AngleGadgetData.AngleGadgetData = GetGadgetData(Gadget)
Protected OldNullTick.f
With *AngleGadgetData
Select Attribute
Case #AngleGadget_MinorTicks
\MinorTicks = Value
Case #AngleGadget_MajorTicks
\MajorTicks = Value
Case #AngleGadget_Orientation
\NullTick = Radian(Value)
Case #AngleGadget_Length
\Range = Radian(Value)
Case #AngleGadget_MajorLabel
\MajorLabel = Value
EndSelect
EndWith
DrawAngleGadget(Gadget)
EndProcedure
Procedure SetAngleGadgetState(Gadget.i, State.i)
Protected *AngleGadgetData.AngleGadgetData = GetGadgetData(Gadget)
With *AngleGadgetData
\State = AngleGadget_Normalize(State, \Min, \Max)
EndWith
DrawAngleGadget(Gadget)
EndProcedure
Procedure GetAngleGadgetState(Gadget.i)
Protected *AngleGadgetData.AngleGadgetData = GetGadgetData(Gadget)
With *AngleGadgetData
ProcedureReturn \State
EndWith
EndProcedure
Procedure FreeAngleGadget(Gadget.i)
Protected *AngleGadgetData.AngleGadgetData = GetGadgetData(Gadget)
FreeMemory(*AngleGadgetData)
FreeGadget(Gadget)
EndProcedure
;- Example / Beispiel
Enumeration
#Window
#AngleGadget0
#AngleGadget1
#AngleGadget2
#AngleGadget3
#AngleGadget4
#AngleGadget5
EndEnumeration
Procedure.s MyLabel2(State.i)
ProcedureReturn " "+Str(State*5)+"°"
EndProcedure
Procedure.s MyLabel3(State.i)
ProcedureReturn Str(State*5)+"%"
EndProcedure
Procedure.s MyLabel4(State.i)
ProcedureReturn Str(State*5)
EndProcedure
Procedure.s MyLabel5(State.i)
Select State
Case -100 : ProcedureReturn "kalt"
Case 100 : ProcedureReturn "warm"
EndSelect
EndProcedure
OpenWindow(#Window, 0, 0, 640, 480, "ColorGadget", #PB_Window_MinimizeGadget|#PB_Window_ScreenCentered)
Frame3DGadget(#PB_Any, 5, 5, 140, 150, "default (closed)")
AngleGadget(#AngleGadget1, 10, 20, 130, 130, 0, 24)
Frame3DGadget(#PB_Any, 155, 5, 210, 220, "closed + label + rotation")
AngleGadget(#AngleGadget2, 160, 20, 200, 200, 0, 72)
SetAngleGadgetAttribute(#AngleGadget2, #AngleGadget_Orientation, -90)
SetAngleGadgetAttribute(#AngleGadget2, #AngleGadget_MajorTicks, 12)
SetAngleGadgetAttribute(#AngleGadget2, #AngleGadget_MajorLabel, @MyLabel2())
Frame3DGadget(#PB_Any, 375, 5, 160, 170, "opened + label + rotation")
AngleGadget(#AngleGadget3, 380, 20, 150, 150, 0, 20)
SetAngleGadgetAttribute(#AngleGadget3, #AngleGadget_Orientation, 120)
SetAngleGadgetAttribute(#AngleGadget3, #AngleGadget_Length, 300)
SetAngleGadgetAttribute(#AngleGadget3, #AngleGadget_MajorTicks, 5)
SetAngleGadgetAttribute(#AngleGadget3, #AngleGadget_MajorLabel, @MyLabel3())
Frame3DGadget(#PB_Any, 155, 235, 410, 180, "the space is always optimally exploits")
AngleGadget(#AngleGadget4, 160, 250, 400, 160, -20, 20)
SetAngleGadgetAttribute(#AngleGadget4, #AngleGadget_Length, 90)
SetAngleGadgetAttribute(#AngleGadget4, #AngleGadget_Orientation, 225)
SetAngleGadgetAttribute(#AngleGadget4, #AngleGadget_MajorTicks, 8)
SetAngleGadgetAttribute(#AngleGadget4, #AngleGadget_MajorLabel, @MyLabel4())
Frame3DGadget(#PB_Any, 5, 165, 140, 250, "another example")
AngleGadget(#AngleGadget5, 10, 180, 130, 230, -100, 100)
SetAngleGadgetAttribute(#AngleGadget5, #AngleGadget_Orientation, 60)
SetAngleGadgetAttribute(#AngleGadget5, #AngleGadget_Length, -120)
SetAngleGadgetAttribute(#AngleGadget5, #AngleGadget_MinorTicks, 20)
SetAngleGadgetAttribute(#AngleGadget5, #AngleGadget_MajorTicks, 2)
SetAngleGadgetAttribute(#AngleGadget5, #AngleGadget_MajorLabel, @MyLabel5())
Repeat
Select WaitWindowEvent()
Case #PB_Event_CloseWindow
End
Case #PB_Event_Gadget
Select EventGadget()
Case #AngleGadget0, #AngleGadget1,#AngleGadget2,#AngleGadget3,#AngleGadget4,#AngleGadget5
If AngleGadgetEvent(EventGadget())
Debug GetAngleGadgetState(EventGadget())
EndIf
EndSelect
EndSelect
ForEver