I have an problem with Caret, Caret didn't blink without focusGained(see code in Swing Action) to 2nd. JTextField and back to 1st. JTextField
how to override DefaultCaret#setBlinkRate() correctly
(without override Caret) by default is Caret at the end of Document and blinking on 1st. focusGained
tested on win7_32b, Java7.011/025 / Java6
tested with a few Standard L&Fs, custom too, every caused with the same issue
please see for more details my answer to question How to retain selected text in JTextField when focus lost? and possible workaround by @kleopatra
my SSCCE
import java.awt.*;
import java.awt.event.ActionEvent;
import javax.swing.*;
import javax.swing.text.DefaultCaret;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.Highlighter;
public class TestTextComponents {
private static final long serialVersionUID = 1L;
private Timer timer;
private JTextField jTextField0 = new JTextField();
private JTextField jTextField1 = new JTextField();
private JTextField jTextField2 = new JTextField();
private JFrame frame = new JFrame("Default Caret");
private JPanel panel = new JPanel();
public TestTextComponents() {
jTextField0.setText("jTextField0");
jTextField1.setText("jTextField1");
jTextField2.setText("jTextField2");
jTextField1.setCaret(new HighlightCaret());
jTextField2.setCaret(new HighlightCaret());
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
panel.add(new JLabel("Please skip between text fields and watch persistent selection: "));
panel.add(jTextField0);
panel.add(jTextField1);
panel.add(jTextField2);
frame.add(panel);
frame.setTitle("Text component persistent selection");
frame.pack();
frame.setVisible(true);
/*timer = new javax.swing.Timer(250, updateCol());
timer.setRepeats(false);
timer.start();*/
}
private Action updateCol() {
return new AbstractAction("Hello World") {
private static final long serialVersionUID = 1L;
@Override
public void actionPerformed(ActionEvent e) {
jTextField2.grabFocus();
jTextField2.requestFocusInWindow();
jTextField1.grabFocus();
jTextField1.requestFocusInWindow();
}
};
}
private class HighlightCaret extends DefaultCaret {
private static final long serialVersionUID = 1L;
private final Highlighter.HighlightPainter unfocusedPainter = new DefaultHighlighter.DefaultHighlightPainter(Color.RED);
private final Highlighter.HighlightPainter focusedPainter = new DefaultHighlighter.DefaultHighlightPainter(Color.ORANGE);
private boolean isFocused;
@Override
protected Highlighter.HighlightPainter getSelectionPainter() {
return isFocused ? focusedPainter /*super.getSelectionPainter()*/ : unfocusedPainter;
}
@Override
public void setSelectionVisible(boolean hasFocus) {
super.repaint();
super.setBlinkRate(500);
if (hasFocus != isFocused) {
isFocused = hasFocus;
super.setSelectionVisible(false);
super.setSelectionVisible(true);
}
}
}
public static void main(String args[]) {
/*try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
e.printStackTrace();
}
try {
for (UIManager.LookAndFeelInfo laf : UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(laf.getName())) {
UIManager.setLookAndFeel(laf.getClassName());
}
}
} catch (Exception e) {
e.printStackTrace();
}*/
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new TestTextComponents();
}
});
}
}
The blinking caret is controlled by the setVisible()
method of the DefaultCaret. The selected text is controlled by the setSelectionVisible()
method.
The focusGained/focusLost
methods of the DefaultCaret to control behaviour of the Caret using these two methods. By default on focusGained both properties are set to true. On focusLost they are set to false. Using your basic logic for different highlighters you can do something like:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;
public class SelectionCaret extends DefaultCaret
{
private final Highlighter.HighlightPainter unfocusedPainter = new DefaultHighlighter.DefaultHighlightPainter(Color.RED);
private final Highlighter.HighlightPainter focusedPainter = new DefaultHighlighter.DefaultHighlightPainter(Color.ORANGE);
public SelectionCaret()
{
setBlinkRate( UIManager.getInt("TextField.caretBlinkRate") );
}
@Override
protected Highlighter.HighlightPainter getSelectionPainter()
{
return getComponent().hasFocus() ? focusedPainter : unfocusedPainter;
}
@Override
public void focusGained(FocusEvent e)
{
setSelectionVisible(false);
super.focusGained(e);
}
@Override
public void focusLost(FocusEvent e)
{
super.focusLost(e);
setSelectionVisible(true);
}
private static void createAndShowUI()
{
JTextField textField1 = new JTextField("Text Field1 ");
JTextField textField2 = new JTextField("Text Field2 ");
JTextField textField3 = new JTextField("Non Editable ");
textField3.setEditable(false);
textField1.setCaret(new SelectionCaret());
textField2.setCaret(new SelectionCaret());
textField3.setCaret(new SelectionCaret());
textField1.select(5, 11);
textField2.select(5, 11);
textField3.select(5, 11);
((DefaultCaret)textField1.getCaret()).setSelectionVisible(true);
((DefaultCaret)textField2.getCaret()).setSelectionVisible(true);
((DefaultCaret)textField3.getCaret()).setSelectionVisible(true);
JPanel north = new JPanel();
north.add( new JTextField("Text Field0 ") );
north.add(textField1);
north.add(textField2);
north.add(textField3);
JFrame frame = new JFrame("Selection Caret");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( north );
frame.pack();
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
Now any text will be selected in both text fields, but only the text field with focus will blink.