Mike's Take

Wednesday, September 19, 2007

Widgetry, Assets and Cairo

I thought I would put together a post on a little work I've done recently with Cairo, Assets and Widgetry. I've been meaning to write about it for a month, but the time slips away.

Cairo Graphics

In my application, I want to have good looking icons. I bought a nice set online. They came in .png and .gif formats. The .png's look really nice, with alpha channel blending. Putting them into VisualWorks resulted in jagged looking edges especially on non-white backgrounds.

So I created a new class in the CairoGraphics package called PngImage that is just a wrapper around an ImageSurface. PngImage implements the necessary interface from the Image class, so it can be displayed in the trippy inspector, and used as the image in Widgetry DisplayImage and ActiveImage classes. PngImages can be instantiated from a file path, from bytes or from a stream.

PngImage pngBytes: aByteArray.
PngImage pngByteStream: anExternalStream.
PngImage pngPath: aFilePathString.

You can inspect one and it displays nicely using Cairo. The code is part of the CairoGraphics package in the Cincom public repository.


Once I had PngImage working well, I wanted a good way to manage all of my icons. Keeping them in the smalltalk image was very desirable, both for deployment and for source code management. I wanted to be able to version the icons and use Store.

Assets

Vassili Bykov started working on a framework for managing external "assets" that Travis Griggs later finished. It can be found in the public store also. You point Assets to a folder, and it reads the files, and based on the file extensions does wonderful things, like install class methods on your Assets subclass with the literal representation of the external asset. By default, Assets deals with external png files and gives cached OpaqueImages (the regular VW way of dealing with alpha channel, basically an image and a mask). So for my work I just made my own subclass of Assets and tweaked the behavior slightly so that it would make a cached CairoGraphics.PngImage for me. This required only one overridden method, and one new method.

MyAssetClass class>>import_png: aFilename
^self importPngImage: aFilename

MyAssetClass class>>importPngImage: aLogicalFile
| bytes |
bytes := aLogicalFile contentsOfEntireBinaryFile.
^'#{CairoGraphics.PngImage} value pngBytes:
(<1s>)'
expandMacrosWith: bytes storeString
Now all you have to do is choose Sync Assets from the Class menu in the Refactoring Browser, and point the file dialog at your folder containing all of your png images. Assets auto-magically creates source code for you to generate your PngImages. One thing to be aware of, is that PngImages are using Cairo surfaces, and the memory for these is handled outside of Smalltalk memory management. These memory locations will survive an image snapshot, but not restarting the image. So you must arrange for the CachedBlockClosures (used by Assets, and really cool by the way) to become un-cached when the image is started so the first call will allocate the Cairo surfaces correctly. My application uses Cincom's Subsystem framework to initialize things properly on image startup, so I just threw in a line to un-cache the blocks.
CachedBlockClosure uncacheAll.
Now when I call
MyAssetClass class>>myIconName
the first time, the Cairo surface is created from the byte array embedded in the method source, and a PngImage is returned. All subsequent calls return the cached PngImage. The caching and the Cairo rendering make this really fast, and the results are great.

Widgetry

Despite the fact that Widgetry is no longer supported, we are moving ahead with it at my company. We have had a really good experience with it so far, and integrating Cairo with it (I implemented a CairoCanvas that lets you do primitive drawing with Cairo) and using Cairo based icons was really easy. I also went ahead and implemented in-place editing for ListBoxes as well. It uses the same api as the in-place editing for TreeView.
We're still really early in our development of the new UI, but so far it has gone quite well. The following screen shot shows PngImages used within Widgetry DisplayImages, and a number of CairoCanvas' within Widgtry Grids.

The combination of Cairo, Assets and Widgetry easy to use and powerful, and is getting me closer to my goals for the UI for KnowledgeScape. I will be working on many more enhancements in the future, hopefully culminating in a Look and Feel that uses Cairo exclusively for rendering and Pango for text layout and fonts.

Friday, February 23, 2007

Unit Test Code Coverage

A couple weeks ago, Michael Lucas-Smith posted about a code coverage tool that he published to the Cincom public repository. Jim mentioned it briefly and then everyone seemed to move on and forget it. People, wake up! This is one of the best tools published in the repository. There should be buzz about this. Think about it. We write tests to make sure our code works. Some of us do this well, some not so well. I thought I was doing great. I'm working on a project writing new code. I have been trying to make sure every Class has a corresponding TestCase, and that at least all of the api methods have corresponding test methods. I even wrote a utility to automagically generate the TestCase and stub test methods based on the protocols in my domain Classes. I make sure all my tests pass, or I stop and fix things before I move on. I figured I had things well covered. Wrong! After installing the code coverage tool, I could see where my deficiencies were.


One of the greatest benefits of the tool, is that it can show you branches of code not taken. Say you remember about some edge case, so you add logic to handle it. Then you write the test like I did, but just test for the common cases. With the code coverage tool, your method doesn't have 100%, it has some value in the middle. (If you didn't test it at all it would have 0%) So you browse to it, then click the coverage tab in the refactoring browser and it shows you (in red) the branches of code that were never called. This is huge. It's the edge cases with code that is rarely called that always bite us. I wrote it, so I interact with it a certain way. My users have no preconceived notions of how to use the software so they do things differently, and find places I wouldn't. But now, the coverage tool is helping me explore those hidden away places before my users. This is great!

One benefit that I hadn't thought of, but that I noticed immediately when I started using the tool, was that I found code that could be removed. It was untested, because it was not needed. I had changed the API slightly, but left the original methods or whatever. I was able to quickly identify code that could be culled. This is a huge benefit for maintenance. One of the big problems we have with one of our existing products is that they did the same thing 12 different ways, and left them all in there. So now, which to I use? Where do hook in to hack my new changes? By eliminating unused code early, I keep it lean and mean.

If you don't have very many unit tests, I can see how this tool could be less than exciting. Wow, only 3%, great.... I know from our legacy code that getting the coverage up is nigh onto impossible. But for anybody doing new development, this is huge. Sames, for example, with like 27,000 tests in Pollock could check if they're doing the job we all want them to be.

If you're doing unit tests in VisualWorks, you really should be using this tool!

Labels:

Sunday, September 10, 2006

STIC should follow Ruby's lead

There has been some work lately on the STIC web site, and some work on a new logo etc to help promote Smalltalk. I was just looking around today and found the new Ruby website. They had a link to an in-browser interactive demo that let you try out ruby through a web app in your browser. No download, no install, just completely easy. I don't know what the funding situation is like, but commissioning someone to create a seaside application that lets you do the 15 minute Smalltalk tutorial, and demonstrates seaside's ability to make a really nice website with all the fancy Ajax stuff, and work with the back button would be well worth the money if you want to get people to try the language. Check out the demo here.

Friday, August 25, 2006

Seaside for Large Health Care Application

Just saw this on reddit and thought I would pass it along. I'll be working on a seaside app for our base product soon.

Saturday, August 19, 2006

Saved by Fink

I have been trying to get Cairo running on my Intel Mac so I could play with Smalltalk bindings that Travis Griggs has published in the public repository. I googled around and started downloading source code. But to build it I needed GCC so I downloaded the Xcode tools from Apple. Then I tried to build it. No luck! I needed to install libpng first. Get it, try to build it, no luck! I needed to install zlib. But then zlib needed expat and so on and so on. Once I got these on and going, Cairo still couldn't find libpng. I disabled png support and tried again. Nope! It needed freetype. And font-config. On and on it went. So finally I downloaded Fink. Wonderful, it has a nice installer and voila, I have tools to help me. Fink lets you use apt to find, download and install binaries. And even more wonderfully, it lets you download and build from source while also downloading and building all the dependencies. One click and down comes Cairo and all its buddies and away I go happy as can be.