Changing nimbus JPopupmenu behaviour - java

I need help about nimbus behaviour for JTree and JPopupMenu. I am setting a right click menu to a JTree. If I left click to a node after open the right click menu with another node, clicked node becoming selected. but in nimbus look and feel, a second click needed for select another node. My code is below, you can try it with default look and feel with comment the nimbus part.
public class JTreeDemo {
public static void main(String[] args) {
try {
for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (Exception e) {
try {
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
} catch (Exception ex) {
}
}
DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode("Root");
rootNode.add(new DefaultMutableTreeNode("Child1"));
rootNode.add(new DefaultMutableTreeNode("Child2"));
rootNode.add(new DefaultMutableTreeNode("Child3"));
DefaultTreeModel model = new DefaultTreeModel(rootNode);
JTree tree = new JTree(model);
tree.addMouseListener(new TreeMouseListener());
JFrame jf = new JFrame();
jf.getContentPane().add(new JScrollPane(tree));
jf.setSize(new Dimension(300, 300));
jf.setVisible(true);
}
}
class TreeMouseListener extends MouseAdapter {
#Override
public void mouseReleased(MouseEvent e) {
if(SwingUtilities.isRightMouseButton(e)) {
JTree tree = (JTree) e.getSource();
TreePath jClickedPath = tree.getPathForLocation(e.getX(), e.getY());
tree.setSelectionPath(jClickedPath);
JPopupMenu menu = new JPopupMenu();
menu.add(new JMenuItem("menu1"));
menu.show(tree, e.getX(), e.getY());
}
}
}

If you print out the pressed, released and clicked mouse events you will see that with the default L&F you get
// right click
tree: pressed
tree: released
tree: clicked
// click on node
tree: pressed
tree: released
tree: clicked
whereas with the Nimbus L&F you get
// right click
tree: pressed
tree: released
tree: clicked
// first click on node, the pressed event is not passed to the listener
tree: released
tree: clicked
// second click on node
tree: pressed
tree: released
tree: clicked
This is the desired behavior of Nimbus popups to consume the event on close. (see the explanation in the bug report #JDK-6770445)
You can change this behavior after setting the L&F.
UIManager.setLookAndFeel(info.getClassName());
UIManager.put("PopupMenu.consumeEventOnClose", false);
edit Snippet to change the default behavior only for a specific JTree
// instruct the JTree not to close the popup
tree.putClientProperty("doNotCancelPopup",
new JComboBox().getClientProperty("doNotCancelPopup"));
// create the popup menu not inside the listener
JPopupMenu popup = new JPopupMenu();
popup.add(new JMenuItem("menu1"));
// add the listener to the JTree
MouseListener popupListener = new PopupListener(popup);
tree.addMouseListener(popupListener);
Show and hide the popup programmatically
static class PopupListener extends MouseAdapter {
JPopupMenu popup;
PopupListener(JPopupMenu popupMenu) {
popup = popupMenu;
}
#Override
public void mousePressed(MouseEvent e) {
togglePopup(e);
}
#Override
public void mouseReleased(MouseEvent e) {
togglePopup(e);
}
private void togglePopup(MouseEvent e) {
if (e.isPopupTrigger()) {
popup.show(e.getComponent(), e.getX(), e.getY());
} else if (popup.isVisible()) {
popup.setVisible(false);
}
}
}

There are 2 solutions that you can try without changing nimbus:
1. Simulate second click using Robot class.
Add this to your MouseListener.
if(SwingUtilities.isLeftMouseButton(e) && e.getSource() instanceof JTree) {
Robot bot = null;
try {
bot = new Robot();
} catch (AWTException e1) {
e1.printStackTrace();
}
bot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
}
2. Add TreeSelectionLisitener and same as in the first method use
MouseListener to select the proper node with TreeSelectionListener.

Related

How to change menu title color on mouse over (in java Swing)?

i want the menu on the menu bar to change its background color on mouse over .. like in most applications.
i want this effect -> Sample Picture
what i tried so far ...
public class Menu extends JMenuBar implements ActionListener {
private JMenuItem fileItem_close;
private final MouseListener mouseAction = new MouseAdapter() { //i use this to apply the mouse event
#Override
public void mouseEntered(MouseEvent e) {
JMenu item = (JMenu)e.getSource(); //is this implementation correct ?
item.setOpaque(true);
};
#Override
public void mouseExited(MouseEvent e) {
JMenu item = (JMenu)e.getSource();
item.setOpaque(false);
};
};
public Menu() {
initFileMenu();
}
private void initFileMenu() {
JMenu fileMenu = new JMenu("File");
fileMenu.setMnemonic('F');
fileMenu.setRolloverEnabled(true);
fileItem_close = new JMenuItem("Close");
fileItem_close.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4, KeyEvent.ALT_MASK)); //exit on pressing (Alt+F4)
fileItem_close.addActionListener(this);
fileMenu.add(fileItem_close);
fileMenu.setRolloverEnabled(true);
fileMenu.addMouseListener(mouseAction);
fileMenu.setBackground(new Color(0x0066FF)); //The background is not visible as JMenu is not opaque by default.
add(fileMenu);
}
#Override
public void actionPerformed(ActionEvent e) {
JMenuItem source = (JMenuItem)e.getSource();
if(source == fileItem_close)
System.exit(0);
}
}
The code above is not working, whenever i hover over that menu title nothing happen.
P.S: i'm not a GUI expert.
EDIT : i'm using Nimbus LaF
You can call setSelected() for a hover effect. And yes, your implementation of e.getSource() was correct. So change it to these lines:
#Override
public void mouseEntered(MouseEvent e) {
JMenu item = (JMenu) e.getSource(); // is this implementation
// correct ?
item.setSelected(true);
};
#Override
public void mouseExited(MouseEvent e) {
JMenu item = (JMenu) e.getSource();
item.setSelected(false);
};
If you also want the menuitems to pop up on mouseEntered(), call item.doClick() in your mouseEntered method instead of setting it selected.
Edit:
For customization:
UIManager.put("Menu.selectionBackground", Color.BLUE);
UIManager.put("Menu.selectionForeground", Color.WHITE);
UIManager.put("Menu.background", Color.WHITE);
UIManager.put("Menu.foreground", Color.BLACK);
UIManager.put("Menu.opaque", false);
You can change these settings to any color you want, and it's more comfortable than creating an own class that extends JMenu.
If you also want to do this with other components (JMenuItems, for example), have a look at this. You can find all UIManager color key values there.
Edit 2:
For Nimbus LAF, create a new class:
class FillPainter implements Painter<JComponent> {
private final Color color;
FillPainter(Color c) {
color = c;
}
#Override
public void paint(Graphics2D g, JComponent object, int width, int height) {
g.setColor(color);
g.fillRect(0, 0, width, height);
}
}
The above is required for the painting of the background. Now, do this:
for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
try {
UIManager.setLookAndFeel(info.getClassName());
UIManager.getLookAndFeelDefaults().put("MenuBar:Menu[Selected].backgroundPainter",
new FillPainter(Color.BLUE));
UIManager.getLookAndFeelDefaults().put("MenuBar:Menu[Selected].textForeground", Color.WHITE);
break;
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException
| UnsupportedLookAndFeelException e) {
e.printStackTrace();
}
break;
}
}
For all other Nimbus LAF color key values, check this

JPopupMenu not showing text of JMenuItem

I'm trying to create a JPopupMenu, but for some reason, it doesn't show the text I've set on the JMenuItems. The menu itself works, there are menuitems in it and they are responsive, but the text is not showing. I'm creating the menu like this:
private void createPopupMenu() {
this.popupMenu = new JPopupMenu();
this.addMouseListener(new PopupListener(this));
JMenuItem addPlaceMenuItem = new JMenuItem(SketchPad.ADD_PLACE_POPUP_TEXT);
addPlaceMenuItem.setAction(new PopupAction(ActionType.AddPlace));
this.popupMenu.add(addPlaceMenuItem);
JMenuItem addTransitionMenuItem = new JMenuItem(SketchPad.ADD_TRANSITION_POPUP_TEXT);
addTransitionMenuItem.setAction(new PopupAction(ActionType.AddTransition));
this.popupMenu.add(addTransitionMenuItem);
}
In case it matters, here is the PopupListener:
class PopupListener extends MouseAdapter {
SketchPad pad;
public PopupListener(SketchPad pad)
{
this.pad = pad;
}
public void mousePressed(MouseEvent e) {
maybeShowPopup(e);
}
public void mouseReleased(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1)
{
this.pad.getController().deselectAllNodes();
}
else
{
maybeShowPopup(e);
}
}
private void maybeShowPopup(MouseEvent e) {
if (e.isPopupTrigger()) {
pad.popupPosition = new Point(e.getX(), e.getY());
pad.popupMenu.show(e.getComponent(), e.getX(), e.getY());
}
}
}
What am I missing here?
but for some reason, it doesn't show the text I've set on the JMenuItems.
addPlaceMenuItem.setAction(new PopupAction(ActionType.AddPlace));
The setAction(...) method reset the properties of the menu item with the properties of the Action. So you need to make sure you set the NAME property of the Action to set the text of the menu item.
So in your case it looks like the value of the NAME property should be:
SketchPad.ADD_PLACE_POPUP_TEXT
Or the other approach is to reset the text of the menu item after you set the Action
JMenuItem addPlaceMenuItem = new JMenuItem( new PopupAction(ActionType.AddPlace) );
addPlaceMenuItem.setText(SketchPad.ADD_PLACE_POPUP_TEXT);
The effect is platform specific. In particular, "In Microsoft Windows, the user by convention brings up a popup menu by releasing the right mouse button while the cursor is over a component that is popup-enabled." Your implementation of mouseReleased() precludes even checking isPopupTrigger(). Instead, handle the selection and check the trigger. A similar approach is shown in GraphPanel in order to handle multiple selection and a context menu.

Swing popup menu on a component and its content

I have a component (Widget - extends a JPanel) on which I have implemented a simple popup menu. It works when clicking on the border of the panel and basically everywhere except for the places where the panel layout contains some other component inside the panel.
So if there is a JTable inside the panel, I can invoke the menu when clicking next to it (if there is nothing else) but when clicking on the JTable, nothing happens (the table is obviously on top of the panel preventing the MouseAdapter to register the click).
Can I somehow make the popup menu invoked when right clicking even on the components inside the panel? Here is the sample code how I create and invoke the menu:
private void initPopupMenu() {
popup = new JPopupMenu();
JMenuItem closeItem = new JMenuItem("Close");
closeItem.setActionCommand(WidgetConstants.Actions.CLOSE.name());
closeItem.addActionListener(this);
popup.add(closeItem);
JMenuItem minimizeItem = new JMenuItem("Minimize");
minimizeItem.setActionCommand(WidgetConstants.Actions.MINIMIZE.name());
minimizeItem.addActionListener(this);
popup.add(minimizeItem);
}
MouseInputListener componentListener = new MouseInputAdapter() {
#Override
public void mousePressed(MouseEvent me) {
// popup
if (me.isPopupTrigger()) {
popup.show(me.getComponent(), me.getX(), me.getY());
}
}
#Override
public void mouseReleased(MouseEvent ev) {
if (ev.isPopupTrigger()) {
popup.show(ev.getComponent(), ev.getX(), ev.getY());
}
}
}
#Override
public void setBorder(Border border) {
removeMouseListener(componentListener);
removeMouseMotionListener(componentListener);
if (border instanceof WidgetBorder) {
addMouseListener(componentListener);
addMouseMotionListener(componentListener);
}
super.setBorder(border);
}
Thank you for any tips.
First of all: you don't need to use a mouse listener. Each JComponent has the method setComponentPopupMenu(JPopupMenu). Secon: you can traverse the component tree an register the popup menu for each component.
Here is example:
/**
* Provides component hierarchy traversal.
*
* #param aContainer start node for the traversal.
*/
private void traverse(Container aContainer, JPopupMenu aMenu) {
for (final Component comp : aContainer.getComponents()) {
if (comp instanceof JComponent) {
((JComponent) comp).setComponentPopupMenu(aMenu);
}
if (comp instanceof Container) {
traverse((Container) comp, aMenu);
}
}
}

Java right-click menu on tab

I've been playing about with mouse listeners etc on my tabbedpane but cant seem to get anything going. Trying to make a little menu appears when you right-click a tab which will give you the option to close that tab. Could someone point me in the right direction please
tabbedPane.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e)
{
if(SwingUtilities.isRightMouseButton(e))
{
System.out.print(tabbedPane.getSelectedIndex());
}
}
});
tabbedPane.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e)
{
if(SwingUtilities.isRightMouseButton(e))
{
JPopupMenu menu = new JPopupMenu();
JMenuItem closer = new JMenuItem(new AbstractAction("Close") {
#Override
public void actionPerformed(ActionEvent e) {
tabbedPane.removeTabAt(tabbedPane.getSelectedIndex());
}
});
menu.add(closer);
menu.show(tabbedPane, e.getX(), e.getY());
}
}
});
It might be better to install the menu on the tab component which can be accessed via tabbedPane.getTabComponentAt. The tab component is the component that renders the text tag for the tab. If you wanted to add an X button to the tab, that is where you put it.

Custom JComboBox hiding JPopupMenu

I'm having a little headache with a situation. Maybe some of you have been through this before and can show me another way or even my error here.
I need to add a JTree inside a JComboBox and the code below works like a charm.
public class HierarchyComboBox extends JComboBox {
HierarchyTree ht = new HierarchyTree();
HierarchyComboBox box;
JPopupMenu popup;
MouseAdapter adapter = new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent arg0) {
if (arg0.getClickCount() == 1) {
removeAllItems();
addItem(ht.getSelectedLevel());
// ((JPopupMenu) comp).setVisible(false);
}
}
};
PopupMenuListener listener = new PopupMenuListener() {
#Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
if (box == null) {
box = (HierarchyComboBox) e.getSource();
if (popup == null) {
final Object comp = box.getUI().getAccessibleChild(box, 0);
if (!(comp instanceof JPopupMenu))
return;
popup = (JPopupMenu) comp;
}
popup.removeAll();
ht.getTreePane().setBorder(null);
ht.getTreePane().setPreferredSize(new Dimension(box.getWidth(), 200));
MyTree tree = (MyTree)ht.getTreePane().getViewport().getComponent(0);
tree.addMouseListener(adapter);
popup.add(ht.getTreePane());
}
}
#Override
public void popupMenuCanceled(PopupMenuEvent arg0) { }
#Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent arg0) { }
};
public HierarchyComboBox() {
setEditable(true);
addPopupMenuListener(listener);
}
}
but I added this component to 2 different dialogs.
The first one I can click and the selection is added to the JComboBox
and the second, doing EXACTLY the same instantiation, and the same tests
The component has a different behaviour:
- The JPopupMenu disappears
- It doesn't add the selection to the combo
Any ideas here?
Thanks in advance..
As shown in Providing a Custom Renderer, "A combo box uses a renderer to display each item in its menu." You could render the tree in a custom ListCellRenderer. Alternatively,
Render the tree in an adjacent component in response to an ActionListener.
Use a hierarchical model, shown here.
I noticed that the JPopupMenu was loosing it's focus.
The solution was to add the component as the last component of the Panel.

Categories