The Artima Developer Community
Sponsored Link

Agile Buzz Forum
How To Create A Custom Widget - Display Part Input Field

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 - Display Part Input Field Posted: Sep 29, 2004 2:53 PM
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 - Display Part Input Field
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 add the InputField that is the "display part" of our Calendar.


Create and Display the Display Part

We simply want an input field as our display part (the left part of our Calendar). The superclass, ActionDisplay, says that it owns the displayPart instance variable. (We will be moving that to the artist where it really belongs, early next year) So, we'll add a new method, to our Calendar pane to create this part. The common name used in other ActionDisplay panes for this is #createDisplayPart... we'll use that name here:

	Calendar>>createDisplayPart
		displayPart := InputField new.
		displayPart frame: (FractionalFrame 
			fractionLeft: 0
			top: 0
			right: 1
			bottom: 1).
		displayPart frame rightOffset: [self artist buttonWidth negated].
		displayPart setEnclosingPane: self.

You see here we do very little to make the part. Like our action part, the button, we use a fractional frame. We use the buttonWidth we created in the artist last time, and set that, negated, as the right offset. Finally, like the button, we tell the input field that it's enclosing pane is self, the Calendar.

Now all we have to do is create it. We'll just call it from our #initialize method:

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

Now that it's created, we need to display it:

	CalendarArtist>>displayOn: aGraphicsContext
		...
		(oldClippingRectangle intersects: self frameDisplayableRectangle) ifTrue:
			[aGraphicsContext clippingRectangle: (self frameDisplayableRectangle intersect: oldClippingRectangle).
			actionPart displayOn: aGraphicsContext.
			self displayPart displayOn: aGraphicsContext].
		aGraphicsContext clippingRectangle: oldClippingRectangle.

That's all there is to it. We can see the results if we run our example:

	CalendarTest new openWindowWithCalendar

Details

If you have good eyes, you'll see that the InputField portion of our Calendar has an extra border around it. That's the interior decoration of the input field. By default, an input field has an interior decoration. In a Windows look though, the input part of a drop down has no interior decoration. Instead, the drop down, in Windows, has an interior decoration around the whole pane. We added that several steps ago. So, we need to turn off the interior decoration of the input field. Fortunately, that's simple. We just give it nil as its interior decoration:

	Calendar>>createDisplayPart
		displayPart := InputField new.
		displayPart frame: (FractionalFrame 
			fractionLeft: 0
			top: 0
			right: 1
			bottom: 1).
		displayPart frame rightOffset: [self artist buttonWidth negated].
		displayPart interiorDecoration: nil.
		displayPart setEnclosingPane: self.

If we run our example now, we see it looks much better. But, if we click on the input field part, it does nothing... just like the action part, we need the CalendarAgent to deal with that in it's handlerForMouseEvent: method:

	CalendarAgent>>handlerForMouseEvent: anEvent
		pane actionPart ifNotNil:
			[:button | 
			(button handlerForMouseEvent: anEvent) ifNotNil: [:controller | ^controller]].
		pane displayPart ifNotNil:
			[:inputField |
			(inputField handlerForMouseEvent: anEvent) ifNotNil: [:controller | ^controller]].
		^nil

Now if we open our example, it will toss a Does Not Understand on #quietlyRequestActivationFor:. We've seen this before. This means that someone (in this case our InputField) is expecting to have a keyboard processor, but doesn't. We can fix that pretty simply:

	Calendar>>createDisplayPart
		displayPart := InputField new.
		displayPart frame: (FractionalFrame 
			fractionLeft: 0
			top: 0
			right: 1
			bottom: 1).
		displayPart frame rightOffset: [self artist buttonWidth negated].
		displayPart interiorDecoration: nil.
		displayPart setEnclosingPane: self.
		self isOpen
			ifTrue: [displayPart setupKeyboardFor: self]
			ifFalse: [self configure: #setupKeyboardFor: for: displayPart argument: self].

If you're paying attention to the name #setupKeyboardFor: and saying to yourself "We want to set it up FOR the display part, not self, the Calendar", you're right. The problem is in the name of the method. We'll be renaming it to #setupKeyboardFrom: early next year.

Now if we open up our example, we can click in the InputField, enter text, and even the right click copy/cut/paste menu works. We're basically done!


Pollock Plug And Play

Before we finish this posting, let's look back at how we set the interior decoration of the InputField to nil. Pollock's plug and play nature says that you can send every message to a pane at any time, and change it dynamically. But, if we look back at the interiorDecoraton: method we created a few steps ago we will notice that we can't set our Calendar's interior decoration to nil without having that method blow up.... Let's look at what it currently looks like:

	CalendarArtist>>interiorDecoration: anInteriorDecorationBorder
		interiorDecoration := anInteriorDecorationBorder.
		interiorDecoration artist setPane: pane.
		self frame clipDisplayableBounds: (self edgeRectangleIncludingBorderEdge: anInteriorDecorationBorder)

As you see, if we tried to send in nil, that would die in a hurry. What we want to do is first, find out if the current interior decoration is nil, and if not, set that artists pane to nil. Then we want to test if the new decoration is nil or not, and if not, do what we did before, but if it is, we want to "unset" the displayable bounds. Finally, we want to have the pane invalidate, because, we might do that at runtime, and otherwise it won't show right away.

So, here's what our new method looks like:

	CalendarArtist>>interiorDecoration: anInteriorDecorationBorder
		interiorDecoration notNil ifTrue: [self interiorDecoration artist setPane: nil].
		interiorDecoration := anInteriorDecorationBorder.
		anInteriorDecorationBorder isNil
			ifFalse: 
				[self interiorDecoration artist setPane: pane.
				self frame clipDisplayableBounds: (self edgeRectangleIncludingBorderEdge: anInteriorDecorationBorder)]
			ifTrue: [self frame clipDisplayableBounds: self borderEdgeRectangle].
		pane invalidate

Here, we use the framework #borderEdgeRectangle which just answers the edge rectangle for our pane as a Zero rectangle if the pane has a border (ActionDisplay panes never do) or if it does, then that border's edgeRectangle.

Now that we've added it, let's have some fun! First, let's add an accessor to our CalendarTest for calendar:

	CalendarTest>>calendar
		^calendar

Open our tester example, but assign the opened object to a variable (this is automatic in VisualWorks workspaces... just do it):

	tester := CalendarTest new openWindowWithCalendar

Now, in the workspace, with the example open, execute the following:

	tester calendar interiorDecoration: nil

Everything still works, but the calendar no longer has the interior decoration around the combined input field an button. I leave it as an exercise for the reader to write workspace code to change it back.

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


Next time we'll start working on the calendar pane which opens when we press the button.

And So It Goes
Sames

Read: How To Create A Custom Widget - Display Part Input Field

Topic: How To Create A Custom Widget - Action Part Events Previous Topic   Next Topic Topic: Engineering Effective Teams

Sponsored Links



Google
  Web Artima.com   

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