I have a JTabbedPane with some tabs and a lot of unused extra space next to the tabs. So I'm trying to use it and place some buttons there (like in Eclipse). I put the buttons on a GlassPane:
JPanel glasspane = getPanelWithButtons();
// panel with FlowLayout.RIGHT
frame.setGlassPane(glasspane);
glasspane.setOpaque(false);
glasspane.setVisible(true);
This works, and I still can click through on the other elements of my gui (most search results I found are about how to prevent this). The only problem so far is that the mouse pointer doesn't change to that double-ended horizontal arrow when it hovers over the bar of a JSplitPane. How can I get this behaviour back?
EDIT
I found that no mouse changing events from any component under the glass pane are shown. Those components would change the mouse cursor to a hand cursor, zoom lenses and others. None of these mouse pointer changes have an effect any more. I guess this is because with the glass pane, the mouse pointer change needs to be made to the glass pane, but I don't want to do all the mouse pointer changing manually.
Well. I figure out how to do it.
Although I spend more than 5 hours to understand all things behind, but the solution is very simple.
Just overwrite 'public boolean contains(int x, int y)' method of glass panel.
public static void main(String[] args)
{
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setSize(800, 600);
final JSplitPane panel = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, new JPanel(), new JPanel());
frame.getContentPane().add(panel, BorderLayout.CENTER);
final JPanel glassPane = new JPanel(){
#Override
public boolean contains(int x, int y)
{
Component[] components = getComponents();
for(int i = 0; i < components.length; i++)
{
Component component = components[i];
Point containerPoint = SwingUtilities.convertPoint(
this,
x, y,
component);
if(component.contains(containerPoint))
{
return true;
}
}
return false;
}
};
glassPane.setOpaque(false);
JButton button = new JButton("haha");
button.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
System.out.println("haha");
}
});
glassPane.add(button);
glassPane.setBorder(BorderFactory.createLineBorder(Color.red));
frame.setGlassPane(glassPane);
//try to comment out this line to see the difference.
glassPane.setVisible(true);
frame.setVisible(true);
}
Well. I have found a solution to the problem "place buttons next to the tabs". I don't use a glass pane any more, but place the buttons directly:
buttonpanel.setBounds(...);
frame.getLayeredPane().add(buttonpanel, 1);
This works and solves my problem. It is a bit more complicated and involves doing layout by hand and listening to frame resize events, though.
Since I still would like to know how to accomplish this with a glass pane, I'm not accepting this answer. Maybe someone comes up with a solution for a glass pane.
Related
I'm trying to make overlapping panels, but widgets below the 'topmost' panel somehow react to mouse events and repaint events and render above the top layer. For example, here I have bottom layer with lots some buttons and labels, and a top layer with up/down buttons, which is fully opaque for this test (gray background):
This uses the OverlayLayout manager to place panels on different Z levels. The buttons pop into view when hovering over them, and the labels on the right, which auto-update, also pop into view.
SSCCE:
public class Temp extends JPanel {
public static void main(String... args) {
SwingUtilities.invokeLater(() -> {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.setContentPane(new Temp());
f.setSize(500, 250);
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
public Temp() {
super(null);
setLayout(new OverlayLayout(this));
JButton bottom = new JButton("Bottom");
JButton top = new JButton("Top");
bottom.setBounds(100, 50, 200, 100);
top.setBounds(200, 100, 200, 100);
// OverlayLayout adds components from top to bottom.
add(new JPanel(null) {{
setBackground(new Color(175, 150, 125));
add(top);
}});
add(new JPanel(null) {{
add(bottom);
}});
}
}
This is basically how I do it now. It looks like this after a bit of mouse hovering, but with hovering I can bring any of the two buttons above the other:
My question is, how can I fix this in the easiest way, or what is the standard way to handle this? Perhaps a Non-modal frame-less JDialog? One argument I have against the dialog is I rather not that the user uses keyboard shortcuts to move it around the screen.
and a top layer with up/down buttons, which is fully opaque for this test (gray background):
It doesn't make sense to have an "opaque" top panel. You will not see the buttons on the bottom panel. The top panel should be transparent and the bottom panel should be your background color.
My question is, how can I fix this
If you are referring to the fact that the buttons ZOrder keeps changing as you hover over each button then you need to override the following method on your Temp panel (ie. the panel using the OverlayLayout):
#Override
public boolean isOptimizedDrawingEnabled()
{
return false;
}
This basically says that the Temp panel may contain overlapping components so all panels need to be repainted in the proper ZOrder each time.
I have recently begun working with JComponents to create GUI systems. Everything is working but the bottom and right sides of the JFrame do not get painted over and remain white.
Screenshot of running GUI:
In the screenshot you can see the 'drknBtn' is displayed correctly; this is because I hovered over it with the mouse before taking the picture. Hovering over the buttons refreshes them and they appear as normal. Due to this, I would assume the panel that holds them, 'bottomPnl' is covering that white space, but that panels background is not showing at the bottom portion. Any ideas on what could cause this? I have tried calling 'bottomPnl.repaint()' directly before calling pack(), but no change.
My code is below.
Note: For each JComponent, I created a class extending that component. This way I could set default values for the components in the constructors of these classes instead of doing each one individually. I'll list the relevant properties of the Frame and Panels.
Frame: setSize(width,height); setResizeable(false); setLocationRelativeTo(null);
Panel: setLayoutManager(from contructor); setPreferredSize(new Dimension(width,height)); same for setMinimumSize and setMaximumSize.
public Display(String title, int w, int h){
width=w;
height=h;
frame = new FrameUI(title,w,h);
//parent panel
parentPnl= new PanelUI(width,height, new FlowLayout(FlowLayout.CENTER,0,0));
parentPnl.setBackground(new Color(100,175,175));
//top panel
topPnl= new PanelUI(width,(int)(height*.15), new FlowLayout(FlowLayout.CENTER,0,0));
topPnl.setBackground(new Color(100,175,175));
chooseFileBtn = new ButtonUI("Browse...",topPnl.getWidth()/4,(int)(topPnl.getHeight()*.9),new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
fc = new FileChooserUI();
fc.setFileFilter(new FileNameExtensionFilter("Image files", ImageIO.getReaderFileSuffixes()));
int result = fc.showOpenDialog(null);
try {
if (result == JFileChooser.APPROVE_OPTION) {
picture.setIcon(new ImageIcon(ImageIO.read(fc.getSelectedFile()).getScaledInstance(picture.getWidth(),picture.getHeight(), 0)));
}
} catch (Exception iOException) {
}
}
});
//middle panel
midPnl= new PanelUI((int)(width*.85),(int)(height*.7), new FlowLayout(FlowLayout.CENTER,0,0));
midPnl.setBackground(new Color(75,125,125));
picture = new LabelUI("",midPnl.getWidth(),midPnl.getHeight());
picture.setBackground(new Color(75,125,125));
picture.setVisible(true);
picture.setOpaque(true);
picture.setIcon(null);
//bottom panel
bottomPnl= new PanelUI(width,(int)(height*.15), new FlowLayout(FlowLayout.CENTER,0,0));
bottomPnl.setBackground(new Color(100,175,175));
ltnBtn = new ButtonUI("Lighten Picture",bottomPnl.getWidth()/3,(int)(bottomPnl.getHeight()*.9),new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
}
});
ltnBtn.setBackground(Color.LIGHT_GRAY);
ltnBtn.setForeground(Color.BLACK);
drknBtn = new ButtonUI("Darken Picture",bottomPnl.getWidth()/3,(int)(bottomPnl.getHeight()*.9),new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
}
});
drknBtn.setBackground(Color.DARK_GRAY);
drknBtn.setForeground(Color.WHITE);
//add UI Objects
topPnl.add(chooseFileBtn);
midPnl.add(picture);
bottomPnl.add(ltnBtn);
bottomPnl.add(drknBtn);
parentPnl.add(topPnl);
parentPnl.add(midPnl);
parentPnl.add(bottomPnl);
Container contentPane = frame.getContentPane();
contentPane.add(parentPnl);
frame.pack();
frame.setVisible(true);
}
}
topPnl= new PanelUI(width,(int)(height*.15), new FlowLayout(FlowLayout.CENTER,0,0));
looks to me like you are manually trying to control the size of the panels and therefore the size of the components added to your panels. Your calculations are wrong and some components aren't displayed properly. Also all your sizes are fixed at creation time and will not adjust if the size of the frame ever changes.
Don't try to control the sizes manually. Use layout managers to dynamically size components based on the properties of the component.
I fail to see why you would want a button to be 15% of the space available to the frame.
If you want the button to be larger than normal you can set extra empty space around the text of the button by using:
button.setMargin( new Insets(50, 50, 50, 50) );
Then just add the button to a panel using a FlowLayout and let the layout manager do its job.
The default layout for a frame is a BorderLayout, so you can then add the "topPnl" to the frame using:
frame.add(topPnl, BorderLayout.PAGE_START);
The other panels can then be added using:
frame.add(midPnl, BorderLayout.CENTER);
frame.add(bottomPnl, BorderLayout.PAGE_END);
This is how Swing was designed to be used with layout managers.
Read the section from the Swing tutorial on How to Use BorderLayout for more information and examples.
The main point is use methods like setMargin(...), to provide hints to the component on what their preferred size should be.
I fixed the problem by removing the 'setSize()' method in the FrameUI constructor. However, I still do not understand how you could dynamically size panels as you said while still maintaining the proportions I want for them. Thank you #camickr for the pointers, my original problem is fixed. I'll look into more javadocs and tutorials on layout managers and such.
I have a JScrollPane attached to a JPanel all contained in a JFrame. I also have a custom slider class that extends JSlider. It's also contained in the JFrame. My issue is, that as the JSlider zooms in on the image in my panel, my JScrollPane isn't resizing with the image zoom. I've tried everything from resetting the bounds on the JScrollPane to manually adjusting the scrollbar to resetting the preferred size. Nothing I've seen on other stackoverflow questions seems to be working for me. Here's some of my code where I add the scroll pane as it currently is (sans a few superfluous details):
JFrame frame = new JFrame("");
frame.setTitle("Image Viewer");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
if (img.getWidth() > START_WIDTH || img.getHeight() > START_HEIGHT) {
frame.setSize(START_WIDTH, START_HEIGHT);//variables set elsewhere in the code
} else {
frame.setSize(img.getWidth(), img.getHeight());//img is the BufferedImage displayed in the JPanel
}
parentPane = new BackgroundPane(img, frame.getWidth(), frame.getHeight()); //BackgroundPane is the JPanel. More accurately, it's a custom class extending JPanel.
scroll = new ImageScroller(parentPane,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS, img); //Custom class extending JScrollPane. This is the problematic area. Its size needs to readjust. It's also a class variable
frame.setLocationRelativeTo(null);
ImageSlider slider = new ImageSlider(SelectionRectangle.this); //the zoom bar extending JSlider
{...}//some removed superfluous code
frame.add(slider, BorderLayout.SOUTH);
frame.add(scroll);
frame.pack();
frame.setVisible(true);
And here is the code currently being used in the ImageSlider for when the panel zooms. Most of my attempts to readjust the scroll bar have occurred here:
#Override
public void stateChanged(ChangeEvent arg0) {
int value = ((JSlider) arg0.getSource()).getValue();
SCALE = value / 100.0; //Scaling factor
ACTIVE_FRAME.revalidateViewport(); //Method within the frame class. See below
}
RevalidateViewport function:
public void revalidateViewport() {
scroll.getViewport().revalidate();
scroll.getViewport().repaint();
}
How can I get the scrollbar to resize based on my scale factor? Is there some simple way I just haven't seen yet? I'm new to JScrollPanes.
Thank you. Let me know if you have any questions.
I have set a game menu screen on my JFrame using a layered panel. I want to remove all the components from the JFrame and add a new content panel. I've spent a few hours trying to work through this issue but I am unable to come up with a good solution.
public myJFrame ()
{
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new BorderLayout());
setSize (1030, 727);
setVisible(true);
newGame = new JButton();
loadGame = new JButton();
quitGame = new JButton();
newGame.setBounds(new Rectangle(10,600,200,110));
newGame.setIcon(pic1);
newGame.setOpaque(false);
newGame.setContentAreaFilled(false);
newGame.setBorderPainted(false);
loadGame.setBounds(new Rectangle(210,600,200,110));
loadGame.setIcon(pic2);
loadGame.setOpaque(false);
loadGame.setContentAreaFilled(false);
loadGame.setBorderPainted(false);
quitGame.setBounds(new Rectangle(410,600,200,110));
quitGame.setIcon(pic3);
quitGame.setOpaque(false);
quitGame.setContentAreaFilled(false);
quitGame.setBorderPainted(false);
background.setBounds(0,0,1030,727);
titletext.setBounds(0,0,726,170);
lp = getLayeredPane();
lp.add(titletext, new Integer (4)); //* I messed around with these integers quite a bit. *//
lp.add(newGame, new Integer (20)); //* They don't seem to be logically adding to the panel. *//
lp.add(loadGame, new Integer(20)); //* I want the background and titletext to appear behind *//
lp.add(quitGame, new Integer (20)); //* the JButtons. *//
lp.add(background, new Integer (1));
newGame.addActionListener(this);
loadGame.addActionListener(this);
quitGame.addActionListener(this);
}
public void actionPerformed(ActionEvent event)
{
Object obj = event.getSource();
if (obj == newGame)
{
System.out.println("New Game");
lp.remove(4);
getContentPane().add(mop, "Center"); //* Removes myWelcomePanel mwp and adds myOptionsPanel mop *//
repaint();
mop.startgame.addActionListener(this);
}
I have tried repainting, revalidating and removing only certain components but nothing seems to work. I would expect the code posted above to remove the titletext image, but it removes the background image instead.
I would prefer not to mess with the second content pane because it contains a number of objects which I have configured previously.
JLayeredPane.remove(int) refers to the child's absolute index, not its layer position. Why don't you either remove by reference, or simply set the child component's visibility to hidden?
I found a way to make it work. It seems that I must remove each component individually instead of using removeAll(). I identified a new GridLayout sp and set that as the layout. Finally, I had to use revalidate instead of repaint. Repaint left me with a blank screen. If anyone could comment on what is happening here, I would appreciate it!
public void actionPerformed(ActionEvent event)
{
Object obj = event.getSource();
if (obj == newGame)
{
System.out.println("New Game");
lp.remove(background);
lp.remove(titletext);
lp.remove(newGame);
lp.remove(loadGame);
lp.remove(quitGame);
getContentPane().setLayout(sp);
getContentPane().add(mop, "Center");
revalidate();
mop.startgame.addActionListener(this);
}
I'm working on a simple painting application in Java but Swing doesn't want to cooperate. At first I tried making a JTabbedPane that holds the DrawArea and a settings panel, but for some bizarre reason the second mouseDragged() (and by extension the drawArea.repaint() method) was triggered, the JTabbedPane seemed to duplicate. Imagine two identical tab panes stacked vertically. I thought this was just some bug in JTabbedPane, but I rewrote everything to a very simple custom menu using JButtons on a panel and the exact same thing happened. It's not like the GUI actually duplicated the top area; it's unusable and I can paint over it. Check it out:
Edit: I found a similar question. If I call super.paintComponent(g), the problem goes away (and drawArea.setBackground(color) actually works!) but the function called on super (a new keyword for me) basically repaints the drawArea so the paint trail is no longer saved.
If I paint a rectangle to fill the drawArea it overwrites the issue even though mouseDragged is still fired. Here's the rendering code:
#Override
public void mouseDragged(MouseEvent e) {
x = e.getX(); y = e.getY();
drawArea.repaint();
}
// (subclass):
class DrawArea extends JPanel {
public void paintComponent(Graphics g) {
g.setColor(Color.WHITE);
g.fillOval(x-3, y-3, 7, 7);
}
}
And here's the GUI code:
frame = new JFrame("Jadra");
frame.setSize(650, 600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
menu = new JPanel();
settingsButton = new JButton("Settings");
clearButton = new JButton("Clear");
exitButton = new JButton("Quit");
menu.setLayout(new FlowLayout());
menu.add(settingsButton);
menu.add(clearButton);
menu.add(exitButton);
menu.setBackground(new Color(30, 90, 60));
drawArea = new DrawArea();
drawArea.setBackground(Color.red);
drawArea.setOpaque(true);
drawArea.addMouseMotionListener(this);
frame.getContentPane().add(menu, BorderLayout.NORTH);
frame.getContentPane().add(drawArea, BorderLayout.CENTER);
Thread pt = new Thread(new Painter());
frame.setVisible(true);
pt.start();
Please tell me I did something really stupid. Otherwise this looks like a really annoying bug. I really appreciate your help. Thanks!
Your DrawPanel.paintComponent() method should chain upward to the method it overrides from JPanel to allow default painting to occur. Simply add
super.paintComponent(g);
As the first line to this method.