Want to create a JMenuBar. If window-JFrame.width to small to show all JMenu of JMenuBar, a Button appears in JMenuBar and all hidden JMenu can chosen in a drop-down list. How can I realize it, please?
I'd look at JToolBar, illustrated here. You can use any required layout and most L&Fs allow the bar to become a floating window.
Use CardLayout to have a panel that contains both the normal menu, and a menu implemented with the button. Then add a ComponentListener (ComponentAdapter) to it and select the desired menu implementation in the listener's componentResized() method.
In code it would look roughly like this:
JMenuBar createCustomMenu() {
final CardLayout layout = new CardLayout();
final JMenuBar menu = new JMenuBar();
menu.setLayout(layout);
// Here you should create the normal, wide menu
JComponent normalMenu = createNormalMenu();
// Here you create the compressed, one button version
JComponent compressedMenu = createCompressedMenu();
menu.add(normalMenu, "normal");
menu.add(compressedMenu, "compressed");
menu.addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
if (menu.getPreferredSize().getWidth() > menu.getWidth()) {
layout.show(menu, "compressed");
} else {
layout.show(menu, "normal");
}
}
});
return menu;
}
(edit: changed to return JMenuBar, since it seems to work just fine)
Here is some old code I was playing with 5 years ago. Its been so long I don't even remember how well the code works. It was designed for a JToolBar but it may give you some ideas on how to do this with a JMenuBar:
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
/**
*
* #author subanark
*/
public class PopupMenuLayout implements java.awt.LayoutManager
{
private JPopupMenu popupMenu = new JPopupMenu();
private JButton popupButton = new JButton(new PopupAction());
/** Creates a new instance of PopupMenuLayout */
public PopupMenuLayout()
{
}
/** If the layout manager uses a per-component string,
* adds the component <code>comp</code> to the layout,
* associating it
* with the string specified by <code>name</code>.
*
* #param name the string to be associated with the component
* #param comp the component to be added
*/
public void addLayoutComponent(String name, Component comp)
{
}
/**
* Lays out the specified container.
* #param parent the container to be laid out
*/
public void layoutContainer(Container parent)
{
// Position all buttons in the container
Insets insets = parent.getInsets();
int x = insets.left;
int y = insets.top;
System.out.println("bottom: " + insets.bottom);
int spaceUsed = insets.right + insets.left;
for (int i = 0; i < parent.getComponentCount(); i++ )
{
Component aComponent = parent.getComponent(i);
aComponent.setSize(aComponent.getPreferredSize());
aComponent.setLocation(x,y);
int componentWidth = aComponent.getPreferredSize().width;
x += componentWidth;
spaceUsed += componentWidth;
}
// All the buttons won't fit, add extender button
// Note: the size of the extender button changes once it is added
// to the container. Add it here so correct width is used.
int parentWidth = parent.getSize().width;
if (spaceUsed > parentWidth)
{
popupMenu.removeAll();
parent.add(popupButton);
popupButton.setSize( popupButton.getPreferredSize() );
int popupX = parentWidth - insets.right - popupButton.getSize().width;
popupButton.setLocation(popupX, y );
spaceUsed += popupButton.getSize().width;
}
// Remove buttons that don't fit and add to the popup menu
// System.out.println(spaceUsed + " ::: " + parentWidth);
int lastVisibleButtonIndex = 1;
while (spaceUsed > parentWidth)
{
lastVisibleButtonIndex++;
int last = parent.getComponentCount() - lastVisibleButtonIndex;
Component component = parent.getComponent( last );
component.setVisible( false );
spaceUsed -= component.getSize().width;
addComponentToPopup(component);
// popupButton.setLocation( button.getLocation() );
// System.out.println(spaceUsed + " : " + parentWidth);
}
}
private void addComponentToPopup(Component component)
{
System.out.println(component.getClass());
if (component instanceof JButton)
{
JButton button = (JButton)component;
JMenuItem menuItem = new JMenuItem(button.getText());
menuItem.setIcon( button.getIcon() );
ActionListener[] listeners = button.getActionListeners();
for (int i = 0; i < listeners.length; i++)
menuItem.addActionListener( listeners[i] );
popupMenu.insert(menuItem, 0);
}
if (component instanceof JToolBar.Separator)
{
popupMenu.insert( new JSeparator(), 0);
}
}
/**
* Calculates the minimum size dimensions for the specified
* container, given the components it contains.
* #param parent the component to be laid out
* #see #preferredLayoutSize
*/
public Dimension minimumLayoutSize(Container parent)
{
return popupButton.getMinimumSize();
}
/** Calculates the preferred size dimensions for the specified
* container, given the components it contains.
* #param parent the container to be laid out
*
* #see #minimumLayoutSize
*/
public Dimension preferredLayoutSize(Container parent)
{
// Move all components to the container and remove the extender button
parent.remove(popupButton);
/*
while ( popupMenu.getComponentCount() > 0 )
{
Component aComponent = popupMenu.getComponent(0);
popupMenu.remove(aComponent);
parent.add(aComponent);
}
*/
// Calculate the width of all components in the container
Dimension d = new Dimension();
d.width += parent.getInsets().right + parent.getInsets().left;
for (int i = 0; i < parent.getComponents().length; i++)
{
Component component = parent.getComponent(i);
component.setVisible( true );
d.width += component.getPreferredSize().width;
d.height = Math.max(d.height, component.getPreferredSize().height);
}
// d.height += parent.getInsets().top + parent.getInsets().bottom + 5;
d.height += parent.getInsets().top + parent.getInsets().bottom;
return d;
}
/** Removes the specified component from the layout.
* #param comp the component to be removed
*/
public void removeLayoutComponent(Component comp)
{
}
protected class PopupAction extends AbstractAction
{
public PopupAction()
{
super(">>");
}
public void actionPerformed(ActionEvent e)
{
JComponent component = (JComponent)e.getSource();
popupMenu.show(component,0,component.getHeight());
}
}
public static void main(String[] argv)
{
ActionListener simpleAction = new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
System.out.println(e.getActionCommand());
}
};
JToolBar toolBar = new JToolBar();
toolBar.setLayout(new PopupMenuLayout());
toolBar.add( createButton("one", simpleAction) );
toolBar.add( createButton("two", simpleAction) );
toolBar.add( createButton("three", simpleAction) );
toolBar.add( createButton("four", simpleAction) );
toolBar.add( createButton("five", simpleAction) );
toolBar.add( createButton("six", simpleAction) );
toolBar.addSeparator();
toolBar.add( createButton("seven", simpleAction) );
toolBar.add( createButton("eight", simpleAction) );
toolBar.addSeparator();
toolBar.add( createButton("nine", simpleAction) );
toolBar.add( createButton("ten", simpleAction) );
JFrame f = new JFrame();
f.getContentPane().setLayout(new BorderLayout());
f.getContentPane().add(toolBar,BorderLayout.NORTH);
f.setBounds(300,200,400,300);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
private static JButton createButton(String text, ActionListener listener)
{
JButton button = new JButton(text);
button.addActionListener( listener );
return button;
}
}
In this case the toolbar button was converted to a JMenu when no space was available. In you case you already have a JMenu, so you should be able to juse move the JMenu from the JMenuBar to the popup menu. However you will need to change the code to always move the menus from the popup menu back to the menu bar before determining the preferred size of the menu bar.
Related
I'm trying to make a simple connect four GUI by using a grid of JPanels each of which paints a colored disk when the button below them is pressed and the panels under it are full. Before adding the game rules I'm trying to just make sure the buttons and display work properly. But it is not working - only the top left panel displays a disk (after pressing button 1 6 times). here is my code:
public class ConnectFourFrame extends JFrame {
private final JPanel gamePanelsPanel; // panel to hold the game panels
private final GamePanel[][] gamePanels; // a 2D array to hold the grid of panels to display the game disks
private final JPanel buttonsPanel; // panel to hold the buttons panels
private final JPanel gameButtonsPanel; // panel to hold the game buttons to add disk to a column
private final JButton[] gameButtons; // an array to hold the game buttons
private final JPanel clearButtonPanel; // panel to hold the clear button
private final JButton clearButton; // button to clear the game grid from disks
private enum Turn {RED_PLAYER, BLUE_PLAYER}; // keeps track of which players turn it is
private Turn turn;
// no argument constructor
public ConnectFourFrame() {
super("Connect Four");
this.setLayout(new BorderLayout());
//add panels to hold the game panel and the buttons
gamePanelsPanel = new JPanel();
add(gamePanelsPanel, BorderLayout.CENTER);
buttonsPanel = new JPanel();
buttonsPanel.setLayout(new BorderLayout());
add(buttonsPanel, BorderLayout.SOUTH);
//set up game panels
gamePanelsPanel.setLayout(new GridLayout(6,7,3,3));
gamePanelsPanel.setBackground(Color.BLACK);
gamePanels = new GamePanel[6][7];
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 7; j++) {
gamePanels[i][j] = new GamePanel(false, Color.WHITE);
gamePanelsPanel.add(gamePanels[i][j]);
}
}
//set up game and clear buttons
gameButtonsPanel = new JPanel();
gameButtonsPanel.setLayout(new GridLayout(1,7));
clearButtonPanel = new JPanel();
gameButtons = new JButton[7];
for (int i = 0; i < 7; i++) {
gameButtons[i] = new JButton("" + (i+1));
gameButtonsPanel.add(gameButtons[i]);
}
clearButton = new JButton("CLEAR");
clearButtonPanel.add(clearButton);
buttonsPanel.add(gameButtonsPanel, BorderLayout.NORTH);
buttonsPanel.add(clearButtonPanel, BorderLayout.SOUTH);
add(buttonsPanel, BorderLayout.SOUTH);
// register event handlers
ClearButtonHandler clearButtonHandler = new ClearButtonHandler();
clearButton.addActionListener(clearButtonHandler);
GameButtonHandler gameButtonHandler = new GameButtonHandler();
for (int i = 0; i < 7; i++) {
gameButtons[i].addActionListener(gameButtonHandler);
}
turn = Turn.RED_PLAYER; //set first turn to player1
}
// inner class for game button event handling
private class GameButtonHandler implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
// get the number of the pressed button
int pressedButtonNum = Integer.parseInt(((JButton) e.getSource()).getActionCommand());
// display disk in top empty panel of the column
for (int i = 5; i >= 0; i--) {
if (!gamePanels[i][pressedButtonNum - 1].isFull()) {
if (turn == Turn.RED_PLAYER) {
gamePanels[i][pressedButtonNum - 1].setDiskColor(Color.RED);
turn = Turn.BLUE_PLAYER;
}
else {
gamePanels[i][pressedButtonNum - 1].setDiskColor(Color.BLUE);
turn = Turn.RED_PLAYER;
}
gamePanels[i][pressedButtonNum - 1].setFull(true);
gamePanels[i][pressedButtonNum - 1].repaint();
return;
}
}
// if column is full display message to try again
JOptionPane.showMessageDialog(gamePanelsPanel, "Column " + pressedButtonNum + " is full. Try again.");
}
}
public class GamePanel extends JPanel{
private boolean isFull; // true if the panel has a disk in it. default is empty (false).
private Color diskColor; //color of disks. default is white (same as background)
public GamePanel(boolean isFull, Color diskColor) {
super();
this.isFull = isFull;
this.diskColor = diskColor;
}
public Color getDiskColor() {
return diskColor;
}
public void setDiskColor(Color diskColor) {
this.diskColor = diskColor;
}
public boolean isFull() {
return isFull;
}
public void setFull(boolean isFull) {
this.isFull = isFull;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
this.setBackground(Color.WHITE);
g.setColor(diskColor);
g.fillOval(this.getX() + this.getWidth()/4 , this.getY() + this.getHeight()/4, this.getWidth()/2, this.getHeight()/2);
}
}
The problem is right here...
g.fillOval(this.getX() + this.getWidth()/4 , this.getY() + this.getHeight()/4, this.getWidth()/2, this.getHeight()/2);
The Graphics context passed to your paintComponent method has already been translated by the components x/y position, meaning that the top/left corner of the component is always 0x0
g.fillOval(this.getWidth()/4 , this.getHeight()/4, this.getWidth()/2, this.getHeight()/2);
will probably work better
Also, calling this.setBackground(Color.WHITE); inside paintComponent is unadvisable, as it will setup a situation where by a new paint cycle will be scheduled, over and over again. Don't change the state of the UI from within a paint method
I want to add the custom button titles in my non-RCP SWT app.
MessageBox messageBox = new MessageBox(shell, SWT.ICON_WARNING | SWT.ABORT | SWT.RETRY | SWT.IGNORE);
messageBox.setText("Warning");
messageBox.setMessage("Save the changes before exiting?");
int buttonID = messageBox.open();
switch(buttonID) {
case SWT.YES:
// saves changes ...
case SWT.NO:
// exits here ...
break;
case SWT.CANCEL:
// does nothing ...
}
System.out.println(buttonID);
}
It works fine but i button titles are "Abort", "Retry", "ignore"
I want custom button titles, like "overwrite", "rename", "cancel".
How it can be done?
Please help.
*** EDIT ********
I also tried
MessageDialog dialog = new MessageDialog(null, "Dangerous Activity", null,
"Are you sure you want to delete?", MessageDialog.CONFIRM,
new String[]{"Preview>", "Delete", "Cancel"}, 0)
{
protected void buttonPressed(int buttonId) {
setReturnCode(buttonId);
// close(); Call close for Delete or Cancel?
}};
But MessageDialog requires app to be RCP, therefore not importing the package required. Help.
Here is a very simple example on how to do your own Dialog in SWT (there are more comfortable ways to do this with JFace though):
public class CustomDialog extends Dialog
{
private String message = "";
private Shell shell;
public CustomDialog(Shell parent)
{
// Pass the default styles here
this(parent, SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL);
shell = parent;
}
public CustomDialog(Shell parent, int style)
{
// Let users override the default styles
super(parent, style);
shell = parent;
}
public String getMessage()
{
return message;
}
public void setMessage(String message)
{
this.message = message;
}
public void open()
{
shell.setText(getText());
createContents(shell);
shell.pack();
shell.open();
Display display = getParent().getDisplay();
while (!shell.isDisposed())
{
if (!display.readAndDispatch())
{
display.sleep();
}
}
}
/**
* Creates the dialog's contents
*
* #param shell
* the dialog window
*/
private void createContents(final Shell shell)
{
shell.setLayout(new GridLayout(3, true));
// Show the message
Label label = new Label(shell, SWT.NONE);
label.setText(message);
GridData data = new GridData();
data.horizontalSpan = 3;
label.setLayoutData(data);
// Display the input box
Label image = new Label(shell, SWT.NONE);
image.setImage(shell.getDisplay().getSystemImage(SWT.ICON_ERROR));
data = new GridData(SWT.FILL, SWT.BEGINNING, true, false);
data.horizontalSpan = 3;
image.setLayoutData(data);
Button preview = new Button(shell, SWT.PUSH);
preview.setText("Preview");
data = new GridData(SWT.FILL, SWT.END, true, true);
preview.setLayoutData(data);
preview.addSelectionListener(new SelectionAdapter()
{
public void widgetSelected(SelectionEvent event)
{
System.out.println("Preview");
}
});
Button delete = new Button(shell, SWT.PUSH);
delete.setText("Delete");
data = new GridData(SWT.FILL, SWT.END, true, true);
delete.setLayoutData(data);
delete.addSelectionListener(new SelectionAdapter()
{
public void widgetSelected(SelectionEvent event)
{
System.out.println("Delete");
}
});
Button cancel = new Button(shell, SWT.PUSH);
cancel.setText("Cancel");
data = new GridData(SWT.FILL, SWT.END, true, true);
cancel.setLayoutData(data);
cancel.addSelectionListener(new SelectionAdapter()
{
public void widgetSelected(SelectionEvent event)
{
shell.close();
}
});
shell.setDefaultButton(preview);
}
public static void main(String[] args)
{
CustomDialog dialog = new CustomDialog(new Shell());
dialog.setText("Title");
dialog.setMessage("Message");
dialog.open();
}
}
It looks like this:
MessageBox cannot be extended this way. Your best option - when not doing an RCP application - probably is to roll your own dialog :-)
But given the simple nature of these dialogs, that should be easy enough.
I'm a little late to this, but if anyone else is looking for a cutomizable MessageBox, here it is. Create a class, then copy/paste the code into it. I want to thank Baz for giving me the starting point :-)
import java.util.ArrayList;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Dialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
/**
* Custom MessageBox which allows custom button captions, such as using language specific text<br>
* <br>
* <code><b>
* int cancelResult = 0;<br>
* int deleteResult = 1;<br>
* <br>
* CustomMessageBox messageBox = new CustomMessageBox( ivShell, CustomMessageBox.ImageCode.QUESTION, "Confirm Delete", "Are you sure you want to delete:\n\n" + key );<br>
* <br>
* messageBox.addButton( "Cancel", cancelResult, true );<br>
* messageBox.addButton( "Delete " + key, deleteResult );<br>
* <br>
* if (messageBox.getResult() == deleteResult)<br>
* remove( key );<br>
* </b></code>
*/
public class CustomMessageBox extends Dialog
{
/**
* Standard SWT icons used in a message box
*/
public enum ImageCode
{
INFORMATION(SWT.ICON_INFORMATION),
QUESTION(SWT.ICON_QUESTION),
WARNING(SWT.ICON_WARNING),
ERROR(SWT.ICON_ERROR);
private int ivImageCode;
private ImageCode( int imageCode )
{
ivImageCode = imageCode;
}
private int getImageCode()
{
return ivImageCode;
}
}
private static final int FORM_SPACING = 5;
private static final String OK_CAPTION = "Ok"; //$NON-NLS-1$
private static final int SCREEN_MINIMUM = 20;
private Shell ivParentShell;
private Shell ivShell;
private Composite ivCompositeImage;
private Composite ivCompositeMessage;
private Composite ivCompositeDummy;
private Composite ivCompositeButtons;
private Label ivLabelImage;
private Label ivLabelMessage;
private Color ivMessageForegroundColor;
private Color ivMessageBackgroundColor;
private Font ivMessageFont;
private ImageCode ivImageCode;
private String ivTitle;
private String ivMessage;
private ArrayList<ButtonParameter> ivButtons;
private int ivResult;
/**
* Create a custom message box. You must {#link #addButton(String, int, boolean)} before calling {#link #getResult()}
* #param parentShell - the parent shell. The message box will be centered within this shell
* #param imageCode - the image to be shown in the upper/left corner
* #param title - the title for the message box
* #param message - the message shown to the user. You can have newlines in the message
*/
public CustomMessageBox( Shell parentShell, ImageCode imageCode, String title, String message )
{
super( parentShell, SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL );
ivParentShell = parentShell;
ivImageCode = imageCode;
ivTitle = title;
ivMessage = message;
ivButtons = new ArrayList<>();
ivMessageForegroundColor = null;
ivMessageBackgroundColor = null;
ivMessageFont = null;
}
/**
* Buttons are created right to left, so add the right-most button first. This will not be the default button
* #param caption - the caption for the button
* #param result - the returned result when the button is chosen. Should be unique!
*/
public void addButton( String caption, int result )
{
addButton( caption, result, false );
}
/**
* Buttons are created right to left, so add the right-most button first.<br><br>
* If more than one button is set to be the default or no buttons are set to be the default, then the last one added will actually become the default.
* #param caption - the caption for the button
* #param result - the returned result when the button is chosen. Should be unique!
* #param isDefault - this will be the default button.
*/
public void addButton( String caption, int result, boolean isDefault )
{
ivButtons.add( new ButtonParameter( caption, result, isDefault ) );
}
/**
* Set the message foreground color
* #param foregroundColor
*/
public void setMessageForegroundColor( Color foregroundColor )
{
ivMessageForegroundColor = foregroundColor;
}
/**
* Set the message background color
* #param backgroundColor
*/
public void setMessageBackgroundColor( Color backgroundColor )
{
ivMessageBackgroundColor = backgroundColor;
}
/**
* Set the message font
* #param messageFont
*/
public void setMessageFont( Font messageFont )
{
ivMessageFont = messageFont;
}
/**
* Open the window and get the result. If no buttons were added, a stock "Ok" button will be created
*/
public int getResult()
{
Display display = Display.getDefault();
// bad programmer, bad
if (ivButtons.size() == 0)
addButton( OK_CAPTION, 0, true );
ivShell = new Shell( SWT.BORDER | SWT.TITLE | SWT.APPLICATION_MODAL );
createContents( display );
ivShell.pack();
ivShell.setLocation( centerOnParent( ivParentShell, ivShell ) );
ivShell.open();
while (!ivShell.isDisposed())
{
if (!display.readAndDispatch())
{
display.sleep();
}
}
return ivResult;
}
/**
* Centers the message box within the parent shell
*/
private Point centerOnParent( Shell parentShell, Shell childShell )
{
Rectangle parent = parentShell.getBounds();
Rectangle child = childShell.getBounds();
int x = (int) (parent.x + (parent.width - child.width) * 0.5f);
int y = (int) (parent.y + (parent.height - child.height) * 0.5f);
// just to keep the left edge on the screen
if (x < SCREEN_MINIMUM)
x = SCREEN_MINIMUM;
// just to keep the top edge on the screen
if (y < SCREEN_MINIMUM)
y = SCREEN_MINIMUM;
return new Point( x, y );
}
/**
* Creates the contents and places them in the shell
*/
private void createContents( Display display )
{
FormData formData;
GridData gridData;
Button button;
Button lastButton;
ivShell.setLayout( new GridLayout( 2, false ) );
ivShell.setText( ivTitle );
{
ivCompositeImage = new Composite( ivShell, SWT.NONE );
ivCompositeImage.setLayout( new FormLayout() );
gridData = new GridData();
gridData.grabExcessHorizontalSpace = true;
gridData.grabExcessVerticalSpace = true;
gridData.verticalAlignment = SWT.TOP;
gridData.horizontalAlignment = SWT.LEFT;
ivCompositeImage.setLayoutData( gridData );
}
{
ivCompositeMessage = new Composite( ivShell, SWT.NONE );
ivCompositeMessage.setLayout( new FormLayout() );
gridData = new GridData();
gridData.grabExcessHorizontalSpace = true;
gridData.grabExcessVerticalSpace = true;
ivCompositeMessage.setLayoutData( gridData );
}
{
ivCompositeDummy = new Composite( ivShell, SWT.NONE );
ivCompositeDummy.setLayout( new FormLayout() );
}
{
ivCompositeButtons = new Composite( ivShell, SWT.NONE );
ivCompositeButtons.setLayout( new FormLayout() );
gridData = new GridData();
gridData.grabExcessHorizontalSpace = true;
gridData.grabExcessVerticalSpace = true;
gridData.horizontalAlignment = SWT.END;
ivCompositeButtons.setLayoutData( gridData );
}
{
ivLabelImage = new Label( ivCompositeImage, SWT.NONE );
formData = new FormData();
formData.top = new FormAttachment( 0, 0 );
formData.left = new FormAttachment( 0, 0 );
ivLabelImage.setLayoutData( formData );
ivLabelImage.setImage( display.getSystemImage( ivImageCode.getImageCode() ) );
}
{
ivLabelMessage = new Label( ivCompositeMessage, SWT.WRAP );
formData = new FormData();
formData.top = new FormAttachment( 0, 0 );
formData.left = new FormAttachment( 0, 0 );
formData.right = new FormAttachment( 100, 0 );
formData.bottom = new FormAttachment( 100, 0 );
ivLabelMessage.setLayoutData( formData );
// we add a new line to place white-space between the message and the buttons
// the trailing space is required!
ivLabelMessage.setText( ivMessage + System.getProperty( "line.separator" ) + " " ); //$NON-NLS-1$ //$NON-NLS-2$
if (ivMessageForegroundColor != null)
ivLabelMessage.setForeground( ivMessageForegroundColor );
if (ivMessageBackgroundColor != null)
ivLabelMessage.setBackground( ivMessageBackgroundColor );
if (ivMessageFont != null)
ivLabelMessage.setFont( ivMessageFont );
}
lastButton = null;
for (ButtonParameter parm : ivButtons)
{
button = new Button( ivCompositeButtons, SWT.PUSH );
formData = new FormData();
if (lastButton == null)
formData.right = new FormAttachment( 100, -FORM_SPACING );
else
formData.right = new FormAttachment( lastButton, -FORM_SPACING, SWT.LEFT );
formData.bottom = new FormAttachment( 100, -FORM_SPACING );
button.setLayoutData( formData );
button.setText( parm.getCaption() );
button.addMouseListener( new ButtonMouseListener( parm.getResult() ) );
lastButton = button;
if (parm.isDefault())
ivShell.setDefaultButton( button );
}
if (ivShell.getDefaultButton() == null)
ivShell.setDefaultButton( lastButton );
}
/**
* Internal class which holds the button parameters. This is created by the {#link CustomMessageBox#addButton(String, int, boolean)} method
*/
private class ButtonParameter
{
private String ivCaption;
private int ivResult;
private boolean ivIsDefault;
/**
* Create a button parameter
* #param caption - the caption for the button
* #param result - the returned result when the button is chosen
* #param isDefault - this will be the default button.
*/
private ButtonParameter( String caption, int result, boolean isDefault )
{
super();
ivCaption = caption;
ivResult = result;
ivIsDefault = isDefault;
}
private String getCaption()
{
return ivCaption;
}
private int getResult()
{
return ivResult;
}
private boolean isDefault()
{
return ivIsDefault;
}
}
/**
* Mouse listener for the buttons.
*/
private class ButtonMouseListener extends MouseAdapter
{
private int ivButtonResult;
/**
* Creates the listener
* #param result - The result returned when the button is pressed
*/
private ButtonMouseListener( int result )
{
super();
ivButtonResult = result;
}
#Override
public void mouseUp( MouseEvent e )
{
ivResult = ivButtonResult;
ivShell.close();
}
}
}
I have a swing component that has several sub components. What I want to do change some label if the mouse is over any of those components, and then change it to something else if the mouse moves off all of the components. I'm trying to find a more efficient way to do this.
Currently I have mouse listeners over all of the child components that look something like:
class AMouseListener extends MouseAdapter {
private boolean mouseOver;
mouseEntered(MouseEvent e) { mouseOver = true; updateLabel(); }
mouseExited(MouseEvent e) { mouseOver = false; updateLabel(); }
void updateLabel() {
String text = "not-over-any-components";
// listeners are each of the listeners added to the child components
for ( AMouseListener listener :listeners ) {
if ( listener.mouseOver ) {
text = "over-a-component";
break;
}
}
}
}
This works, but I feel like there should be a better way to handle this by only handling mouseEntered and mouseExited events on the parent container, but since the child components intercept these events, I'm not sure how to go about doing this (I don't necessarily have control over the child components so I Can't forward the mouse events to the parent event if I wanted to).
For example
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
public class TestMouseListener {
public static void main(String[] args) {
final JComboBox combo = new JComboBox();
combo.setEditable(true);
for (int i = 0; i < 10; i++) {
combo.addItem(i);
}
final JLabel tip = new JLabel();
tip.setPreferredSize(new Dimension(300, 20));
JPanel panel = new JPanel();
panel.add(combo);
panel.add(tip);
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(panel);
frame.pack();
frame.setVisible(true);
panel.addMouseListener(new MouseAdapter() {
#Override
public void mouseEntered(MouseEvent e) {
tip.setText("Outside combobox");
}
#Override
public void mouseExited(MouseEvent e) {
Component c = SwingUtilities.getDeepestComponentAt(
e.getComponent(), e.getX(), e.getY());
// doesn't work if you move your mouse into the combobox popup
tip.setText(c != null && SwingUtilities.isDescendingFrom(
c, combo) ? "Inside combo box" : "Outside combobox");
}
});
}
private TestMouseListener() {
}
}
Check out the docs and examples for the "glass pane".
This should give you what you need: The Glass Pane
I know this is very old, but here's a simple solution with which you can create a mouse listener for a component and all components inside it's bounds (without adding the listener to all components individually):
/**
* Creates an {#link AWTEventListener} that will call the given listener if
* the {#link MouseEvent} occurred inside the given component, one of its
* children or the children's children etc. (recursive).
*
* #param component
* the component the {#link MouseEvent} has to occur inside
* #param listener
* the listener to be called if that is the case
*/
public static void addRecursiveMouseListener(final Component component, final MouseListener listener) {
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
#Override
public void eventDispatched(AWTEvent event) {
if(event instanceof MouseEvent) {
MouseEvent mouseEvent = (MouseEvent) event;
if(mouseEvent.getComponent().isShowing() && component.isShowing()){
if (containsScreenLocation(component, mouseEvent.getLocationOnScreen())) {
if(event.getID() == MouseEvent.MOUSE_PRESSED) {
listener.mousePressed(mouseEvent);
}
if(event.getID() == MouseEvent.MOUSE_RELEASED) {
listener.mouseReleased(mouseEvent);
}
if(event.getID() == MouseEvent.MOUSE_ENTERED) {
listener.mouseEntered(mouseEvent);
}
if(event.getID() == MouseEvent.MOUSE_EXITED) {
listener.mouseExited(mouseEvent);
}
if(event.getID() == MouseEvent.MOUSE_CLICKED){
listener.mouseClicked(mouseEvent);
}
}
}
}
}
}, AWTEvent.MOUSE_EVENT_MASK);
}
/**
* Checks if the given location (relative to the screen) is inside the given component
* #param component the component to check with
* #param screenLocation the location, relative to the screen
* #return true if it is inside the component, false otherwise
*/
public static boolean containsScreenLocation(Component component, Point screenLocation){
Point compLocation = component.getLocationOnScreen();
Dimension compSize = component.getSize();
int relativeX = screenLocation.x - compLocation.x;
int relativeY = screenLocation.y - compLocation.y;
return (relativeX >= 0 && relativeX < compSize.width && relativeY >= 0 && relativeY < compSize.height);
}
Note: Once the mouse exits the root component of this listener the mouseExited(mouseEvent) will probably not fire, however you can just add the mouse listener to the root component itself and it should fire.
mouseExited(mouseEvent) is unreliable in general though.
You could initiate a single instance of the listener and add that instance to each component.
Like this:
AMouseListener aMouseListener=new AMouseListener();
for each(Component c:components) {
caddMouseListener(aMouseListener);
}
I have to design a batleships game for friday but the course im doing seems to have skipped a few things because although i managed all the other assignments this final project is unbelievably above my grasp but i have to do something.
i have the following GUI code which gives me my playing grids but i have absolutely no idea how to do the following things
assign a ship to some cells - and color these cells to reflect this
how to do the actual hit,miss,sunk and update the grid
i figure if i can at least do these i can duplicate the code for the cpu but im sooooooo stuck so any help is really appreciated please guys work some magic :)
/**
* BattleGui:
*
*/
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
import java.io.*;
public class BattleGui implements ActionListener
{
// Default filename to use for saving and loading files
// Possible improvement: replace with a FileChooser
private final static String DEFAULT_FILENAME = "battlegui.txt";
private int GRID_SIZE = 8;
private JButton [] buttonArray;
public JMenuBar createMenu()
{
JMenuBar menuBar = new JMenuBar();;
JMenu menu = new JMenu("Battle Menu");
JMenuItem menuItem;
menuBar.add(menu);
// A group of JMenuItems. You can create other menu items here if desired
menuItem = new JMenuItem("New Game");
menuItem.addActionListener(this);
menu.add(menuItem);
menuItem = new JMenuItem("Load Game");
menuItem.addActionListener(this);
menu.add(menuItem);
menuItem = new JMenuItem("Save Game");
menuItem.addActionListener(this);
menu.add(menuItem);
menuItem = new JMenuItem("Quit");
menuItem.addActionListener(this);
menu.add(menuItem);
//a submenu
menu.addSeparator();
return menuBar;
}
public Container createContentPaneCPU()
{
int numButtons = GRID_SIZE * GRID_SIZE;
JPanel grid = new JPanel(new GridLayout(GRID_SIZE,GRID_SIZE));
buttonArray = new JButton[numButtons];
for (int i=0; i<numButtons; i++)
{
buttonArray[i] = new JButton(" ");
// This label is used to identify which button was clicked in the action listener
buttonArray[i].setActionCommand("" + i); // String "0", "1" etc.
buttonArray[i].addActionListener(this);
grid.add(buttonArray[i]);
}
return grid;
}
public Container createContentPane()
{
int numButtons = GRID_SIZE * GRID_SIZE;
JPanel grid = new JPanel(new GridLayout(GRID_SIZE,GRID_SIZE));
buttonArray = new JButton[numButtons];
for (int i=0; i<numButtons; i++)
{
buttonArray[i] = new JButton(" ");
// This label is used to identify which button was clicked in the action listener
//buttonArray[i].setActionCommand("" + i); // String "0", "1" etc.
// buttonArray[i].addActionListener(this);
grid.add(buttonArray[i]);
}
return grid;
}
/**
* This method handles events from the Menu and the board.
*
*/
public void actionPerformed(ActionEvent e)
{
String classname = getClassName(e.getSource());
JComponent component = (JComponent)(e.getSource());
if (classname.equals("JMenuItem"))
{
JMenuItem menusource = (JMenuItem)(e.getSource());
String menutext = menusource.getText();
// Determine which menu option was chosen
if (menutext.equals("Load Game"))
{
/* BATTLEGUI Add your code here to handle Load Game **********/
LoadGame();
}
else if (menutext.equals("Save Game"))
{
/* BATTLEGUI Add your code here to handle Save Game **********/
SaveGame();
}
else if (menutext.equals("New Game"))
{
/* BATTLEGUI Add your code here to handle Save Game **********/
NewGame();
}
}
// Handle the event from the user clicking on a command button
else if (classname.equals("JButton"))
{
JButton button = (JButton)(e.getSource());
int bnum = Integer.parseInt(button.getActionCommand());
int row = bnum / GRID_SIZE;
int col = bnum % GRID_SIZE;
System.out.println(e.getSource());
/* BATTLEGUI Add your code here to handle user clicking on the grid ***********/
button.setBackground(Color.GREEN);
fireShot(row, col);
}
}
/**
* Returns the class name
*/
protected String getClassName(Object o)
{
String classString = o.getClass().getName();
int dotIndex = classString.lastIndexOf(".");
return classString.substring(dotIndex+1);
}
/**
* Create the GUI and show it.
* For thread safety, this method should be invoked from the event-dispatching thread.
*/
private static void createAndShowGUI()
{
// Create and set up the window.
JFrame frame = new JFrame("Battleships");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
int maxGap = 20;
int ButtonWidth = 20;
int ButtonHeight = 1;
BattleGui battlegui = new BattleGui();
frame.setJMenuBar(battlegui.createMenu());
JPanel gui = new JPanel(new GridLayout(2,2,20,5));
gui.setBorder(new EmptyBorder(5,5,5,5));
//Set up components preferred size
JButton b = new JButton("Just fake button");
Dimension buttonSize = b.getPreferredSize();
gui.add(new JButton("Player"));
gui.add(new JButton("CPU"));
b.setPreferredSize(new Dimension(ButtonWidth, ButtonHeight));
gui.add(battlegui.createContentPane());
gui.add(battlegui.createContentPaneCPU());
frame.setContentPane(gui);
// Create and set up the content pane.
/*
BattleGui battlegui = new BattleGui();
frame.setJMenuBar(battlegui.createMenu());
frame.setContentPane(battlegui.createContentPane());
*/
// Display the window, setting the size
frame.setSize(800, 600);
frame.setVisible(true);
}
/**
* Sets a Gui grid square at row, col to display a character
*/
public boolean setGuiSquare(int row, int col, char c)
{
int bnum = row * GRID_SIZE + col;
if (bnum >= (GRID_SIZE*GRID_SIZE))
{
return false;
}
else
{
buttonArray[bnum].setText(Character.toString(c));
}
return true;
}
/**
* This is a standard main function for a Java GUI
*/
public static void main(String[] args)
{
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
//Deploy();
}
});
}
//************************************************************************
//*** BATTLEGUI: Modify the methods below to respond to Menu and Mouse click events
/**
* This method is called from the Menu event: New Game.
* BATTLEGUI
*/
public void NewGame()
{
System.out.println("New game selected");
}
/**
* This method is called from the Menu event: Load Game.
* BATTLEGUI
*/
public void LoadGame()
{
System.out.println("Load game selected");
}
/**
* This method is called from the Menu event: Save Game.
* BATTLEGUI
*/
public void SaveGame()
{
System.out.println("Save game selected");
}
/**
* This method is called from the Mouse Click event.
* BATTLEGUI
*/
public void fireShot(int row, int col)
{
System.out.println("Fire shot selected: at (" + row + ", " + col + ")");
}
}
I would suggest, take a step back and think about the problem domain.
You have a BattleScene, which contains BattleSquares. Each battleSquare can have atmost 1 ship, and can have a color. You also have Ship objects (which can belong to a particular player, indicates if it is damaged or not)...
BattleSquare needs to decide if it is a Hit or Miss, because it has all the information. It knows wether it has a ship or not.
/**true if had a ship, false if it was a miss
*/
public class BattleSquare{
public boolean processHit(){
if (hasShip()){
ship.setState(DESTROYED);
return true;
}
return false;
}
public void setShip(Ship ship){ .... }
public boolean hasShip() { ... } } ... methods for color too
If you isolate your code into manageable snippets, where some classes represent the model, you will be able to manage things better. You appear to be mixing everything in one class and hence are feeling lost.
Similarly, your BattleScene will contains a List of BattleSquares. Once you fire, you can individuall seek a particular BattleSquare and tell it to process itself. If it is a hit, you update the state.
Idea is that your model classes only are responsible for managing state. Your controller classes can fire events, which are intercepted by views, who update the models and refresh themselves.
hopefully it helps.
I have a JFrame with a JSplitPane that is OneTouchExpandable.
I want to remember the last Divider position of the JSplitPane on JFrame dispose and restore the Position if the JFrame is reopened.
It works well, but if the User expand one Side via the oneTouchExpandable UI-Widget then
I store only the 'int'-Position on dispose and set the 'int'-Position back again with the consequence on JFrame-resizing the JSplitPane-Divider jumps to the collapsed Component preferredSize.
How can I get/set the collapse/expand State?
EDIT
Now: the resize-Behavior is OK, but it is not exactly the same behavior like the first-time-open - cause now I have no MinimumDividerLocation. I wanted the SnapIn but further the collapsedState.
public class SplitPaneState {
public static void main( String[] args ) {
SwingUtilities.invokeLater( new Runnable() {
#Override
public void run() {
new SplitPaneState().createAndSowGUI();
}
});
}
private int position = -1;
private Dimension size = new Dimension( 500, 300 );
private void createAndSowGUI() {
final JFrame frame = new JFrame("frame");
frame.setSize( 200, 100 );
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.setLocationRelativeTo( null );
frame.getContentPane().add( new JButton( new AbstractAction(){
{
putValue( Action.NAME, "Open Dialog" );
}
#Override
public void actionPerformed( ActionEvent e ) {
final JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, new JLabel( "left Component" ), new JLabel( "right Component" ));
splitPane.setContinuousLayout( true );
splitPane.setOneTouchExpandable( true );
if(position != -1) {
boolean LeftIsCollapsed = position < splitPane.getMinimumDividerLocation();
if(LeftIsCollapsed) {
splitPane.getLeftComponent().setMinimumSize(new Dimension()); // fix by Martijn Courteaux
splitPane.setDividerLocation(0.0d); // fix by Martijn Courteaux
}else {
splitPane.setDividerLocation(position);
}
}
JDialog dialog = new JDialog(frame,"dialog"){
#Override
public void dispose() {
position = splitPane.getDividerLocation();
size = this.getSize();
super.dispose();
}
};
dialog.setSize( size );
dialog.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE );
dialog.setLocationRelativeTo( frame );
dialog.getContentPane().add( splitPane );
dialog.setVisible( true );
}
}
));
frame.setVisible( true );
}
}
I found that it is possible to collapse one side of the splitpane by setting the minimum size of the component to new Dimension() and then set the divider location:
// Hide left or top
pane.getLeftComponent().setMinimumSize(new Dimension());
pane.setDividerLocation(0.0d);
// Hide right or bottom
pane.getRightComponent().setMinimumSize(new Dimension());
pane.setDividerLocation(1.0d);
You can play with these settings to store and restore the collapse/expand state.
Forcing the divider position to a very small/large value to hide the top/bottom component works, but is defeated when the split pane gets resized, because of the component minimum size. Setting that size to 0 (as proposed in the accepted answer) works, but there are cases when you cannot/don't want to override that.
After looking into the BasicSplitPaneUI and associated classes, it turns out the "one touch expanding" buttons are calling BasicSPlitPaneUI.setKeepHidden(true), so the divider will stick to either end when resized.
Unfortunately, that method is package-private but setting the associated keepHidden field can be done using introspection, as shown in another answer:
sp.setDividerLocation(<0 or 999999>); // Divider is positioned at the top/bottom
Field m = BasicSplitPaneUI.class.getDeclaredField("keepHidden");
m.setAccessible(true);
m.set(sp.getUI(), true); // Divider position will stick
I improved version of toggle function:
/**
* toggle JSplitPane
* #param sp - splitpane to toggle
* #param upLeft - is it left or top component to collapse? or button or right
* #param collapse - true component should be collapsed
*/
public void toggle(JSplitPane sp, boolean upLeft, boolean collapse) {
try {
//get divider object
BasicSplitPaneDivider bspd = ((BasicSplitPaneUI) sp.getUI()).getDivider();
Field buttonField;
//get field button from divider
if (upLeft) {
if (collapse != (sp.getDividerLocation() < sp.getMinimumDividerLocation())) {
return;
}
buttonField = BasicSplitPaneDivider.class.getDeclaredField(collapse ? "rightButton" : "leftButton");
} else {
if (collapse != (sp.getDividerLocation() > sp.getMaximumDividerLocation())) {
return;
}
buttonField = BasicSplitPaneDivider.class.getDeclaredField(collapse ? "leftButton" : "rightButton");
}
//allow access
buttonField.setAccessible(true);
//get instance of button to click at
JButton button = (JButton) buttonField.get(((BasicSplitPaneUI) sp.getUI()).getDivider());
//click it
button.doClick();
//if you manage more dividers at same time before returning from event,
//you should update layout and ui, otherwise nothing happens on some dividers:
sp.updateUI();
sp.doLayout();
} catch (Exception e) {
e.printStackTrace();
}
}
Is hiding your dialog/frame an option?
// Create the dialog/frame which contains the JSplitPane
final JFrame frame = new JFrame("JSplitPane Problem");
frame.setCloseOperation(JFrame.HIDE_ON_CLOSE);
// ...
myButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent ae)
{
if (!frame.isVisible())
frame.setVisible(true);
}
});
http://docs.oracle.com/javase/7/docs/api/javax/swing/JSplitPane.html
void javax.swing.JSplitPane.setDividerLocation(double proportionalLocation)
Sets the divider location as a percentage of the
JSplitPane's size. This method is implemented in terms of
setDividerLocation(int). This method immediately changes the size of
the split pane based on its current size. If the split pane is not
correctly realized and on screen, this method will have no effect (new
divider location will become (current size * proportionalLocation)
which is 0).
So basically you need to have created your whole UI, called .pack() on the main JFrame
AND after that you can use JSplitPane.setDividerLocation(double). If you do it before UI layouting and all that stuff is done, the method will just do nothing as it states in the documentation and you already experienced.
JSplitPane has a method setDividerLocation(double), which sets the divider location as a percentage of the JSplitPane's size.
I tried to create similar functionality some time ago. I had the same problem. But even when I use the setDividerLocation(double) method, it didn't work properly. I believe that it's just JSplitPane bug.
public static void toggle(JSplitPane sp, boolean collapse) {
try {
BasicSplitPaneDivider bspd = ((BasicSplitPaneUI) sp.getUI()).getDivider();
Field buttonField = BasicSplitPaneDivider.class.
getDeclaredField(collapse ? "rightButton" : "leftButton");
buttonField.setAccessible(true);
JButton button = (JButton) buttonField.get(((BasicSplitPaneUI) sp.getUI()).getDivider());
button.getActionListeners()[0].actionPerformed(new ActionEvent(bspd, MouseEvent.MOUSE_CLICKED,
"bum"));
} catch (Exception e) {
e.printStackTrace();
}
}