The Artima Developer Community
Sponsored Link

Agile Buzz Forum
How To Create A Custom Widget - Agent, Controllers & Frames

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 - Agent, Controllers & Frames Posted: Aug 18, 2004 2:55 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 - Agent, Controllers & Frames
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

Because I'm away from my desk next week, I'm going to cover a couple of subjects this time, so this is going to be a bit long.

We left off talking about the need for us to have an Agent for our Calendar.

Choosing an Agent or Subclassing An Existing Agent

If you only need your pane to have enablement and visibility behavior, and doesn't need any mouse or keyboard behavior, you don't have to create any Agent subclass. Instead, you can just use the existing VisiblityAgent. The Divider, GroupBox and Region panes are examples that use this existing Agent.

In the current version of Pollock, most Agents subclass off of AbstractAgent. This is wrong. Because all panes, at the very least need enablement and visibility behavior, they all should subclass off of VisiblityAgent. Unfortunately, I realized this much later than I should have. But, all is not lost. There is a task on hand to change this and move all existing direct AbstractAgent subclasses to VisiblityAgent.

If you need behavior that is associated with an editing pane, you should subclass from EditingAgent. The EditingAgent has all the base functionality and API to delete and insert text. It also has behavior to support text selection with the mouse, keyboard and API, as well as standard copy/cut/paste. It also has the behavior to support unlimited undo/redo.

If you need behavior that is associated with a list like pane, you should subclass from EnumerationAgent. The EnumerationAgent has the base behavior for scrolling lists, both horizontally and vertically. It is also where the ability to change dynamically from single select to multi select lives. Of course, it is where the action takes place for selection by way of mouse, keyboard and API.

If you have an ActionDisplay widget, like we do, you should subclass from ActionDisplayAgent. This is what we will do.

Of course, if nothing of the above suites your needs, you can subclass directly from VisiblityAgent, and go it alone. The Slider and Progress bars are existing examples of this... and in the future all of the above, as well as TabControl and ResizingSplitter.

About Controllers

If you are an MVC maven, or Wrapper knowledgeable, you might be wondering... What is all this Agent stuff, isn't that what Controllers are for?

The answer of course is : Yes. But with a caveat: But only in terms of Theory.

In my investigations, not a single framework ever giving itself the name MVC, excepting the original Smalltalk-80 implementation, has Controllers that do what Controllers are supposed to do. Forward keyboard and mouse input to the View. Period. Every single supposed heir to the MVC legacy, including Wrapper, muddies their Controllers with View specific code, and worse. For instance, in Wrapper, some Controllers talk to the Model, others have ugly switch code, depending on the current Look.

A goal of Pollock is to get rid of Controllers completely. Right now, Pollock and Wrapper share a "middleware" event dispatching and widget focus system that is based on Controllers. A task for the near future is to give Pollock it's own middleware for these systems. This middleware will consist of a standard smart event forwarding directly to the Agents, based on an a known API. This API is already in the Pollock Agents.

In the mean time, Controllers continue to exist in Pollock. These controllers adapt the old Wrapper event API to the new Pollock Agent based API.

Unlike for Wrapper, you don't have to create your own Controller. Instead, all you have to do is assign which existing Pollock Controller you want to use for your pane.

In fact, there are really just three to choose from: TrackingController, ToggleController and ScrollButtonController

The first, TrackingController, is the standard controller, and does almost everything you could ever want. In fact, that's the one we'll use for our Calendar.

The ToggleController exists because of how the old Wrapper middleware works, and is used for Buttons. This is because buttons can be set to be "default" buttons on a form or dialog, and when you press Enter, you want that button to execute it's behavior. Also, you can have "cancel" buttons, which you want to execute when on a dialog when you press ESC. The old middleware had a direct call to a method called "simulateMousePress". The ToggleController has this extra behavior.

In fact, you can use a ToggleController anywhere you can use a TrackingController... but for a Button, which has can be a default or cancel button, has to use the ToggleController. Yes, I know, ToggleControler probably should be renamed. In time, it might.

Finally, there is the (also poorly named) ScrollButtonController. Basically, this is the controller you want to use when you want to get dinged. What does that mean? It is a behavior you see in a scroll bar's button. When you press it, it executes once, then, if you continue to press, it repeats the last action again, and continues to repeat until you release. This controller is used in the Pollock ScrollBar, as well as in the up and down arrow buttons of the Spin button.

Calendar Agent

As usual, we play anal XP and create our tests first:

	testCalendarAgentClassExistence
		self should: [#{Smalltalk.Pollock.CalendarAgent} ifDefinedDo: [:value | true] elseDo: [false]].

-+-+-

	testCalendarAgentClassHierarchy
		self should: 
			[#{Smalltalk.Pollock.CalendarAgent} 
				ifDefinedDo: [:value | value superclass = ActionDisplayAgent] 
				elseDo: [false]].

Then we create our CalendarAgent:

	Smalltalk.Pollock defineClass: #CalendarAgent
		superclass: #{Pollock.ActionDisplayAgent}
		indexedType: #none
		private: false
		instanceVariableNames: ''
		classInstanceVariableNames: ''
		imports: ''
		category: 'Pollock-Calendar'

Finally, we go to our Calendar, and add a method. We tell it which agent to use.

	agentClass
		^CalendarAgent

We can now write a test to see if sending new to our Calendar doesn't fail:

	testCalendarNew
		self shouldnt: [Calendar new] raise: Error

Why, you may ask, didn't I write this test immediately after (or before) creating the Calendar class?

It is probably a stupid answer, but one of the tenets of XP is that you don't release any code until all tests pass, and, well, I couldn't bring myself to write the test I knew would fail and publish it. So, there you go. In reality, one could and probably would in practice, do all the tests and work in one sitting, and then be able to publish the result with all tests working.

Final Details

You may notice I haven't stated which Pollock Controller I'm using. This is because the superclass of CalendarAgent, ActionDisplayAgent, already has a definition of #defaultControllerClass which uses ToggleController.

If you are subclassing from elsewhere in the hierarchy for your pane, you may have to stipulate your own choice of controller in a #defaultControllerClass method.

If you look at the protocol 'constants - classes' in AbstractPane, you'll see four methods defined there: #artistClass, #agentClass, #defaultControllerClass and #frameClass. Each of these is implemented as self subclassResponsibility.

We've already dealt with the first three, but not #frameClass. For Calendar, again the superclass ActionDisplay comes to our aid. It already defines that method as:

	frameClass
		^OriginExtentFrame

But, if you are subclassing from elsewhere, you may have to stipulate a Frame of your own choice to be the default frame used by your pane. In fact though, you probably can just always use OriginExtentFrame. It's good enough, and, since Frames are plugable and changeable at widget configuration time, as well as runtime, just which of the standard Frames you put in here (OriginExtentFrame, AlignmentFrame, FractionalFrame or RelationalFrame) really has little effect.

The #frameClass method is used mostly only internally for panes that need special framing mechanisms, like toolbar buttons and windows and dialogs.

Finishing Up

We'll end today with a handful of guarding tests. These tests are meant to make sure that what we said is what we get, and also protect us against our own future fiddling with the Calendar:

	testCalendarFrame
		calendar := Calendar new.
		self should: [calendar frame class == OriginExtentFrame]
		
-+-+-

	testCalendarController
		calendar := Calendar new.
		self should: [calendar controller class == ToggleController]
		
-+-+-

	testCalendarArtist
		calendar := Calendar new.
		self should: [calendar artist class == CalendarArtist]
		
-+-+-

	testCalendarAgent
		calendar := Calendar new.
		self should: [calendar agent class == CalendarAgent]

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

Next time, we'll try to actually display our new Calendar pane.


And So It Goes
Sames

Read: How To Create A Custom Widget - Agent, Controllers & Frames

Topic: Funny quiz Previous Topic   Next Topic Topic: Hacking a language

Sponsored Links



Google
  Web Artima.com   

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