An interactive tool for constructing Three20's TTStyle rendering pipelines
TTStyleBuilder is an interactive tool for creating and exploring Three20's TTStyle rendering pipelines. The tool can run in either the iPhone simulator or directly on the device. Some more information about the design and motivation of TTStyle is available here.
v0.1 (July 7, 2009)
If you have already cloned TTStyleBuilder before this release, you may need to do 'git submodules sync' because I switched the three20 submodule URL from Joe Hewitt's Three20 repository on GitHub to my Three20 fork (klazuka) on GitHub.
TTStyleBuilder has 3 modes
When you load a TTStyle into the editor, TTStyleBuilder displays the style linked list (hereafter referred to as the 'pipeline') from top to bottom. So the first row in the table view is the head of the pipeline, and the last row is the tail of the pipeline.
Below the pipeline table view is the "live style preview" area. This is where the result of the pipeline renders to. Whenever you make a change to the pipeline or the properties of the individual style nodes, the "live style preview" will refresh to display the current state.
You can append new styles to the end of the pipeline by tapping the "+" button in the upper-right corner.
You can delete and re-arrange styles within the pipeline by tapping the "Edit" button.
Tap the "Settings" button to change the settings for the live style preview view. The live style preview also acts as the TTStyleDelegate, so you can specify the text and image to be used when styles like TTTextStyle query their delegate for an NSString or UIImage.
Because of the limitations of the iPhone's display size, I have implemented a simple Bonjour client that runs on Mac OS X and displays the current style that the user is editing. The rendering client ("StyleRenderClient.app"), along with its source code, is included with the TTStyleBuilder distribution.
The StyleRenderClient can also provide the data for the TTStyleDelegate protocol. This protocol provides the means for a style pipeline to pull in external data from its runtime environment. Any text that you type in the "Text" field will be used any time the style pipeline asks its delegate for textForLayerWithStyle: (if you want to try it out, add a TTTextStyle to your pipeline). Likewise, any image that you drag and drop into the "Image" well will be used any time the style pipeline asks its delegate for imageForLayerWithStyle: (to try it out, add a TTImageStyle to your pipeline, but see known issues below).
If you change the "Text" or "Image" values, you need to click the "Submit Configuration" button to upload your data to TTStyleBuilder (the server).
When the server (TTStyleBuilder) goes down and comes back up, the client is usually able to reconnect. But sometimes it doesn't. Until I fix this, you will just have to relaunch the client.
If you provide an image without an alpha channel to be used by the TTStyleDelegate, oftentimes it will not render correctly. This isn't actually a bug in the client, it's a result of the underlying TTStyle/TTImageStyle implementation. Perhaps Joe designed it this way, or maybe it is something that we need to work on.
TTStyleBuilder runs a TCP listener (called RenderService) that is exposed on the local network via Bonjour. The StyleRenderClient browses for the server via Bonjour and automatically connects as soon as the server is found. When TTStyleBuilder generates a notification that the current style needs to be refreshed, the RenderService (running on the iPhone) iterates over every connected client and renders the style once for each client. A separate render for each client is required because each client is able to specify the raster dimensions that it wants.
To render the style for a single client, RenderService has a reference to a TTView that is never displayed onscreen. When it needs to render the style to a bitmap, it first sets the TTView's style property to the style being rendered. Then it uses the client configuration (style width and height) to setup a bitmap context and configure the dimensions of the TTView to match the client's preference. Then it asks the TTView's layer (a CALayer) to render into the bitmap context. Finally, it converts the bytes in the bitmap context into a PNG and ships it over the wire via the BLIP protocol directly to the client.
One of the guiding principles while implementing this tool is that it should put very little burden on subclassers of TTStyle, and it should avoid patching Three20 as much as possible. In order to do this, TTStyleBuilder relies heavily on the Objective-C runtime to dynamically reflect on the system in memory. TTStyleBuilder implements a generic object/property editing system that could, theoretically, be used in other iPhone apps that need an easy way to provide a UI for editing objects. Rather than defining an editor plugin for each TTStyle subclass (which would increase the burden on subclassers), I instead chose to write the plugins for each basic type (int, float, CGSize, UIColor, etc.).
Note 1: Actually, there is not enough runtime information to determine which methods return TTStyle*, so I just list all methods that return an 'id'. When the user taps on a method name row, I first verify that calling that method actually returns a TTStyle object before allowing the user to edit the returned object.
Note 2: The style archives directory is different depending on whether you are running TTStyleBuilder on the simulator versus on the device.