Edit: I’ve updated this intro after discovering that I somehow replaced it with some unrevised notes from my initial outline. Oops.
I was reading an argument that progressive find is the best (and only proper) search design, I think in the comments of this article. I got to thinking about what makes progressive, and occasionally modal, search optimal for certain tasks. Each has distinct advantages. Progressive find is rapid, it provides instant indication of most typos, it completes upon minimum unique string, and it can show near matches on failure (specifically, longest common initial sequence).
Modal find, however, is less disorienting, and a failed search doesn’t leave user in at some random location within the document.
I’ve noticed other issues, too. In a misguided attempt to minimize disorienting motion, most software scrolls text only exactly far enough to expose the next match for the search term. This means that except when it is already visible, exactly half the context of the search term–either everything above or everything below—ends up hidden by the edge of the viewport. Additionally, many programs depend on awkward non-central keyboard keys for searching, including the totally arbitrary F3 key. This is a pain for anyone using a laptop with shrunk-and-shifted function keys, a Datahand, or a Happy Hacker (I currently use two of those three).
With all of this in mind, I thought of what sort of search design would marry all the advantages of both designs as well as fixing my big annoyances (half-context results).
[Author's note 1: Though I appreciate links and references relevant to my posts, please don't accuse me of "stealing" other people's ideas. Granted, I may have seen it and forgotten, or I may also be oblivious of it. If I know about a source I try very hard to cite it, and if I discover omissions I link to them as soon as I can.]
[Author's note 2: Yes, this is a long post about a "small" feature, but perhaps every feature should undergo this level of scrutiny. It's amazing how much frustration a few small flaws can induce, or how much benefit seemingly tiny enhancements can impart.]
Here are my requirements for a search function, then:
* Must be instantly gratifying (i.e. progressive)
* Must clearly indicate when (and where) the search fails
* Must use the absolute minimum unique search string, and preferably teach the user what that is (again, inherently progressive)
* Must not be disorienting
* Must let me get back to where I was no matter whether I found what I was looking for or not (i.e. no dumping me in some random location, or even worse, changing any state)
* Must not intrude on my screen real estate more than absolutely necessary
* Must support regular expressions
* Must allow literal search strings (and default to them)
* Must use only central-keyboard keys common to every significant platform
Note that I am not specifying the way to activate search, as that’s something that depends heavily on the application. However, as always, the more consistent across applications it can be, the better (ex. word processors and applications that have text entry Ctrl-f, modal editors (like Vim) and applications that have no text input /, etc.). In fact, if there isn’t a shortage of keybindings available, redundant bindings—Firefox being an example of this with / and Ctrl-f—would be even better.
Naturally, since my requirements include it, search must be progressive. Reasonably well-implemented progressive search also meets my requirements for search with the minimum possible string. Instant notification of search failure is easily achieved by changing the background of the search box to red (again, like Firefox), and this also shows the user where in the search string there is no longer a match.
Minimum real estate would be box exactly as small as to hold the text. The least intrusive place to put it would be inside the top or bottom of the window. If there’s a status bar, inside it would actually be an excellent place—it rarely holds any information of more importance than the current search string and it can be returned to its original state as soon as search is finished. There is a distinct temptation to have a small fixed-size or shrinking/growing box that only takes up a corner and leaves the rest of the area [figure coming soon] visible behind. This might be useful for graphical applications, but beware of this in text applications. If the box covers the first part of a line, the gap in the text can frustrate users. This is less of a problem if the text covered is at the top of the screen instead of the bottom—the former only slightly reduces the length of visible text, but the latter creates an annoying discontinuity therein.
If the search bar is too awkward being only part of the width of the window or there is enough room, the search bar can be extended all the way across. The extra space would be an excellent place to reminders for the shortcuts (more on these later).
One of the traditional problems for progressive find is that the first few letters nearly always match something, so if the search eventually fails, the user is dumped in some arbitrary place. However, sometimes this leads a person to find what they were really looking for. For example, say someone’s a terrible French speller, and they search for “Versaie” instead of “Versailles.” Well, progressive find will fail eventually—but the point in the text it fails on will almost certainly be on the correctly spelled “Versailles.” If it’s case-sensitive, “Versa” followed by almost anything is fairly likely match the right place in the document before failing (well, assuming the document is in English and a sentence doesn’t start with one of the words beginning in “versa” that will inevitably be posted in the comments once I post this. . .). In fact, those of us who went to school before spell check will recall that correct spelling was found in a dictionary: the same principal as this—we can usually get enough of the beginning of a word right to find the rest of it.
Let’s assume, however that the user either starts out with the wrong letters “veenershnitsl” for “wiener schnitzel” and winds up somewhere random, or they don’t want to find anything but the exact string they type: that’s where exit-mode keys (I think I just made that term up.) come in. Once search is initiated, there are two exit modes: stay and return. The former selects the matching text and remains in place; the latter simply returns to wherever the user was before the search began. If the user likes the result, they can hit the stay key, otherwise the return key takes them back to where they were before. If they want the next match, it’s just a keystroke away, too. Doing a search and then using the return keystroke when it matches is also an excellent way to “peek” at a function declaration or quickly check contexts, etc.
These keys should be assigned as follows: Next Match [Tab], Return to Pre-search Location [Escape], Select Match and Stay [Enter]. This is less arbitrary than it appears. Keys that are found on every keyboard (remember the limited set of keys on laptops and the differences between PC (windows/*nix) and Mac keyboards) are either printable characters, and thus needed for literal strings, or one of those three keys plus control, shift, or backspace. Naturally, backspace is out—users ought to be allowed corrective powers. Shift is out for the obvious reasons, and using control as an independent keystroke breaks with more tradition than I care to consider.
Integrating RegExes into progressive search poses several problems: how to differentiate RegExes from regular search, how to handle invalid RegExes, how to ensure that processing the RegExes doesn’t leave the interface unresponsive and frustrating. The first can be handled in one of several ways. Either RegEx search can be initiated by a separate keystroke (Ctrl+Shift+F instead of Ctrl+F), it can be indicated by starting the search string with a particular letter, or it can be the only search method. The last is not uncommon in very technically-oriented applications such as vi(m), but it will seriously confuse most users. The first option guarantees that users will not be confused, but requires using more of the command space for the separate key combination(s). The middle option is a happy medium, assuming that the RegEx trigger character is well-chosen and the user gets notification when using a RegEx (and how to avoid it) when it’s activated. The activation character could be “/”. [Author's note: I put the "." after the quotes intentionally. More people would erroneously think I meant "/." than realize that grammatically, the sentence-terminating "." belongs inside the quotes. Besides, I think that rule removes precision from written language, anyway.] I picked this character because /RegEx/ is a common way to create regular expressions in programming languages, and if the search activation character is “/” a second tap is easy to do to activate RegEx search instead of literal search.
To reduce RegEx processing unresponsiveness, the search can be threaded. If the search string changes before the last search processing returns, just kill that thread and spawn a new one. For even more robust handling, test the new string for validity and if it isn’t valid don’t kill and re-spawn the thread with the new term. On the interface side, just add a “thinking/processing” icon and animate it to show that the last search is still running. This would also be useful in very long search environments where searches even of literal strings would be less than instantaneous.
Of course, no progressive search would be complete without visual feedback. Changing the color of the background (again, drawn from Firefox) is an excellent way to provide it. These colors must be of close in value to the default background, otherwise the foreground would either lack sufficient contrast for easy readability or would require changing the text color, which also makes reading hard. Search failure color would, naturally, be light red; RegEx mode light green; RegEx failure mode yellow. I haven’t any real logic for the latter two beyond that green implies correctness and yellow implies warning.
A nice additional feature (I’ve seen in some applications, but I cannot recall which) is that the search should start out being whatever text is selected. This is an easy way to find the declaration of a given function. Beyond that, it would be trivial to peek at the code inside a simple function or see what other contexts surround a certain phrase—the latter being a real boon to writers who want to avoid repeating phrases or structures too much.
Finally, using hardware-accelerated sliding of the document or objects inside the viewport when available helps give the user feedback on how they got to a result from where they were. Obviously switching to the jerkier “jump” mode for people who dislike the delay that might cause should be a user-available option.
Now, here are some mock-ups of my design:
Bookmarks Tagged English | 30-Dec-07 at 1:28 pm | Permalink
[...] bookmarks tagged english Search Done Right—A *Progressive* Progressive Fi… saved by 8 others Zelda1link bookmarked on 12/30/07 | [...]