Right aligned cursor not showing through in a JTex

2019-08-03 21:58发布

问题:

I have a JTextPane sandwiched between 2 JLabels - is there a known reason why the cursor shows through if i have it on the left most part of the textpane but not on the right?

Here is the code to better demonstrate what i mean:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;

public class Testing {
    /**
     * @param args
     */
    public static void main(String[] args) {
        JFrame f = new JFrame("Test");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JPanel cp = new JPanel(new BorderLayout());
        f.setContentPane(cp);

        final SubPanel subPanel = new SubPanel();

        cp.add(subPanel, BorderLayout.CENTER);

        JPanel buttonPanel = new JPanel(new FlowLayout());
        buttonPanel.add(new JLabel("Align"));
        final JComboBox alignCB = new JComboBox(new String[] {"left", "centre", "right"});
        alignCB.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                subPanel.align((String) alignCB.getSelectedItem());
            }
        });
        buttonPanel.add(alignCB);
        buttonPanel.add(new JLabel("Justify"));
        final JComboBox justifyCB = new JComboBox(new String[] {"left", "centre", "right"});
        justifyCB.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                subPanel.justify((String) justifyCB.getSelectedItem());
            }
        });
        buttonPanel.add(justifyCB);

        JTextField tf = new JTextField("TF");
        tf.setBorder(null);
        buttonPanel.add(tf);

        cp.add(buttonPanel, BorderLayout.NORTH);

        f.pack();
        f.setSize(new Dimension(300,300));
        f.setLocation(300, 300);
        f.setVisible(true);

    }

    public static class SubPanel extends JPanel {
        JPanel innerPanel = new JPanel(new GridBagLayout());
        TextPaneWidget[] tps = new TextPaneWidget[3];

        public SubPanel() {
            //                setBorder(BorderFactory.createLineBorder(Color.RED));
            setBorder(null);
            //                innerPanel.setBorder(BorderFactory.createLineBorder(Color.BLUE));
            innerPanel.setBorder(null);

            for (int i = 0; i < tps.length; i++) {
                tps[i] = new TextPaneWidget();
            }

            int gridy = 0;
            for (TextPaneWidget tp : tps) {
                innerPanel.add(tp, new GridBagConstraints(0,gridy, 1,1, 0.0, 0.0, GridBagConstraints.NORTHEAST, GridBagConstraints.HORIZONTAL, new Insets(0,0,0,0), 0, 0));
                gridy++;
            }

            setLayout(new GridBagLayout());

            add(innerPanel, new GridBagConstraints(0,0, 1,1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0,0,0,0), 0, 0));
        }

        public void align(String alignment) {
            System.out.println("Align: " + alignment);

            int anchor = GridBagConstraints.CENTER;
            if ("right".equals(alignment)) {
                anchor = GridBagConstraints.EAST;
            } else if ("left".equals(alignment)) {
                anchor = GridBagConstraints.WEST;
            }

            GridBagLayout gbl = (GridBagLayout) getLayout();
            gbl.setConstraints(innerPanel, new GridBagConstraints(0,0, 1,1, 1.0, 0.0, anchor, GridBagConstraints.NONE, new Insets(0,0,0,0), 0, 0));

            revalidate();
            repaint();
        }

        public void justify(String justification) {
            System.out.println("Justify: " + justification);

            for (TextPaneWidget tp : tps) {
                tp.justify(justification);
            }
        }
    }

    public static class MyDocument extends DefaultStyledDocument {
        @Override
        public void insertString(int offset, String text, AttributeSet attributeSet) throws BadLocationException {
            SimpleAttributeSet attrs = new SimpleAttributeSet(attributeSet);
            StyleConstants.setForeground(attrs, Color.WHITE);
            StyleConstants.setBackground(attrs, Color.RED);
            super.insertString(offset, text, attrs);
        }
    }

    public static class TextPaneWidget extends JPanel {
        JTextPane tp = new JTextPane();
        JLabel lSpace = new JLabel("   ");
        JLabel rSpace = new JLabel("   ");

        public TextPaneWidget() {
            //                setBorder(BorderFactory.createLineBorder(Color.GREEN));
            setBorder(null);

            Font font = new Font("monospaced", Font.BOLD, 13);
            tp.setBorder(null);
            tp.setDocument(new MyDocument());
            tp.setFont(font);
            tp.setText("Text");
            tp.setOpaque(true);

            setLayout(new GridBagLayout());

            lSpace.setBackground(Color.MAGENTA);
            lSpace.setOpaque(true);
            lSpace.setBorder(null);
            add(lSpace, new GridBagConstraints(0,0, 1,1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.VERTICAL, new Insets(0,0,0,0), 0, 0));

            add(tp, new GridBagConstraints(1,0, 1,1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0,0,0,0), 0, 0));

            rSpace.setBackground(Color.MAGENTA);
            rSpace.setOpaque(true);
            rSpace.setBorder(null);
            add(rSpace, new GridBagConstraints(2,0, 1,1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.VERTICAL, new Insets(0,0,0,0), 0, 0));

            addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    tp.setCaretPosition((e.getX() < tp.getX()) ? 0 : tp.getText().length());
                    tp.requestFocusInWindow();
                }
            });

            lSpace.addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    tp.setCaretPosition(0);
                    tp.requestFocusInWindow();
                }
            });

            rSpace.addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    tp.setCaretPosition(tp.getText().length());
                    tp.requestFocusInWindow();
                }
            });

        }

        public void justify(String justification) {
            double leftWeight = 0.5;
            double rightWeight = 0.5;
            if ("right".equals(justification)) {
                leftWeight = 1.0;
                rightWeight = 0.0;
            } else if ("left".equals(justification)) {
                leftWeight = 0.0;
                rightWeight = 1.0;
            }

            GridBagLayout gbl = (GridBagLayout) getLayout();
            gbl.setConstraints(lSpace, new GridBagConstraints(0,0, 1,1, leftWeight, 0.0, GridBagConstraints.EAST, GridBagConstraints.VERTICAL, new Insets(0,0,0,0), 0, 0));
            gbl.setConstraints(rSpace, new GridBagConstraints(2,0, 1,1, rightWeight, 0.0, GridBagConstraints.WEST, GridBagConstraints.VERTICAL, new Insets(0,0,0,0), 0, 0));
            revalidate();
            repaint();
        }
    }
}

回答1:

I think I understand what's happening. Thanks for providing the code.

When you define a JTextPane, the default border is a 3 pixel empty border. This empty border provides a place for the text pane cursor to show when the cursor is at the rightmost position. The cursor is at the rightmost position to allow characters to be typed at the end of a line of characters.

When you define a null border, which is the same as a 0 pixel empty border, there's no place for the text pane cursor to be drawn when it's in the rightmost position.

In order to see the cursor in the rightmost position, you have to define an empty border with at least 1 right pixel. If you want it to be more visually appealing, include 1 left pixel.

tp.setBorder(BorderFactory.createEmptyBorder(0,1,0,1));

You have to define an empty border, because an empty border is the only Border that does not paint. A Border that paints will paint over the text pane cursor in the rightmost position.

So, you are required to use an empty border with at least one right pixel for a JTextPane to display the rightmost cursor.

Edited to add:

When you're using the GridBagLayout, a method like this one reduces the number of parameters that you have to deal with when you add a component.

protected void addComponent(Container container, Component component,
        int gridx, int gridy, int gridwidth, int gridheight, 
        Insets insets, int anchor, int fill) {
    GridBagConstraints gbc = new GridBagConstraints(gridx, gridy,
            gridwidth, gridheight, 1.0D, 1.0D, anchor, fill, insets, 0, 0);
    container.add(component, gbc);
}


回答2:

Setting the background to the following fixes this...

tp.setBackground(Color.RED);
tp.setOpaque(true);