The Artima Developer Community
Sponsored Link

Agile Buzz Forum
Fun With SpinButtons

0 replies on 1 page.

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 0 replies on 1 page
James Robertson

Posts: 29924
Nickname: jarober61
Registered: Jun, 2003

David Buck, Smalltalker at large
Fun With SpinButtons Posted: May 4, 2007 3:32 PM
Reply to this message Reply

This post originated from an RSS feed registered with Agile Buzz by James Robertson.
Original Post: Fun With SpinButtons
Feed Title: Pollock
Feed URL: http://www.cincomsmalltalk.com/rssBlog/pollock-rss.xml
Feed Description: Pollock - the next VW GUI
Latest Agile Buzz Posts
Latest Agile Buzz Posts by James Robertson
Latest Posts From Pollock

Advertisement

Today we show how the SpinButton works and some of its more interesting features.

Design

We'll start off with a SpinButton of course, and add some radio buttons, one each for having no top or bottom limit on the value, one each for bouncing at a top or bottom limit, and finally a single check box to tell the spin button to wrap around, or not.

As always, we start off with our UserInterface subclass:

	Smalltalk defineClass: #SpinButtonWork
		superclass: #{Widgetry.UserInterface}
		indexedType: #none
		private: false
		instanceVariableNames: 'spinButton noTopLimit noBottomLimit bounceTop bounceBottom wrapTop wrapBottom '
		classInstanceVariableNames: ''
		imports: 'private Widgetry.*'
		category: '(none)'

Now here is our #createInterface will all the widgets laid out:

	createInterface

		spinButton := SpinButton new.
		spinButton frame: (FractionalFrame
			fractionLeft: 0.05
			right: 0.95
			top: 0
			bottom: 0).
		spinButton frame 
			topOffset: 10;
			bottomOffset: 40.
		self addComponent: spinButton.
		noTopLimit := RadioButton withLabelString: 'No Top Limit'.
		(noTopLimit frame)
			inset: 10 @ 80;
			extent: 75 @ 30.
		self addComponent: noTopLimit.
		noBottomLimit := RadioButton withLabelString: 'No Bottom Limit'.
		(noBottomLimit frame)
			inset: 100 @ 80;
			extent: 75 @ 30.
		self addComponent: noBottomLimit.
		bounceTop := RadioButton withLabelString: 'Bounce Top'.
		(bounceTop frame)
			inset: 10 @ 110;
			extent: 75 @ 30.
		self addComponent: bounceTop.
		bounceBottom := RadioButton withLabelString: 'Bounce Bottom'.
		(bounceBottom frame)
			inset: 100 @ 110;
			extent: 75 @ 30.
		self addComponent: bounceBottom.
		wrapAround := CheckBox withLabelString: 'Wrap Around'.
		(wrapAround frame)
			inset: 50 @ 140;
			extent: 75 @ 30.
		self addComponent: wrapAround.

Nothing very special there... And if we execute SpinButtonWork open, we see this:

Lance Burton

There is some basic behavior that automatically comes with a SpinButton. If you press the up or down buttons it automatically does number conversions, and adds or subtracts 1 from the current value. Holding down a button makes it repeat of course. Finally, if you enter some non numeric text and you then hit the up or down button, it takes that and quietly ignores it and starts as if your text were '0'.

Under the hood, there is a TextConverter in the model object of the SpinButton. All TextConverters have default #increment and #decrement methods. As an example, #increment in TextStringConverter calls #incrementBy: with the value of 1 as the argument, and that is written as:

	incrementBy: anInteger
		^value ifNotNil: [value := (value asNumber + anInteger) printString].

And this is why it takes non digit text and turns it into a '0'. If we entered something like 54.9454, and then hit the up button, this is what we'd see:

Match Game

We want to use numbers, not textified numbers, so first we'll start with changing the model our SpinButton uses:

	hookupInterface

		spinButton model: 0 asTextConverter.

Now let's hookup our RadioButtons with fancy all at once coolness, as well as our check box and its update method:

	hookupInterface

		| topModel bottomModel |
		spinButton model: 0 asTextConverter.
		topModel := [spinButton maximumValue: nil] asObservedValue.
		noTopLimit model: topModel.
		bounceTop model: topModel.
		bounceTop selectValue: [spinButton maximumValue: 4].
		noTopLimit selectValue: topModel value.
		topModel when: ValueChanged do: [topModel value value].
		bottomModel := [spinButton minimumValue: nil] asObservedValue.
		noBottomLimit model: bottomModel.
		bounceBottom model: bottomModel.
		bounceBottom selectValue: 
			[spinButton minimumValue: -4].
		noBottomLimit selectValue: bottomModel value.
		bottomModel when: ValueChanged do: [bottomModel value value].
		wrapAround when: ValueChanged send: #updateWrapAround to: self

	updateWrapAround
		spinButton wrapAround: wrapAround model value.
		wrapAround model value ifTrue:
			[spinButton minimumValue: -4.
			spinButton maximumValue: 4]

As we stated in our Fun With RadioButtons post, we can do really cool things with RadioButtons besides just telling it that the #selectValue: is a symbol. It can be anything that the system can match with #='. Here, we use blocks which happily use identity when executing #='. Our trick is to then, when the change happens is to send #value to the value in the ObservedValue.

This sometimes takes a minute to get your mind around. Basically, we have an ObservedValue. To get at the underlying object that is being observed, we send #value to that. Since the underlying object is a block, when we send #value to a block, it executes itself. So, value value means: Get The Value And Then Execute The Block (which is the object we got from the first #value).

Not MOMA

Basically, #wrapAround: takes a Boolean and tells the SpinButton that if there is a maximum value for the top then go to the minimum value when you reach the maximum value when asked to increment. Similarly, if there is a minimum value for the bottom, then go to the maximum value when you reach the minimum value when asked to decrement.

By setting a minimum or maximum value, without setting wrapAround to true, you are in bounce mode. Simply stated, when you reach the maximum and do an increment, it doesn't. Similarly, when you reach the minimum and do a decrement, it doesn't.

You can play with the RadioButtons and the CheckBox and see what you get.

Extra Credit

You can use the SpinButton with Time objects, Date objects, Timestamp objects and even true and false.

Instead of:

	0 asTextConverter

Try one of the following:

	Time now asTextConverter
	
	Date today asTextConverter
	
	Timestamp now asTextConverter
	
	true asTextConverter

Isn't that pretty?

And So It Goes
Sames

Read: Fun With SpinButtons

Topic: Extreme UI Testing Previous Topic   Next Topic Topic: GLASS: Gemstone, Linux, Apache, Seaside and Smalltalk

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use