0

I have a Swing based logger that comes packaged with my application, the logger should display line numbers next to each line of the log output..

The log output is displayed within a JTextArea, within a JScrollPane.

I am using setRowHeaderView() with a custom view to display the line numbers.

The issue that I am having, is that within the custom iew to display line numbers, the paintComponent() method seems to be called twice for each update, the second time having incorrect clipBounds.

Creating the TextArea:

public static void main(String[] args) {

    JFrame frame = new JFrame();

    JScrollPane scroll = new JScrollPane(new JTextArea(50, 150));
    scroll.setRowHeaderView(new MyHeaderView());

    frame.add(scroll);
    frame.pack();
    frame.setVisible(true);
}

Then

public class MyHeaderView extends JPanel {

    private final int pxPerLine;

    public MyHeaderView() {
        super();

        this.setPreferredSize(new Dimension(50, 10));

        FontMetrics fm = getFontMetrics(getFont());
        this.pxPerLine = (int) (fm.getHeight() / 2.0f) + 2;
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);

        Rectangle clip = g.getClipBounds();

        System.out.println(clip.toString());

        g.setColor(Color.BLACK);
        g.fillRect(clip.x, clip.y, clip.width, clip.height);

        int pxStart = clip.y;
        int stStart = pxStart / pxPerLine;
        int stCount = clip.height / pxPerLine;

        g.setColor(Color.YELLOW);
        for (int x = 0; x < stCount; x++) {
            String t = "" + (stStart + x);
            g.drawString(t, 5, pxStart + (pxPerLine * x));
        }
    }
}

What this result in, is something that looks like this, no matter where I scroll to:

enter image description here

However, if I use the debugger and step through the paintComponent method, I can see that each time the numbers should draw, the method is called twice. The first time through, g.getClipBounds() returns the correct values:

enter image description here

However, the second time, the clip bound are always back to the origin bounds, causing just the first few line numbers to be drawn.

Question

Why is the final time through the paintComponent method called with incorrect clip bounds, when the text area itself is in the correct position?

After adding a bunch of new lines to the above code and scrolling around the UI:

java.awt.Rectangle[x=0,y=497,width=50,height=16]
java.awt.Rectangle[x=0,y=0,width=50,height=16]
java.awt.Rectangle[x=0,y=481,width=50,height=16]
java.awt.Rectangle[x=0,y=0,width=50,height=16]
java.awt.Rectangle[x=0,y=465,width=50,height=16]
java.awt.Rectangle[x=0,y=0,width=50,height=16]
java.awt.Rectangle[x=0,y=446,width=50,height=16]
java.awt.Rectangle[x=0,y=0,width=50,height=16]

Note that the the update jump from valid to origin and back again, and the seconds update always seems to reset me back to the origin..

Why is the being updated twice?

Matt Clark
  • 27,671
  • 19
  • 68
  • 123
  • 1
    I just want to point out that you're *"Creating the TextArea"* doesn't make sense, as you're creating two `JTextArea`s, one which is added to a `JSrollPane` and ignored and one which is added to `frame` - Please, in the future, start with a [Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve) - I really shouldn't be having to tell you this – MadProgrammer Aug 13 '18 at 05:05
  • My "guess" is, it's an optimisation of the paint system, telling you that two parts of the view have changed and need repainting. – MadProgrammer Aug 13 '18 at 05:08
  • One of the "odd" things about the header view, is it never actually changes size, it's always the height of the `JScrollPane`, so when dealing with something like this, you need to make determinations about where to "offset" your code from the viewable areas starting point. You might consider having a look at this [example](https://stackoverflow.com/questions/21766588/make-jscrollpane-control-multiple-components/21767752#21767752) and [this example](https://tips4java.wordpress.com/2009/05/23/text-component-line-number/) as a starting point – MadProgrammer Aug 13 '18 at 05:12
  • The size _should_ never change, that is the size of the actual JPanel where alll the nubmers are drawn. what **should** change is the clip.y, which does about half the time. The other half of the time, it is seemingly `0`. – Matt Clark Aug 13 '18 at 05:13
  • I should always be starting my drawing a `0`, because that would be top left, I just need to calculate how many px are not displayed, and skip those line numbers, which I think I am doing. Again, stepping through line by line, every other time though the method, the UI looks as expeted. – Matt Clark Aug 13 '18 at 05:15
  • Doing this is a little more complicated - see the two examples I linked earlier – MadProgrammer Aug 13 '18 at 05:18
  • As I understand the painting process been used, the `JScrollPane` is making a number of updates based on the `JViewPort`'s "view", this is providing you information about how the `JScrollPane`'s viewable area is been changed, not your components, meaning that the offset (generally) is the "viewable" area - I randomised the colors on each paint request and it's quite informative – MadProgrammer Aug 13 '18 at 05:24
  • There you go @AndrewThompson, an **MCVE**. Complete will all the boiler plate – Matt Clark Aug 13 '18 at 05:27
  • Any idea what all of these updates are triggered by, or how I can filter the updates and just pick the view that I want? – Matt Clark Aug 13 '18 at 05:29
  • @MattClark Using `setPreferredSize` seems to the preliminary issue, when compared to the previous two work examples. I replaced it with the implementation of `getPreferredSize` from the first example and it "works", more or less – MadProgrammer Aug 13 '18 at 05:40
  • My impl was loosely based off of your second link, but wanted to do thins my own way here. That example also calls setPreferredSize. Printing the clip bounds to stdout confirmed what I saw in the debugger, don't know why I didn't do that first.. It just flips between valid / origin / valid / origin.. – Matt Clark Aug 13 '18 at 05:43
  • Take a look at [Text Component Line Number](https://tips4java.wordpress.com/2009/05/23/text-component-line-number/) for a working line number component. – camickr Aug 13 '18 at 13:45

0 Answers0