Came to think of it, I did write a quick hack to get similar functionality on other platforms some time ago. It doesn't look anything like the Apple search widget (nor the Windows search widget on Windows). But it could be used as a starting point. Feel free to include the code in Mac Widgets.
I have a ComponentFactory with a static get(), that returns the factory for the current environment, then I use the factory to create or decorate different components.
It's not LAF-change aware, nor is it safe if you don't use the system LAF. But I guess at least the last issue could be fixed. And changing LAF in a running application isn't really that useful IMHO...
abstract JTextField decorateSearchTextField(JTextField pTextField, ActionListener pResetAction);
@Override
JTextField decorateSearchTextField(final JTextField pTextField, final ActionListener pResetAction) {
Icon icon = new MetalIconFactory.PaletteCloseIcon();
final JButton reset = new JButton(new AbstractAction("", icon) {
public void actionPerformed(ActionEvent e) {
pTextField.setText("");
if (pResetAction != null) {
pResetAction.actionPerformed(e);
}
}
});
reset.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
pTextField.requestFocusInWindow();
}
});
reset.setCursor(Cursor.getDefaultCursor());
reset.setFocusable(false);
reset.setOpaque(true);
reset.setBackground(Color.LIGHT_GRAY);
reset.setBorderPainted(false);
reset.setContentAreaFilled(false);
reset.setBorder(EMPTY_BORDER);
reset.setHorizontalAlignment(JButton.LEFT);
pTextField.add(reset);
// Makes sure the reset button is only visible when there's text in the field
final DocumentAdapter resetVisibleHandler = new DocumentAdapter() {
protected void editHappened(DocumentEvent pDocumentEvent) {
reset.setVisible(pDocumentEvent.getDocument().getLength() > 0);
}
};
reset.setVisible(pTextField.getDocument().getLength() > 0);
pTextField.getDocument().addDocumentListener(resetVisibleHandler);
pTextField.addPropertyChangeListener("document", new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent pEvent) {
Document oldDoc = (Document) pEvent.getOldValue();
if (oldDoc != null) {
oldDoc.removeDocumentListener(resetVisibleHandler);
}
Document newDoc = (Document) pEvent.getNewValue();
if (newDoc != null) {
newDoc.addDocumentListener(resetVisibleHandler);
}
}
});
// Make sure icon does not overlap text
Insets margin = (Insets) pTextField.getMargin().clone();
margin.right += reset.getPreferredSize().width;
pTextField.setMargin(margin);
// Layout reset when text field changes size
pTextField.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent pEvent) {
JTextField textField = (JTextField) pEvent.getComponent();
Dimension size = textField.getSize();
Insets insets = (Insets) textField.getInsets().clone();
int w = reset.getPreferredSize().width;
insets.right -= w;
reset.setBounds(new Rectangle(size.width - w - insets.right, 0, w + insets.right, size.height));
}
});
return pTextField;
}