I am working on a GUI where the JComponents are "stamped" on the screen. In other words, the actual components aren't displayed, but images of the components. This is a graph, where the nodes of the graph are custom Swing components - or rather stamped images of Swing components.
Now I want to display tooltips for specific components within my nodes.
To do this, I create a JComponent that is identical to the one displayed and using the mouse x and y values, I ask findComponentAt() for the proper component. This doesn't work very well. If I reuse JComponents for the nodes, it gets confused if I try to get a tooltip for a node that is a different size than the last one painted. If I create a new JComponent for each node and a new one when calculating the tooltip, the initial size of the new one is 0,0. I can set the size using the getPreferredSize() calculation, but that still doesn't work. The root JComponent (a JPanel) has the right size, but none of it's children have any size yet.
A sample of the tooltip calculation code:
// Get a component that matches the stamped component
JComponent nodeComponent = getNodeComponent();
// These next two lines get the size right
nodeComponent.setSize(nodeComponent.getPreferredSize());
nodeComponent.revalidate();
Component componentTop = nodeComponent.findComponentAt(relativeX, relativeY);
componentTop comes back as the root JComponent no matter what x and y values it is passed.
So is it possible to get Swing to properly calculate the size and locations of the JComponents without actually painting them?
You have images of your components in the graph they have to have their sizes to be able to paint correctly.
To find your "stamp" you should walk backwards (in z-order) in your graph and find the first image your mouse position fall into.
Preferred sizes will not work, you should rely on size of "stamps" i think.
I found the answer myself. The key problem is that Swing doesn't want to layout a component unless that component has a proper parent. So, I changed my code to this:
parentComponent.add(nodeComponent);
// Set the node's size and validate it so it's laid out properly
nodeComponent.setBounds((int)realizer.getX(), (int)realizer.getY(), (int)realizer.getWidth(), (int)realizer.getHeight());
nodeComponent.validate();
// Now we can properly find the child component under our mouse
Component componentTop = nodeComponent.findComponentAt(relativeX, relativeY);
// Now remove it from the view
parentComponent.remove(nodeComponent);
And that works like a charm. You should be able to use a similar process to find child components in JLists or JTables (which also use this Renderer pattern).
Related
I've implemented a custom JPanel, whose paint method I've extended to do a lot of manual rendering in full screen mode. Now I would like to integrate another JComponent to this (in my case a JPanel that contains a JScrollpane with a JTextPane as its viewport) that should appear on top of my first panel, but because my custom rendering pipeline is complex, adding the JComponent to my panel and having it painted the traditional way through the AWT system is not an option (I tried and it's quirky at best, not functional at worst), so my question is: is it possible to manually order the JComponent to be painted at one point in my program by calling its regular paint method without tying it to a JContainer and if yes, how do I do this?
Thanks in advance for your answers.
See the LabelRenderTest.java source on this thread. The label is eventually drawn to screen, but it is painted to BufferedImage before ever being displayed.
The important line of the source is..
textLabel.setSize(textLabel.getPreferredSize());
You can take a look at CellRendererPane and see how for example BasicTableUI paints component images with it.
Yes, just call the normal paint method on the object and pass the Graphics you want it to paint on. However, this is just going to paint it and it sounds like you want it to possibly scroll which means you will need to add it to your custom JPanel. In that case just add the panel and you a layout manager that will place the component where you need it.
You should set size for the component. Then to position it use your Graphics' translate(x,y) to position the component in desired Point.
if there is any container higher level in the hierarchy you can use
validate(); repaint();
pair to do that.
if not you can change it's size or bounds ( like +1 , -1 ) at the end to make it repaint itself.
i'm looking for this effect, but PulpCore doen't works with swing. Do you know what library use, or how to make a TileMap? Also, I'd like to move forever in it.
Thanks.
I'm guessing you don't want to use an actual world-map implementation like JXMapViewer...
The basic functionality appears to be: click on some coordinates, and that point becomes centered in the viewport. One fairly simple way to do this is to have the component(s) that you want to view all contained within a JPanel and have that panel inside a JScrollPane with its scrollbars turned off (setHorizontalScrollbarPolicy(HORIZONTAL_SCROLLBAR_NEVER) and so on). Then, set up a click or action listener for your elements that calculates the new center point, what the new viewport rectangle coordinates will be, and use scrollRectToVisible on the panel to shift the view. For animation, you can use a Swing Timer to set up a series of incremental scrolls in the required direction until you reach the target.
There is a component I would like to make which shows all matches of a set of words in a JTextArea, along with some context (N lines, probably user-configurable.)
I already have the code for highlighting the matches so I know their offsets and can therefore determine their bounds. I know the line height of the text so I can determine the bounds of the context areas I want to paint.
But is there some class like JViewport only able to paint multiple views of the underlying component? Since JViewport is a normal Swing component, as soon as you put the same text area under another viewport, it gets detached from the first one.
Edit: Actually as it turns out, I can't seem to determine the bounds of the context areas as the lines-to-offset mapping JTextArea gives me doesn't count wrapped lines.
The problem here is that JViewport is a normal container, and treats its view component as a child component (and each component can only be child in one container). This is quite reasonable, given that always only one component can have the focus, and similar things.
I don't know of any JViewport-like class that would do what you want (painting a view of a component without being parent of it), but in your case, you can achieve most of the same by using two JTextAreas sharing the same Document. You then only would have to synchronize the caret movements (if so wished), I think.
There is nothing that exists as you describe. In fact it sounds like you want to customize the display of a JTextArea. In which case you can override and change the way JTextArea paints itself. Either painting more on top, or changing it completely.
Paint your JTextArea in an Image and use necessary fragments of the image. Keep the image's bounds in original JTextArea so clicking bu the image you can calculate click on the JTextArea to get proper position.
In short, my need is to have a background Image in my java app, and upon some event, create some other graphics on top of that image.
I was thinking I would use a JPanel to draw the background image in, add it at to my JFrame the start of program, and then add other JPanels on top of that upon certain events. The problem is Swing gives the JPanels added first the highest Z index, so what should be my background is showing up on top of everything.
Is there any way to control the Z index/order of the JPanels, or am I going about this completely wrong?
You can use the setComponentZOrder() to handle Z-Order in your application.
Resources :
JavaDoc - Container.setComponentZOrder
oracle.com - Mixing heavy and light components
Sounds strange to add mutiple JPanels and use z-order. I would suggest you either simple add ONE JPanel with the paintComponent(Graphics g) method overwritten, where you draw the correct image according to your events.
Or use the CardLayout, and add JLabels (with different images), then when your event triggers, use
CardLayout cl = (CardLayout)getLayout();
cl.show(this, "card3");
to show the correct JLabel.
The JLayeredPane is designed for just this purpose. It allows you to control the Z-order of components.
I was thinking I would use a JPanel to
draw the background image in, add it
at to my JFrame the start of program,
Sounds reasonable.
and then add other JPanels on top of
that upon certain events. The problem
is Swing gives the JPanels added first
the highest Z index, so what should be
my background is showing up on top of
everything.
Adding a panel to a panel is just like adding another component to the panel. The child component will be painted on top of the parent panel. There is no need to play around with Z-Order.
The key is to set a layout manager on the panel with the image. Then each panel you add to the image panel must be made non-opaque so that the background image will be painted.
Maybe the Background Panel can help you out. It automatically makes any component added directly to the panel non-opaque.
I've created an app with a small window (the size of a combo box). I need to create a floating panel that sits outside the window, next to the selected item in a JComboBox. (See attached image).
I've been reading about the JComboBox.setRenderer(customRenderer) etc. But was just wondering before I go down this path, whether it is at all possible to render something outside the window. I suspect it is, as the combobox itself manages to render it's popup list outside the window.
I'm very new to Swing, so any advice would be appreciated.
It's not possible with the custom renderer since Swing components are light weight. That is, Java is given a native window and all the component drawing takes place in that window. In your case, that is the JFrame containing the combo box.
What you can do though is create a new undecorated window and set it's location accordingly and draw whatever you want inside it.
EDIT: When Java needs to paint outside it's window bounds (like the case of pop up messages or combo boxes drop downs) if the component falls inside the bounds it uses the swing light weight mechanism. But if the component falls out side the bounds it is automatically substituted with a awt heavy weight component that has it's own native drawing surface outside the active window.
I've implemented similar idea using combobox renderers and tooltips on them. Content of every item's tooltip can be customized and rendered using HTML. Location of the tooltip can be set outside of the item itself thus creating design very similar to the one presented in your question.
Here is the starting point for you:
http://www.java2s.com/Code/Java/Swing-Components/ToolTipComboBoxExample.htm