How to override DefaultCaret#setBlinkRate()
Asked Answered
L

3

3
  • 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



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();
            }
        });
    }
}
Lownecked answered 14/8, 2013 at 22:4 Comment(5)
This doesn't make sense. A focus caret is only ever displayed one the text component that has current keyboard focus. It doesn't, IMHO, make sense to display a caret on fields that don't have focus, as you would end up with multiple carets on the screen and confuse the user as to which one they are actually typing into...Vasculum
I copied your SSCCE into my IDE. Can you explain what behavior you want to see different when running this code?Silicate
I see what happens. This only happens when using TAB right?Silicate
aaach I see to disable, remove panel.add(jTextField0);Lownecked
doesn't matter if key or mouse eventLownecked
M
2

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.

Mantis answered 15/8, 2013 at 0:59 Comment(0)
S
2

I have found the solution. Override the focusGained method of your HighlightCaret and set the blink rate there as well.

    @Override
    public void focusGained(FocusEvent e)
    {
        isFocused = true;
        super.setBlinkRate(500);
        super.focusGained(e);
    }

This did the trick for me in OS X.

Silicate answered 14/8, 2013 at 22:47 Comment(1)
Confirmed on Java 6, AquaLookAndFeel.Crassulaceous
M
2

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.

Mantis answered 15/8, 2013 at 0:59 Comment(0)
A
1

But your previous code works OK if setBlinkRate(500); is placed in HighlightCaret class constructor:

class HighlightCaret extends DefaultCaret {

    private static final Highlighter.HighlightPainter unfocusedPainter = new DefaultHighlighter.DefaultHighlightPainter(Color.RED);
    private static final Highlighter.HighlightPainter focusedPainter = new DefaultHighlighter.DefaultHighlightPainter(Color.YELLOW);
    private static final long serialVersionUID = 1L;
    private boolean isFocused;

    HighlightCaret(){
     setBlinkRate(500);//Placed here
    }

    @Override
    protected Highlighter.HighlightPainter getSelectionPainter() {
       // setBlinkRate(500); // otherwise is disabled, stopped
        return isFocused ? focusedPainter/*super.getSelectionPainter()*/ : unfocusedPainter;
    }

    @Override
    public void setSelectionVisible(boolean hasFocus) {
        if (hasFocus != isFocused) {
            isFocused = hasFocus;
            super.setSelectionVisible(false);
            super.setSelectionVisible(true);
        }
    }
}

Tested in Java7 WinXP. Have you tried it?

Aubine answered 15/8, 2013 at 7:24 Comment(2)
+1 agree, as I described, I want more, I want control over this process, now is answer by @Mantis quite L&Fs insensitive, and his workaround is quite safe, maybe (StanislavL) will be comment this post if answers here missing something important,Lownecked
The blinking caret is controlled by the setVisible() and managable in focusGained/focusLost, this info I would needed to know about ...Lownecked

© 2022 - 2024 — McMap. All rights reserved.