Another spinner gadget

BlitzMax Forums/MaxGUI Module/Another spinner gadget

JoshK(Posted 2008) [#1]
To avoid floating-point errors this always uses integers. The SPINNER_DECIMAL style will use increments of 0.1. However, spinner values will still be stored as integers. The SPINNER_WRAP flag does not currently work.

The float handling works perfectly in this, unlike some other implementations I have seen.

Function CreateSpinner:TSpinner( x:Int, y:Int, w:Int, h:Int, group:TGadget, style:Int=0 )
	Return TSpinner.Create(x,y,w,h,group,style)
EndFunction

Function SetSpinnerRange( spinner:TSpinner, low:Float, high:Float )
	spinner.SetRange low,high
End Function

Function SetSpinnerValue( spinner:TSpinner, value:Int )
	spinner.SetValue_ value
EndFunction

Function SpinnerValue:Int( spinner:TSpinner)
	Return spinner.getvalue()
EndFunction

Const SPINNER_DECIMAL=1
Const SPINNER_WRAP=2

Type TSpinner Extends TProxyGadget
	Field panel	:TGadget
	Field textField:TGadget
	Field stepper:TGadget
	Field trackbar:TGadget
	Field range	=10
	Field precision#=1.0
	Field value
	Field low=0,high=9
	
	Function Create:TSpinner( x:Int, y:Int, w:Int, h:Int, group:TGadget, style:Int=0 )
		Local spinner:TSpinner=New TSpinner
		spinner.panel = CreatePanel(x,y,w,h,group,PANEL_BORDER)
		spinner.stepper = CreateSlider(w-h+4,0,h-8,h-4,spinner.panel,SLIDER_STEPPER)
		spinner.textField = CreateTextField(-2,-2,w-h+8,h,spinner.panel)
		spinner.setrange spinner.low,spinner.high
		SetGadgetFilter spinner.textField,NumFilter
		spinner.SetProxy spinner.textField
		spinner.style=style
		AddHook EmitEventHook,SpinnerHook,spinner
		Return spinner
	EndFunction
	
	Method Free()
		RemoveHook EmitEventHook,SpinnerHook,Self
		'textField is freed in super
		If stepper FreeGadget stepper
		If panel FreeGadget panel
		stepper=Null
		panel=Null
		Super.Free
	End Method

	Method SetRange( lo, hi)
		low=lo
		high=hi
		range=high-low+1
		SetSliderRange stepper,0,range-1
		SetValue low
	EndMethod
	
	Method FixValue:Int(i)
		'If (SPINNER_WRAP & style)
		'If i<low Notify i+", "+(( (i-low) Mod range )+low)
		'	i=( (i-low) Mod range )+low
		'Else
			i=Max(i,low)
			i=Min(i,high)
		'EndIf
		Return i
	EndMethod
	
	Method SetValue_( i,updatetext=1)
		value=FixValue(i)
		If SliderValue(stepper)<>value-low SetSliderValue stepper,value-low
		If updatetext
			If SPINNER_DECIMAL & style
				SetGadgetText textfield,String(PrettyFloat(value/10.0))
			Else
				SetGadgetText textfield,String(value)
			EndIf
		EndIf
	EndMethod

	Method GetValue:Int()
		Return value
	EndMethod
	
	Function SpinnerHook:Object( id:Int, data:Object, context:Object )
		Local spinner:TSpinner
		Local event:TEvent
		event=TEvent(data)
		If event
			spinner=TSpinner(context)	
			If spinner Return spinner.HandleEvents(event)
		EndIf
		Return data
	EndFunction
	
	Method HandleEvents:TEvent( event:TEvent )
		Select event.id
			Case EVENT_GADGETACTION
				If event.source = stepper
					If FixValue(event.data+low)<>value
						SetValue_ event.data+low,1
						EmitEvent CreateEvent(EVENT_GADGETACTION,Self,event.data)
						SendMessageA QueryGadget(textfield,QUERY_HWND),177,0,-1
						ActivateGadget textfield
						Return
					EndIf
				EndIf
				
				If event.source = Self
					Local i
					If SPINNER_DECIMAL & style
						i=Round(Float(TextFieldText(textfield))*10.0)
					Else
						i=Int(TextFieldText(textfield))
					EndIf
					Local newvalue=FixValue(i)
					If newvalue<>value
						SetValue_ newvalue,0
						EmitEvent CreateEvent(EVENT_GADGETACTION,Self)
						Return
					EndIf
				EndIf
				
		EndSelect
		Return event
	EndMethod

	Function NumFilter:Int(event:TEvent, context:Object)
		If event.ID = EVENT_KEYCHAR
			If event.data = KEY_BACKSPACE Return 1
			If event.data = 47 Return
			If event.data < 45 Or event.data > 57 Return
		EndIf
		Return 1
	End Function

	Function PrettyFloat:String(value:Float,places=1)
		Local s$
		value=value*(10.0^places)
		s=Round(value)
		Local i$=Int(Left(s,s.length-1))
		Return i+"."+Right(s,1)
	EndFunction
	
EndType



dmaz(Posted 2008) [#2]
nevermind.


degac(Posted 2008) [#3]
Add this

	Method SetEnabled(_state:Int=True)
		If _state=False
			DisableGadget stepper
			DisableGadget panel
		Else
			EnableGadget stepper
			EnableGadget panel
		End If
		Super.setenabled(_state)
	End Method


to make the Spinner gadget acts like standard MaxGUI gadgets. This means you can use
DisableGadget myspinner:tspinner
EnableGadget myspinner:tspinner

to disable properly the gadget (spinner+textfied).

And replace the method SetValue_ with this one
Method SetValue_( i,updatetext=1)
value=FixValue(i)
If SliderValue(stepper)<>value-low SetSliderValue stepper,value-low
If updatetext
If SPINNER_DECIMAL & style
	SetGadgetText textfield,String(PrettyFloat(value/10.0))
Else
	SetGadgetText textfield,String(value)
EndIf
			
If value<0 SetGadgetColor textfield,220,20,20 Else SetGadgetColor textfield,255,255,255
			
					
	Local temp$="                                      "+TextFieldText(textfield)
	temp=Right(temp,16+(Len(temp)>3))
	SetGadgetText textfield,temp
EndIf
endMethod


to have a 'visual' response on negative value: this could be useful.
And I added an 'hack' for the right alignment, but I cant' guarantee if it will work well.