How to add letter spacing (TextAttribute.TRACKING) on JPasswordField when there is an echo char?

124 Views Asked by At

I have answered How to add letter spacing in Swing. However, this way does not seem to have impact for JPasswordField(s):

enter image description here

When I passwordField.setEchoChar((char)0), it works:

enter image description here

Is there a way to apply the spacing when the dots () are shown?

public class PasswordFieldLetterSpacing {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JPasswordField field = new JPasswordField(15);
            Map<TextAttribute, Object> attributes = new HashMap<TextAttribute, Object>();
            attributes.put(TextAttribute.TRACKING, 0.5);
            field.setFont(field.getFont().deriveFont(attributes));

            JLabel showPassLabel = new JLabel("move mouse here");
            showPassLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
            showPassLabel.addMouseListener(new MouseAdapter() {
                @Override
                public void mouseEntered(MouseEvent ev) {
                    field.setEchoChar((char) 0);
                }

                @Override
                public void mouseExited(MouseEvent ev) {
                    field.setEchoChar((char) UIManager.get("PasswordField.echoChar"));
                }
            });

            JFrame frame = new JFrame("");
            frame.setLayout(new FlowLayout());

            frame.add(field);
            frame.add(showPassLabel);

            frame.pack();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setLocationByPlatform(true);
            frame.setVisible(true);
        });
    }

}

I don't know if look and feel plays a role on this. It seems to happen in both Windows Look and Feel and in the Java's default one.

1

There are 1 best solutions below

1
camickr On

As best as I can tell the non-echo text is painted in the PlainView class using the Utilities.drawTabbedText(...) method which would paint the text as a string so the text attributes work as expected.

However, for some reason it appears that the echo character is painted one at a time using the width of the character for spacinging, instead of painting the entire string at once:

protected float drawEchoCharacter(Graphics2D g, float x, float y, char c) {
    return drawEchoCharacterImpl(g, x, y, c, true);
}

private float drawEchoCharacterImpl(Graphics g, float x, float y,
                                    char c, boolean useFPAPI) {
    ONE[0] = c;
    SwingUtilities2.drawChars(Utilities.getJComponent(this),
                              g, ONE, 0, 1, x, y);
    if (useFPAPI) {
        return x + g.getFontMetrics().charWidth(c);
    } else {
        FontRenderContext frc = g.getFontMetrics().getFontRenderContext();
        return x + (float) g.getFont().getStringBounds(ONE, 0, 1, frc).getWidth();
    }
}

So I would guess you need to write a custom PasswordView class. (I have no idea how to do this).

Check out: the PasswordView for the full source.