Here's (a siplified version of) what I have. (I found the code that way):
class CustomPopup extends JPopupMenu {
public CustomPopup() {}
#Override
public void setVisible(boolean visible)
{
// Case 1:
//if (visible) super.setVisible(visible);
// Case 2:
super.setVisible(visible);
}
}
class CustomPanel extends JPanel {
// .../...
public CustomPanel() {
setSize(200, 200);
addMouseListener( new MouseAdapter(){
#Override
public void mousePressed( MouseEvent e ){
onMousePressed( e );
}
});
}
public void onMousePressed( MouseEvent e )
{
JPopupMenu pop = new JPopupMenu();
pop.add( new AbstractAction( "foo" )
{
#Override
public void actionPerformed( ActionEvent e )
{
// do stuff
System.out.println("this is executed");
}
});
pop.show( e.getComponent(), e.getX(), e.getY() );
}
}
public class TestPopup extends JFrame {
CustomPanel _pp;
CustomPopup _cpop;
public TestPopup () {
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(333, 333);
_cpop = new CustomPopup();
_pp = new CustomPanel();
_cpop.add(_pp);
addMouseListener( new MouseAdapter(){
#Override
public void mousePressed( MouseEvent e ){
_cpop.show(e.getComponent(), 0, 0);
}
});
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
(new TestPopup()).setVisible(true);
}
});
}
}
A popup menu displays a custom panel. When interacting with it, it show a classical popup menu with a list of choices.
My problem is that the CustomPopup doesn't close itself as it should. The culprit is obviously the override of setVisible, BUT if I remove the override method (or comment out the condition) I have another problem : the actionPerformed method added on pop is never called.
In the first case:
CustomPopup opens
I click on something in it
The JPopupMenu opens and the CustomPopup remains visible
I click on an item of the new menu
The Action is triggered
In the second case:
CustomPopup opens
I click on something in it
The JPopupMenu opens and the CustomPopup is hidden
I click on an item of the new menu
The Action is not triggered
My conclusion is that the CustomPopup must be visible for the action to trigger, even if I don't really understand why. So my question is: how to keep CustomPopup open in a way that doesn't break the default JPopupMenu behavior OR how make the action trigger properly with CustomPopup hidden?
It's looks that the parent of popup must be visible for action triggering (sounds plausible for me). So you made all to get it working, except the last step: close the custom popup by yourself. Here is the code:
class CustomPopup extends JPopupMenu {
public CustomPopup() {}
#Override
public void setVisible(boolean visible)
{
// Case 1:
if (visible) super.setVisible(visible);
// Case 2:
// super.setVisible(visible);
}
public void makeInvisible() {
super.setVisible(false);
}
}
class CustomPanel extends JPanel {
// .../...
public CustomPanel() {
setSize(200, 200);
addMouseListener( new MouseAdapter(){
#Override
public void mousePressed( MouseEvent e ){
onMousePressed( e );
}
});
}
public void onMousePressed( MouseEvent e )
{
JPopupMenu pop = new JPopupMenu();
pop.add( new AbstractAction( "foo" )
{
#Override
public void actionPerformed( ActionEvent e )
{
// do stuff
System.out.println("this is executed");
Component comp = (Component) e.getSource();
if (comp != null && comp.getParent() instanceof JPopupMenu) {
JPopupMenu popupMenu = (JPopupMenu) comp.getParent();
if (popupMenu.getInvoker() instanceof CustomPanel) {
CustomPopup cpop = (CustomPopup) popupMenu.getInvoker().getParent();
cpop.makeInvisible();
}
}
}
});
pop.show( e.getComponent(), e.getX(), e.getY() );
}
}
public class TestPopup extends JFrame {
CustomPanel _pp;
CustomPopup _cpop;
public TestPopup () {
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(333, 333);
_cpop = new CustomPopup();
_pp = new CustomPanel();
_cpop.add(_pp);
addMouseListener( new MouseAdapter(){
#Override
public void mousePressed( MouseEvent e ){
_cpop.show(e.getComponent(), e.getX(), e.getY());
}
});
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
(new TestPopup()).setVisible(true);
}
});
}
}
Related
I'm blocked with a probelm about Java and the use of JTree:
I want to create a JTree with, node by node, some JButtons components (or Images, I don't mind), like in the following picture. It will be 3 or 4 buttons in the same row. I succeed to do that.
But where I'm blocked is when I want to add a mouselistener on each of this button to manage their tooltip or an action on them.
In fact the JTree component is most of the time used to manage the action on the full node, but not on its inside components.
I did a short code, in comparaison at the real big code I have to work in, to quickly test what I say:
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.tree.*;
import java.awt.event.*;
import java.awt.*;
import java.io.IOException;
import java.net.URL;
public class TreeWithPopup extends JPanel {
DefaultMutableTreeNode root, node1, node2, node3;
public TreeWithPopup() {
MyJTree tree;
root = new DefaultMutableTreeNode("root", true);
node1 = new DefaultMutableTreeNode("node 1", true);
node2 = new DefaultMutableTreeNode("node 2", true);
node3 = new DefaultMutableTreeNode("node 3", true);
root.add(node1);
node1.add(node2);
root.add(node3);
setLayout(new BorderLayout());
tree = new MyJTree(root);
tree.setCellRenderer(new PCellRenderer());
add(new JScrollPane((JTree) tree), "Center");
}
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
public static void main(String s[]) {
JFrame frame = new JFrame("Tree with button");
TreeWithPopup panel = new TreeWithPopup();
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.setForeground(Color.black);
frame.setBackground(Color.lightGray);
frame.getContentPane().add(panel, "Center");
frame.setSize(panel.getPreferredSize());
frame.setVisible(true);
frame.addWindowListener(new WindowCloser());
}
}
class WindowCloser extends WindowAdapter {
public void windowClosing(WindowEvent e) {
Window win = e.getWindow();
win.setVisible(false);
System.exit(0);
}
}
class MyJTree extends JTree implements ActionListener {
MyJTree(DefaultMutableTreeNode dmtn) {
super(dmtn);
addMouseListener(new MouseListener() {
public void mouseClicked(MouseEvent arg0) {
System.out.println("JTree.MouseListener");
}
public void mouseEntered(MouseEvent arg0) {
System.out.println("JTree.MouseListener");
}
public void mouseExited(MouseEvent arg0) {
System.out.println("JTree.MouseListener");
}
public void mousePressed(MouseEvent arg0) {
System.out.println("JTree.MouseListener");
}
public void mouseReleased(MouseEvent arg0) {
System.out.println("JTree.MouseListener");
}
});
}
public void actionPerformed(ActionEvent ae) {
System.out.println("MyJTree.ActionPerformed");
}
}
class PCellRenderer extends DefaultTreeCellRenderer {
public Component getTreeCellRendererComponent(JTree ptree, Object pvalue, boolean psel, boolean pexpanded, boolean pleaf, int prow,
boolean phasFocus) {
Box myPanel = new Box(BoxLayout.X_AXIS);
JButton myButton = new JButton("test");
Image imgToUse = null;
Image imgRollOver = null;
try {
URL urlIcon = new URL("file:///C:/1.jpg"); // <===== change their the path to icons
imgToUse = ImageIO.read(urlIcon);
urlIcon = new URL("file:///C:/2.jpg"); // <===== change their the path to icons
imgRollOver = ImageIO.read(urlIcon);
} catch (IOException e) {
e.printStackTrace();
}
myButton.setRolloverIcon(new ImageIcon(imgRollOver));
myButton.setIcon(new ImageIcon(imgToUse));
myButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
System.out.println(" detected on ");
}
});
myButton.addMouseListener(new MouseListener() {
public void mouseClicked(MouseEvent arg0) {
System.out.println("myButton.MouseListener");
}
public void mouseEntered(MouseEvent arg0) {
System.out.println("myButton.MouseListener");
}
public void mouseExited(MouseEvent arg0) {
System.out.println("myButton.MouseListener");
}
public void mousePressed(MouseEvent arg0) {
System.out.println("myButton.MouseListener");
}
public void mouseReleased(MouseEvent arg0) {
System.out.println("myButton.MouseListener");
}
});
myPanel.add(myButton);
return myPanel;
}
}
You just have to change the icon path or put the two following icons at "c:/"
I also searched to use the x/y position of the row event but I was unable to find the position of my button after rendering.
If anyone can have an idea how to do that, he could be very helpful.
Thanks, at least for read this question ;-)
Tooltip support is actually provided by the TableCellRenderer directly. Your TableCellRenderer is a little muddled. It extends from DefaultTreeCellRenderer but makes no use of any of it's features, instead creating a new Box, JButton and loading icons each time a cell is rendered...
This is going to increase your memory usage a slow you application down...
Instead, try something like...
class PCellRenderer extends Box implements TreeCellRenderer {
private Image imgToUse = null;
private Image imgRollOver = null;
public PCellRenderer() {
super(BoxLayout.X_AXIS);
JButton myButton = new JButton("test");
try {
URL urlIcon = new URL("file:///C:/1.jpg"); // <===== change their the path to icons
imgToUse = ImageIO.read(urlIcon);
urlIcon = new URL("file:///C:/2.jpg"); // <===== change their the path to icons
imgRollOver = ImageIO.read(urlIcon);
} catch (IOException e) {
e.printStackTrace();
}
myButton.setRolloverIcon(new ImageIcon(imgRollOver));
myButton.setIcon(new ImageIcon(imgToUse));
add(myButton);
}
public Component getTreeCellRendererComponent(JTree ptree, Object pvalue, boolean psel, boolean pexpanded, boolean pleaf, int prow,
boolean phasFocus) {
// set the tooltip text here...
// Maybe change the icon...
return this;
}
}
Now, actually doing something...
Renderers are rubber stamps. That are not actually life components. Think of them like photos. You can take a snap shot of your friends, but you can't interact with them...same thing here...
Your idea of a MouseListener on the JTree is a correct one, in fact the JavaDocs actually have a demonstration of this...
public void mousePressed(MouseEvent e) {
int selRow = tree.getRowForLocation(e.getX(), e.getY());
TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());
if(selRow != -1) {
if(e.getClickCount() == 1) {
mySingleClick(selRow, selPath);
}
else if(e.getClickCount() == 2) {
myDoubleClick(selRow, selPath);
}
}
}
So I have a class that extends a JPanel and within the constructor I add my JButtons and whatever else I need to add. I also have a MainFrame class that is the container (JFrame) and this class will take an argument from a class called FrameSwitcher (Controller) which will assess what buttons were clicked, and pass the information to the MainFrame
I'm having troubles doing this, I can't find a proper way to do this. I do also wish to maintain the JButtons private and non static.
JPanel example:
public class MainMenu() {
private JButton btnSinglePlayer, btnMultiPlayer;
public MainMenu() {
setLayout(null);
btnSinglePlayer = new JButton("singlePlayer");
btnSinglePlayer.setBounds(320, 25, 275, 130);
add(btnSinglePlayer);
btnMultiPlayer = new JButton("MultiPlayer");
btnMultiPlayer.setBounds(320, 170 , 275, 130);
add(btnMultiPlayer);
}
}
FrameSwitcher:
public class FrameSwitcher implements panelListener { // panelListener is an interface defined else where.
public FrameSwitcher(MainFrame frame) {
// This is irrelevant to the question.
}
#Override
public void gamePanel() {
System.out.println("GamePanel Event: Recieved");
}
#Override
public void mainMenu() {
System.out.println("mainMenu Event: Recieved");
}
#Override
public void scoreBoardPanel() {
System.out.println("scoreBoardPanel Event: Recieved");
}
}
Then my MainFrame:
public class MainFrame extends JFrame implements ActionListener {
private PanelListener panelListener;
private JFrame mainContainer = new JFrame("Game");
private JPanel mainMenu = new MainMenu();
public void start() {
mainContainer(mainMenu);
}
public MainFrame(JPanel frame) {
mainContainer.getContentPane().add(frame);
mainContainer.pack();
// Other methods to initialize the frame
return mainContainer;
}
public void switchFrames(PanelListener panelListener) {
this.panelListener = panelListener; // PanelListener is an interface.
}
public void actionPerformed(ActionEvent e) {
JButton source = (JButton)e.getsource();
if(source == MainMenu.btnSinglePlayer) {
if(panelListener != null) {
System.out.println("Recieved the event approriately.");
}
}
}
}
In this example, it does compile, but doesn't do what it is supposed to. Another thing is I currently have the JButtons as public and static, I don't want that.
In your MainMenu class, you need to add some kind of listener that interested parties can register with, so when some event occurs, they can be notified.
The simplest solution would be to provide a addActionListener method which delegated to each of the buttons. This, however, has may expose portions of the application you don't exposed (a listener now has direct access to the JButton and can do all kinds of nasty things to it).
A better solution would be to create something like a MainMenuListener which had methods like startSinglePlayer and startMultiPlayer
You would then provide a add/removeMainMenuListener method within in your MainMenu class.
Each button would then register there own actionListener and fire the appropriate menu listener event
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.EventListener;
import javax.swing.JButton;
import javax.swing.JPanel;
public class MainMenu extends JPanel {
private JButton btnSinglePlayer, btnMultiPlayer;
public MainMenu() {
setLayout(null);
btnSinglePlayer = new JButton("singlePlayer");
btnSinglePlayer.setBounds(320, 25, 275, 130);
add(btnSinglePlayer);
btnSinglePlayer.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
fireStartSinglePlayer();
}
});
btnMultiPlayer = new JButton("MultiPlayer");
btnMultiPlayer.setBounds(320, 170, 275, 130);
add(btnMultiPlayer);
btnMultiPlayer.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
fireStartMultiPlayer();
}
});
}
public void addMainMenuListener(MainMenuListener listener) {
listenerList.add(MainMenuListener.class, listener);
}
public void removeMainMenuListener(MainMenuListener listener) {
listenerList.remove(MainMenuListener.class, listener);
}
public void fireStartSinglePlayer() {
MainMenuListener[] listeners = listenerList.getListeners(MainMenuListener.class);
if (listeners != null && listeners.length > 0) {
for (MainMenuListener listener : listeners) {
listener.startSinglePlayer();
}
}
}
public void fireStartMultiPlayer() {
MainMenuListener[] listeners = listenerList.getListeners(MainMenuListener.class);
if (listeners != null && listeners.length > 0) {
for (MainMenuListener listener : listeners) {
listener.startMultiPlayer();
}
}
}
public interface MainMenuListener extends EventListener {
public void startSinglePlayer();
public void startMultiPlayer();
}
}
First you have to create an ActionListener like this
class MyActionListener implements ActionListener{
public void actionPerformed(ActionEvent ae){
//do your stuff
}
}
Then call,
yourButton.addActionListener(new MyActionListener());
I have a JDialog as a Popup which shows up for 3 seconds and dispose.
It comes up at cursor position and has to dispose only if the cursor exits the popup.
If the cursor entered the Popup the timer stops and start again on exit.
But my first idea with a dispose-Timer that starts and stops via MouseListener doesn't work with some JComponents, which causes a mouseExited().
My second idea will never start the Timer
public void mouseExited( MouseEvent e ) {
if(!Popup.this.getBounds().contains( e.getLocationOnScreen() )){
timer.start();
}
}
I don't want to add the Listener to every component in the popup.
Is there an easy way to do that.
Example:
public class Popup extends JDialog {
private static final long serialVersionUID = 1337L;
private final Timer timer = new Timer( 3000, new ActionListener() {
#Override
public void actionPerformed( ActionEvent e ) {
Popup.this.dispose();
System.exit( 0 );
}
});
public Popup() {
setBounds( 100, 100, 300, 300 );
addMouseListener( new PopupBehavior() );
getContentPane().setLayout( new BorderLayout() );
getContentPane().add( new JTextArea(), BorderLayout.NORTH );
getContentPane().add( new JSplitPane(0,new JPanel(), new JLabel("2")), BorderLayout.CENTER );
getContentPane().add( new JProgressBar(), BorderLayout.SOUTH );
getContentPane().add( new JLabel("west"), BorderLayout.WEST );
getContentPane().add( new JSpinner(), BorderLayout.EAST );
}
public static void main( String[] args ) {
SwingUtilities.invokeLater( new Runnable() {
#Override
public void run() {
new Popup().setVisible( true );
}
});
}
private class PopupBehavior extends MouseAdapter {
#Override
public void mouseEntered( MouseEvent e ) {
System.out.println("mouseEntered");
timer.stop();
}
#Override
public void mouseExited( MouseEvent e ) {
System.out.println("mouseExited");
timer.start();
}
}
}
As of jdk7 you can decorate your container with a JLayer and stop/start the timer in its processMouseEvent
class DisposingLayerUI extends LayerUI {
#Override
public void installUI(JComponent c) {
super.installUI(c);
JLayer jlayer = (JLayer)c;
jlayer.setLayerEventMask(
AWTEvent.MOUSE_EVENT_MASK
);
}
#Override
public void uninstallUI(JComponent c) {
JLayer jlayer = (JLayer)c;
jlayer.setLayerEventMask(0);
super.uninstallUI(c);
}
#Override
protected void processMouseEvent(MouseEvent e, JLayer l) {
if (e.getID() == MouseEvent.MOUSE_ENTERED) {
timer.stop();
} else if (e.getID() == MouseEvent.MOUSE_EXITED){
timer.start();
}
}
}
// to use in your code, do all init and then decorate the contentPane
...
setContentPane(new JLayer(getContentPane(), new DisposingLayerUI()));
For older versions, you can use the layer from the JXLayer project (it's were the current was developed) or go for a manually implements global listener, as f.i. shown in Global Event Listeners
just for example JButton (and as I know majorities of JComponents) has Model
if you'll start javax.swing.Timer from Model or from mousemoved out from pre-defined Rectangle
if mouse returns back to the pre-defined Rectangle then just restart javax.swing.Timer
excelent sugestion since about JToolTip here
I am trying to catch an event of user clicking on and "X" button of a JDialog and only close if a user confirms. So here is skeleton of what I am doing:
public class MyDialog extends JDialog {
public MyDialog(){
super();
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
.........
}
.........
}
public class Waiter implements WindowStateListener{
#Override
public void windowStateChanged(WindowEvent event) {
System.out.println(event);
if (event.getNewState() == WindowEvent.WINDOW_CLOSING) {
if (shouldClose()) {
dialog.close();
}
}
}
}
MyDialog dialog = new MyDialog();
Waiter waiter = new Waiter();
dialog.addWindowStateListener(waiter);
As you can guess, when I click on "X" for the dialog, I do not get a message printed becasue the methodis never called. I am not sure where is the problem.
You want to use a WindowListener instead of a WindowStateListener.
Try this:
MyDialog dialog = new MyDialog();
dialog.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(final WindowEvent event) {
System.out.println(event);
if (shouldClose()) {
dialog.close();
}
}
});
My problem is when I right click on the JFrame in the example below, the JPopupMenu shows up but if I click anywhere outside the JFrame, the menu does not disappear. I have to click somewhere on the JFrame to get rid of it which is not the expected behavior. Here are the steps to reproduce:
Run the window class from Eclipse (JFrame appears)
Click into the Eclipse workspace (JFrame loses focus and is hidden behind eclipse)
Minimize Eclipse (JFrame appears)
Go with your mouse over JFrame and make a right click (Popup appears)
Click somewhere (Not into JFrame or Popup). The Popup will not disappear
I'm running OS X 10.6.7 and Java full version 1.6.0_24-b07-334
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class test
{
static class window extends JFrame implements MouseListener,
MouseMotionListener
{
JPopupMenu popMenu;
JPanel panel = new JPanel();
Point location;
MouseEvent pressed;
public window()
{
addMouseListener(this);
addMouseMotionListener(this);
JLabel label = new JLabel("JFrame", JLabel.CENTER);
initPopMenu();
add(label);
setUndecorated(true);
setVisible(true);
// setAlwaysOnTop(true);
setLocationRelativeTo(null);
pack();
}
public void initPopMenu()
{
popMenu = new JPopupMenu();
JMenuItem item;
item = new JMenuItem("Title");
item.setEnabled(false);
popMenu.add(item);
popMenu.addSeparator();
item = new JMenuItem("Item One");
popMenu.add(item);
item = new JMenuItem("Item 2");
popMenu.add(item);
item = new JMenuItem("Item 3");
popMenu.add(item);
}
public void mousePressed(MouseEvent e)
{
pressed = e;
int nModifier = e.getModifiers();
if (((nModifier & InputEvent.BUTTON2_MASK) != 0)
|| ((nModifier & InputEvent.BUTTON3_MASK) != 0))
popMenu.show( this, e.getX(), e.getY() );
}
public void mouseClicked(MouseEvent e)
{
}
public void mouseReleased(MouseEvent e)
{
}
public void mouseDragged(MouseEvent me)
{
}
public void mouseMoved(MouseEvent e)
{
}
public void mouseEntered(MouseEvent e)
{
}
public void mouseExited(MouseEvent e)
{
}
}
public static void main(String[] args)
{
window dw = new window();
}
}
you can add a windowFocusListener, hide the menu when window lost focus
this.addWindowFocusListener(new WindowFocusListener() {
#Override
public void windowLostFocus(WindowEvent e) {
if(popMenu != null){
popMenu.setVisible(false);
}
}
#Override
public void windowGainedFocus(WindowEvent e) {
//System.out.println(e);
}
});