——————————————————————————————————————————————————————————————————
Yesterday you saw how to provide a highly customized menu bar in a NetBeans Platform application. Let's now take it a few steps further. We imagine that our requirements have changed, we now need to provide a Login feature from our menu bar and we need the menus to resemble hyperlinks, both in terms of appearance and in terms of behavior. In the end, we'll have a menu bar that looks like this:
Indeed, our application is going to pick up a few idioms from the web! To show some more detail, when you click on "User Name" above, you will see this, i.e., menu items for logging in/out of the application:
And when you move your mouse over one of the hyperlinks, the underlying menu items are shown:
So, to achieve the above result, we need to customize the menus in the menu bar, create the Login feature, and add the Login feature to the menu bar.
Seriously, it's all reasonably trivial to do.
First create a new JPanel named "LoginPanel". Add two JLabels, one named "welcomeLabel" and the other "userNameLabel". In the second label, i.e., "userNameLabel", add a "down arrow" icon and pass "SwingConstants.LEFT" to the label's "setHorizontalTextPosition". And here's the constructor of my LoginPanel, where the JPopupMenu is constructed:
initComponents();
setOpaque(false);
JMenuItem signInItem = new JMenuItem("Sign In");
signInItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//do something here
}
});
JMenuItem signOutItem = new JMenuItem("Sign Out");
signOutItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//do something here
}
});
menu.add(signInItem);
menu.add(signOutItem);
}
FileObject menuFolder = FileUtil.getConfigFile("Menu");
FileObject[] menuKids = menuFolder.getChildren();
setLayout(new MigLayout("align right"));
JPanel p = new LoginPanel();
add(p, "span, align right");
for (final FileObject menuKid : FileUtil.getOrder(Arrays.asList(menuKids), true)) {
final JMenu m = new JMenu();
m.setText("<html><body><font color=\"blue\"><u>" + menuKid.getName() + "</u></font></body></html>");
if (m != null) {
m.addMouseListener(new MouseListener() {
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent evt) {
m.doClick(1);
}
@Override
public void mouseExited(MouseEvent e) {
}
});
add(m);
}
...
...
...
m.setText("<html><body><font color=\"blue\">" + name + "</font></body></html>");
if (m != null) {
m.addMouseListener(new MouseListener() {
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent evt) {
m.setText("<html><body><font color=\"red\"><u>" + name + "</u></font></body></html>");
m.doClick(1);
}
@Override
public void mouseExited(MouseEvent e) {
m.setText("<html><body><font color=\"blue\">" + name + "</font></body></html>");
}
});
So, the above means that when the user moves over and out of a menu, they will see the kind of effects they know from the web. By default, the display text of a menu is blue, when the mouse moves over the menu the menu is red and underlined and when the mouse moves out of the menu the default state is returned. Now, don't you think Swing is a lot cooler than you might already have thought?
Finally, there are still a couple of imperfections in our code. The final result of what we're trying to achieve should be as follows:
Note that the names of the menus are now correct (i.e., "GoTo" is now "Navigate") and that you can see the keyboard shortcuts that need to be entered as an alternative way to access the menus. (Lack of keyboard shortcut support is frequently a reason why the desktop is favored over the web, by the way.) Here's the complete & full code of my menu bar that achieves the above result:
public MyMenuBar() {
final FileObject menuFolder = FileUtil.getConfigFile("Menu");
setLayout(new MigLayout("align right"));
JPanel p = new LoginPanel();
add(p, "span, align right");
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
buildPopup(menuFolder, null, true);
}
});
setPreferredSize(new Dimension(50, 50));
}
private void buildPopup(FileObject fo, JComponent comp, boolean folder) {
DataObject dob = null;
try {
dob = DataObject.find(fo);
} catch (DataObjectNotFoundException ex) {
Exceptions.printStackTrace(ex);
}
if (folder) {
DataFolder df = DataFolder.findFolder(fo);
DataObject[] childs = df.getChildren();
for (int i = 0; i < childs.length; i++) {
dob = childs[i];
if (dob.getPrimaryFile().isFolder()) {
final JMenu menu = new JMenu();
final String name = dob.getNodeDelegate().getDisplayName();
Mnemonics.setLocalizedText(menu, "<html><body><font color=\"blue\">" + name + "</font><body>");
menu.addMouseListener(new MouseListener() {
@Override
public void mouseClicked(MouseEvent e) {}
@Override
public void mousePressed(MouseEvent e) {}
@Override
public void mouseReleased(MouseEvent e) {}
@Override
public void mouseEntered(MouseEvent evt) {
Mnemonics.setLocalizedText(menu, "<html><body><font color=\"red\"><u>" + name + "</u></font><body>");
menu.doClick(1);
}
@Override
public void mouseExited(MouseEvent e) {
Mnemonics.setLocalizedText(menu, "<html><body><font color=\"blue\">" + name + "</font><body>");
}
});
FileObject menuFolder = dob.getPrimaryFile();
for (FileObject menuItemFolder : FileUtil.getOrder(Arrays.asList(menuFolder.getChildren()), true)) {
if (menuItemFolder.isFolder()) {
buildPopup(menuItemFolder, menu, true);
} else {
buildPopup(menuItemFolder, menu, false);
}
}
add(menu);
}
}
} else {
Object instanceObj;
InstanceCookie ck = (InstanceCookie) dob.getCookie(InstanceCookie.class);
try {
instanceObj = ck.instanceCreate();
} catch (Exception ex) {
instanceObj = null;
}
if (instanceObj instanceof JSeparator) {
comp.add((JSeparator) instanceObj);
} else if (instanceObj instanceof BooleanStateAction) {
JCheckBoxMenuItem menuItem = new JCheckBoxMenuItem();
Actions.connect(menuItem, (BooleanStateAction) instanceObj, true);
} else if (instanceObj instanceof Action) {
JMenuItem menuItem = new JMenuItem();
Actions.connect(menuItem, (Action) instanceObj, false);
comp.add(menuItem);
}
}
}
//Paint the banner image into the menu bar:
private final Paint bannerPaint = makeBannerPaint();
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setPaint(bannerPaint);
g2.fillRect(0, 0, getWidth(), getHeight());
}
private Paint makeBannerPaint() {
//Pointing to an image in org/menu/bar:
BufferedImage img = (BufferedImage) ImageUtilities.loadImage("org/menu/bar/banner.jpg");
return new TexturePaint(img, new Rectangle(0, 0, img.getWidth(), img.getHeight()));
}
}