MouseEvent does not fire when implemented by a JPanel - java

Okay, so in this program I'm making, users will be able to create shortcuts to their favorite apps on their computer. My program will be kind of like a hub, I guess, for apps. I have a small problem though, which involves two classes: AppButton and AppButtonContainer. They both implement MouseListener, but AppButton extends JComponent and AppButtonContainer extends JPanel. Basically, when an AppButton is clicked, it sets a draws the border in a different color to make it look selected. Otherwise, it sets the border to the background color. When you double click it, it opens up the application specified. I have a method in AppButton to remove the focus, and therefore setting the border to the background color. In AppButtonContainer, there is a bit of code so that when, the panel is clicked, it removes the focus from the AppButton.
That's my problem, though. The AppButtonContainer doesn't realize that it's clicked. I'm thinking it has something to do with top level containers or something, but I'm not sure. Can anybody help?
EDIT: I found out that I didn't put the addMouseListener(this) in the constructor of the AppButtonContainer. Thank you for everyone who helped me clear up this problem and give me tips along the way :)
AppButtonContianer:
public class AppButtonContainer extends JPanel implements MouseListener {
private static final long serialVersionUID = 6485231881729120957L;
public List<AppButton> appButtons;
private static final Color BACKGROUND_COLOR = new Color(18, 18, 18);
public AppButtonContainer(List<AppButton> buttons) {
this.appButtons = buttons;
setLayout(new GridLayout(5, 5, 20, 20));
addButtonsToPane();
}
private void addButtonsToPane() {
List<AppButton> buttons = this.appButtons;
for (int i = 0; i < buttons.size(); i++) {
this.add(buttons.get(i));
}
}
private void removeAllButtonFocus() {
for (int i = 0; i < this.appButtons.size(); i++) {
this.appButtons.get(i).removeFocus();
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
setBackground(BACKGROUND_COLOR);
repaint();
}
#Override
public void mouseClicked(MouseEvent e) {
System.out.println("Pane Clicked");
removeAllButtonFocus();
}
...Other MouseEvent methods

You can solve the problem at hand by putting addMouseListener(this) in the constructor for your AppButtonContainer class. Otherwise, it'll never pick up mouse events.
Generally, though, it's not good to turn your classes into mouselisteners like that. Perhaps try making an inner class to listen for mouse events and pass them to the AppButtonContainer instead.

Related

When I modify a JPanel after it becomes visible, paintComponent gets called before componentResized

I have a data plot with a color bar that's a JPanel with a layout that has two JPanels inside of it. One JPanel is the data plot itself, and the other is the color bar. I'd like to add functionality so the color bar can be toggled on and off, and I've gone about this by simply removing the JPanel containing the color bar. Something like this:
public class Data2DPlotWithColorBar extends JPanel {
public Data2DPlotWithColorBar() {
this.data2DPlot = new Data2DPlot();
this.colorBar = new VerticalColorBar();
this.setPlot();
}
public final void toggleColorBar() {
enableColorBar = !enableColorBar;
setPlot();
}
private void setPlot() {
this.removeAll();
this.setLayout(new BorderLayout());
if (enableColorBar) {
this.add(colorBar, BorderLayout.EAST);
}
this.add(this.data2DPlot, BorderLayout.CENTER);
this.revalidate();
this.repaint();
}
private final Data2DPlot data2DPlot;
private final VerticalColorBar colorBar;
private boolean enableColorBar;
}
The problem is that when the color bar is removed, the data plot has a component listener with the componentResized method overrided which correctly resizes the data (maintains fixed aspect ratio) to fit the size of the JPanel. Something like this:
public class Data2DPlot extends JPanel {
...
#Override
public final void componentResized(ComponentEvent e) {
double scaleFactorBuf = Math.min((double)getPixelMaxViewWidth()/getNativeWidth(),
(double)getPixelMaxViewHeight()/getNativeHeight());
// Make sure scaleFactorBuf isn't close to zero
if (Math.abs(scaleFactorBuf) > MathUtilities.LAMBDA) {
scaleFactor = scaleFactorBuf;
}
}
...
#Override
protected final void paintComponent(Graphics g) {
super.paintComponent(g);
....
}
}
It turns out that as-is, the dataplot is not resizing properly. I did some debugging and I found out that componentResized gets called AFTER the paintComponent method when I toggle the color bar off and on. This means the image gets painted, and then the scaleFactor gets updated afterwards, which is incorrect. The only way I've been able to fix it so far is to call repaint() at the very end of the componentResized method. However, repaint() is already called when the component is resized, so I feel like this is the incorrect approach. Some googled led me to solutions involving the use of revalidate and repaint after modifying a JPanel on demand. However, any combination of doing this still led to componentResized being called after repaint. Is there a standard fix for this?
An answer proposed in this thread offers an easy solution; rather than overriding the componentResized method, do the setBounds(int,int,int,int) one.
The call order of componentResized, setBounds, and repaint is strange; on program startup it is like this;
setBounds
componentResized
repaint
while if you manually resize it later (I did not test with in-code resizing order) it goes
setBounds
repaint
componentResized
By setting your flags in setBounds rather than componentResized, you can know to recompute your repaint size-sensitive variables on panel resizing, effective immediately.

Custom JFrame movement "slipping"

Recently, I have been trying to make my gui's look good and part of it is implementing my own titlebar, maximize button, minimize button, etc.
I am having issues on the dragging, I want to be able to drag the JFrame by clicking anywhere in it.
I attempted to create a mousemotionlistener to handle it however it slips when I use it. As in I attempt to drag the window to a location however it appears to be skipping calls to mouseDragged and attempting to call mouseMoved instead.
Here is my code
public class MouseWindowDragManager implements MouseMotionListener{
private JFrame frame;
int prevPosX = -1;
int prevPosY = -1;
public MouseWindowDragManager(JFrame frame){
this.frame = frame;
}
#Override
public void mouseDragged(MouseEvent e) {
int newX = (frame.getLocation().x + (e.getXOnScreen() - prevPosX));
int newY = (frame.getLocation().y + (e.getYOnScreen() - prevPosY));
frame.setLocation(newX, newY);
prevPosX = e.getXOnScreen();
prevPosY = e.getYOnScreen();
}
#Override
public void mouseMoved(MouseEvent e) {
prevPosX = e.getXOnScreen();
prevPosY = e.getProperty("apple.awt.draggableWindowBackground", true);tYOnScreen();
}
}
Recently, I have been trying to make my gui's look good and part of it is implementing my own titlebar, maximize button, minimize button, etc.
Gee, Microsoft and Apple spend millions of dollars to develop GUI's that users can use. They will be disappointed to hear this :)
I want to be able to drag the JFrame by clicking anywhere in it.
Check out Moving Windows for a class that allows you to drag a window around. The example code shows how to use the class by just dragging on your custom title bar. However, if you really want to be able to drag the frame by clicking anywhere then you can register the frame's root pane with the ComponentMover.

Java swing: Models and Button color changes

I'm super confused with what exactly models are supposed to do in java swing. At present I'm basically trying to create a model for JButton to detect if it isPressed(); My essential goal of the model is to do something like this:
if(myButton.isPressed() ) {
myButton.setBackground(Color.RED);
}
else{//when any other button is pressed?
myButton.setBackground(Color.WHITE);
}
At present my code is something like this:
numberButton = new JButton("Num");
numberButton.setBounds(20,40,80,30);
numberButton.addChangeListener(new ChangeListener() {
public void stateChanged (ChangeEvent e){
if (model.isPressed() ){
doColorChange(model);
}
}
});
I understand that this is totally wrong, but I have no idea where, and I haven't found a tutorial that really explains what I'm doing wrong or why I need a model for this at all.
Please help me restore my sanity! Thanks a lot!
Use radio buttons in a button group.
import java.awt.image.BufferedImage;
import java.awt.*;
import javax.swing.*;
import java.net.URL;
import javax.imageio.ImageIO;
class RedAndWhite {
public static Image getColoredImage(Color color, int size) {
BufferedImage bi = new BufferedImage(
size,
size,
BufferedImage.TYPE_INT_RGB);
Graphics g = bi.getGraphics();
g.setColor(color);
g.fillRect(0,0,size,size);
g.dispose();
return bi;
}
public static void main(String[] args) throws Exception {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
Image red = getColoredImage(Color.RED, 32);
Image white = getColoredImage(Color.WHITE, 32);
JPanel p = new JPanel(new GridLayout(1,0,5,5));
ButtonGroup bg = new ButtonGroup();
for (int ii=0; ii<6; ii++) {
JRadioButton b = new JRadioButton();
b.setSelectedIcon(new ImageIcon(red));
b.setIcon(new ImageIcon(white));
b.setContentAreaFilled(true);
bg.add(b);
p.add(b);
}
JOptionPane.showMessageDialog(null, p);
}
});
}
}
I think what you want is register an ActionListener or Action with the button in order to handle button presses, right?
Edit: rereading your answer, it seems you want to highlight the button that is being pressed, right? In that case, try to use your own button ui (subclass the one of the look and feel you're using). BasicButtonUI has a method protected void paintButtonPressed(Graphics g, AbstractButton b) that you could override to apply the highlight when the button is being pressed.
there are follows ways how to make JButton nicer
1) most nicest way is painting only Borders from ButtonModel on Mouse events
2) override whole BasicButtonUI with all rellevant Look and Feels
3) put there Icons and override all possible methods implemented in JButton API
4) best at all would be implement Custom Look and Feel
5) create Custom JComponent based on JLabel/JComponent
I always took it that the ButtonModel is intended (mostly) for use by the UI class to render the button in its pressed/not-pressed/armed/selected/... state and track changes to that state.
If you simply want to paint the button red (that is waht you want?) while it is pressed your solution seems fine to me.
If you have a Toggle Button (that remains pressed after it has been clicked to indicate a "selected" state, you might want to use an ItemListener and check for
ItemEvent.ITEM_STATE_CHANGED == ItemEvent.SELECTED // paint red
ItemEvent.ITEM_STATE_CHANGED == ItemEvent.DESELECTED // paint white
If you want to execute application logic when the button is clicked, use an ActionListener.

MouseListener MouseDragged doesnt work as expected

I have a grid where the boxes become red (I basically want to paint the grid) when I left click, hold and move the cursor on them (i.e: drag mouse). I have the code below. When I do the mouse dragging. The MouseDragged method is called properly however only one box becomes red and nothing happens while I drag after that (although the method is still called). Any ideas ? Hope I was clear. Thanks
public static class DragListener implements MouseMotionListener
{
#Override
public void mouseDragged(MouseEvent me) {
JPanel current =(JPanel)me.getSource();
current.setBackground(Color.RED);
}
}
This is the definition of the grid:
public static class GridPane extends JPanel {
public GridPane(int row, int col) {
int count = 0 ;
setLayout(new GridLayout(row, col));
setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
for (int i = 1; i <= (row * col); i++) {
JPanel lab = new JPanel();
lab.setEnabled(true);
lab.setBackground(Color.WHITE);
lab.setPreferredSize(new Dimension(3, 3));
lab.setBorder(BorderFactory.createLineBorder(Color.BLACK));
lab.addMouseMotionListener(new DragListener());
lab.addMouseListener(new ClickListener());
lab.setName(count+"");
++count;
add(lab);
}
}
}
You need something like this
public MouseMotionEventDemo() {
addMouseMotionListener(this);
setVisible(true);
}
public void mouseMoved(MouseEvent me) {
mX = (int) me.getPoint().getX();
mY = (int) me.getPoint().getY();
repaint();
}
public void mouseDragged(MouseEvent me) {
mouseMoved(me);
}
I think your problem is stemming from the fact that you are dragging your mouse over multiple JPanels, and because of how java recognizes dragging. Java recognizes dragging by the following algorithm:
Within a single Component "c":
The mouse is pressed while inside "c"
The mouse is then moved around inside "c" - this constitutes dragging within "c"
Because your mouse is ultimately leaving one component and entering a second component while pressed, the second component never registers the mousePressed action, so it doesn't think you're dragging the mouse. I'd recommend maintaining some flag that tells your GridPane when the mouse is pressed inside ANY of your "lab" JPanels (mousePressed()), then implement the mouseMoved() method to check that flag and color if it has been set. Then implement mouseReleased() to reset your flag back to the normal state so you don't continue coloring after you stop dragging.
For more info on mouse motion in Java, check out this link:
http://docs.oracle.com/javase/tutorial/uiswing/events/mousemotionlistener.html

.drawLine on event (button click) Jpanel/Jbutton/JTabbedPane

I know how to do action listeners for button clicks in/on swing, but I have this class which does some stuff but I want it a function/event that when a button is clicked it runs a method similiar to the PaintComponent below... (draws a line)
class CustomPanel extends JPanel {
private int destx = 100;
private int desty = 100;
private int startx = 0;
private int starty = 0;
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawLine(startx, starty, destx, desty);
}
}
How would I call this paintcomponent (Or a similar one which draws a line) from a action listener?
Here is my actionlistener: (Its on GUI.java while the code above is on CustomPanel.java)
public void actionPerformed(ActionEvent e)
{
if (e.getSource() == loginButton)
{
//Does other stuff but removed for simplifying
CustomPanel cp = new CustomPanel();
}
}
Thanks alot,
Your question didn't make sense to me in your last posting and it still doesn't make sense to me in this posting.
You still haven't posted a SSCCE that attempts to demonstrate what you want to do.
If you have a "login panel", typically that is done by creating a modal JDialog.
If you are trying to draw a diagonal across the top of all components in the frame, then you would need to use a Glass Pane or a Layered Pane.
Read the section from the Swing tutorial on How to Use Root Panes for examples and more detailed information.
You need to add it to gui. Something like this:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
parentPanel.add(new CustomPanel());
parentPanel.revalidate();
parentPanel.repaint();
}
});
But if you only want to draw a line on the current container that's another thing...
Simply adding your CustomPanel to any other JComponent and updating the UI should do the trick. Swing takes care of all the painting for you.
Here is a reslly useful guide to swing painting;
http://java.sun.com/products/jfc/tsc/articles/painting/#paint_process

Categories