I'm trying to make a program that lets the user manually place and resize components within a JScrollPane, a bit of a special-case UI Builder. I managed to make a custom JPanel class that allows the user to move it around manually however when it's added to the JScrollPane and moved around, if it goes outside of the visual bounds of the JScrollPane the scrollbars don't appear or adjust.
Here is my main class that includes the JFrame and JScrollPane.
package quickscrolltest;
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class QuickScrollTest {
JFrame wnd;
JScrollPane scroll;
JPanel pnl;
MovablePanel pnl1;
public QuickScrollTest() {
wnd = new JFrame();
scroll = new JScrollPane();
pnl = new JPanel();
pnl.setLayout(null);
scroll.setViewportView( pnl );
wnd.setContentPane(scroll);
wnd.pack();
pnl1 = new MovablePanel();
Dimension dim1 = new Dimension( 300, 400 );
pnl1.setSize( dim1 );
pnl1.setPreferredSize(dim1);
pnl1.setBackground( Color.CYAN );
pnl1.setLocation( 10, 10 );
/*scroll.getViewport().add(pnl1,null);*/
pnl.add(pnl1);
wnd.setSize( 800, 600 );
wnd.setLocationRelativeTo(null);
wnd.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
wnd.setVisible(true);
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
new QuickScrollTest();
}
}
Here is the MovablePanel class
package quickscrolltest;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JPanel;
public class MovablePanel extends JPanel implements MouseListener, MouseMotionListener {
Color bg = Color.GRAY;
Point clickPoint;
public MovablePanel() {
addMouseListener(this);
addMouseMotionListener(this);
}
#Override
public void setBackground( Color col ) {
super.setBackground(col);
bg = col;
}
#Override
public void mouseClicked(MouseEvent e) {}
#Override
public void mousePressed(MouseEvent e) {
clickPoint = e.getPoint();
}
#Override
public void mouseReleased(MouseEvent e) {
setCursor( Cursor.getDefaultCursor() );
}
#Override
public void mouseEntered(MouseEvent e) {
setCursor( Cursor.getPredefinedCursor(Cursor.HAND_CURSOR) );
super.setBackground( Color.RED );
}
#Override
public void mouseExited(MouseEvent e) {
setCursor( Cursor.getDefaultCursor() );
super.setBackground( bg );
}
#Override
public void mouseDragged(MouseEvent e) {
setCursor( Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR) );
int lastX = getX() - (int) clickPoint.getX();
int lastY = getY() - (int) clickPoint.getY();
setLocation(lastX + e.getX(), lastY + e.getY() );
}
#Override
public void mouseMoved(MouseEvent e) {}
}
How can I make it so that users can freely move components around in the JScrollPane and have the scrollbars update as required?
My final code will have a standalone mouselistener/mousemotionlistener class that generically works on a given JComponent but I coded the listeners directly into the JPanel for simplicity.
Thanks
Scrollbars automatically appear/disappear when the preferred size of the component displayed in the viewport of the scroll panes changes.
You can use the Drag Layout. This class is a custom layout manager and will automatically recalculate the preferred size of the panel as components are dragged around the panel.
You will need to handle the mouseReleased event so you can revalidate() the panel, and invoke the DragLayout once you are finished dragging the component so the preferred size can be reset. Or you could use the ComponentMover class which is referenced in the blog article to do the dragging of the component for you. It supports an auto resize property which will do the revalidate() for you.
Related
Hello first of all when I run the program a button appear , when I press the button the image will go from top to down.
I try the code when the image go from top to down , it work very well
BUT when I put all the codes together there is an error in ( frame.add(new AnimationPane() ); )
Question : How to add AnimationPane() to the frame ???
because this is my problem.
The idea that I want to make two scenes , the first one have a button to make go to the second scene which will have an image (it must be pushed from top until reach down ).
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package maincontentpaneswitching;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class MainContentPaneSwitching {
private static class ChangeContentPaneListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
// I want to put the image here
JPanel newFrameContents = new JPanel(); //Uses FlowLayout by default.
newFrameContents.add(new JLabel("You have successfully changed the content pane of the frame!", JLabel.CENTER));
/*We assume that the source is a JButton and that the Window is of type JFrame, hence
the following utility method call is possible without letting any errors appear:*/
JFrame frame = (JFrame) SwingUtilities.getWindowAncestor((JButton) e.getSource());
frame.setSize(600, 300);
frame.setContentPane(newFrameContents); //Change the content pane of the frame.
frame.revalidate(); //Notify the frame that the component hierarchy has changed.
frame.add(new AnimationPane() );
frame.pack(); //Resize the frame as necessary in order to fit as many contents as possible in the screen.
frame.setLocationRelativeTo(null); //Place the frame in the center of the screen. As you can tell, this needs its size to calculate the location, so we made sure in the previous line of code that it is set.
frame.repaint(); //Repaint frame with all its contents.
}
}
public class AnimationPane extends JPanel {
private BufferedImage boat;
private int yPos = 0;
private int direction = 1;
public AnimationPane() {
try {
boat = ImageIO.read(new URL("https://i.stack.imgur.com/memI0.png"));
Timer timer = new Timer(50, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
yPos += direction;
if (yPos + boat.getHeight() > getHeight()) {
yPos = getHeight() - boat.getHeight();
direction *= +1;
} else if (yPos < 0) {
yPos = 0;
direction *= +1;
}
repaint();
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return boat == null ? super.getPreferredSize() : new Dimension(boat.getHeight()*2 , boat.getWidth() *2);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int x = getWidth() - boat.getWidth();
g.drawImage(boat, x, yPos, this);
}
}
private static class MainRunnable implements Runnable {
#Override
public void run() {
JButton changeContentPaneButton = new JButton("Click to go to the next image!");
changeContentPaneButton.addActionListener(new ChangeContentPaneListener());
JPanel frameContents = new JPanel(); //Uses FlowLayout by default.
frameContents.add(changeContentPaneButton);
JFrame frame = new JFrame("My application");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //Tells the frame that when the user closes it, it must terminate the application.
frame.setContentPane(frameContents); //Add contents to the frame.
frame.pack(); //Resize the frame as necessary in order to fit as many contents as possible in the screen.
frame.setLocationRelativeTo(null); //Place the frame in the center of the screen. As you can tell, this needs its size to calculate the location, so we made sure in the previous line of code that it is set.
frame.setVisible(true);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new MainRunnable()); //Swing code must always be used in the Event Dispatch Thread.
}
}
Introduction
As I said in my comment, I couldn't get the image animation to work properly. At least this code would give you a solid foundation to start with.
Here's the GUI I came up with.
Here's the GUI after you left-click on the button.
If you're going to add comments to your code, put the comments on separate lines from the code. Not everyone has a large monitor and can read 200+ character lines of code.
Explanation
Oracle has a rad tutorial, Creating a GUI With Swing. Skip the Netbeans section.
When I create a Swing GUI, I use the model/view/controller (MVC) pattern. This pattern allows me to separate my concerns and focus on one part of the application at a time.
In Swing, the MVC pattern means:
The view reads information from the model
The view may not update the model
The controller updates the model and repaints/revalidates the view.
There's usually not one controller to "rule them all". Each listener controls its portion of the model and the view.
When I put together an application, I code one tiny tiny piece of it, then run tests. I probably ran two to three dozen tests, and this was mostly coded by you.
Model
I created a BoatImage class to read the boat image. It's a separate class, so I can read the image before I start to construct the GUI.
View
I created a JFrame. I created a main JPanel with a CardLayout.
I use a CardLayout to layout the button JPanel and the image JPanel. This way, the JFrame is not constantly changing size.
I create the JFrame and JPanels as separate methods/classes. This makes it much easier for people, including yourself, to read and understand the view code.
Controller
I coded the ChangeContentPaneListener to change from the button JPanel to the image JPanel. This is where you would put your image animation code.
Code
Here's the complete runnable code. I made all the additional classes inner classes so I could post this code as one block.
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class MainContentPaneSwitching implements Runnable {
public static void main(String[] args) {
// Swing code must always be used in the Event Dispatch Thread.
SwingUtilities.invokeLater(new MainContentPaneSwitching());
}
private AnimationPane animationPane;
private BoatImage boatImage;
private CardLayout cardLayout;
private JPanel mainPanel;
public MainContentPaneSwitching() {
this.boatImage = new BoatImage();
}
#Override
public void run() {
JFrame frame = new JFrame("My application");
// Tells the frame that when the user closes it, it
// must terminate the application.
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.mainPanel = createMainPanel();
frame.add(mainPanel, BorderLayout.CENTER);
// Resize the frame as necessary in order to fit as many contents
// as possible in the screen.
frame.pack();
// Place the frame in the center of the screen. As you can tell, this
// needs its size to calculate the location, so we made sure in the
// previous line of code that it is set.
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private JPanel createMainPanel() {
cardLayout = new CardLayout();
JPanel panel = new JPanel(cardLayout);
panel.add(createButtonPanel(), "button");
animationPane = new AnimationPane(boatImage);
panel.add(animationPane, "image");
return panel;
}
private JPanel createButtonPanel() {
JPanel panel = new JPanel(new FlowLayout());
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
JButton changeContentPaneButton = new JButton(
"Click to go to the next image!");
changeContentPaneButton.addActionListener(
new ChangeContentPaneListener(this, boatImage));
panel.add(changeContentPaneButton);
return panel;
}
public JPanel getAnimationPane() {
return animationPane;
}
public void repaint() {
animationPane.repaint();
}
public class AnimationPane extends JPanel {
private static final long serialVersionUID = 1L;
private BoatImage boat;
public AnimationPane(BoatImage boat) {
this.boat = boat;
BufferedImage image = boat.getBoat();
this.setPreferredSize(new Dimension(image.getWidth(),
image.getHeight()));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
BufferedImage image = boat.getBoat();
int x = getWidth() - image.getWidth();
g.drawImage(image, x, boat.getyPos(), this);
}
}
private class ChangeContentPaneListener implements ActionListener {
private int direction, yPos;
private final MainContentPaneSwitching view;
private final BoatImage model;
public ChangeContentPaneListener(MainContentPaneSwitching view,
BoatImage model) {
this.view = view;
this.model = model;
this.direction = 1;
this.yPos = 0;
}
#Override
public void actionPerformed(ActionEvent e) {
cardLayout.show(mainPanel, "image");
}
}
public class BoatImage {
private int yPos;
private BufferedImage boat;
public BoatImage() {
try {
URL url = new URL("https://i.stack.imgur.com/memI0.png");
boat = ImageIO.read(url); // boat.jpg
} catch (MalformedURLException e) {
e.printStackTrace();
boat = null;
} catch (IOException e) {
e.printStackTrace();
boat = null;
}
this.yPos = 0;
}
public BufferedImage getBoat() {
return boat;
}
public void setyPos(int yPos) {
this.yPos = yPos;
}
public int getyPos() {
return yPos;
}
}
}
I have a container in which components can be dragged. The problem I encountered is whenever I picked up a component and it was automatically added to a window, the component would stop firing drag events even if the mouse was still dragging on the handle. Then the next time I would click and drag on the handle (now in the floating window) it would continue dragging. Here's some basic code:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.Box.Filler;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JWindow;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
import javax.swing.border.LineBorder;
/**
*
*/
#SuppressWarnings("serial")
public class DraggableDemo extends JPanel {
private class DraggablePanel extends JPanel {
private class MyMouseAdapter extends MouseAdapter {
private DraggablePanel floater;
private Point dragOffset;
#Override
public void mouseDragged(MouseEvent e) {
if (floater == null) {
startDragging(e);
}
Point transformedEventPoint = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), owner);
updateWindowLocation(transformedEventPoint);
}
private void startDragging(MouseEvent e) {
Component component = e.getComponent();
floater = (DraggablePanel) SwingUtilities.getAncestorOfClass(DraggablePanel.class, component);
Point floaterAbsoluteLocation = SwingUtilities.convertPoint(floater.getParent(), floater.getLocation(), owner);
Point transformedEventPoint = SwingUtilities.convertPoint(component, e.getPoint(), owner);
// designate a drag offset so the component's corner doesn't teleport to mouse location
dragOffset = new Point(transformedEventPoint.x - floaterAbsoluteLocation.x, transformedEventPoint.y - floaterAbsoluteLocation.y);
swapComponents(getFiller(), floater);
// place the floating component in a window
window.add(floater);
window.pack();
floater.setBorder(new LineBorder(Color.YELLOW));
updateWindowLocation(transformedEventPoint);
window.setVisible(true);
}
private void updateWindowLocation(Point point) {
Point p = new Point(point.x - dragOffset.x, point.y - dragOffset.y);
SwingUtilities.convertPointToScreen(p, owner);
window.setLocation(p);
}
private JComponent getFiller() {
Dimension dimension = floater.getSize();
JComponent filler = (JComponent) Box.createRigidArea(dimension);
// border
Border dashedBorder = BorderFactory.createDashedBorder(Color.gray, 3f, 3f, 2f, true);
filler.setBorder(dashedBorder);
filler.setOpaque(true);
return filler;
}
#Override
public void mouseReleased(MouseEvent e) {
Point transformedEventPoint = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), owner);
Component compDroppedOn = SwingUtilities.getDeepestComponentAt(owner, transformedEventPoint.x, transformedEventPoint.y);
if (compDroppedOn instanceof Filler) {
window.remove(floater);
swapComponents(floater, compDroppedOn);
window.setVisible(false);
floater = null;
}
}
}
private JWindow window;
private MyMouseAdapter mouseAdapter;
private DraggableDemo owner;
DraggablePanel(DraggableDemo owner) {
this.owner = owner;
JPanel smallPanel = new JPanel();
smallPanel.setPreferredSize(new Dimension(200, 100));
smallPanel.setBackground(Color.green);
JPanel bigPanel = new JPanel();
bigPanel.setPreferredSize(new Dimension(200, 400));
bigPanel.setBackground(Color.red);
setLayout(new BorderLayout());
add(smallPanel, BorderLayout.NORTH);
add(bigPanel, BorderLayout.CENTER);
setBorder(new LineBorder(Color.blue));
mouseAdapter = new MyMouseAdapter();
smallPanel.addMouseListener(mouseAdapter);
smallPanel.addMouseMotionListener(mouseAdapter);
owner.addMouseListener(mouseAdapter);
owner.addMouseMotionListener(mouseAdapter);
window = new JWindow();
window.setAlwaysOnTop(true);
}
}
private GridBagConstraints gbc;
/**
*
*/
public DraggableDemo() {
setLayout(new GridBagLayout());
gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.weightx = 1;
for (int i = 0; i < 2; i++) {
add(new DraggablePanel(this), gbc);
}
}
private static void createAndShowGUI() {
JFrame frame = new JFrame("Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DraggableDemo newContentPane = new DraggableDemo();
newContentPane.setOpaque(true);
frame.setContentPane(newContentPane);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGUI();
}
});
}
/**
*/
private void swapComponents(Component toAdd, Component toRemove) {
Container parent = toRemove.getParent();
int index = parent.getComponentZOrder(toRemove);
parent.remove(toRemove);
parent.add(toAdd, gbc, index);
revalidate();
repaint();
}
}
What I've tried:
The original container holds a few components per column, each firing different mouse events.
Initially I registered the mouse listener to the main container and got and moved the component based on coordinates, but that didn't satisfy the condition of mouse entering / exiting smaller components firing events themselves.
After this I attempted to register multiple mouse listeners, each doing its own thing, but I've learned that these would eat up events that would happen in the hierarchy.
Finally, I decided to register a single listener to every single component I needed to register to and based on the component instance returned from events, I would delegate these events to an appropriate mouse 'adapter'.
How can I fix the component in order to drag correctly when it is picked up? Like I've said, I can't register only to the main container, because then I wouldn't have access to events fired by smaller components, and I can't register multiple listeners because events would then be eaten up by the first one that fired.
whenever I picked up a component and it was automatically added to a window, the component would stop firing drag events
It is not just the component that stops firing the drag event. It appear that no drag event is generated for any component:
In your createAndShowGUI() method, after the frame is visible, I added:
long eventMask = AWTEvent.MOUSE_MOTION_EVENT_MASK + AWTEvent.MOUSE_EVENT_MASK;
Toolkit.getDefaultToolkit().addAWTEventListener( new AWTEventListener()
{
public void eventDispatched(AWTEvent e)
{
System.out.println(e.getID());
}
}, eventMask);
Which should display all mouse events generated for any component. However, once the Window is displayed, no further events are generated for any component, confirming your problem.
Next I removed the above code and replaced it with a custom EventQueue to simply display every event that is generated:
EventQueue queue = new EventQueue()
{
protected void dispatchEvent(AWTEvent event)
{
System.out.println(event);
super.dispatchEvent(event);
}
};
Toolkit.getDefaultToolkit().getSystemEventQueue().push(queue);
Now I do see all the mouse drag events.
So maybe you can create a custom EventQueue that handles mouse events that are generated on your DraggablePanel?
Using LayerUI to add labels to the upper corner of a tabbed pane. Would like to allow these labels to display as hyperlinks, so I set the color blue, the cursor to a hand and I added a mouselistener.
Howev,er when I paint the component the cursor customization and mouse listener are not not working.
sample image
Sample Application:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JLayer;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
import javax.swing.plaf.LayerUI;
public class TopRightCornerLabelLayerUITest {
public static JPanel makeUI() {
JPanel resultPanel = new JPanel();
resultPanel.setLayout( new BorderLayout());
resultPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
JTabbedPane tabbedPane = new JTabbedPane();
tabbedPane.add("Tab 1", new JPanel());
tabbedPane.add("Tab 2", new JPanel());
resultPanel.add(new JLayer<JComponent>(tabbedPane, new TopRightCornerLabelLayerUI()), BorderLayout.CENTER);
return resultPanel;
}
private static void initandShow()
{
JDialog dialog = new JDialog();
dialog.getContentPane().add(makeUI());
dialog.setSize(520, 240);
dialog.setLocationRelativeTo(null);
dialog.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
initandShow();
}
});
}
}
class TopRightCornerLabelLayerUI extends LayerUI<JComponent> {
private JPanel rubberStamp = new JPanel();
#Override public void paint(Graphics g, JComponent c) {
super.paint(g, c);
JLabel layoutHyperlink = new JLabel("<html><a href=''>File Layout and Descriptions</a></html>");
JLabel templateHyperlink = new JLabel("<html><a href=''>Download Template</a></html>");
layoutHyperlink.setForeground(Color.BLUE.darker());
layoutHyperlink.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
layoutHyperlink.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
// the user clicks on the label
System.err.println("clicked");
}
});
templateHyperlink.setForeground(Color.BLUE.darker());
templateHyperlink.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
templateHyperlink.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
// the user clicks on the label
System.err.println("clicked");
}
});
// Add components
Dimension templateDimension = templateHyperlink.getPreferredSize();
int x = c.getWidth() - templateDimension.width - 5;
SwingUtilities.paintComponent(g, templateHyperlink, rubberStamp, x, 2, templateDimension.width , templateDimension.height);
Dimension layoutDimension = layoutHyperlink.getPreferredSize();
x = c.getWidth() - layoutDimension.width - 15 - templateDimension.width;
SwingUtilities.paintComponent(g, layoutHyperlink, rubberStamp, x, 2, layoutDimension.width, templateDimension.height);
}
}
I was actually unaware of class JLayer until I read your question. I don't have a complete answer but I think it's enough to give you a push in the right direction. I was helped by the lesson in Oracle's Java tutorial: How to Decorate Components with the JLayer Class. That lesson has a section entitled Responding to Events which helped me to figure out how to partially solve your issue. Basically you are just painting the labels and not actually adding them as components and therefore they will not respond to mouse events. Since the labels can be considered part of the JLayer component that is added as a component, you can configure that JLayer to respond to mouse events. As stated in the tutorial lesson, you need to override some other methods in your TopRightCornerLabelLayerUI class. The code below contains two of those methods. Add them to your code and see if they give you the expected result.
public void installUI(JComponent c) {
super.installUI(c);
((JLayer<?>) c).setLayerEventMask(AWTEvent.MOUSE_EVENT_MASK);
}
protected void processMouseEvent(MouseEvent e, JLayer l) {
if (e.getID() == MouseEvent.MOUSE_CLICKED) {
Point pt = e.getPoint();
if (pt.x >= xTemplateHyperlink && pt.x <= (xTemplateHyperlink + widthTemplateHyperlink)) {
System.out.println("clicked");
}
}
}
EDIT:
Forgot to mention that I added the following members to your TopRightCornerLabelLayerUI class...
private int xTemplateHyperlink;
private int yTemplateHyperlink;
private int widthTemplateHyperlink;
private int heightTemplateHyperlink;
And set their values in method paint() like so...
Dimension templateDimension = templateHyperlink.getPreferredSize();
xTemplateHyperlink = c.getWidth() - templateDimension.width - 5;
yTemplateHyperlink = 2;
widthTemplateHyperlink = templateDimension.width;
heightTemplateHyperlink = templateDimension.height;
which explains the code in method processMouseEvent().
I have been trying to implement a GlassPane for my games' in-game HUD, but right now I cant seem to get the JFrame to set my GlassPane as its own i;ve used setGlassPane() and Ive been reading up a few examples trying to find my mistake, but nothing. So I wrote a SSCCE that demonstrates my problem. I have a JFrame to which I add a Jpanel with a label "TESTIING" then I initiate my galss pane and call setGlassPane() on my frames instance. My GlassPane has a MouseListener, a JPanel and 2 JLabels, and an overriden paint() however the MouseListener wont work the paint() wont show and my labels are not there (so basically my GlassPane is not being set as my frames new GlassPane)-
/*Main.java*/
import java.awt.EventQueue;
public class Main {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
TestGlassPane testGlassPane=new TestGlassPane();
testGlassPane.setVisible(true);
}
});
}
}
/*TestGlassPane.java*/
import java.awt.BorderLayout;
import java.awt.Container;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
class TestGlassPane extends JFrame{
private GlassGamePane m_glassPane;
private JPanel drawingPanel;
private JLabel testLabel;
public TestGlassPane() {
createUI();
}
private void createUI() {
setTitle("Test GlassGamePane");
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setSize(800, 700);
setResizable(false);
setLocationRelativeTo(null);
createComponents();
//add components to frames content pane
addComponentsToContentPane(getContentPane());
//setting glassPane
m_glassPane = new GlassGamePane();
//set opaque to false, i.e. make transparent
m_glassPane.setOpaque(false);
m_glassPane.setVisible(true);
getRootPane().setGlassPane(m_glassPane);
}
private void addComponentsToContentPane(Container contentPane) {
drawingPanel.add(testLabel);
contentPane.add(drawingPanel, BorderLayout.CENTER);
}
private void createComponents() {
drawingPanel=new JPanel(new BorderLayout());
testLabel=new JLabel("TESTIING");
}
}
/*GlassGamePane.java*/
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class GlassGamePane extends JPanel implements MouseListener {
private JPanel statusPanel;
private JLabel healthLabel;
public GlassGamePane() {
createGlassPane();
}
private void createGlassPane() {
setLayout(new BorderLayout());
createComponents();
statusPanel.add(healthLabel);
add(statusPanel, BorderLayout.NORTH);
addMouseListener(this);
}
private void createComponents() {
statusPanel = new JPanel(new GridLayout(2, 6));
healthLabel = new JLabel("Player Health:");
healthLabel.setForeground(Color.RED);
healthLabel.setBackground(Color.BLUE);
}
#Override
public void paintComponent(Graphics g) {
g.setColor(Color.red);
//Draw an oval in the panel
g.drawOval(10, 10, getWidth() - 20, getHeight() - 20);
}
#Override
public void mouseClicked(MouseEvent me) {
Toolkit.getDefaultToolkit().beep();
}
#Override
public void mousePressed(MouseEvent me) {
Toolkit.getDefaultToolkit().beep();
}
#Override
public void mouseReleased(MouseEvent me) {
}
#Override
public void mouseEntered(MouseEvent me) {
}
#Override
public void mouseExited(MouseEvent me) {
}
}
Thanks you.
It seems that you have to first add the custom GlassPane as your frames GlassPane before making the GlassPane visible! This code here seemed to work:
//setting glassPane
m_glassPane = new GlassGamePane();
setGlassPane(m_glassPane);
//set opaque to false, i.e. make transparent
m_glassPane.setOpaque(false);
m_glassPane.setVisible(true);
From the JavaDoc: http://docs.oracle.com/javase/7/docs/api/javax/swing/JRootPane.html#setGlassPane(java.awt.Component)
You need to set the glasspane to visible after adding it the JRootPane.
seems like as Glasspane is correcty created, but Glasspane to overlay only lightweight JComponent otherwise is behind, hidden be heavyweight Swing or AWT J/Component
Glasspane has not implemented any LayoutManager, you have to set proper LayoutManager
for testing put non opaque JLabel with some Background
I m love Glasspane, but you can to use JLayer (Java7) based on JXLayer (Java6) or OverlayLayout, as easiest of ways
I want to set the location of a JPopupMenu depending of the y location of the button that opens the menu. My code works fine on my first monitor, but fails on my second monitor, wich has a different height.
The problem is getLocationOnScreen() delivers the location relative to the main screen, not the actual screen on which the component is shown.
My code:
// screenSize represents the size of the screen where the button is
// currently showing
final Rectangle screenSize = dateButton.getGraphicsConfiguration().getBounds();
final int yScreen = screenSize.height;
int preferredY;
// getLocationOnScreen does always give the relative position to the main screen
if (getLocationOnScreen().y + dateButton.getHeight() + datePopup.getPreferredSize().height > yScreen) {
preferredY = -datePopup.getPreferredSize().height;
} else {
preferredY = getPreferredSize().height;
}
datePopup.show(DateSpinner.this, 0, preferredY);
How can I get the location of a component on its actual monitor?
I got a solution for this using the bounds of the second screen, it's quite simple:
public static Point getLocationOnCurrentScreen(final Component c) {
final Point relativeLocation = c.getLocationOnScreen();
final Rectangle currentScreenBounds = c.getGraphicsConfiguration().getBounds();
relativeLocation.x -= currentScreenBounds.x;
relativeLocation.y -= currentScreenBounds.y;
return relativeLocation;
}
Thanks for your answers!
Usually when you call "getLocationOnScreen()" it gets the location of the component "this" (from the code I don't quite understand who "this" is).
Maybe you can try to get location of the button by using "button.getLocationOnScreen()".
Here is a small snippet that shows how to position elements relatively to another one. It displays a popup menu below the button, and a JDialog to its left. I tested it on a multi-screen environment where secondary screen is on the right of the main one.
Also, use getSize(), getWidth() and getHeight() instead of getPreferredSize(). getSize(), getWidth and getHeight return the actual dimensions of the component, while getPreferredSize() is only an indicator to the LayoutManager to what the component wishes to have.
If you use the method JPopupMenu.show() make sure to use coordinates and sizes relative to the invoker component.
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
public class Test2 {
public static void main(String[] args) {
final JFrame frame = new JFrame();
final JButton button = new JButton("Hello");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JPopupMenu popupMenu = new JPopupMenu();
popupMenu.add(new JMenuItem("Some test"));
System.err.println(button.getLocationOnScreen());
popupMenu.show(button, 0, button.getHeight());
JDialog dialog = new JDialog(frame);
dialog.setSize(100, 30);
Point locationOnScreen = button.getLocationOnScreen();
locationOnScreen.x += button.getWidth();
dialog.setLocation(locationOnScreen);
dialog.setVisible(true);
}
});
frame.addComponentListener(new ComponentListener() {
#Override
public void componentShown(ComponentEvent e) {
}
#Override
public void componentResized(ComponentEvent e) {
info(button);
}
private void info(final JButton button) {
if (button.isShowing()) {
System.err.println(button.getLocationOnScreen());
System.err.println(button.getGraphicsConfiguration().getBounds());
}
}
#Override
public void componentMoved(ComponentEvent e) {
info(button);
}
#Override
public void componentHidden(ComponentEvent e) {
}
});
button.setPreferredSize(new Dimension(200, 60));
frame.add(button);
frame.pack();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setVisible(true);
}
}