Tuesday, September 27, 2005

Surprising reduction in image size, and more outliner goodness

In Factor, each word has a word properties hashtable associated with it. Word properties can be read and written with the word-prop and set-word-prop words. When browsing around in the inspector, I noticed that a lot of word properties simply had the value f; denoting no value. So I tweaked set-word-prop to simply call remove-hash if the new value is f, thus removing a hashtable key instead of storing a key/value pair with f as the value. This elimination of redundant key/value pairs has reduced image size by a whopping 500Kb! So a full factor.image is only 3.1mb now. This is a 1.3Mb reduction since Factor 0.77, and a 700Kb reduction since Factor 0.78.

I wrote a couple of days ago about a new outliner gadget in the UI. I have cleaned it up, made it look better and decided on a protocol for calling it.

The new protocol for outputting outliners is decoupled from the UI itself and can be used in plain text streams (of course no outliner is displayed in that case). To output an outliner, you call the write-outliner ( string object quot -- ) word. When the outliner is expanded, the quotation is called with the default stream rebound to another stream; any output written to the stream is displayed in the outliner's expanded area. This quotation can therefore output more outliners, creating a nested tree.

Here is a example implementing a call hierarchy outliner; notice how simple it is, with no calls into the UI implementation, or event handling. First we have the utility object-outline word, which takes a sequence of objects, and a quotation that can expand an outline for each object. It prints a set of outliners, one for each object in the sequence, with the quotation as the callback. The quotation receives the object on the stack.

: simple-outliner ( seq quot -- | quot: obj -- )
swap [
[ unparse-short ] keep rot dupd curry write-outliner
] each-with ;

Now, the usage. word uses simple-outliner to display a hierarchy of words that call a given word:

: usage. ( word -- )
#! Outlining usages browser.
usage [ usage. ] simple-outliner ;

Here is what it looks like after a few nested outliners have been expanded. Note that each word in the outline is a live presentation that can be clicked to display a command menu:

I have similarly updated the inspector and stack display to use outlining. As previously mentioned, the new vocabs. displays an outliner for browsing vocabularies and word definitions.

As you can see in the screenshot, scroll bars look a bit nicer now, and have up/down arrows. There is a clipping flaw in the arrows; its either an sdl-gfx problem or with how I'm calling it, but in any case it should be easy to fix.

I have been studying the part of the CLIM spec for accepting presentations, input editing, and completion. This is a major omission from the Factor UI toolkit at this point. I'll probably implement something rather different from the CLIM way, however understanding how others have solved similar problems helps a lot.

Sunday, September 25, 2005

CLIM-like features in Factor UI

(First, I have to say that Blogger.com's user interface is much nicer than JRoller. It supports WYSIWYG editing of weblog posts and makes great use of AJAX for a snappier user interface.)

While my understanding of CLIM is incomplete, the most striking aspect is how central input/output streams are. CLIM extends output streams to support marked up text and graphics. To output a set of widgets with a vertical layout, you write them to an extended output stream, with a line break between each widget; horizontal layout is the same except with no intermediate line breaks. The extended output stream is connected to a pane which displays the output. Live presentations may be written to the output stream; they may be clicked and used as input to commands. CLIM supports a notion of extended input streams, which support input of presentations, together with completion.

The Factor UI already supports a pane gadget, with extended stream output protocol, and (currently) only plain text input. Today, I spent some time cleaning up the pane gadget to be more general purpose, and changed the stack display widget in the listener to use a pane instead of constructing a vertical layout by hand.

I implemented an outliner gadget that uses CLIM concepts; it is similar to "Tree" gadgets in other GUI toolkits, but there is a fundamental difference. The outliner gadget is given a quotation; when the outliner is expanded, the quotation is run inside a dynamic scope whose standard output is bound to a new pane. The quotation can then write attributed text and live gadgets to the stream; some of those gadgets might themselves be outliners. This results in much simpler code than a typical "tree view"/"tree model" separation. Of course there is a limitation in that it is not easy to remove individual tree nodes; instead, you must regenerate the outliner view. However I don't expect this to be a problem.

This screenshot shows the outliner in action, with a simple word browser. I know it is ugly; I will be working on appearance later:

The code is trivial:

: word. ( word -- )
swap [ literalize , \ see , ] [ ] make outliner. ;

: vocab. ( vocab -- )

Here is another one. This time its a messier hack and I won't show the source; I still have to think about a few things and integrate it in a clean manner. This shows expandable slots in an outlining inspector.

The new outliner allows a very powerful class of UI tools to be developed; however the tools will continue to work on the command line, albeit with reduced functionality.

Saturday, September 24, 2005