Wednesday, June 13, 2007

Selecting text in pane gadgets

A long-overdue Factor UI feature is the ability to select and copy text in pane gadgets. This is now possible in the darcs repository. Prior to this, there was no way to copy UI listener output; this was rather annoying. Factor 0.89 had some preliminary code for this which didn't work to well, and I didn't want to delay the release further so I left it as-is, and didn't mention it in the changelog. Now I've fixed this code up and it is mostly usable.

What makes this code interesting is that a pane gadget is very general. It simply lays out its children, which are added as a result of formatted text being written to a stream. There is no underlying document model; writing a line of text just creates a label gadget, sets its font and color attributes accordingly, and adds it to the pane. More complex layouts such as lists, grids, borders and so on create more complex gadgets, some of them containing child panes.

To make selection work, I first devised a generic protocol whereby a gadget can be asked for a textual representation of itself. For label gadgets, this returns the label's string; for horizontal, vertical and grid layouts, this recursively asks each child for its text then combines it in the appropriate way.

The second component is a simple word which takes a gadget and a point inside the gadget, and returns a path (a sequence of integers) to reach the deepest child gadget at that point.

The final component, and the one which took the most work to get right, takes two such paths from a common parent, and outputs the gadget subtree spanned by these two paths.

Now given these components, pane selection is easy to build: when the user drags the mouse between two ranges, you compute the paths from the pane to the deepest child at each endpoint, then you traverse the gadget tree between these two paths, obtaining the text of each gadget along the way. This text can then be copied to the clipboard when the user invokes the copy action.

All in all, this was a lot trickier to implement than text selection in a plain text editor gadget, however the code for turning gadgets into text and for traversing gadget trees should be usable in its own right, and could form the basis for better support for accessibility in the UI.

No comments: