Macifying SWT


Macifying SWT

Elliotte Rusty Harold

Wednesday, August 31, 2005

elharo@metalab.unc.edu

http://www.cafeaulait.org/


Users Punish Applications with Poor Interfaces

I remember an early database, MacLion, which was a bad port of a DOS application, right down to the 24-by-80 monospaced scrolling text window. Boy, it was ugly. It eventually lost in the marketplace. Apple also spent a lot of time working with major DOS application vendors to get them to “get it” about the graphic user interface. Lotus received a lot of personal attention from Apple for their Jazz product, and later 1-2-3 for Mac.

But the folklore that has come down through the years is that Apple defended the purity of the interface by punishing the developers who built applications that broke the rules. And that’s just not true. The rules were vague; they were revised several times over the first five years; we broke the rules ourselves (starting early, with MacPaint); and to tell you the truth, we were so desperate for software that we even put that ugly, DOSish MacLion on our poster of the first 100 apps.

The truth is that the punishment for inconsistency came from the Mac community itself. Magazine reviewers and pundits were the first to appreciate the consistency and simplicity of Mac applications, especially in contrast with the growing mess in the DOS world. Influential users and purchasers followed suit. Programs with inconsistent interfaces did suffer; but they suffered at the hands of the marketplace, not of a dictatorial Apple.

I Was a Teenage Thought Policeman

SWT programs that run on Mac OS X


Prerequisites for SWT development on Mac OS X


Setting up an SWT Project on Mac OS X


Learning the Mac Interface

The Macintosh computer sports the most jumbled-up, inconsistent, confusing interface ever made, with the exception of those of all the other computers.
Tog on Interface

SWT Address Book


What SWT Gets Right


What AddressBook Gets Wrong


Determining whether you're running on Mac OS X

final static boolean thisIsAMac = System.getProperty("mrj.version") != null;


Menus Make the Difference

Users believe in the menu bar. The menu bar tells them where they are: in the safe, protective environment of the Macintosh, where consistency reigns.

The menu bar is the most constant object in the Macintosh. When it disappears, non-computer-oriented users assume they, the users, have moved, navigated, to a different planet, a world where all the rules may have changed. They are no longer within the familiar Macintosh world. And their only known way back, Quit on the menu bar, has been stripped away.

I have seen, during user testing, the very real fear etched on the face of users when the menu bar disappears. I have watched them literally panic as they realize they are trapped in a strange world.

Tog on Interface

Menu Layouts


Application Menu


Windows vs. Mac


Removing Exit Menu Item from the File Menu

 //File -> Exit.
    if (!thisIsAMac) {
        new MenuItem(menu, SWT.SEPARATOR);
  
        //File -> Exit.
      subItem = new MenuItem(menu, SWT.NULL);
      subItem.setText(resAddressBook.getString("Exit"));
      subItem.addSelectionListener(new SelectionAdapter() {
        public void widgetSelected(SelectionEvent e) {
          shell.close();
        }
      });
    }

File/New


Save and Save As...


While we're at it...


Edit Menu


Repaired Edit Menu

        //Edit -> Undo
        MenuItem subItem = new MenuItem(menu, SWT.NULL);
        subItem.setText(resAddressBook.getString("Undo"));
        subItem.setAccelerator(SWT.MOD1 + 'Z');

        new MenuItem(menu, SWT.SEPARATOR);        
        
        //Edit -> Cut
        subItem = new MenuItem(menu, SWT.NULL);
        subItem.setText(resAddressBook.getString("Cut"));
        subItem.setAccelerator(SWT.MOD1 + 'X');
        
        //Edit -> Copy
        subItem = new MenuItem(menu, SWT.NULL);
        subItem.setText(resAddressBook.getString("Copy"));
        subItem.setAccelerator(SWT.MOD1 + 'C');
        subItem.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                TableItem[] items = table.getSelection();
                if (items.length == 0) return;
                copyBuffer = new String[table.getColumnCount()];
                for (int i = 0; i < copyBuffer.length; i++) {
                    copyBuffer[i] = items[0].getText(i);
                }
            }
        });
        
      //Edit -> Paste
      subItem = new MenuItem(menu, SWT.NULL);
      subItem.setText(resAddressBook.getString("Paste"));
      subItem.setAccelerator(SWT.MOD1 + 'V');
      subItem.addSelectionListener(new SelectionAdapter() {
        public void widgetSelected(SelectionEvent e) {
          if (copyBuffer == null) return;
          TableItem item = new TableItem(table, SWT.NONE);
          item.setText(copyBuffer);
          isModified = true;
        }
      });
      
      //Edit -> Delete
      subItem = new MenuItem(menu, SWT.NULL);
      subItem.setText(resAddressBook.getString("Delete"));
      subItem.addSelectionListener(new SelectionAdapter() {
        public void widgetSelected(SelectionEvent e) {
          TableItem[] items = table.getSelection();
          if (items.length == 0) return;
          items[0].dispose();
          isModified = true;    }
      });
      
      new MenuItem(menu, SWT.SEPARATOR);
      
        //Edit -> Edit
        subItem = new MenuItem(menu, SWT.CASCADE);
        subItem.setText(resAddressBook.getString("Edit"));
        subItem.setAccelerator(SWT.MOD1 + 'E');
        subItem.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                TableItem[] items = table.getSelection();
                if (items.length == 0) return;
                editEntry(items[0]);
            }
        });
    
      //Edit -> Sort(Cascade)
      subItem = new MenuItem(menu, SWT.CASCADE);
      subItem.setText(resAddressBook.getString("Sort"));
      Menu submenu = createSortMenu();
      subItem.setMenu(submenu);
      
      return item;

Search Menu


Help Menu


Multiple Windows


Keeping the menu bar on the screen when all windows are closed

  1. Set up a special menu bar to be used when no windows are open.

  2. Add this to your first shell.

  3. Set its size to zero.

  4. Set its location to negative coordinates.

  5. Display the shell.

public Shell openHiddenWindow(Display display) {
  createShell(display);
  createMenuBar();
  shell.setSize(0, 0);
  shell.setLocation(-100, -100);
  shell.open();
  return shell;
}

Be careful if you have any bring to Front/Send to Back/Move Forward/Move backward functionality


Window Size


Changing the Name of the Application Menu


Reserved command keys


The Mouse


General Issues


Encodings


Line ends


Special Files


Where to Store Preferences


FileManager


Building a Double Clickable Application


Inside the Package


The Dock


MRJAdapter


Disk Images


Apple Installer


QuickTime


One Big Warning


Bugs to Watch (and Vote For)

67384: Can't mix AWT into SWT73816

A Final Thought

FileMaker MacLion
Quark XPress FrameMaker
Excel Lotus 1-2-3
Word WordPerfect
Microsoft Word 5 Microsoft Word 6
iTunes WinAmp
Adobe Illustrator CorelDraw
MacroMedia Director-->ShockWave-->Flash VRML, Java

To Learn More


Index | Cafe con Leche

Copyright 2005 Elliotte Rusty Harold
elharo@metalab.unc.edu
Last Modified July 27, 2005