I want to remove title bar from JFrame, so I call setUndecorated(true) on that JFrame, but I would like to preserve border (nifty gradient) on that JFrame, which is present when decoration is on? Can I do that? Something like getting border instance for LookAndFeel default or make gradient border myself?
The default system LookAndFeel window borders are drawn by system, not Java, so there is no way to remove title bar from the window alone. The only thing you can do is undecorate your window and draw border by yourself (and yes, to fully copy system border you will have to put a lot of effort into it).
Maybe something like that could be available in SWT, but to use it you will have to abandon standart Swing.
You can accomplish this visually by creating a JPanel and giving it a border, then setting the panel as your frame's content.
public class Undecorated {
public static void main(String[] args) {
JFrame frame = new JFrame();
JPanel borderedPanel = new JPanel();
//Use any border you want, eg a nice blue one
borderedPanel.setBorder(BorderFactory.createMatteBorder(5, 5, 5, 5, Color.BLUE));
frame.setContentPane(borderedPanel);
frame.setUndecorated(true);
frame.setSize(new Dimension(200, 200));
frame.setVisible(true);
}
}
Related
I'm pretty new to GUI and Java as a whole so I hope that I can explain this well enough and understand people's answers.
For a school project, I need to put a bunch of stuff on some rectangles but I'm having issues even adding one rectangle properly.
From researching online, this is what I have (the JPanel and GridBagConstraints are just there to show what I'd like to use):
public class GUI extends JPanel
{
public static void main (String [] args)
{
GUI g = new GUI();
JFrame window = new JFrame("Java Window");
window.setSize(1280, 960);
window.add(g);
window.setVisible(true);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel layout = new JPanel(new GridBagLayout());
GridBagConstraints constraints = new GridBagConstraints();
}
#Override
public void paintComponent(Graphics g)
{
Color boxColour = new Color(194, 190, 190);
super.paintComponent(g);
g.setColor(boxColour);
g.fillRect(10, 10, 100, 100);
}
}
So right now, the rectangle appears in the window. But how can I add constraints to it? Is that possible? I would think that I should use JPanel to keep everything more organized since there will be many components so I tried adding this:
layout.add(g);
window.add(layout);
window.setVisible(true);
However, the rectangle no longer appeared. What am I doing wrong and how can I fix it and add constraints to my shapes? Thanks!
In your first scenario, the default layout manager of the frame is the BorderLayout and you are adding your "g" panel to the BorderLayout.CENTER. So based on the rules of the BorderLayout your "g" panel will take up all the space available in the frame. So you have plenty of space to paint your rectangle.
However, in your second scenario, your "layout" panel is using the default layout manager of a JPanel which is a FlowLayout which respects the preferred size of any component added to it.
The preferred size of the "g" panel is 10 x 10. So, when you add the "g" panel to the "layout" panel there is nothing to see because all you custom painting is done outside the bounds of the panel.
You need to override the getPreferredSize() method of your GUI panel to return a preferred size of (120, 120) so you can see your rectangle painted with a 10 pixel border around all the edges.
Read the section from the Swing tutorial on Custom Painting for more information and working examples demonstrating how to override the `getPreferredSize() method.
You will also need to read the Swing tutorial on How to Use GridBagLayout for example of using the constraints to add multiple components.
First, to get it out of the way, I absolutely NEED to use heavyweight AWT component with a swing application. I need features from both of them.
The task is simple - render a heavyweight AWT Canvas (or any other element), render OpenGL scene directly onto it, then display Swing buttons above it for the UI.
My problem is that it works half-way.
I don't seem to have problems with Z-ordering. I am using jLayeredPanes for it, and I can move Canvas between layers and it actually works, popping on top or below other elements.
The problems are with transparency.
The thing is, Swing elements have Opaque parameter, and when it's set to false (non-opaque) - it should basically be transparent and you should see the next element below it. In my case, however, the AWT Canvas gets ignored, and you instead only see the next underlying SWING element.
Here are a couple of screenshots. They are taken from a standalone test project of mine. The canvas is stretched to the size of the frame, and in the upper left there is a JLayeredPane dummy element that is a simplified version of the menu.
On the first screenshot, the JLayeredPane's Opaque setting is set to true, and you can see that it's background property is set to Blue color.
On the second screenshot, everything is exactly the same but Opaque is set to false. Instead of displaying whatever is on the Canvas - what gets drawn in empty grey jFrame background.
Lastly, on the third screenshot I have put Canvas into a jPanel instead of leaving it on its own. As you can see, the Panel's orange color is seen through the transparent jLayeredPane, but the Canvas is yet again hidden.
Here's the code for the Frame layout. I would not post my rendering/context code right now
frame = new JFrame("AWT test");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.setPreferredSize(new Dimension(width, height));
canvas = new Canvas();
canvas.setSize(width,height);
//this part exists only in the third example
JPanel p = new JPanel();
p.setSize(width,height);
p.setBackground(Color.orange);
p.add(canvas);
// third example end
JLayeredPane pane = new JLayeredPane();
JLayeredPane paneMenu = new JLayeredPane();
JButton button = new JButton();
button.setSize(20,20);
paneMenu.setSize(200,200);
paneMenu.add(button, new Integer(1));
paneMenu.setBackground(Color.BLUE);
paneMenu.setOpaque(false); //True for the first example
pane.add(p, new Integer(1)); // canvas for the first two examples
pane.add(paneMenu, new Integer(2));
pane.setOpaque(false);
frame.add(pane);
frame.pack();
frame.setVisible(true);
frame.transferFocus();
Could anyone please explain me what is going on and how to do what I need to do.
I will repeat again - I have to use heavyweight component as render target. I am aware of solutions like JOGL's GLPanel which is a lightweight Swing-compatible component. But I tried that method and the performance is really slow, because instead of directly rendering onto it as a context target - it reads FrameBuffer from memory, flips it, and then paints it as BufferedImage. This path is not fitting for the limited resources of an embedded system that I'll be running on.
c0der said: Please post minimal reproducible example
Errrrm.... Didn't I?
Here, you can have it in complete java class form if you want, but I literally changed some variables for constants.
import javax.swing.*;
import java.awt.*;
public class Main
{
public static void main(String[] args)
{
JFrame frame = new JFrame("AWT test");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.setPreferredSize(new Dimension(500, 500));
Canvas canvas = new Canvas();
canvas.setSize(500,500);
canvas.setBackground(Color.RED);
//this part exists only in the third example
JPanel p = new JPanel();
p.setSize(500,500);
p.setBackground(Color.orange);
p.add(canvas);
// third example end
JLayeredPane pane = new JLayeredPane();
JLayeredPane paneMenu = new JLayeredPane();
JButton button = new JButton();
button.setSize(20,20);
paneMenu.setSize(200,200);
paneMenu.add(button, new Integer(1));
paneMenu.setBackground(Color.BLUE);
paneMenu.setOpaque(false); //True for the first example
pane.add(p, new Integer(1)); // canvas for the first two examples
pane.add(paneMenu, new Integer(2));
pane.setOpaque(false);
frame.add(pane);
frame.pack();
frame.setVisible(true);
frame.transferFocus();
}
}
A little update:
I initially suspected that because Swing elements delegate all their drawing to the underlying heavyweight element (In my case JFrame), then what happens is that the frame generates a single frameBuffer for itself and then displays on top of Canvas. Canvas itself is not handled in this generation and thus the frame "covers" over the canvas.
That doesn't seem to be the case. I tried making the JFrame undecorated, all panels non-opaque, and display the picture. The result - canvas is still "cut", and through the hole you can see the underlying IDE menu.
This makes me think that somewhere during Drawing, the Canvas itself detects that it is obscured by another element, and that it doesn't need to draw that area. So it "optimizes" itself and doesn't update these pixels.
Maybe I'm wrong. But here's another screenshot. This is the same example as before, but I took out 3d rendering and simply trying to display Canvas with background set to Red.
Once again, going to reply to my own question.
It turned out that I need to do
setComponentMixingCutoutShape(paneMenu, new Rectangle());
for the menu pane that lies underneath the button. That essentially tells java not to cut out the element from the heavyweight underlying component.
I am making a window that has a large text area and a small text area under it. This is what i have so far:
This is the code I have for it:
JPanel window=new JPanel(){
protected void paintComponent(Graphics g){
super.paintComponent(g);
ImageIcon ii=new ImageIcon("textEffect.png");
Image i=ii.getImage();
g.drawImage(i,0,0,null,this);
}
};
JLabel label=new JLabel("Say: ");
JTextArea dialog=new JTextArea(20,50);
JTextArea input=new JTextArea(1,46);
JScrollPane scroll=new JScrollPane(
dialog,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER
);
//main method
public static void main(String[] args){
new Window();
}
//Makes window and starts bot
public Window(){
super("Pollockoraptor");
setSize(600,400);
setResizable(false);
setDefaultCloseOperation(EXIT_ON_CLOSE);
dialog.setEditable(false);
dialog.setOpaque(false);
dialog.setBackground(new Color(0, 0, 0, 0));
dialog.setLineWrap(true);
input.addKeyListener(this);
label.setVerticalTextPosition(SwingConstants.BOTTOM);
label.setHorizontalTextPosition(SwingConstants.LEFT);
window.add(scroll);
window.add(label);
window.add(input);
window.setBackground(new Color(97,118,131));
add(window);
setVisible(true);
}'
How do I make the larger textarea partially transparent so I can see the background and how do I move "Say:" to be infront of the smaller textarea?
JPanel uses a FlowLayout by default. You'll want to change the layout manager, personally, I'd recommend something like GridBagLayout, but that's just me.
See Laying Out Components Within a Container for more details
To make the JTextArea see through, you're going to have to make the JScrollPane and it's JViewPort transparent as well.
Swing only knows how to paint fully opaque or fully transparent components. You can create a translucent effect by making the component transparent and overriding it's paintComponent method and using an AlphaComposite or painting a with a Color which has an alpha value set to something below 255
For example...
Can not draw image on JTextArea background when using Nimbus Look And Feel
JTextArea not selectable, but still showing a "ghost" cursor
Swing Graphics on JFrame
And general advice...
Key Bindings over KeyListener
Override getPreferredSize of your custom component to get better results when been laid out
Use JFrame#pack over setSize, this will calculate the window size based on the needs of the content and take into account the window frame decorations as well
How do I make the larger textarea partially transparent so I can see the background
Check out Backgrounds With Transparency for problems when using a transparent background and a general purpose solution you can use to you don't need to do custom painting all the time.
I would like to be able to have a JPanel within my JFrame of a fixed size 400x400.
I would also like the to be a 20px wide border all around it.
The main problem is the following code doesnt stick it its size.` JScrollPane runningAni = new JScrollPane(new views.cRunningAnimation(
model));
runningAni.setMaximumSize(new Dimension(400,400));
this.setSize(new Dimension(600,600));
this.add(runningAni,BorderLayout.CENTER);`
When doing this the runningAni panel just strethces accross the whole frame.
public void paint(Graphics g) {
this.setBackground(new Color(0,255,0));
}
I know this because my full frame paints itself green rather than just the JPanel (The above paint code is for my panel not the frame)
How would i create the panel so it always stays the same size and so there is always a 20px colored border around it?
BorderLayout ignores the size. You need to set a LayoutManager that either allows you to set the size to a fixed size or one that cares for the sizes set. There are different layout managers that allow this (e.g. GrindBagLayout or no layout manager at all). Some are not that easy to use (e.g. GridBagLayout). What to use depends on the rest of the layout.
You could probably use a layout panel that contains your custom panel. The layout panel needs an appropriate layout manager and could be put into the center of the BorderLayout. This would mean nearly no modifications to existing layout code.
The whole point of BorderLayout is to fill the center with the center component.
Don't override the paint() method to set the color of the panel. Use:
panel.setBackground(...);
When you create the panel.
How would i be able to set a border around my Jpanel
See How to Use Borders.
Just set your layout to null, to what ever class your adding your JPanel.
Then use the setBounds() method to set your location and size!
For example:
public class Main extends JFrame{
YourPanelClass panel = new YourPanelClass();
public Main(){
// I didn't want to put all the, everyday JFrame methods...
setLayout(null);
/*
First two coordinates indicate the location of JPanel inside JFrame.
The seconds set of coordinates set the size of your JPanel.
(The first two coordinates, 0 and 0, tell the JPanel to start at the
top left of your JFrame.)
*/
panel.setBounds(0, 0, 100, 100);
add(panel);
}
}
And i would GREATLY recommend using the paintComponent() method.
For instance:
(Obviously you put this in your JPanel's class.)
public void paintComponent(Graphics g){
super.paintComponent(g); // don't forget this if you are going to use this method.
//Basically this makes your JPanel's background green(I did it this way because I like doing it this way better.)
g.setColor(new Color(0, 255, 0));
g.fillRect(0, 0, getWidth(), getHeight());
}
Please don't forget to thumbs up if this helped!
setPreferredSize()
setMinimumSize()
setMaximumSize()
should do the trick
I have an AWT canvas which I cannot convert to a Swing component (it comes from VTK). I wish to display a few of these canvases inside of a JSplitPane. I've read about mixing heavy and light weight components in Java and know that it's a pain in the butt, but I don't have a choice. If I wrap the AWT canvas inside of a JPanel and then put that on the split pane the split pane doesn't function at all. However, if I put the AWT canvas inside of a JPanel and then that inside of a JScrollPane and then those scroll panes on the JSplitPane the split pane does function, but the AWT canvas components don't resize properly. I'm lost about how to get the AWT canvas components to resize properly when the JSplitPane's divider is moved. I can catch the divider moving operation and operate on the AWT canvases at that time, but I don't know what to do. I've tried calling invalidate() then validate() then repaint(), but that didn't work.
Any ideas?
Here's a example of the problem
import javax.swing.*;
import java.awt.*;
public class SwingAWTError {
public static void main(String[] args) {
Canvas leftCanvas = new Canvas();
Canvas rightCanvas = new Canvas();
leftCanvas.setBackground(Color.RED);
rightCanvas.setBackground(Color.BLUE);
JPanel leftPanel = new JPanel();
JPanel rightPanel = new JPanel();
leftPanel.setLayout(new BorderLayout());
rightPanel.setLayout(new BorderLayout());
leftPanel.add(leftCanvas, BorderLayout.CENTER);
rightPanel.add(rightCanvas, BorderLayout.CENTER);
JScrollPane leftScroll = new JScrollPane();
JScrollPane rightScroll = new JScrollPane();
leftScroll.getViewport().add(leftPanel);
rightScroll.getViewport().add(rightPanel);
JSplitPane split = new JSplitPane();
split.setLeftComponent(leftScroll);
split.setRightComponent(rightScroll);
split.setDividerLocation(400);
JFrame frame = new JFrame();
frame.getContentPane().setLayout(new BorderLayout());
frame.getContentPane().add(split, BorderLayout.CENTER);
frame.setSize(800, 800);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
It is a dirty way but this will solve it:
When you call pack() without resizing the window, not much happens. So, when you first resize the window and then call pack(), your components are correcly drawn. This means you can put this dirty method in your divider moved listener method:
frame.setPreferredSize(frame.getSize()); // store the current size to restore it after packing.
frame.setSize(frame.getWidth() + 1, frame.getHeight()); // resize it!!
frame.pack();
I don't know what it is exactly but it is a strange behavour in Java...
Hope this helps until you've found a better solution...
You are kind of out of luck here. There's a pretty good article on this on the sun/oracle website:
http://java.sun.com/products/jfc/tsc/articles/mixing/
Essentially it boils down to this guideline (taken from that link, under the z-ordering heading):
Do not mix lightweight (Swing) and
heavyweight (AWT) components within a
container where the lightweight
component is expected to overlap the
heavyweight one.
Edit: I kept browsing that site and came across another link, and it would appear that the situation has improved slightly: http://java.sun.com/developer/technicalArticles/GUI/mixing_components/ But I think your case is one of those listed at the bottom in the limitations section:
Limitations
A few situations are not supported:
* Non-opaque lightweight components that have translucent
pixels (0 < alpha < 255) are not
supported. If a partially translucent
lightweight component overlaps a
heavyweight component, the heavyweight
component will not show through.
* Embedded heavyweight components must belong to the process that
created the frame or applet. The
heavyweight component must have a
valid peer within the main process of
the application (or applet).
* Advanced Swing key events, such as those events maintained in an
InputMap, might not work correctly
where lightweight and heavyweight
components are being mixed. There are
no known workarounds.