Quairo

July 25, 2007 21:27:06.423

For a while now, one of the bummers about Cairo is that you can't use it with VisualWorks on OS X with an Aqua/Cocoa/Quartz based VM. You have to use the X11 VM to use Cairo under OS X (or at least to draw on windows; you can always just draw on Pixmaps or use it to save .png files). There are a number of issues with the VM that keep the integration from working. Just when I'd get it drawing, I'd find that it hangs the VM when you close the window.

Those VM Boys in the Back Room though have been cooking up new VMs and they've made some changes that make this a lot easier. This VM is not part of the build yet, it has some user-interaction performance issues that need to be ironed out still, but those Helpful Guys were nice enough to help me build it on my machine so I could play with it.

I do hope they get this on to the main line soon, because I'm happy to report that it works. The new VM interface is far simpler/easier for me to interface with for Cairo. And the fact that it works is nice too. Of course, you can't talk about this stuff without doing eye candy. I was imagining some sort of cool progress widget where things fly around in circles, and the the message text "fills up" as it nears completion. With help from ExtraActivity, this was a snap and I threw together a SnazzyProgressView with an example. If the code is of interest, ping me, and I can send it to you. Here's the progress view in action with a tribute to John Sarkela, one of those VM Boys that made this possible:

General

Functional Layouts

July 20, 2007 22:01:24.833

Many moons ago, Vassili Bykov put up a good writeup or two regarding VisualWorks widget layout mechanisms.

There are multiple aspects to widget layout; there's a dance that gets played between the subView and superView as they come to an agreement about where a subView goes. For this post will leave the faults of VisualWorks superView roles alone, and just concentrate on this layout thing which is what a subView uses to tell its superView where it wants to be.

All current Layout objects are basically a set of control variables used to tune a specific layout function. If we were speaking functional parlance, we might say layout is "f of r, where r is the containing rectangle, and f outputs a rectangle relative to the input." We have a couple of different ones of these in VisualWorks. I have a couple of problem with these.

The first is that there's a limited amount of algorithms. You can pick one of a couple of flavors. The objects are not easily decomposable, so you can't easily synthesize them together. For example, what if you want the behavior of a LayoutFrame in the horizontal, but that of an AlignmentOrigin in the vertical. Here's a couple of scenarios that I've happened upon over the years:

  • Position fractionally AFTER an initial pixel offset (this is inside out from a LayoutFrame).
  • Center the preferredBounds in the provided rectangle, but if we run out of space, align to the top/left. This is usually the desired thing to do for labels.
  • Maintain square proportions, centered within the provided rectangle.
  • etc. (add your own in the comments?

My second beef with them is that it doesn't feel correctly encapsulated. Object Oriented Programming, is about binding behavior to data. The data here is rectangles. And these various algorithms are methods (or combinations of them) that belong on Rectangle. In truth, many do. But the Layout objects ignore that. Or remove that. Or something. Adding a new algorithm involves adding a whole new object with it's control values, rather than adding a method to Rectangle. Extending a base object like Rectangle is where Smalltalk draws some of its best power from. The other part of a Layout object, acting as a function... we have a wonderful power object in Smalltalk for full closure functions already: the BlockClosure. I also feel that having the algorithm exposed through it's control variables only "hides" the intent often. For example, what does the following code specify?

LayoutFrame
  leftFraction: 0.5
  offset: 10
  topFraction: 0
  offset: 10
  rightFraction: 1
  offset: -10
  bottomFraction: 0.5
  offset: -10
The new programmer has to be taken to the white board and have the details of LayoutFrame explained. What if we could just express that via "expressive" Smalltalk code? Maybe something like:
(rect topCenter corner: rect rightCenter) insetBy: 10
Personally, I like this better. I can visualize in my mind right away these center points along the edges, and the rectangle that is drawn between them. Rather than having to wonder why some offset values are -10 and some are +10, I just know right away that we're going to inset it by 10 pixels.

About 6 months ago, I thought I had a solution to this. I've been playing around with it in a couple of contexts. It is frankly, inspired by VSE's framingBlock, which goes way back in time. I've now realized this code in a package called FunctionalLayouts which I hope to integrate in to the base real soon. It has been placed in the open repository.

Said package adds a FunctionalLayout object to the Layout hierarchy. Basically, the idea is that use expressions like the one above in a block to make your layout more clear and gain all the flexibility in the world. Here's some examples:

FunctionalLayout block: [:rect : | | inner | inner := (rect insetBy: 20).
  (0@ (1/3) corner: (2/3) @ (1/2)) scaledBy: inner extent ]
  "a candidate of example 1 above"
FunctionalLayout block: [:rect :desired | (rect center - desired half max: rect origin) extent: (desired extent min: rect extent)] "example 2 from above"
FunctionalLayout block: [:rect : | | square | square := (rect width min: rect height) asPoint.
  rect center - square half extent: square ] "example 3 above"
FunctionalLayout block: [:rect | rect ] "fully attached, the default state"
FunctionalLayout block: [10@100 extent: 40@ 50] "good old fashioned direct rectangle specification"
Note, there is nothing that precludes making a first class object out of a given algorithm here. Or we can reduce some of these expressions to helper methods on Rectangle. What's telling to me, is that I can use FunctionalLayout to do what every existing Layout does. It subsumes them all. A sort of "One Layout to Rule Them All and In Position Bind Them" sort of thing.

One may also observe from the examples, that the arguments are optional. Remember that post about block culling?

For a final fun touch, here's a quick prototype I whipped up in a workspace which does a clock layout of VisualBlocks. You definitely won't do this with the current Layout objects as easy. Especially when you resize the window and numbers layout correctly still. With relative aspect too.

composite := CompositePart new.
1
	to: 12
	do:
		[:index | 
		| label |
		label := Label with: index printString.
		vb := VisualBlock
			block:
				[:gc :box | 
				gc
					paint: ColorValue red;
					displayWedgeBoundedBy: box startAngle: 0 sweepAngle: 360;
					paint: ColorValue black;
					display: label
						at: box center - label bounds extent half].
		composite
			add: vb
			in:
				(FunctionalLayout
					block:
						[:box | 
						extent := box extent * 0.1.
						angle := (index / 12 * 360 - 90) degreesToRadians.
						box center + (box extent * 0.4 * (angle cos @ angle sin))
							- extent half extent: extent])].
(ApplicationWindow new)
	component: composite;
	open

Handy A Couple of Hours Later

July 20, 2007 20:06:47.253

It wasn't a couple of hours before I ended up using a similar technique as yesterday's post. In this case, it was a multi selection list. Multiple selections were allowed, but some combinations were not allowed. I used the notification property (rather than the verification) and put together a method like this:

clearOtherVersions: aController
	| targetPackage cleanedSelections |
	targetPackage := packagesList list at: aController view targetIndex.
	cleanedSelections := packagesList selections
		reject: [:each | each name = targetPackage name and: [each ~~ targetPackage]].
	packagesList selections: cleanedSelections
Basically, we find what element was just selected, and then reject any other like elements of different versions from the current selection set.

Keeping a List Selected

July 19, 2007 15:34:44.353

Often we write all kinds of code for a case we don't want to have happen. For example, a UI where a list controls what is viewed in other parts of the UI. Often, there is no real use of never having something selected. But hey, lists can have no selection, so we code our UI to deal with the null selection, showing no associated widgets (or disabling them) when nothing is selected. Wouldn't it just be easier to never let the list be deselected?

Happily this is quite easy in VisualWorks. Simply add a validation in the properties tool as such: Picture of Properties Tool

Then in your ApplicationModel, you implement that method as:

verifyStillSelected: aSequenceController
    ^aSequenceController view targetIndex ~= aSequenceController view selectionIndex

Basically, unless we're selecting something different than the current selection, we void the selection, because it would end up toggling it off.