The Artima Developer Community
Sponsored Link

Agile Buzz Forum
How To Build A GUI With Pollock - TabControl - Part 2

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 Build A GUI With Pollock - TabControl - Part 2 Posted: Jun 23, 2004 2:10 PM
Reply to this message Reply

This post originated from an RSS feed registered with Agile Buzz by James Robertson.
Original Post: How To Build A GUI With Pollock - TabControl - Part 2
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

We left off last week with little more than a pretty picture that does nothing. This time, we'll add all the functionality to make our basic ClassHierarchyBrowser work, and while doing so, we'll steal some of the code from Vassili's work to make ours that much better.

To start, do come refactoring. (Note, I actually used the RefactoringBrowser to do this work, but since I'm not in the business here of explaining how that works, I'll just explain how to do it by hand). We use the same symbols over and over, such as #MethodList and #ProtocolList, so let's instead put them into their own accessing methods:

	methodListID
		^#MethodList

-+-+-+-

	protocolListID
		^#ProtocolList

Now we'll go to #protocolAndMethodListForm, and replace them with the accessors... (Note: I put this stuff in the category "constants")... While we're at it, we'll make BOTH lists multi select, and tell the method list to use the display selector #selector:

		(pane frame)
			rightOffset: -1;
			bottomOffset: -1.
		pane id: self protocolListID.
		pane beMultiSelect.
		pane verticalScrollbar: true.
	...
		(pane frame)
			leftOffset: 1;
			bottomOffset: -1.
		pane id: self methodListID.
		pane displaySelector: #selector.
		pane beMultiSelect.
		pane verticalScrollbar: true.

This last bit about using the displaySelector: #selector is where Vassili's fine work comes in. By default, items in a list are displayed by sending the #displayString method to the item, and then showing the result. In this case, using Vassili's work, instead of filling our Method List with symbols representing the method selectors, we'll be filling our Method List with instances of MethodDefinition. This gives us more flexibility. MethodDefinitions answer the symbol representing the selector when sent the #selector method... so that's why we're setting the displaySelector to #selector.

Next, we'll add some accessing methods to access the various panes:

	classTree
		^self paneAt: #ClassTree

-+-+-+-

	tabControl
		^self paneAt: #Tab

-+-+-+-

	classMethodList
		^self methodListOnPage: self classPage

-+-+-+-

	classProtocolList
		^self proctocolListOnPage: self classPage

-+-+-+-

	instanceMethodList
		^self methodListOnPage: self instancePage

-+-+-+-

	instanceProtocolList
		^self proctocolListOnPage: self instancePage

These are of course not complete without the accompanying #methodListOnPage:, #protocolListOnPage:, #classPage and #instancePage methods:

	classPage
		^2

-+-+-+-

	instancePage
		^1

-+-+-+-

	methodListOnPage: anInteger 
		^self list: self methodListID onPage: anInteger

-+-+-+-

	proctocolListOnPage: anInteger 
		^self list: self protocolListID onPage: anInteger

Lastly, we need the #list:onPage: method:

	list: aSymbol onPage: anInteger 
		^(self tabControl pageAt: anInteger) paneAt: aSymbol

The code here evolved while I was writing it, via refactoring. In fact, it isn't very interesting in itself, but it does make the code cleaner.

Now we need to hook it all up! So, now is time to rewrite our #hookupInterface method that we commented out last time. We'll start with just hooking up the ClassTree to the protocol list(s):

	hookupInterface
		self classTree when: #selectionChanged send: #fillProtocolList to: self.

In turn, we have to modify the old #fillProtocolList method:

	fillProtocolList
		self classTree selection ifNotNil: 
			[:value | self instanceProtocolList list: value organization categories].
		self classTree selection ifNotNil: 
			[:value | self classProtocolList list: value class organization categories]

Note here that what we are filling both the Class protocol list and the Instance protocol list. One thing you should be aware of is that even when a TabControl page is not active, the widgets on that pane are. This allows us to fill both protocol lists even though only one is visible, and that is exactly what the above does.

Next we'll hook up the two protocol lists in our hookupInterface method:

		self classTree when: #selectionChanged send: #fillProtocolList to: self.
		self instanceProtocolList 
			when: #selectionChanged send: #fillInstanceMethodList to: self;
			when: #listChanged send: #fillInstanceMethodList to: self.
		self classProtocolList 
			when: #selectionChanged send: #fillClassMethodList to: self;
			when: #listChanged send: #fillClassMethodList to: self.

Here, we fill the appropriate method list (class or instance) when the list is changed (such as when a new class is chosen in the Class Tree) or when a new selection is made. So, we need to write a #fillInstanceMethodList and #fillClassMethodList method (and remove the old #fillMethodList that was hanging around):

	fillClassMethodList
		| methodDefinitions |
		methodDefinitions := SortedCollection new.
		self classTree selectionDo:
			[:class |
			self classProtocolList selectionDo:
				[:protocol | 
				(class class organization listAtCategoryNamed: protocol) do:
					[:selector |
					methodDefinitions add: (MethodDefinition class: class class selector: selector)]]].
		self classMethodList list: methodDefinitions

-+-+-+-

	fillInstanceMethodList
		| methodDefinitions |
		methodDefinitions := SortedCollection new.
		self classTree selectionDo:
			[:class |
			self instanceProtocolList selectionDo:
				[:protocol | 
				(class organization listAtCategoryNamed: protocol) do:
					[:selector |
					methodDefinitions add: (MethodDefinition class: class selector: selector)]]].
		self instanceMethodList list: methodDefinitions

You see here, we're filling the associated method list (again, class or instance) based on what selections are made in the associated protocol list. Here again we steal from Vassili, both using the #selectionsDo: method, that iterates over all selections, and filling the list with MethodDefinitions. Note also that one deals with the class, while the other deals with the class class. That's standard Smalltalk stuff, the Class holds instance definition values, while the Class's class owns the class definition values.

Next, we want to hook up the Text pane in our hookupInterface method and fill it with do the displayMethodSource method:

			when: #listChanged send: #displayMethodSource to: self.
		self classMethodList 
			when: #selectionChanged send: #displayMethodSource to: self;
			when: #listChanged send: #displayMethodSource to: self.

-+-+-+-

	displayMethodSource
		| stream sourceList |
		sourceList := self tabControl activePageNumber = self instancePage 
			ifTrue: [self instanceMethodList] 
			ifFalse: [self classMethodList].
		stream := TextStream on: (String new: 200).
		sourceList selectionDo: 
			[:methodDefinition | 
			stream nextPutAllText: methodDefinition formattedSourceCode.
			stream cr; cr].
		(self paneAt: #MethodText) model value: stream contents

Again, we steal from Vassili to use #selectionDo: as well as #formattedSourceCode. In terms of the widgets, the most interesting thing is the code at the top of the #displayMethodSource. Here, we ask the TabControl, what is the active page and compare it to the constant we created for the Instance page... If it matches, we use the instanceMethodList as our source pane, otherwise we use the classMethodList. This makes sure that a new selection will always display the right thing, depending on which page is showing.

We're almost done, but not quite. Let's take the case where someone chooses a class, say Object, then chooses an instance protocol, and then a method, then clicks on the class page, and selects a class protocol and then a class method.

What happens when they then simply select the instance page? Currently, the class method text remains visible. We want to make it so that if there IS something to see when they change pages, that the method text is updated. We'll do that by adding a final hookup in our hookupInterface method:

			when: #listChanged send: #displayMethodSource to: self.
		self tabControl
			when: #pageChangedTo: send: #displayMethodSource to: self.

The #pageChangedTo: event is fired when a page is changed. The trailing colon in the event name says that it can supply an argument. In this case, it supplies as an argument the page number of the newly visible page. We've written #displayMethodSource so that it doesn't need to have the page number passed in as a parameter, so we don't use the parameter, and when the event gets triggered, the page number is sent to the bit bucket.

The above is published as version 1.18 in the Package named "PollockBlog" on the Cincom public repository.

Well, that's it. Our ClassHierarchyBrowser is now more than just a pretty picture. It changes information in various panes as you select on stuff. It allows you to edit the method source, but it doesn't allow you to save it or anything... we'll leave that as an exercise for the reader with one caution: If you do it wrong, you can really screw stuff up.

Next time, we'll add a simple MenuBar to our ClassHierarchyBrowser.

Read: How To Build A GUI With Pollock - TabControl - Part 2

Topic: One more Unit of Understanding Previous Topic   Next Topic Topic: About that Joel Article...

Sponsored Links



Google
  Web Artima.com   

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