title: JTextFields on top of active drawing on JPanel, threading problems
body: <p>Has anyone ever tried to use Swing to construct a proper multi-buffered rendering environment <em>on top of which Swing user interface elements can be added</em>? </p>
  
  <p>In this case I have an animating red rectangle drawn onto a background. The background does not need to be updated every frame so I render it onto a BufferedImage and redraw only the portion necessary to clear the previous location of the rectangle. See the complete code below, this extends the example given by @trashgod in a previous thread, <a href="https://stackoverflow.com/questions/3119826/java-page-flipping-not-supported-on-mac-os">here</a>. </p>
  
  <p>So far so good; smooth animation, low cpu usage, no flickering.</p>
  
  <p>Then I add a JTextField to the Jpanel (by clicking on any position on the screen), and focus on it by clicking inside the text box. Clearing the previous location of the rectangle now fails on every cursor blink, see the image below.</p>
  
  <p>I am curious if anyone has an idea of why this might happen (Swing not being thread-safe? The image being painted asynchronously?) and in what direction to look for possible solutions.</p>
  
  <p>This is on Mac OS 10.5, Java 1.6</p>
  
  <p><a href="https://i.stack.imgur.com/trayv.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/trayv.png" alt="JPanel redraw fail"></a><br>
  <sub>(source: <a href="http://www.arttech.nl/javaredrawerror.png" rel="nofollow noreferrer">arttech.nl</a>)</sub>  </p>
  
  <pre><code>import java.awt.Color;
  import java.awt.Dimension;
  import java.awt.EventQueue;
  import java.awt.Graphics;
  import java.awt.GraphicsConfiguration;
  import java.awt.GraphicsDevice;
  import java.awt.GraphicsEnvironment;
  import java.awt.Insets;
  import java.awt.Rectangle;
  import java.awt.Transparency;
  import java.awt.event.ActionEvent;
  import java.awt.event.ActionListener;
  import java.awt.event.ComponentEvent;
  import java.awt.event.ComponentListener;
  import java.awt.event.MouseEvent;
  import java.awt.event.MouseListener;
  import java.awt.image.BufferedImage;
  import javax.swing.JFrame;
  import javax.swing.JPanel;
  import javax.swing.JTextField;
  import javax.swing.Timer;
  
  public class NewTest extends JPanel implements 
      MouseListener, 
      ActionListener, 
      ComponentListener, 
      Runnable 
  {
  
  JFrame f;
  Insets insets;
  private Timer t = new Timer(20, this);
  BufferedImage buffer1;
  boolean repaintBuffer1 = true;
  int initWidth = 640;
  int initHeight = 480;
  Rectangle rect;
  
  public static void main(String[] args) {
      EventQueue.invokeLater(new NewTest());
  }
  
  @Override
  public void run() {
      f = new JFrame("NewTest");
      f.addComponentListener(this);
      f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      f.add(this);
      f.pack();
      f.setLocationRelativeTo(null);
      f.setVisible(true);
      createBuffers();
      insets = f.getInsets();
      t.start();
  }
  
  public NewTest() {
      super(true);
      this.setPreferredSize(new Dimension(initWidth, initHeight));
      this.setLayout(null);
      this.addMouseListener(this);
  }
  
  void createBuffers() {
      int width = this.getWidth();
      int height = this.getHeight();
  
      GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
      GraphicsDevice gs = ge.getDefaultScreenDevice();
      GraphicsConfiguration gc = gs.getDefaultConfiguration();
  
      buffer1 = gc.createCompatibleImage(width, height, Transparency.OPAQUE);        
  
      repaintBuffer1 = true;
  }
  
  @Override
  protected void paintComponent(Graphics g) {
      int width = this.getWidth();
      int height = this.getHeight();
  
      if (repaintBuffer1) {
          Graphics g1 = buffer1.getGraphics();
          g1.clearRect(0, 0, width, height);
          g1.setColor(Color.green);
          g1.drawRect(0, 0, width - 1, height - 1);
          g.drawImage(buffer1, 0, 0, null);
          repaintBuffer1 = false;
      }
  
      double time = 2* Math.PI * (System.currentTimeMillis() % 5000) / 5000.;
      g.setColor(Color.RED);
      if (rect != null) {
          g.drawImage(buffer1, rect.x, rect.y, rect.x + rect.width, rect.y + rect.height, rect.x, rect.y, rect.x + rect.width, rect.y + rect.height, this);
      }
      rect = new Rectangle((int)(Math.sin(time) * width/3 + width/2 - 20), (int)(Math.cos(time) * height/3 + height/2) - 20, 40, 40);
      g.fillRect(rect.x, rect.y, rect.width, rect.height);
  }
  
  @Override
  public void actionPerformed(ActionEvent e) {
      this.repaint();
  }
  
  @Override
  public void componentHidden(ComponentEvent arg0) {
      // TODO Auto-generated method stub
  
  }
  
  @Override
  public void componentMoved(ComponentEvent arg0) {
      // TODO Auto-generated method stub
  
  }
  
  @Override
  public void componentResized(ComponentEvent e) {
      int width = e.getComponent().getWidth() - (insets.left + insets.right);
      int height = e.getComponent().getHeight() - (insets.top + insets.bottom);
      this.setSize(width, height);
      createBuffers();
  }
  
  @Override
  public void componentShown(ComponentEvent arg0) {
      // TODO Auto-generated method stub
  
  }
  
  @Override
  public void mouseClicked(MouseEvent e) {
      JTextField field = new JTextField("test");
      field.setBounds(new Rectangle(e.getX(), e.getY(), 100, 20));
      this.add(field);
      repaintBuffer1 = true;
  }
  
  @Override
  public void mouseEntered(MouseEvent arg0) {
      // TODO Auto-generated method stub
  
  }
  
  @Override
  public void mouseExited(MouseEvent arg0) {
      // TODO Auto-generated method stub
  
  }
  
  @Override
  public void mousePressed(MouseEvent arg0) {
      // TODO Auto-generated method stub
  
  }
  
  @Override
  public void mouseReleased(MouseEvent arg0) {
      // TODO Auto-generated method stub
  
  }
  }
  </code></pre>