SWT: Flat Icon Buttons Next to a StyledText
How to place icon buttons beside a text input so the whole area reads as one flat white field — on both macOS and Windows — without CSS.
Phase 1 Target
┌─────────────────────────────────────────────┐ ← SWT.BORDER on outer wrapper
│ StyledText (native white) [🎤] [▶] │ ← flat icon Buttons, no chrome
└─────────────────────────────────────────────┘2
3
Key Lesson — What NOT to do
A first intuition is to force a white background on the wrapper so everything inherits it:
wrapper.setBackground(white); // ❌ don't
wrapper.setBackgroundMode(SWT.INHERIT_FORCE); // ❌ don't2
On macOS this overrides StyledText's native rendering and produces a gray field instead of white. StyledText only paints correctly when no background is set.
Rule: never call setBackground or setBackgroundMode on any composite that contains a StyledText, and never on the StyledText itself.
The Right Pattern
Leave backgrounds alone.
StyledTextpicks up native OS white. Sibling composites inherit the same native white from their parent. No calls needed.One
SWT.BORDER— on the outermost wrapper composite only. None on inner composites, none on the text widget.Flat icon buttons — a regular
Buttonwould draw the OS push-button frame. To suppress it, useSWT.PUSH | SWT.NATIVEplus aPaintListenerthat takes over rendering:javaButton btn = new Button(parent, SWT.PUSH | SWT.NATIVE); btn.setImage(icon); btn.addPaintListener(e -> { Rectangle b = btn.getBounds(); e.gc.fillRectangle(0, 0, b.width, b.height); // fills with btn.getBackground() Image img = btn.getImage(); if (img != null) { Rectangle ib = img.getBounds(); e.gc.drawImage(img, (b.width - ib.width) / 2, (b.height - ib.height) / 2); } });1
2
3
4
5
6
7
8
9
10
11
12
13
14Because
fillRectangleuses the button's inherited background (native white), and the OS frame is suppressed, only the icon is visible.Hover highlight — add a
MouseTrackListenerthat switches the background to a light gray and restores it on exit. The PaintListener repaints automatically:javabtn.addMouseTrackListener(new MouseTrackAdapter() { Color original; Color hoverBg = new Color(display, 232, 232, 232); public void mouseEnter(MouseEvent e) { original = btn.getBackground(); btn.setBackground(hoverBg); } public void mouseExit(MouseEvent e) { btn.setBackground(original); } });1
2
3
4
5
6
7
8
9
10
11Dynamic state (e.g. recording red) — call
btn.setBackground(red)thenbtn.redraw(). The PaintListener fills with red. Reset tonullto restore the inherited native white.Kill GridLayout seams. A
horizontalSpacingorverticalSpacingof even 2 px between the StyledText and the button column shows up as a thin gray line on macOS — the parent composite's native gray bleeds through the gap. Set both to0between the text widget and the button column, and between stacked buttons in the right column. UsemarginWidth/marginHeighton the outer row layout for breathing room instead.
Reusable helper
SwtUtil.createIconButton(parent, icon, tooltip) wraps the pattern above. See org.sterl.llmpeon.parts.shared.SwtUtil.
Why this mirrors Copilot
This is the same technique used by the GitHub Copilot Eclipse plugin (copilot_src/.../utils/UiUtils.java createIconButton). Their tree contains zero setBackground or setBackgroundMode calls on the composites around their StyledText — the native white shows through on both macOS and Windows.
