Alex headshot

AlBlue’s Blog

Macs, Modularity and More

Effective UX with the Clipboard

2010, eclipse, git, tip

One of the things that becomes apparent if you use OSX for a while is that small things matter. It's not necessarily that writing in any particular target language is more or less capable than the other, but rather attention to detail – I've written about this before.

However, attention to UX can be the icing that turns a good product into a great product. The phrase “It just works” is often used with Mac products (both Apple and non-Apple) – when in fact, most software works in any case. So how is it done?

One tip I want to share here is the benefits of using the clipboard automatically where appropriate. I've used this before in examples, and proposed it for EGit (bug, review) as well. My feeling of why it's not more widely used is simply that people haven't thought about doing it before.

If a user navigates into a dialog where they're expected to put in a line of structured text (say, a git URL for cloning), then the user really has two choices. The first is to re-type the text, character for character, by reading it off a page or e-mail. The second is to copy the text from that page and paste it in (with Command/Ctrl+C/V or mouse, for preference). The majority of times, especially if you're using a URL, you'll be using the clipboard to copy and paste the data in.

The second thing to observe is that you almost certainly have a good idea of what the text needs to look like. If that text begins with git://, then there's a pretty good chance that it's the URL in the right format. On the other hand, if it's Did you enjoy the curry last night? then it's probably some other irrelevant stuff from an e-mail or chat window. The point is the program can automatically detect if it's valid or not.

So, when activating the dialog, either at construction time or (better) at focus time, you can detect if the user's input is already in the right form, and use that. (A common case is when the dialog gains focus-switch, especially if the user already has the dialog open, Alt/Command tabs over to Firefox/Safari to copy the URL, then Alt/Command tabs back again.) There's really three cases here:

  • The clipboard text is a random chat transcript, so we ignore it – net result, same as if we didn't introspect the clipboard in the first place
  • The clipboard text is in the right format, and the user has to paste it in manually upon dialog activation
  • The clipboard text is in the right format, and the program pastes it in automatically

The number of cases where the data is almost in the right format (but not quite) is vanishingly small, and often can be resolved with a tighter regular expression. In fact, some programs that need a licence code to activate will send the code by mail, and then allow the entire message to be copied. Given that the licence code is a known format, it's possible to detect even if it appears anywhere in a block of text, not just if the entire text matches.

This technique has been pushed to the EGit project for cloning git repositories. If you copy a git URL and go to the clone dialog (providing that you're using the development or nightly builds) then it will pre-fill the dialog out with that value.

Here's an example of detecting a URL inside a block of text on the clipboard:

package com.example.clippy;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.widgets.Display;

public class ClipboardDemo {
  /**
   * Must be run in the main SWT thread
   */
  public static String getClipboardText() {
    Clipboard clippy = null;
    String text = null;
    try {
      clippy = new Clipboard(Display.getDefault());
      text = (String) clippy
          .getContents(TextTransfer.getInstance());
    } finally {
      if (clippy != null)
        clippy.dispose();
    }
    return text;
  }
  public static String getURL(String text) {
    Pattern p = Pattern.compile(".*(https?://\\S+).*"
      ,Pattern.DOTALL);
    if (text != null) {
      Matcher m = p.matcher(text);
      if (m.matches())
        return m.group(1);
    }
    return null;
  }
  public static void main(String[] args) {
    System.out.println("URL detected was: " + 
      getURL(getClipboardText()));
  }
}