The Artima Developer Community
Sponsored Link

Agile Buzz Forum
How To BUild A GUI With Pollock - The Panes

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 - The Panes Posted: Mar 7, 2004 5:47 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 - The Panes
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

It's time to add the panes for our Class Hierarchy Browser. Our design is simple, to start with. We want three panes across the top half of the window. The left one, a TreeView, which will show the class hierarchy itself. The middle one, a ListBox, that will show the protocols for the currently selected class in the TreeView. The right one, another ListBox, which will show the methods for the currently selected protocol(s). Below these, across the bottom half of the window, a TextEdit, which will show the definition for the currently selected method, if a method is selected otherwise the class definition.

Before we start, we need to modify our class definition. What we need to do is import the Pollock namespace to our class, so that our class, when compiled, can find the Pollock widgets. This is not a "Forever" requirement, just for now, while Pollock is the new kid on the block. So, here's how our new class definition looks like:

	Smalltalk defineClass: #ClassHierarchyBrowser
		superclass: #{Pollock.UserInterface}
		indexedType: #none
		private: false
		instanceVariableNames: ''
		classInstanceVariableNames: ''
		imports: 'Pollock.*'
		category: 'PollockBlog'

Back to adding our panes. As we saw in the last session, the #createInterface method is there to allow us to add new panes to our user interface:

	createInterface
		| pane |
		pane := TreeView new.
		pane frame: (FractionalFrame fractionTop: 0 right: 0.33 bottom: 0.5 left: 0).
		pane frame
			rightOffset: -1;
			bottomOffset: -1.
		self addComponent: pane.
		pane := ListBox new.
		pane frame: (FractionalFrame fractionTop: 0 right: 0.66 bottom: 0.5 left: 0.33).
		pane frame
			rightOffset: -1;
			leftOffset: 1;
			bottomOffset: -1.
		self addComponent: pane.
		pane := ListBox new.
		pane frame: (FractionalFrame fractionTop: 0 right: 1 bottom: 0.5 left: 0.66).
		pane frame
			leftOffset: 1;
			bottomOffset: -1.
		self addComponent: pane.
		pane := TextEdit new.
		pane frame: (FractionalFrame fractionTop: 0.5 right: 1 bottom: 1 left: 0).
		pane frame topOffset: 1.
		self addComponent: pane

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

With luck, the above doesn't require too much explanation, but I'll assume nothing. There are basically three steps in creating a new pane and adding it to the main window. First, you have to create a new instance of the pane you want to add. Then, you need to set it's Frame. Finally, you have to add your pane to the main window

The part about creating a new instance of a pane doesn't need explanation. The other two, setting a pane's frame and adding the pane... well. Let's talk.

By default, every pane already has a default frame. That frame is an instance "OriginExtentFrame." An OriginExtentFrame is the most simple frame available in Pollock. Basically, it allows you to specify the origin and extent for the pane that is using it. An example of using an OriginExtentFrame is:

	pane frame: (OriginExtentFrame origin: 10 @ 10 extent: 100 @ 100).

The origin is always relative to the pane that hosts the frame's pane. Uh-oh! That reads a bit awkward. Here's the scoop. In Pollock, some panes are allowed to have subpanes. For example, a window is in effect a pane that can have subpanes. In our example ,the window has four subpanes. Other panes in Pollock that have subpanes are the TabControl and the Form... We'll see more about both of them in later sessions. Any pane that can have subpanes has an instance variable named "components." You can get a copy of a pane's components collection by sending the #components message to it:

	paneCompnents := somePaneThatHasComponents components.
	
	widowComponents := self mainWindow components.

Note that I said, "a copy of a pane's components." This means that you can't actually add a new component to the target pane by sending something like:

	self mainWindow components add: newComponent

This is because since you only have a copy of the actual pane's components collection, any attempt to modify the collection is ignored by the owning pane. That's just standard Smalltalk.

But, back to the frame. Whenever any pane has components, the components are laid out in the enclosing pane frame. Any pane that can have subpanes has a method named #addComponent:. This method allows you to add the pane as a subpane. An example is in order:

	...
	somePaneThatAllowsSubpanes := PaneThatAllowsSubpanes new.
	somePaneThatAllowsSubpanes frame: (OriginExtentFrame origin: 10 @ 10 extent: 150 @ 150).
	self addComponent: somePaneThatAllowsSubpanes.
	button := Button new.
	button frame: (OriginExtentFrame origin: 10 @ 10 extent: 50 @ 25).
	somePaneThatAllowsSubpanes addComponent: button.
	...

In our example, you'll note that both frames have an origin of 10 @ 10. The first pane, the "PaneThatAllowsSubpanes" is added to the main window (via the "self addComponent:") The button is added to somePaneThatAllowsSubpanes (via the "somePaneThatAllowsSubpanes addComponent: button"). Even though both panes have the same given origin, 10 @ 10, the button will appear at 20 @ 20 inside the window. That is because, as a subpane of the PaneThatAllowsSubpanes, it's position will be relative to it's "enclosing pane." Indeed, while the PaneThatAllowsSubpanes displays at 10 @ 10 inside the window, in fact, it actually displays relative to it's enclosing pane... which just happens to be window.

When a pane is added as a subpane with the #addComponent: method, it's enclosing pane is set for it behind the scenes. Initially, every pane, before being added as a subpane answers nil for it's enclosingPane. Every pane responds to #enclosingPane. Even windows respond to #enclosingPane, however, they always answer nil. Thus, you see, for any pane, it always points back to it's enclosing pane, and thus you can walk up the hierarchy of panes. Also, all panes (even those that allow no components) all respond to #components. Those that have components will respond with a copy of the target pane's collection of subpanes, those that don't (or can't) have subpanes, simply answer an empty collection. In Pollock, it is common for such instance hierarchies to have this kind of up and down access

About a note way above that said: "By default, every pane already has a default frame. That frame is an instance "OriginExtentFrame."" What it didn't say is that the default origin for an OriginExtentFrame is 0 @ 0, and the extent is 0 @ 0. This makes for a nice "Never Blows Up If You Do Nothing" environment, but it also makes for a "Doesn't Do Much Of Use" situation. However, it does mean, that if you are satisfied with using an OriginExtentFrame, you don't have to actually create a new one, like our first examples do. Since an OriginExtentFrame is automatically the default for all non window panes (it always uses a WindowFrame by default, as discussed in the previous session), you could re-write the example pane/subpane code above as:

	...
	somePaneThatAllowsSubpanes := PaneThatAllowsSubpanes new.
	(somePaneThatAllowsSubpanes frame) 
		origin: 10 @ 10; 
		extent: 150 @ 150.
	self addComponent: somePaneThatAllowsSubpanes.
	button := Button new.
	(button frame) 
		origin: 10 @ 10;
		extent: 50 @ 25.
	somePaneThatAllowsSubpanes addComponent: button.
	...

The choice, is yours. Pollock doesn't care. Here's another thing that Pollock allows you to not care about. It allows you to not care about the order in which you send messages to an pane when you are configuring it. In our above example, we could have added the button to the PaneThatAllowsSubpanes before adding it to the main window, and even set the frames any time after they are created. Pollock has mechanisms, under the hood, that allow you to never have to worry about if a window is opened or a pane is displayed on an opened window or not when you are configuring the pane or window. If, for whatever reason, Pollock determines that the message you're sending to a pane to configure it is actually not able to be sent to the underlying pane because it isn't visible yet, or have all of the information in order to execute the message yet, Pollock caches the method, in what is called a DelayedConfigurationScript object, and then executes it "at the right time."

Does that sound a bit scary? Does it bring up questions in your mind like "Mightn't Pollock Do Something 'For Me' When I'm Not Expecting It?" Well, let me assuage any fears you might have. Pollock is designed to be extremely dynamic in the first place. There are not many times and places that you can send a message to a pane to configure it, that it can't just do what you asked it to do right then. The few places it can't are highly deterministic. There are no "other" cases. Sounds pretty bold, no? It's true though... Just because other GUI frameworks (even such as Wrapper) don't do it, has absolutely nothing to do with if it could be done.... Pollock is just like Nike: "Just Do It."

Back again to our code for the Class Hierarchy Browser. You can see we're not using the default OriginExtentFrame. Instead, we're using a FractionalFrame. The FractionalFrame currently has two basic creation methods:

	FractionalFrame 
		fractionTop: <aFloat between 0 and 1 - topFraction>
		right: <aFloat between 0 and 1 - rightFraction>
		bottom: <aFloat between 0 and 1 - bottomFraction>
		left: <aFloat between 0 and 1 - leftFraction>
		
	FractionalFrame 
		offsetTop: <anInteger - topOffset>
		right: <anInteger - rightOffset>
		bottom: <anInteger - bottomOffset>
		left: <anInteger - leftOffset>

You'll notice that both creation methods go "around the horn", starting at the top and ending at the left. As you see in our code for the ClassHierarchyBrowser, that the values 1 and 0 for the fractions can be just the integer values. You could just send "new" to a FractionalFrame. By default, if you do so, the values for all fractional positions is 0, and all offsets is 0. There is a complete instance protocol for setting a FractionalFrame's fraction and offset values you can use:

	topFraction: <aFloat>
	rightFraction: <aFloat>
	bottomFraction: <aFloat>
	leftFraction: <aFloat>

	topOffset: <anInteger>
	rightOffset: <anInteger>
	bottomOffset: <anInteger>
	leftOffset: <anInteger>

	topFraction: <aFloat> offset: <anInteger>
	rightFraction: <aFloat> offset: <anInteger>
	bottomFraction: <aFloat> offset: <anInteger>
	leftFraction: <aFloat> offset: <anInteger>

As with the OriginExtentFrame (and the not yet discussed AlignmentFrame and RelationalFrame), the position on the screen, is dependent on the enclosing pane for the pane that is assigned the frame.

This leaves just two last notes for this session

First, you may be wondering why we sent "self addComponent:" in our #createInterface method of our ClassHierarchyBrowser, instead of "self mainWindow addComponent:". #addComponent: in UserInterface is a convenience method, that itself sends #addComponent: to the main window. You're welcome.

Last, you may be wondering why we didn't hold on to the instances of the panes we added to the main window. You may be wondering how we can communicate with these panes, to further configure them, if we've so blithely just ignored holding onto them for "later."

We'll get into this last issue next time, when we further configure our panes, give them model objects to display, and communicate with each other when events occur.


And So It Goes
Sames

Read: How To BUild A GUI With Pollock - The Panes

Topic: Church of the Obvious Previous Topic   Next Topic Topic: Another CST Blogger joins

Sponsored Links



Google
  Web Artima.com   

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