5

The main problem is that I want to draw a graph with a few labels. For drawing I use the following class:

public class Canvas extends JPanel {

    // Lot of other methods

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

        // Other code

        g2.setColor(Style.LIGHT_GREY);
        g2.fillOval(node.getPosition().x, node.getPosition().y, Style.SHAPE_SIZE, Style.SHAPE_SIZE);

        g2.setColor(Style.DARK_GREY);
        g2.setStroke(new BasicStroke(1.5f));
        g2.drawOval(node.getPosition().x, node.getPosition().y, Style.SHAPE_SIZE, Style.SHAPE_SIZE);

        //Draw token
        g2.setColor(Style.DARK_GREY);
        g2.setFont(new Font("Monospaced", Font.PLAIN, 12));
        String token = ((Place)node).getTokens().toString();
        g2.drawString(token, 
                (int) (node.getNodeCenterPosition().x - 3.5*token.length()), node.getNodeCenterPosition().y + CHAR_HEIGHT);

        //Draw label
        g2.drawString("P1", node.getPosition().x-8, node.getPosition().y-8);

        // More code
    }

}

Nevermind the coordinates, they are all set and correct. The weird behaviour starts with the last drawString here:

//Draw label
g2.drawString("P1", node.getPosition().x-8, node.getPosition().y-8);

All of the previous code is rendered once paintComponent is called but this one. Though if I trigger paintComponent again (from outside by calling canvas.repaint()) it will show up.

Here is the state of the canvas after the first repaint:

No label

And here is the state of the canvas after a second repaint:

With label

Additional note: If I put the label at the right side of the disc, it is rendered normally at the first repaint:

g2.drawString("P1", node.getPosition().x+50, node.getPosition().y+30);

Label on the right side

The position of the code makes no difference in the behavior. If I comment out all of the code but the label or I put the label drawing at the top of the function it makes no difference.

So, how could I make this label rendered at the first time I call repaint?

EDIT: Here is the code which renders the window:

public class MainWindow extends JFrame {

    private MainWindow() {
        super();

        setVisible(true);
        setResizable(false);
        setSize(1280, 750);

        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setLayout(new BorderLayout());

        this.setJMenuBar(MainMenu.getInstance());       
        add(Canvas.getInstance(), BorderLayout.CENTER);     
        add(ToolBar.getInstance(), BorderLayout.PAGE_START);        
        add(LeftPanel.getInstance(), BorderLayout.LINE_START);
    }

}

And the code in the Canvas' contructor:

private Canvas() {
    super();
    this.setFocusable(true);
    this.setBackground(Style.BACKGROUND);

    this.popupNewNode = new JPopupMenu();
    this.popupNewNode.setFocusable(false);

    this.newPlace = new JMenuItem("New Place");
    this.newTransition = new JMenuItem("New Transition");

    this.popupNewNode.add(newPlace);
    this.popupNewNode.add(newTransition);
}

The canvas and a few other components are singletons but I suppose this shouldn't be a problem as only one window exists with one canvas on it.

mKorbel
  • 109,525
  • 20
  • 134
  • 319
András
  • 693
  • 5
  • 17
  • 1
    Show how you place `Canvas` on parent container. – alex2410 Apr 28 '15 at 10:16
  • 1
    Use `FontMetrics` to determine the length and height of text – MadProgrammer Apr 28 '15 at 10:19
  • 1
    Do you have something that would make the [_Graphics Context misaligned on first paint_](http://stackoverflow.com/q/23507422/261156)? – Catalina Island Apr 28 '15 at 10:21
  • override getPreferredSize for JPanel and to use its getHeight/Weight instead of your wrong attempt for pixels perfections, then works correctly on resize too – mKorbel Apr 28 '15 at 10:27
  • JFrame.pack(instead of setSize(1280, 750); == there you lost pixels perfection - Jframes Borders) in the case that JPanel has getPreferredSize, – mKorbel Apr 28 '15 at 10:29
  • 2
    setVisible(true); must be last code line == your major issue – mKorbel Apr 28 '15 at 10:30
  • JFrame has setLayout(new BorderLayout()); as default LayoutManager in API – mKorbel Apr 28 '15 at 10:31
  • 1
    For better help sooner, post an [MCVE](http://stackoverflow.com/help/mcve) (Minimal Complete Verifiable Example) or [SSCCE](http://www.sscce.org/) (Short, Self Contained, Correct Example). – Andrew Thompson Apr 28 '15 at 11:04
  • I were creating an MCVE of the application when I noticed that after creating the node the repaint wasn't called. After putting the repaint at the end of the method it worked. Thank you all for the answers, though I wonder why the node itself were rendered without the label. – András Apr 28 '15 at 12:37

1 Answers1

1

It's difficult to know without a fully runnable example, but, you shouldn't be making assumptions about the size of text and instead should be using the FontMetrics

FontMetrics fm = g.getFontMetrics();
int width = fm.stringWidth("P1");
int height = fm.getHeight();
g.drawString("P1", node.getPosition().x - width, (node.getPosition().y - height) + fm.getAscent());

Take a closer look at Working with Text APIs for more details.

Another issue might be, if you have previously translated the Graphics context without first making a copy of it or reversing the translation

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366