Multiple viewports onto the same JTextArea? - java

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.

Related

JTextPane insert component, faulty vertical alignment

I have a JTextPane, into which I need to insert a JComponent. I'm using
JTextPane.insertComponent(Component)
The item is indeed inserted, but the vertical positioning is too high. Instead of having the bottom of the component aligned with the baseline of the current line of text, the component is way above that position, blocking out/over-painting lines of text appearing above.
I have tried calling setAlignmentY(float) with various values, on both the inserted component and the JTextPane, but it doesn't affect the behavior at all.
My guess: there seems to be some state inside my JTextPane or its Document that I need to be changing. But I don't know what it is.
Have you tried calling setSize(width, height) on the JComponent before you insert it into the JTextPane? It should work for most components.
I know, this is a pretty old question, but your approach of using setAlignmentY is totally correct. Don't know what code caused it to not work, but the javadoc of JTextPane.insertComponent(Component) says the following about the alignment:
The component is placed relative to the text baseline according to the
value returned by Component.getAlignmentY. For Swing components this
value can be conveniently set using the method
JComponent.setAlignmentY. For example, setting a value of 0.75 will
cause 75 percent of the component to be above the baseline, and 25
percent of the component to be below the baseline
So using textPane.setAlignmentY(1.0f) has the desired effect.
I ran into the same problem and could not find a solution using JTextPane or JEditorPane. But I was able to use JavaFX/WebView/WebEngine/JFXPanel. You will need to update to Java 8 (JDK 1.8). I made my own class HTMLPaneType, an extension of JFXPanel, and use HTMLPaneType in place of a JTextPane.
A JTextPane requires adding a HyperlinkListener if you want to respond to href clicks. HTMLPaneType requires adding a listener if you want to NOT respond to href clicks or to respond differently. In my case, I wanted to launch an external browser on an href click. I was able to do that with both the JTextPane and the extended JFXPanel. See also
http://blogs.kiyut.com/tonny/2013/07/30/javafx-webview-addhyperlinklistener/#.VK-JIHsueWN

Component for glass pane console-style text display

I'm trying to provide a progress report for a slow operation, in the form of text scrolling up from the bottom of the screen with details on what's going on - it's an effect you may have seen a few times in video games when they're loading maps, making network connections and suchlike.
Glass pane seems to be the way to get the text overlay, that much I have working. My problem is exactly what component to use for the actual text display.
JTextArea can display text, but as far as I can see, it can only do it from the top of the screen down - is there a way to make it scroll text up from the bottom of the screen?
JLabel by contrast can align the first line of text to the bottom of the screen, and even take appended text on that line, but when I add more lines separated by newline characters, it just seems to swallow them up even after calling repaint and validate. Is there a way to make it scroll up with the new text?
Or is there another component I should be using instead?
I really like JXLayer for effects layered over Swing components. JXLayer was at one point scheduled to be included in Java 7. Unfortunately the moving around that has been going on Java.net lost all the good content that the author had. There are still some other great resources around (Java 7 required for this one) on the web. I use JXLayer to provide panels with a busy state having a web-like spinner and greyed out appearance.
Another alternative (not as capable as JXLayer IMHO) is MigLayout has absolute positioning, which is maybe easier than the GlassPane.
JLabel would be the easiest. Otherwise you will have to override paintComponent to do anything fancy like animating the text movement.

Painting a JComponent without adding it to a container

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.

How do I repaint after tooltip disappears?

I've created an applet which has one large panel to display data surrounded by several controls (buttons, textfields, etc.). The large panel contains several layers of labels which I render myself.
The controls all have tooltips associated with them, and some of these tooltips overlap the main panel. When they disappear, they leave a hole in the main panel image until the main panel is repainted.
Now mind you, this does not always happen. It only occurs when the cursor is in a certain range. If you get far enough to either the left or right (no difference noted for changes along the Y axis), the holes are painted over when the tooltip disappears.
I'm not well-versed on how tooltips and repainting are supposed to work, and if this is a sign that there's something dreadfully wrong with my program, but if I can just call repaint on the main panel whenever the tooltip disappears, I should be fine. Is there something I can override in tooltip to make this happen?
I'm using Swing
Thanks.
To answer your question (after you found a solution by the comments): Swing has some quite elaborate repaint management built in. When a tooltip disappears, the rectangle below it is repainted.
Now, which components have to be repainted? All those who overlap with the given rectangle, and are not themselves hidden (in the region in question) by other components - but only opaque components count here. (This is the whole reason we need the opaque property on JComponent - to optimize repainting.)
Your label declared itself being opaque, but did not really paint its whole area on a paintComponent, and such the region of the tooltip which should have been covered by the label stayed unpainted.
Declaring your label to be partly transparent caused also the concerning region of the component behind it to be repainted.

Can findComponentAt() work before a JComponent is painted?

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).

Categories