The Artima Developer Community
Sponsored Link

Agile Buzz Forum
How To Create A Custom Widget - Interior Decoration

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
How To Create A Custom Widget - Interior Decoration Posted: Sep 10, 2004 1:39 AM
Reply to this message Reply

This post originated from an RSS feed registered with Agile Buzz by James Robertson.
Original Post: How To Create A Custom Widget - Interior Decoration
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 create the interior decoration for our Calendar, and display it.


InteriorDecoration

As always, we start off with a test:

	CalendarTest>>testRawInteriorDecoration
		self openWindowWithCalendar.
		self shouldnt: [calendar interiorDecoration] raise: Error

If we run this, it will fail because there is no #interiorDecoration method. Before we go ahead and create this method, we need to discuss where things go and why. In the case where we are creating or placing an object in the world that is basically a display thing, we give it to the artist. That is where we'll create our instance variable for this internal decoration. But we need to go further. A Pane is always the public interface to all that is going on in a widget. Interior decorations, like most things in Pollock, can be changed at runtime. That means we need to give the Pane, our Calendar, an interface to the interior decoration. So, we'll add setters and getters to the Calendar also:

	Smalltalk.Pollock defineClass: #CalendarArtist
		superclass: #{Pollock.ActionDisplayArtist}
		indexedType: #none
		private: false
		instanceVariableNames: 'interiorDecoration '
		classInstanceVariableNames: ''
		imports: ''
		category: 'Pollock-Calendar'
-+-+-

	CalendarArtist>>interiorDecoration
		^interiorDecoration

-+-+-

	CalendarArtist>>interiorDecoration: anInteriorDecorationBorder
		interiorDecoration := anInteriorDecorationBorder

-+-+-

	Calendar>>interiorDecoration
		^artist interiorDecoration

-+-+-

	Calendar>>interiorDecoration: anInteriorDecorationBorder 
		self artist interiorDecoration: anInteriorDecorationBorder.

Now if we run our test, we'll get a green bar.

In general, a drop down widget has an interior decoration by default. We can write a testing method on Calendar to tell us if there is one. First the test case method:

	CalendarTest>>testHasInteriorDecoration
		self openWindowWithCalendar.
		self should: [calendar hasInteriorDecoration]

This will blow up with an error, because there is no #hasInteriorDeocration method, much less one that answers true or false. So, let's fix that... we'll write the #hasInteriorDeocration method:

	Calendar>>hasInteriorDecoration
		^self interiorDecoration notNil

Now if we run our test, we still get a red bar, but it now says it's a failure. We need to have our Calendar create the interior decoration for us. The easiest way, and it turns out the Pollock way, is to just add an initialize method. We don't have to play with special class side overrides of new, all base GUI classes in Pollock already do that, and call initialize.


About Borders & Decorations

The question now is, what do we use for an interior decoration? Pollock provides a whole host of ready to use BorderDecoration classes, and BorderDecorationArtists to go with them, which take care of almost any kind of border like thing you can imagine. Looking in the "Pollock-Pane-Decoration" package, you'll see our vast selection. Most of these border decoration objects subclass from BorderDecoration. Others, such as ScrollbarDecoration and TabControlDecoration are more complex, and have both border decorations in them, as well as other things, such as, well, scrollbars, tabs and or grid headers.

Border decorations are in a way second class Panes. They have artists, which do the drawing for them, but don't have their own frames or agents. Being able to have a distinct artist, as well as the general capability for changing things at runtime, allows us to build many different kind of borders by combining a basic border with a border artist. In a Windows9x/2k look, the interior decoration of a drop down widget is two pixels wide. Each side of the border has two colors, one for the inside line, one for the outside line. Fortunately, Pollock has run into this before, and has a BorderDecoration class and artist all ready to go. The class is TwoToneRaisedDecoration. I admit freely that the name is wrong, it shouldn't have the word "Raised" in it, and we'll fix that in 2005.

Basically, this is a border, of any width for each side, which can have two colors one each for the inner and outer lines. The default line width for each side is 2 pixels. Creating a border is a one step, two part thing. We send #on: to the border class, and send in an instantiated border artist for it to use. The border artist we'll use is TwoTonedRaisedDecorationArtist. The artists job in a border / border artist pair, is to supply the colors for line drawing, and to call back (a bit of double dispatch) to the decoration to do the individual drawing of lines by whatever means it decides is right. In turn, the border artist may (and in our case does) call back into the pane's artist that is hosting the decoration to get colors.

Before we go ahead and create our initialize method we should comment on one Pollockism. We could just write our initialize method with a direct setting using the classes hard coded in the initialize method. That is pretty much a no-no in Pollock. So, we'll create a private #getInteriorDecoration method, which will return our interior decoration object, all created and ready to go, and then assign it:

	Calendar>>getInteriorDecoration
		^TwoToneRaisedDecoration on: TwoTonedRaisedDecorationArtist new
		
-+-+-

	Calendar>>initialize
		super initialize.
		self interiorDecoration: self getInteriorDecoration

Now if we run our test, everything is fine, and we get a green bar. But if we open our window with the Calendar, we still see nothing:

	tester := CalendarTest new openWindowWithCalendar

Our next step is to display the interior decoration. This is pretty simple. In our #displayOn: method in the artist, we tell the interior decoration (if there is one) to display itself. Decorations take a graphics context and a rectangle as their parameters. In our case, the rectangle we want to display it within is the visible rectangle of the pane:

	CalendarArtist>>displayOn: aGraphicsContext
		| oldClippingRectangle |
		self isVisible ifFalse: [^self].
		oldClippingRectangle := aGraphicsContext clippingBounds.
		(oldClippingRectangle intersects: self frameVisibleRectangle) ifFalse: [^self].
		aGraphicsContext clippingRectangle: (self frameVisibleRectangle intersect: oldClippingRectangle).
		self hasInteriorDecoration ifTrue: [self interiorDecoration displayOn: aGraphicsContext in: self frameVisibleRectangle].
		(oldClippingRectangle intersects: self frameDisplayableRectangle) ifTrue:
			[aGraphicsContext clippingRectangle: (self frameDisplayableRectangle intersect: oldClippingRectangle)].
		aGraphicsContext clippingRectangle: oldClippingRectangle.

Now we run our test... and all is well.. but... if we retry to open our window, nothing shows! Well, #hasInteriorDecoraton in AbstractArtist answers false by default. So we need to override it in our artist:

	CalendarArtist>>hasInteriorDecoration
		^interiorDecoration notNil

Now, we run our test again, and everything is ok, and when we open our example, it shows a nice little lowered border, just like Poppa Gates bakes in his billion dollar oven. We'll finish this round with an "Ounce Of Prevention" tests for the class of the decoration and the artist... this way we can see if we accidentally screw something up in our later work... Note, the four tests we created today cover the behavior of the initialize method, the getInteriorDecoration method, the setters and getters in the Calendar and CalendarArtist as well as if we got the correct decoration and decoration artist:

	CalendarTest>>testInteriorDecorationClass
		self openWindowWithCalendar.
		self should: [calendar interiorDecoration class == TwoToneRaisedDecoration]

-+-+-

	CalendarTest>>testInteriorDecorationArtistClass
		self openWindowWithCalendar.
		self should: [calendar interiorDecoration artist class == TwoTonedRaisedDecorationArtist]

Now if we run all of our CalendarTest cases, we get a full green bar, and 16 tests.

The above is published as version 1.5 in the Package named "Pollock-Calendar" on the Cincom public repository.

Next time, we'll create the "action part", which is the button which will eventually show the calendar navigation and selection pane.


And So It Goes
Sames

Read: How To Create A Custom Widget - Interior Decoration

Topic: Really eXtreme Feedback for Software Development Previous Topic   Next Topic Topic: How To Create A Custom Widget - Get Ready To Display

Sponsored Links



Google
  Web Artima.com   

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