Caret in transparent JTextPane - java

I have a transparent, editable JTextPane in a program I'm writing (to allow a background to show through), and everything works pretty well, except for the text caret.
The caret itself acts normally, but every time it's drawn, it breaks the transparency of the area right behind it, as seen in the image:
Is there any way to make the background of the caret transparent without repainting the whole frame? DefaultCaret doesn't have a setOpaque() method, so I'm not quite sure how to go about this.
Thanks!
EDIT: I can't answer my own question yet, but I ended up writing an SSCCE that works perfectly, so there is definitely something else wrong with my program. For future reference, this is the (working) code I wrote:
import java.awt.*;
import javax.swing.*;
class TransparentScrollPane extends JScrollPane {
public TransparentScrollPane(Component view) {
super(view);
this.setPreferredSize(new Dimension(540,480));
this.setOpaque(false);
this.viewport.setOpaque(false);
this.setBorder(null);
}
}
public class TextPaneExample extends JTextPane {
public TextPaneExample()
{
super();
this.setOpaque(false);
}
public void paintComponent(Graphics g)
{
g.setColor(new Color(255,255,255,128));
g.fillRect(0, 0, getWidth(), getHeight());
super.paintComponent(g);
}
public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.setSize(640, 480);
JPanel panel = new JPanel();
panel.setBackground(new Color(255,128,255));
TextPaneExample textPane = new TextPaneExample();
TransparentScrollPane scroller = new
TransparentScrollPane(textPane);
textPane.setBackground(new Color(255,255,255,128));
frame.add(panel);
panel.add(scroller,BorderLayout.CENTER);
frame.setVisible(true);
}
}

See Backgrounds With Transparency for the problem and a couple of solutions.
Basically you can just use:
panel.add( new AlphaContainer(componentWithTransparency) );
For your first question you get one free answer :) Future questions should include a proper SSCCE that demonstrates the problem.

Related

Using Graphics to display shapes on a JFrame

I am learning how to program a graphical user interface in Java. I pretty much know some basics but in this program, I am trying to draw onto a JFrame with a black background, but as soon as I run the program the JFrame only displays a white line on a white background. I would appreciate it very much if anyone knew how to fix this, I have been trying myself but I can't seem to figure it out.
Thanks for your attention. I’m looking forward to a reply.
public class test1 {
public static void main (String[] args)
{
JFrame frame = new JFrame();
frame.setSize(1835,1019);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.setResizable(false);
frame.getContentPane().setBackground(Color.BLACK);
JPanel raum = new JPanel()
{
public void paint(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
g2.setPaint(Color.WHITE);
g2.drawLine(500,500,500,800);
}
};
frame.add(raum);
}
}
There a number of issues which are going to cause you endless amount of problems going into the future.
The obvious one is the fact that the background color of the panel is very close to WHITE, so it makes it very difficult to see the line. You could change the background color of the panel or the line and it should solve the immediate issue.
You really need to take a look at Performing Custom Painting and Painting in AWT and Swing to get a better understanding of how painting works in Swing.
It is generally recommended to override paintComponent and avoid overriding paint. paint does a lot work and unless you're willing to take over ALL it's workload, you're better off avoiding it.
As a general rule, you should also call the super.paintXxx method before you do any custom painting. Again, painting is generally a complex workflow, best to just let the parent class do its job.
A component should also provide sizing hints back to the parent container, the parent container can then make better decisions (via the LayoutManager) as to how all the components should be laid out. Because different platforms (and even same platforms with different settings) can generate different size window decorations, you're better off managing the size of the "content" over the size of the "window". Again, this is going to save you no end of headaches into the future.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setBackground(Color.BLACK);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(1080, 1920);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setPaint(Color.WHITE);
g2d.drawLine(500, 500, 500, 800);
g2d.dispose();
}
}
}

How to successfully draw background JPanel once and update foreground JPanel constantly?

I have a custom JLayeredPane, and I am repainting it in my game loop. There are two custom JPanels added into the JLayeredPane. These are foreground and background JPanels. How do I successfully only draw my background JPanel once, (And repaint when window is re-sized or any other reason) to reduce impact on system resources, while continuing to update my foreground JPanel constantly.
To re-iterate, I dont want to constantly repaint the background JPanel in a loop. I want to repaint it only when it is nessessary, as the background does not change. and is large.
In my attempt to do this, I have only drawn the background once. However. the background JPanel is simply not visible. while the foreground JPanel updates as normal. It is almost as if the foreground JPanel paints ontop of the background JPanel, even though I have both of the JPanels set to setOpaque(false)
I have made a mvce which shows my attempt at only drawing the background JPanel once, while updating the foreground JPanel constantly.
The problem with my code is that the background JPanel does not show.
Now. I know that if I were to draw it constantly it would show. But that defeats the purpose of what i'm trying to do. I am trying to only draw it once, and have be seen at the same time
My code successfully only draws the background JPanel once. The problem is that the background JPanel does not show. How do I fix THIS problem
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Main extends JLayeredPane {
static JFrame frame;
static Main main;
static Dimension screenSize;
public Main() {
JPanel backPanel = new BackPanel();
JPanel frontPanel = new FrontPanel();
add(backPanel, new Integer(7));
add(frontPanel, new Integer(8));
new Thread(() -> {
while (true){
repaint();
}
}).start();
}
public static void main(String[] args) {
screenSize = Toolkit.getDefaultToolkit().getScreenSize();
frame = new JFrame("Game"); // Just use the constructor
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
main = new Main();
frame.add(main, BorderLayout.CENTER);
frame.pack();
frame.setSize(screenSize);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public class BackPanel extends JPanel{
public boolean drawn = false;
public BackPanel(){
setVisible(true);
setOpaque(false);
setSize(screenSize);
JLabel test1 = new JLabel("Test1");
JLabel test2 = new JLabel("Test2");
add(test1);
add(test2);
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
drawOnce(g);
}
public void drawOnce(Graphics g){
if (!drawn){
g.setColor(Color.red);
g.fillRect(0, 0, screenSize.width, 200);
drawn=true;
}
}
}
public class FrontPanel extends JPanel{
public FrontPanel(){
setVisible(true);
setOpaque(false);
setSize(screenSize);
JLabel test = new JLabel("Test");
add(test);
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
g.setColor(Color.blue);
g.fillRect(0+screenSize.width/2, 0, screenSize.width/4, 300);
}
}
}
Try RepaintManager.currentManager(component).markCompletelyClean(component). It will prevent the component from repainting. You might need to do this after each time you add new components.
http://docs.oracle.com/javase/6/docs/api/javax/swing/RepaintManager.html#markCompletelyClean%28javax.swing.JComponent%29
I don't know if this two lines of code
super.paintComponent(g);
drawOnce(g);
are the root of problem, I sincerly don't remember how paintComponent works (a test could help) but try to swap them :
drawOnce(g);
super.paintComponent(g);
maybe, on your original version, you tells JVM to paint the whole component and, only after the AWTEvent has been added to the queue, to draw what you need.
I guess that the awt's documentation will explain it.

Add GUI with Swing, but unsure of the sequence to take

I'm building a GUI for a data processing algorithm. I can instantiate the window, give it a background, title, etc., but when I try adding panels to it, I run into trouble. What I'm really looking for more than a proofreader is a suggestion for the sequence in which to build, configure, and add objects in Java Swing so that they behave correctly, in a generic sense. So, is this the best way to build a JFrame with a different-colored panel in it?
Declare JFrame
Set JFrame color (background color)
Declare JPanel (box to represent data graphically)
Set JPanel color (box color)
Add JPanel to JFrame
Set JFrame to visible = true
It makes sense intuitively but it doesn't seem to work, no matter what I do. I've found step-by-step instructions elsewhere but they tend to explain what to type more than why you're typing it, so you get a very narrow understanding of what's going on. Thanks for any help!
Below is the full code; I hesitated to post it because I'd begun experimenting with Graphics2D and it isn't well-commented, but if it helps:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import javax.swing.*;
import javax.swing.border.TitledBorder;
public class GUI extends JFrame
{
JFrame mainWindow = new JFrame();
JPanel backgroundPanel = new JPanel();
JPanel subPanel = new JPanel();
Color background = new Color(40,40,40);
Color subWindow = new Color(255, 255, 255);
TitledBorder title = BorderFactory.createTitledBorder("title");
Rectangle rect1 = new Rectangle(10, 10, 40, 40);
Graphics2D g;
public static void main (String[] args)
{
new GUI();
}
public GUI()
{
initializeGUI();
}
private void initializeGUI()
{
mainWindow.setSize(1340, 880);
backgroundPanel.setBackground(background);
subPanel.setBackground(subWindow);
subPanel.setBorder(title);
mainWindow.setTitle("Ed");
mainWindow.setLocationRelativeTo(null);
mainWindow.setDefaultCloseOperation(EXIT_ON_CLOSE);
mainWindow.add(backgroundPanel);
backgroundPanel.add(subPanel);
updateGUI();
}
public void updateGUI()
{
mainWindow.setVisible(false);
mainWindow.setVisible(true);
}
public void paintComponent(Graphics g)
{
this.g.setColor(subWindow);
this.g.fill(rect1);
this.g = (Graphics2D) g;
}
}
Let's break this down....
public class GUI extends JFrame {
JFrame mainWindow = new JFrame();
There is no need to extend from JFrame as you are neither using it nor are you adding any value to the class.
This...
public void paintComponent(Graphics g) {
this.g.setColor(subWindow);
this.g.fill(rect1);
this.g = (Graphics2D) g;
}
is doing nothing and will never be called, as nothing you've extended from implements a paintComponent method (that is, JFrame does not have a paintComponent methd) (and you class is not attached to anything displayed on the screen anyway). Also, you should NEVER maintain a reference to ANY Graphics context you did not create yourself.
The reason that subPanel is appearing so "small" is because it has not definable size, aside from the border.
You could rectify this in one of three ways...
You could change the layout manager of backgroundPanel to something like BorderLayout
You could override the getPreferredSize method of the subPanel to return a more suitable size or
You could add other components to it and let the layout manager figure it out...
In any case, you should have a look at Laying Out Components Within a Container.
You should also have a look at Painting in AWT and Swing and Performing Custom Painting for more details about how painting is done in Swing

Wrong getClipBounds in combination with JScrollPane

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.

Turning a JFrame black when opening another window

I wan't to make my main JFrame become darken when the focus is on another window.
This is an example from the game Football Manager 2012. First the main window is selected and it looks like it should, then when it is loading, it turns darker and unselectable. I wan't to have this effects on my own application, but im not really sure how, not even sure what to google?
Im guessing its a JWindow that appears and the JFram becomes unselectable in the background. I'm planing to use it on a help-window in my application, that is a JWindow right now.
Andrew Thompson has the right idea, only it's easier to use the glass pane feature of your frame's JRootPane. Here's some working code: In your frame class, invoke
getRootPane().setGlassPane(new JComponent() {
public void paintComponent(Graphics g) {
g.setColor(new Color(0, 0, 0, 100));
g.fillRect(0, 0, getWidth(), getHeight());
super.paintComponent(g);
}
});
Then, to show the "curtain", invoke
getRootPane().getGlassPane().setVisible(true);
In the code above, change the alpha transparency value of 100 in order to find the suitable darkness.
..wan't the JFrame to go back to normal after the new window is closed. I tried setVisible(false) but it didn't work.
It works in this example.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class ShadowedFrame extends JFrame {
ShadowedFrame() {
super("Shadowed Frame");
getRootPane().setGlassPane(new JComponent() {
public void paintComponent(Graphics g) {
g.setColor(new Color(0, 0, 0, 100));
g.fillRect(0, 0, getWidth(), getHeight());
super.paintComponent(g);
}
});
JButton popDialog = new JButton("Block Frame");
popDialog.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
getRootPane().getGlassPane().setVisible(true);
JOptionPane.showMessageDialog(ShadowedFrame.this, "Shady!");
getRootPane().getGlassPane().setVisible(false);
}
});
setContentPane(popDialog);
pack();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationByPlatform(true);
setSize(350,180);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new ShadowedFrame().setVisible(true);
}
});
}
}
(Untested, but..) Seems like a good task for a JLayeredPane. Create a JComponent that is set transparent and add that to the top level of the layered pane. In the paintComponent(Graphics) method of the component, set a semi-transparent color and fill the full area with it. In normal use (non-dimmed), call customComponent.setVisible(false).
Update
Or, as Ingo pointed out, use the glass pane.
I'm guessing its a JWindow that appears and the JFrame becomes unselectable in the background
It is more likely a modal JDialog. When a modal dialog is visible, the frame/window that is the owner becomes inaccessible (cannot be clicked on).

Categories

Resources