I had been frustrated with some really odd behavior in my code for a while, and after taking time to slowly widdle down my code bit by bit, I finally found the source of the issue.
General overview: using Java Swing, the following code creates a tabbed interface. The first tab that is visible to the user has a button. The second tab has a blue square in the upper left hand corner.
What should happen: Once the program is open, click the button first and then open the other tab. The button calls a function on the other tab, causing the square to move to a new location. Thus, the other tab should show the square in its new location, not the upper left hand corner.
What actually happens: If you click the button first and then open the tab, the square's position is unchanged. It remains in the upper left hand corner, as if the button was never pressed. If you open the tab first, it seems to "prime" the program somehow, so now the button works as expected.
Sure, it seems like a minor annoyance to click on the tab first to ensure the program works, but potentially this is a really big problem. Why is the tab unable to be updated until it is viewed at least once?
Observations: When debugging the code, I can step through the setUnit() function and verify that the square is, in fact, being successfully changed, completely overwriting the previous position. And yet, when I then open the second tab the square's position is now reverted back to where it was previously. If the variables are inspected at that point, it shows the square's original position has remained completely unchanged, as if the setUnit() function was never called. Knowing that these components don't visually update unless repainted, I made sure to add the repaint() function call within the setUnit() function. It really baffles me to wonder where the original values of the square's location are even stored? I can see in the debugger the values are overwritten, so they should completely cease to exist, right?
Code:
DragPanel.java:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import javax.swing.JPanel;
class DragPanel extends JPanel
{
private static final long serialVersionUID = 1L;
boolean isFirstTime = true;
Rectangle area;
Rectangle rect = new Rectangle(0, 0, 20, 20);
private Dimension dim = new Dimension(400, 300);
public DragPanel() {
setBackground(Color.white);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
if (isFirstTime) {
area = new Rectangle(dim);
rect.setLocation(0, 0);
isFirstTime = false;
}
g2d.setColor(Color.blue);
g2d.fill(rect);
}
public void setUnit()
{
rect.setLocation(200, 50);
repaint();
}
}
ShapeMover.Java:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
public class ShapeMover {
public ShapeMover() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
initComponents(frame);
}
public static void main(String s[]) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new ShapeMover();
}
});
}
private void initComponents(JFrame frame) {
JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.TOP);
tabbedPane.setBounds(10, 93, 426, 527);
frame.getContentPane().add(tabbedPane);
DragPanel shaper = new DragPanel();
shaper.setBounds(0, 79, 420, 420);
JPanel input = new JPanel();
tabbedPane.addTab("Test", null, input, null);
input.setLayout(null);
JButton add = new JButton("Click this");
add.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
shaper.setUnit();
}
});
add.setBounds(201, 64, 65, 23);
input.add(add);
JPanel output = new JPanel();
tabbedPane.addTab("Second", null, output, null);
output.setLayout(null);
output.add(shaper);
frame.pack();
frame.setVisible(true);
}
}
Based on tgdavies comment, I found the solution to the situation.
paintComponent is not part of the construction of the object, but rather is only called whenever the panel is seen. That is, when it is painted. Even calling repaint() is not going to call paintComponent until the panel is seen. Thus, the first time the panel is viewed then this section of code is executed exactly once:
if (isFirstTime)
{
area = new Rectangle(dim);
rect.setLocation(0, 0);
isFirstTime = false;
}
After being executed once, isFirstTime is set to false, and thus doesn't run the section of code again. Thus, calling setUnit() does, indeed, overwrite the square's original position. And then the section of code shown above sets it back to 0,0.
Simply commenting out the line of code that reads rect.setLocation(0, 0); fixes the problem.
Related
I'm new at working with JFrames. So I dont really know how to draw/paint/display everything in a JFrame. Im working on a memory game. Currently im working at the first form, this form must display a background image, a welcome text, a dropdown list with the amount of cards for the memory game and a button which should start the game. I succesfully displayed the background image and the welcome text. After adding the JButton and combobox my form got messed up (I only see a blue background). I have no idea what im doing wrong, also I dont know how I can place the button and dropdown on the right x and y position. I want to place the dropdown box under the welcome text and the button to the right of the dropdownbox.
This is my code:
Main:
package Memory;
public class Main {
public static Memory memory;
public static void main(String[] args) {
memory = new Memory();
}
}
Renderer:
package Memory;
import javax.swing.*;
import java.awt.*;
public class Renderer extends JPanel {
private static final long serialVersionUID = 1L;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Memory.backImage = new ImageIcon("Memory/memoryGame.jpg");
Main.memory.repaint(g);
}
}
Memory:
package Memory;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.io.*;
import javax.imageio.*;
import java.awt.FlowLayout;
public class Memory implements ActionListener {
public String[] amountOfCards = {"8","16","32"};
private JButton startButton;
public Renderer renderer;
public static ImageIcon backImage;
public boolean screen1;
public static final int WORLD_WIDTH=1250, WORLD_HEIGHT=800;
public Memory() {
JComboBox comboBox = new JComboBox(amountOfCards);
comboBox.setSelectedIndex(1);
startButton = new JButton("Start game");
JFrame jframe = new JFrame();
Timer timer = new Timer(20,this);
renderer = new Renderer();
jframe.add(renderer);
jframe.setTitle("Memory game");
jframe.setSize(WORLD_WIDTH,WORLD_HEIGHT);
jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jframe.setResizable(false);jframe.add(startButton);
jframe.add(comboBox);
jframe.setVisible(true);
screen1=true;
timer.start();
}
#Override
public void actionPerformed(ActionEvent e) {
renderer.repaint();
}
public void repaint(Graphics g) {
//welcome screen
if(screen1) {
BufferedImage scaledImage = getScaledImage();
g.drawImage(scaledImage, 0, 0, null);
g.setColor(new Color(150, 196, 100));
g.setFont(new Font("TimesRoman", Font.PLAIN, 75));
g.drawString("MEMORY GAME", WORLD_WIDTH / 2 - 275, 100);
g.setFont(new Font("TimesRoman", Font.PLAIN, 25));
g.drawString("Please select the amount of cards u want to play with and start the game!", WORLD_WIDTH / 2 -400, 200);
}
}
public BufferedImage getScaledImage() {
BufferedImage image = new BufferedImage(WORLD_WIDTH,WORLD_HEIGHT, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = (Graphics2D) image.createGraphics();
g2d.addRenderingHints(new RenderingHints(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY));
g2d.drawImage(backImage.getImage(), 0, 0,WORLD_WIDTH,WORLD_HEIGHT, null);
return image;
}
}
Alright, multiple errors in a first view of your code
public static Memory memory; : That line, having static and being a public member of class will harm you a lot! Why? Because your memory variable is the same for all the instances you create, because it's a "Class variable" not an "instance variable", and being public is going against the convention and security reasons. For reference see: What does the 'static' keyword do in a class? and Why use getters and setters?
From the above point, static is not a "magic cross method / class" word that allows you to use variables through classes or with main and other methods... and thus this line public static ImageIcon backImage; and this one: Memory.backImage = new ImageIcon("Memory/memoryGame.jpg"); shouldn't exist. And the same for this one: Main.memory.repaint(g);
That being said, you should NEVER NEVER NEVER EVER!!! override repaint() method, all of that code should be into the paintComponent(...) mehtod!
Also it is wise to treat images as embedded resources, because, once you export your program as a JAR file, they will become embedded resources, it's better to treat them like if they already were, here you can find information about how to change your program accordingly.
Try and set your ImageIcon BackImage from public static, to public. Also, try and write the jframe.setVisible(true); command after timer.start()
note that setting your JFrame to visible, before completing every initialisation, might lead to unwanted behaviour of your program.
To set the position of your button on the screen, after adding your JButton to your JFrame, you can use: startButton.setLocation(x,y);
After you play a little bit with the coordinates of your window, you'll get the spot you want for your JButton. Keep in mind that (0,0) is usually the upper left corner of your JFrame.
Be careful when using static variables in your program. There seems to be no reason in setting your Memory class, your width and height variables as static.
For more information regarding this, check out this post: Why are static variables considered evil?
From Java's documentation it says that the repaint mechanism of a Component is optimized and cached, so that even if it is called a lot of times, it won't clog the drawing pipeline.
It seems though that under really heavy invocation, sometimes it fails to draw the latest frames. Consider the following example:
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.WindowConstants;
public class TestRepaint extends JPanel {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setContentPane(new TestRepaint());
frame.setSize(300, 100);
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private long start;
{
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
start = System.currentTimeMillis();
System.out.println("Repaint");
repaint();
}
});
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
long now = System.currentTimeMillis();
int where = (int) ((now - start) / 5);
if (where >= 200)
g.drawString("DONE", 200, 30);
else {
g.drawString("Working...", where, 30);
repaint();
}
}
}
When mouse clicked on the window, then a "Working..." text will scroll to the right. On the last frame it is supposed to change into "DONE". It seems that (randomly) it fails to draw the last frame and the previous "Working..." text remains. If for example we force redraw the display (i.e. change window size), then "DONE" appears.
It seems that, since the repaint is requested too fast, the last repaint of the component fails to happen. So, is there any guaranteed way to do this?
Maybe i have encountered a bug or more probably doing something wrong ;)
I try to translate the content of a user drawn JPanel using a JScrollPanel. Inside the panel the drawing i would like to access the visible area through the Graphics class getClipBounds method to improve rendering performance.
Searching on SO brings a lot results referring to JScrollPane but none is mentioning a problem with the clip bounds. Google the same.
user drawn panel
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JPanel;
public class Content extends JPanel {
#Override
protected void paintChildren(Graphics g) {
super.paintChildren(g);
// intense clip bounds dependent rendering here
System.out.println(g.getClipBounds());
}
#Override
public Dimension getPreferredSize() {
return new Dimension(2000,2000);
}
}
main frame setup
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import java.awt.BorderLayout;
public class ClipBoundsIssue {
private JFrame frame;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
ClipBoundsIssue window = new ClipBoundsIssue();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public ClipBoundsIssue() {
frame = new JFrame();
frame.setBounds(100, 100, 450, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JScrollPane scrollPane = new JScrollPane();
frame.getContentPane().add(scrollPane, BorderLayout.CENTER);
Content content = new Content();
scrollPane.setViewportView(content);
}
}
to reproduce
Just run the code, move one of the scrollbars and inspect the console output of System.out. The following picture depicted scrolling the bar on the x axis.
actual System.out result
Which produced the following results
java.awt.Rectangle[x=0,y=0,width=416,height=244]
java.awt.Rectangle[x=416,y=0,width=16,height=244]
java.awt.Rectangle[x=432,y=0,width=15,height=244]
java.awt.Rectangle[x=447,y=0,width=16,height=244]
java.awt.Rectangle[x=463,y=0,width=15,height=244]
expected result
I would have expected to have the width of the bounds to keep the same. But it changes from 416 to 16.
The question now is
Does anybody know why this happens, or how it can be avoided??
discared WAs
A possible workaround would be to lookup the view port's view bounds. But if possible i would like to avoid the Content class making any such lookup. Another alternative would be to pass the information into the Content class, but this i would like to avoid as well.
I would have expected to have the width of the bounds to keep the same.
Why? It is so simple that it is hard to explain, but let me try.
When you scrolling, only small new portion if the JPanel is appearing if you scroll slowly.
The produced output is absolutely correct:
java.awt.Rectangle[x=0,y=0,width=416,height=244] Control is shown first time, you need to redraw it completely
java.awt.Rectangle[x=416,y=0,width=16,height=244] You scrolled to the right by 16 pixels, so only narrow strip of you control must be redrawn.
You must understand that these coordinates are related to your control which has size set to 2000x2000 pixels.
Try to scroll the window created with this code and you will see what I am talking about:
import javax.swing.*;
import java.awt.*;
import java.util.Random;
public class ScrollPaneRepaintDemo extends JPanel {
public ScrollPaneRepaintDemo() {
setPreferredSize(new Dimension(2000,2000));
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.add(new JScrollPane(new ScrollPaneRepaintDemo()));
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setVisible(true);
}
#Override protected void paintComponent(Graphics g) {
Rectangle clip = g.getClipBounds();
g.setColor(new Color(new Random().nextInt()));
g.fillRect(clip.x, clip.y, clip.width, clip.height);
}
}
By the way - it works so because of JPanel's internal implementation. If you extend JComponent instead, the whole viewport will be clipped. I add also that JPanel repaints completely when resizing, its optimizations are only for scrolling.
Sorry about my English, and my ignorance in programming, its because I'm new at this , and I'm having problem with Buttons and JFrame, please help me ;)
I'll post the print of the problem, and the codes of my the two classes I have so far, Game and Menu, hope you guys can solve it, I want the buttons to paint inside the gray panel.
Thanks.
Print of my Problem
Print
(GAME CLASS)
package br.com.lexo.dagame;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import javax.swing.JFrame;
import br.com.lexo.dagame.menu.Menu;
public class Game extends Canvas implements Runnable {
private static final long serialVersionUID = 1L;
private static int width = 300;
private static int height = width / 16 * 9;
private static int scale = 3;
private static String title = "Da Game";
private Thread thread;
public JFrame janela;
private Menu menu;
private boolean running = false;
public Game() {
Dimension size = new Dimension(width * scale, height * scale);
setPreferredSize(size);
janela = new JFrame();
menu = new Menu(janela, this);
}
private synchronized void start() {
if (running) return;
running = true;
thread = new Thread(this, "Thread_01");
thread.start();
}
private synchronized void stop() {
if (!running) return;
running = false;
try {
thread.join();
} catch (Exception e) {
e.printStackTrace();
}
}
public void render() {
BufferStrategy bs = getBufferStrategy();
if (bs == null){
createBufferStrategy(3);
return;
}
Graphics g = bs.getDrawGraphics();
g.setColor(Color.LIGHT_GRAY);
g.fillRect(0, 0, getWidth(), getHeight());
g.dispose();
bs.show();
}
public void update() {
}
public void run() {
while (running){
render();
update();
}
stop();
}
public static void main(String[] args) {
Game game = new Game();
game.janela.add(game);
game.janela.setTitle(title);
game.janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
game.janela.pack();
game.janela.setLocationRelativeTo(null);
game.janela.setResizable(false);
game.janela.setVisible(true);
game.start();
}
}
(MENU CLASS)
package br.com.lexo.dagame.menu;
import java.awt.Canvas;
import java.awt.Graphics;
import java.awt.GridBagLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import br.com.lexo.dagame.Game;
public class Menu extends Canvas {
private static final long serialVersionUID = 1L;
public boolean inMenu = false;
JButton startGame = new JButton("Começar Jogo");
JButton exitGame = new JButton("Sair do Jogo");
JButton howToPlay = new JButton("Como Jogar");
private Game game;
public Menu(JFrame janela, Game game){
this.inMenu = true;
this.game = game;
game.janela.setLayout(new GridBagLayout());
game.janela.add(startGame);
game.janela.add(exitGame);
game.janela.add(howToPlay);
howToPlay.setEnabled(false);
}
#Override
public void paint(Graphics g) {
super.paint(g);
}
}
I don't know what are you trying to accomplish but you are not adding the components correctly:
Look at:
game.janela.setLayout(new GridBagLayout());
game.janela.add(startGame);
game.janela.add(exitGame);
game.janela.add(howToPlay);
This is incorrect, the add method has two arguments, like this: container.add(component, constraints); your error is not specifying the constraints. The constraints contains all the details to know where in the panel you want to add that component.
For each LayoutManager the Object constraints is diferent. For the GridBagLayout the constraints is a GridBagConstraints object.
However GridBagLayout is a the most difficult layout to use and you don't really need it. I recommend you to look at this visual guide pick a layout and learn it properly. The tutorial for each LayoutManager explains what do you need to put in the constraints parameter.
The call container.add(component) exists because sometimes the LayoutManager does not need extra information (like the BoxLayout), in the other cases it just uses the "default" constraints for the LayoutManager in use, which may not be what you need.
For example the line in your main:
game.janela.add(game);
Is correct, but what it actually does is calling game.janela.add(game, defaultConstraints); where defaultConstraints is the default constraints value for the LayoutManager of the JFrame janela. Because you didn't explicitely specify a layout for the frame it is using the default layout for JFrames: BorderLayout, and the default constraints for the BorderLayout is the constant BorderLayout.CENTER.
So what that line actually does is:
game.janela.add(game, BorderLayout.CENTER);
Which incidentally is what you wanted to do.
To summarize:
Most calls to add must have two parameters: the component and the constraints. Each LayoutManager uses different constraints. You must be aware of what means to not specify the constraints for your LayoutManager. Do not start learning about how to properly use LayoutMangers with the GridBagLayout it's much more complex.
A quick way to somehow paint components to a graphics object is calling the paint method of component class. So in your render method:
g.fillRect(0, 0, getWidth(), getHeight());
menu.startGame.paint(g);
...
But as you'll soon see that everything is painted on the top left as components are laid out as said in the other answer and to get everything working to how you want them to work is a bit more complicated.
Now the following advice is based on my limited knowledge and was quickly put together so there are probably better ways.
About the menu class:
You are extending java.awt.Canvas when I think it would be best to extend a container like javax.swing.JPanel as you want it (I assume) to hold those 3 buttons.
Next would be to set the appropriate layout for this application, which would be null. So instead of:
game.janela.setLayout(new GridBagLayout());
it would now be:
setLayout(null);
This is because you want components (which are those buttons) to be paint on top of another component which is the Game class that extends Canvas and null allows you to do that.
Because the layout is now null, you must specify the bounds of the components which are the x and y coordinates alone with the width and the height otherwise everything will just be 0, 0, 0, 0 and nothing would show up.
So in the Game's constructor
setBounds(0, 0, width * scale, height * scale);
and janela.setPreferredSize(size); instead of setPreferredSize(size);
Back in the Menu class you will have to set the bounds of the buttons like so:
Dimensions sgSize = startGame.getPreferredSize();
startGame.setBounds(50, 50, sgSize.width, sgSize.height);
I am using preferred size to get the optimal width and height of the button that was set in the buttons UI (I think).
and add them to the Menu which is now a JPanel instead of adding them to the JFrame(janela). (add(startGame);) Also, don't forget to add the game to the menu panel.
and it should work like so:
(http://i.imgur.com/7cAopvC.png) (image)
Alternatively you could make your own widget toolkit or custom layout, but I wouldn't recommend that. I had this same problem last year but ended up moving to OpenGL but anyway, I hope this has helped :)
I've been working with JGraph for a while and it appears there is a painting issue when you set label clipping to true:
The following boiled-down example shows the problem in a living application that you can mess with:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.view.mxGraph;
/** it's an app! */
public class GraphApp extends JFrame {
private mxGraph graph;
private mxGraphComponent graphComponent;
private boolean labelsClipped = false;
/** #return the splitpane */
public JSplitPane getSplitpane() {
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
graph = new mxGraph();
graph.getModel().beginUpdate();
graph.removeCells(graph.getChildCells(graph.getDefaultParent(), true, true));
for (int i = 0; i < 10; i++)
graph.insertVertex(null, null, "very_very_long_vertex_" + i, 10 * i, 10 * i, 100, 50);
graph.getModel().endUpdate();
graph.setLabelsClipped(labelsClipped);
graphComponent = new mxGraphComponent(graph);
JTextArea area = new JTextArea("There's overpaint below this text."); //$NON-NLS-1$
splitPane.add(graphComponent, JSplitPane.LEFT);
splitPane.add(area, JSplitPane.RIGHT);
splitPane.setDividerLocation(70);
return splitPane;
}
private JButton getButton() {
JButton button = new JButton(new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
labelsClipped = !labelsClipped;
graph.setLabelsClipped(labelsClipped);
GraphApp.this.repaint();
}
});
button.setText("Swap setLabelsClipped");
return button;
}
private JPanel getPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.add(getSplitpane(), BorderLayout.CENTER);
panel.add(getButton(), BorderLayout.SOUTH);
return panel;
}
public static void main(String[] args) {
GraphApp app = new GraphApp();
app.add(app.getPanel());
app.setPreferredSize(new Dimension(300, 100));
app.setVisible(true);
app.pack();
}
}
It's also interesting to note, that overpaint only happens within the confines of the vertex. Here are the names with clipping off:
And with clipping on:
I'm looking into the JGraphx source now to see where the problem lies. Has anyone worked around this before? Obviously setting graph.setLabelsClipped(false) works around it, but I'd rather not have my vertex labels spill out over the bounds of my vertices.
I found where the issue lies.
From com.mxgraph.view.mxGraph, if you add this little bit of painting code, you can see that the clip is incorrectly getting set to a rectangle that can be, as evidenced in my example, outside of the actual graph component.
if (clippedCanvas instanceof mxGraphics2DCanvas)
{
System.out.println("setting new clip");
Graphics g = ((mxGraphics2DCanvas) clippedCanvas).getGraphics();
clip = g.getClip();
g.setClip(newClip);
((mxGraphics2DCanvas) clippedCanvas).paintRectangle(((mxGraphics2DCanvas) clippedCanvas).getGraphics().getClipBounds(), Color.GREEN, Color.WHITE);
}
If we paint the clipping area that the label is working with, we can see where the problem lies.
Realistically, we should only be painting the intersection of the original canvas, and the new clipping rectangle. This image shows what the clipping rectangle was before being trampled by the new one:
The fix for this is a simple one:
if (clippedCanvas instanceof mxGraphics2DCanvas)
{
Graphics g = ((mxGraphics2DCanvas) clippedCanvas).getGraphics();
clip = g.getClip();
if (clip instanceof Rectangle)
{
g.setClip(newClip.intersection((Rectangle) clip));
}
else
{
g.setClip(newClip);
}
}
I'd be interested to hear if the original code did the clip setting as it did intentionally. I kind of doubt it.
My fix also defaults to their implementation in the case that someone does something wacky with their clip shapes, just as a sort of CYA. Probably not needed as clipping isn't guaranteed to work with anything other than rectangles:
http://docs.oracle.com/javase/1.4.2/docs/api/java/awt/Graphics.html#setClip(java.awt.Shape)
It appears that the code lives here on github, so hopefully I can get the fix pushed there:
https://github.com/jgraph/jgraphx