Case Study 2: UI Design Principles for Desktop Applications
Overview
Good code with a bad interface is bad software. This case study steps back from implementation to examine the design principles that separate professional desktop applications from amateur ones. We apply these principles to PennyWise and evaluate the design decisions made in this chapter and the previous one.
Principle 1: Consistency
Users build mental models of how your application works. Every inconsistency forces them to rebuild that model. Consistency means:
- Labels go in the same place relative to their fields — either above or to the left, but not a mix of both.
- Button placement follows platform conventions. On Windows, OK is to the left of Cancel. On macOS, Cancel is to the left of OK (and the default button is rightmost). On Linux, it depends on the toolkit.
- Keyboard shortcuts follow standards. Ctrl+S saves. Ctrl+Z undoes. Ctrl+C copies. Do not reassign these.
- Spacing is uniform. If fields are 8 pixels apart, all fields should be 8 pixels apart. If labels are bold, all labels should be bold.
Application to PennyWise
In our PennyWise forms, we use labels above fields and a consistent 8-pixel BorderSpacing. The Add and Cancel buttons follow Windows conventions (OK on the left). The category dropdown uses the same set of options on every form. This consistency means the user learns the interface once.
Principle 2: Feedback
Every user action should produce visible feedback:
- Button clicks should produce an immediate response. If a save takes time, show a progress indicator or change the cursor to an hourglass.
- Validation errors should be visible and specific. "Error" is useless. "Amount must be a positive number" is helpful.
- Successful operations should confirm success. After saving, show "Saved successfully" in the status bar or briefly flash the Save button.
- Disabled controls should explain why they are disabled (via tooltips or a status bar message).
Example: Feedback in the Expense Detail Form
procedure TfrmExpenseDetail.edtAmountExit(Sender: TObject);
var
Amount: Double;
begin
if edtAmount.Text = '' then
begin
lblAmountHint.Caption := ''; { no feedback needed for empty field }
edtAmount.Color := clDefault;
Exit;
end;
if not TryStrToFloat(edtAmount.Text, Amount) then
begin
edtAmount.Color := $CCCCFF; { light red }
lblAmountHint.Caption := 'Enter a valid number';
lblAmountHint.Font.Color := clRed;
end
else if Amount <= 0 then
begin
edtAmount.Color := $CCFFFF; { light yellow }
lblAmountHint.Caption := 'Amount must be positive';
lblAmountHint.Font.Color := clMaroon;
end
else
begin
edtAmount.Color := clDefault;
lblAmountHint.Caption := Format('$%.2f', [Amount]);
lblAmountHint.Font.Color := clGreen;
end;
end;
The user sees immediate, specific, color-coded feedback as they interact with each field.
Principle 3: Minimize User Effort
Every click, keystroke, and decision costs the user time and cognitive energy. Reduce them:
- Smart defaults. The date field defaults to today. The most common category is pre-selected. The cursor starts in the first field.
- Auto-completion. If the user has entered "Food & Dining" before, offer it as a suggestion.
- Keyboard efficiency. Tab moves between fields. Enter submits. Escape cancels. The user should be able to complete the entire form without touching the mouse.
- Remember state. If the user was looking at March expenses, they should return to March expenses when they reopen the application.
Application to PennyWise
Our expense entry form keeps the same category selected between entries (for rapid entry of similar expenses), defaults the date to today, and places focus in the description field automatically. The OK button is the Default button (Enter confirms). The Cancel button has Cancel := True (Escape dismisses).
Principle 4: Forgiveness
Users make mistakes. Good software makes mistakes reversible:
- Confirmation dialogs before destructive actions (delete, clear all).
- Undo support where possible.
- Edit, do not re-enter. If the user makes a mistake, they should be able to fix the one field, not retype everything.
- Non-destructive close. If the form has unsaved changes and the user tries to close it, ask "Save changes?" with Yes/No/Cancel options.
procedure TfrmMain.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
if FHasUnsavedChanges then
begin
case MessageDlg('Unsaved Changes',
'You have unsaved changes. Save before closing?',
mtConfirmation, [mbYes, mbNo, mbCancel], 0) of
mrYes:
begin
SaveData;
CanClose := True;
end;
mrNo:
CanClose := True;
mrCancel:
CanClose := False;
end;
end;
end;
Principle 5: Accessibility
Not every user interacts with your application the same way:
- Keyboard navigation must work completely. Every control must be reachable by Tab. Every action must have a keyboard shortcut or accelerator.
- Font sizes should be adjustable or at least respect the system's DPI settings.
- Colors should not be the only indicator. A color-blind user cannot distinguish red from green. Pair color with text, icons, or shapes.
- Labels should be associated with their controls via
FocusControlso screen readers can announce what each field is for.
{ Accessible label-field pairing: }
lblName.Caption := '&Name:'; { Alt+N accelerator }
lblName.FocusControl := edtName; { screen reader association }
Principle 6: Visual Hierarchy
The eye should be drawn to the most important information first:
- Size communicates importance. The total amount is in a larger font than individual expense entries.
- Position communicates flow. The most important fields are at the top. The submit button is at the bottom (after all fields).
- Grouping communicates relationships. Related fields are inside a group box. Separate concerns are in separate panels.
- Whitespace communicates separation. Do not cram controls together. Use BorderSpacing and margins.
PennyWise Visual Hierarchy
[ Title: PennyWise Personal Finance Manager ] ← largest, boldest
[ Input Panel: Date | Description | Category | Amount ] ← functional group
[ ─────────────────────────────────────────── ]
[ Expense Grid ] ← dominant, fills most space
[ ─────────────────────────────────────────── ]
[ Total: $1,234.56 ] ← bold, prominent, color-coded
[ [Add Expense] [Clear All] ] ← action buttons at bottom
Anti-Patterns to Avoid
The Wall of Fields
A form with 30 ungrouped fields is overwhelming. Break it into logical groups using group boxes or tabs (TPageControl). Each group should have 5–7 fields at most.
The Invisible State
If the application is in a different mode (edit mode vs. view mode), make it obvious. Change the form's caption, enable/disable buttons, or show a prominent indicator.
The Cryptic Error
"Error 0x8004010F" helps no one. Translate technical errors into human language: "Could not save the file. The disk may be full or the file may be in use by another program."
The Surprise Dialog
Do not pop up dialogs for routine events. Use inline feedback (labels, color changes, status bar messages) for warnings and minor issues. Reserve modal dialogs for critical decisions (delete confirmation, unsaved changes).
A Design Checklist for PennyWise
Use this checklist before releasing any form:
- [ ] Tab order matches visual order
- [ ] Every field has a label with a FocusControl association
- [ ] Enter confirms the dialog; Escape cancels it
- [ ] Invalid input produces specific, visible feedback
- [ ] Destructive actions require confirmation
- [ ] The form resizes gracefully (or has a fixed, appropriate size)
- [ ] Colors are not the only indicator of state (pair with text/icons)
- [ ] Font sizes are readable at 100% and 150% DPI
- [ ] No control overlaps another at any reasonable window size
Lessons Learned
- UI design is a discipline separate from programming. Learning the controls is necessary but not sufficient — you must also learn the principles that govern how controls should be arranged and behave.
- Consistency, feedback, minimal effort, forgiveness, accessibility, and visual hierarchy are the six principles that transform a functional form into a pleasant one.
- Platform conventions matter. A Lazarus application on macOS should feel like a macOS application, not a Windows application that happens to run on macOS.
- Test with real users if possible. What seems obvious to the developer is often confusing to the user.
- Good design is invisible. The user should never think about the interface — they should think only about their task.