I am currently facing a small issue that goes like this: I have a button which, when clicked, will trigger the display of a JPopupMenu under it ( I grab the location of the button, substract a bit from it's height and display the JPopupMenu). This is all made in NetBeans with the GUI builder.
The JPopupMenu is composed of several JPanels, which contain a JLabel, a Button, a TextField and a CheckBox. Those JPanels, I want them to be first initialized in a "minimized" mode( display only the label), but, when the user clicks on them, to expand and display the other components as well. On a second click, the JPanel will revert to the "minimized" state.
Now, I don't know exactly where the issue is, whether in the way the JPanel is resized or not, but although when I click the button the popup menu is displayed correctly and everything else, if I expand a JPanel inside it, the popup menu does not get resized -> it has the same width/height no matter what I do. I tried to change the Layout Manager used by JPopupMenu to FlowLayout,BoxLayout, but ultimately they had no effect. Now, a few snippets of points of interest in the code:
public class WifiItem extends javax.swing.JPanel {
private final Dimension maxDimension;
private final Dimension minDimension;
private boolean maximized;
public WifiItem() {
initComponents();
setBorder(BorderFactory.createLineBorder(Color.black, 1));
maxDimension = new Dimension(386, 62);
minDimension = new Dimension(386, 32);
maximized = false;
setSize(minDimension);
setPreferredSize(minDimension);
setMinimumSize(minDimension);
setMaximumSize(minDimension);
this.setPreferredSize(minDimension);
this.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
if (maximized) {
setToMin();
} else {
setToMax();
}
}
});
}
public final void setToMax() {
setSize(maxDimension);
setPreferredSize(maxDimension);
setMinimumSize(maxDimension);
setMaximumSize(maxDimension);
maximized = true;
getParent().revalidate();
}
public final void setToMin() {
setSize(minDimension);
setPreferredSize(minDimension);
setMinimumSize(minDimension);
setMaximumSize(minDimension);
maximized = false;
getParent().revalidate();
}
This is where I build the JPopupMenu and set up the display.
public class WifiConnections {
private static final List<WifiItem> connectionsList = new ArrayList<>();
public static JPopupMenu displayPanel;
public WifiConnections() {
displayPanel = new JPopupMenu();
displayPanel.setLayout(new BoxLayout(displayPanel, BoxLayout.Y_AXIS));
WifiItem newItem = new WifiItem();
newItem.setConnectionId("Connection 1.");
WifiItem newItem2 = new WifiItem();
newItem2.setConnectionId("Connection 2.");
connectionsList.add(newItem);
connectionsList.add(newItem2);
connectionsList.forEach((item) -> {
displayPanel.add(item);
});
}
public void displayPopup(Point p) {
displayPanel.setLocation(p.x, p.y + 34);
displayPanel.setVisible(true);
}
public void hidePopup() {
displayPanel.setVisible(false);
}
}
As you can see, if I expand one item, the other one gets covered and the popup does not resize to fit the JPanels, and I am really at a loss at how to continue.
Related
I'm trying to create JPanel with two different buttons which one of them increasing and second decreasing size of text or window. I have class with button declaration. Everything is working when I put these buttons on JFrame separately.
I don't know how to get Action Listener in JPanel of each buttons. All I possibly do is listener of mouse click on JPanel...
Could you help me? I'm really begginer with coding so be polite please :]
public class ButtonMy extends Component {
private ButtonIncrease increase;
private PropertyChangeSupport propertyChangeSupport;
public ButtonMy() {
setPreferredSize(new Dimension(30,30));
kolor = Color.blue;
setForeground(kolor);
propertyChangeSupport = new PropertyChangeSupport(this);
increase = ButtonIncrease.Powieksz;
}
public ButtonIncrease getIncrease() {
return increase;
}
public void setIncrease(ButtonIncrease increase) {
ButtonIncrease oldIncrease = this.increase;
this.increase = increase;
propertyChangeSupport.firePropertyChange("increase", oldIncrease, increase);
}
public void addPropertyChangeListener(PropertyChangeListener l) {
propertyChangeSupport.addPropertyChangeListener(l);
}
public void removePropertyChangeListener(PropertyChangeListener l) {
propertyChangeSupport.removePropertyChangeListener(l);
}
}
There is JPanel for bind 2 buttons. Here is the biggest problem :/ I'm lack of ideas.
public class ButtonB extends JPanel implements ActionListener{
public ButtonMy b1 = new ButtonMy();
public ButtonMy b2 = new ButtonMy();
public ButtonB (){
init();
}
public final void init(){
setLayout(new GridLayout(1,2));
this.przycisk1.setIncrease(ButtonIncrease.Powieksz);
this.przycisk2.setIncrease(ButtonIncrease.Zmniejsz);
add(b1);
add(b2);
}
}
JFrame where I test this component is very common. Code below shows only function for inc and dec size when separate button is clicked (not in JPanel).
private void buttonMy3MouseClicked(java.awt.event.MouseEvent evt) {
switch(buttonMy3.getIncrease()) {
case Powieksz: setSize(1);
break;
case Zmniejsz: setSize(0);
break;
}
}
I didn't paste full of my code. There some of math functions left which I think they are not needed here (setSize for example).
I'm not sure if i understand the problem correctly but I think under the actionListener class you should have a method called actionPerformed& it will say that if button1 is clicked increase the number, if button2 is clicked decrease the number:
public void actionPerformed( ActionEvent event ) {
if (event.getSource()== b1) // your "increase size" code
if(event.getSource()== b2)// your "decrease size" code
}
button listeners are actually different from mouse listeners; buttons implements ActionListeners and have the actionPerformed method with event variable. you could handle the event by:
getSource() -this method is inherited from java.util.EventObject and returns the OBJECT on which the event initially occurred (the button itself)
or by getActionCommand() -this method is available to action events, or any event that inherits from ActionEvent and returns the command STRING associated with this action.
however mouse listeners implements MouseListener and has a lot of methods depending on what the mouse does (pressed, clicked, released, etc.).
How would I call an Action from another class in Java? I got a CloseTabButton class online that allows a simple close tab button on each JTabbedPane, but when the tab is closed, I would like a dialog to pop up based on information (if file is not saved, ask to save it, etc.). This is the file:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class CloseTabButton extends JPanel implements ActionListener {
private JTabbedPane pane;
public CloseTabButton(JTabbedPane pane, int index) {
this.pane = pane;
setOpaque(false);
// CloseIcon class just had a button with an x painted on it
Icon closeIcon = new CloseIcon();
JButton close = new JButton(closeIcon);
close.setPreferredSize(new Dimension(closeIcon.getIconWidth(), closeIcon.getIconHeight()));
close.addActionListener(this);
add(new JLabel(pane.getTitleAt(index), pane.getIconAt(index), JLabel.LEFT));
add(close);
pane.setTabComponentAt(index, this);
}
#Override
public void actionPerformed(ActionEvent e) {
int i = pane.indexOfTabComponent(this);
String fileName = pane.getToolTipTextAt(i);
// Where I want to ask if user wants to save, etc.
if (fileName == "Untitled") {
// Do stuff
}
pane.remove(i); // Removes the tab
// If tab count < 1, then disable the save and save as buttons on menu
if (pane.getTabCount() < 1) {
JFrame frame = (JFrame) pane.getParent().getParent().getParent().getParent().getParent(); // Yes, there is that many in my code to get the parent JFrame
int menuCount = frame.getJMenuBar().getMenuCount();
for (int a = 0; a < menuCount; a++) {
int itemCount = frame.getJMenuBar().getMenu(a).getItemCount();
for (int b = 0; b < itemCount; b++) {
Component component = frame.getJMenuBar().getMenu(a).getMenuComponent(b);
if (!(component instanceof JSeparator)) {
// Not a seperator
String itemName = frame.getJMenuBar().getMenu(a).getItem(b).getAccessibleContext().getAccessibleName();
if (itemName == "Save As..") {
frame.getJMenuBar().getMenu(a).getItem(b).setEnabled(false);
}
}
}
}
}
}
}
In my main class I have actions listed like this:
static Action Close = new AbstractAction("Close") {
public void actionPerformed(ActionEvent e) {
closeCurrentWindow(); // function that will close tab
}
}
The other menu items are Actions as well, and as you can see, what I'm currently doing in the CloseTabButton class is quite frustrating, and most likely the wrong way to code it. Is there a much simpler way to do what I'm doing?
The first thing I might do is provide ActionListener support to the CloseTabButton, for example...
public class CloseTabButton extends JPanel {
private JTabbedPane pane;
public CloseTabButton(JTabbedPane pane, int index) {
this.pane = pane;
setOpaque(false);
// CloseIcon class just had a button with an x painted on it
Icon closeIcon = new CloseIcon();
JButton close = new JButton(closeIcon);
close.setPreferredSize(new Dimension(closeIcon.getIconWidth(), closeIcon.getIconHeight()));
close.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
fireActionPerformed();
}
});
add(new JLabel(pane.getTitleAt(index), pane.getIconAt(index), JLabel.LEFT));
add(close);
pane.setTabComponentAt(index, this);
}
public void addActionListener(ActionListener listener) {
listenerList.add(ActionListener.class, listener);
}
public void removeActionListener(ActionListener listener) {
listenerList.remove(ActionListener.class, listener);
}
protected void fireActionPerformed() {
ActionListener[] listeners = listenerList.getListeners(ActionListener.class);
ActionEvent evt = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "You could provide you own action command for each tab here");
for (ActionListener listener : listeners) {
listener.actionPerformed(evt);
}
}
}
Basically, this now allows you to register your own ActionListeners to the CloseTabButton
Next, this, fileName == "Untitled", is not how you compare Strings in Java, you should be using something more like "Untitled".equals(fileName)
If you're menus are based on actual Actions, then you can simply disable the Actions themselves. This would require a little bit of work, but a lot less of "guess" work then you're doing now.
Basically, you would monitor the JTabbedPane itself, monitoring for changes to the selected tab and updating the states of the individual Actions themselves
There a number of ways you could do this, like passing a reference of the JTabbedPane to the Actions so they can perform there own monitoring (but I'd use some kind of management interface which could more easily provide information to the Actions and decouple the code and the reliance on JTabbedPane directly, then you could be free to use JInternalFrames instead).
You could have a "menu manager" which did a similar job, monitoring changes to the document container and changing the state of the menu Actions based on the current state, as an example
Updated
If you're making use of the Action API (which I would recommend), then you could simply do something like...
public class CloseTabButton extends JPanel {
private JTabbedPane pane;
public CloseTabButton(JTabbedPane pane, Action action, int index) {
this.pane = pane;
setOpaque(false);
// CloseIcon class just had a button with an x painted on it
Icon closeIcon = new CloseIcon();
JButton close = new JButton(action);
close.setIcon(closeIcon);
close.setPreferredSize(new Dimension(closeIcon.getIconWidth(), closeIcon.getIconHeight()));
add(new JLabel(pane.getTitleAt(index), pane.getIconAt(index), JLabel.LEFT));
add(close);
pane.setTabComponentAt(index, this);
}
}
Passing in the Action for the close operation, then use the same action for both the JMenuItem and JTabbedPane.
The "core" issue would be how you would identify the "current" tab and document in a uniform manner
I want to have a JInternalFrame that will be able to handle JTabbedPane much like the Eclipse IDE. I want the tabs to sit on top of the title bar. Each tab should have its own close button. The InternalFrame should also have a close button so that a user can close all the tabs in one go.
This is what I have:
This is what I want to have (screenshot taken from Eclipse IDE):
I don't know how I can achieve this. Can anyone please point me in the right direction?
EDIT:
Based on a comment to look into UI Delegate, I created a UI delegate subclass that is able to remove the menu, but there are some problems with this:
It looks kind of funny in comparison to a normal JInternalFrame, even though I haven't done anything to it but comment out the "createActionMap" and "add(menuBar)" lines.
I can't find anywhere in the library code to indicate how the title bar and contentPane positions are set - obviously I want to move the position of the contentPane to overlap the title bar.
Here are the codes:
public class MyInternalFrameUI extends BasicInternalFrameUI {
public MyInternalFrameUI(JInternalFrame b) {
super(b);
// TODO Auto-generated constructor stub
}
public static ComponentUI createUI(JComponent b) {
return new MyInternalFrameUI((JInternalFrame)b);
}
protected JComponent createNorthPane(JInternalFrame w) {
titlePane = new MyBasicInternalFrameTitlePane(w);
return titlePane;
}
}
public class MyBasicInternalFrameTitlePane extends BasicInternalFrameTitlePane {
public MyBasicInternalFrameTitlePane(JInternalFrame f) {
super(f);
}
protected void installTitlePane() {
installDefaults();
installListeners();
createActions();
enableActions();
//createActionMap(); // This method is package protected and not visible
setLayout(createLayout());
assembleSystemMenu();
createButtons();
addSubComponents();
}
protected void addSubComponents() {
//add(menuBar); // Remove this to disable the menu
add(iconButton);
add(maxButton);
add(closeButton);
}
}
To answer one part with the close button. A solution is to add the close button yourself (there is no option for that built in Swing).
You have to implement the closeTab(…) method or a better solution would be a callback handler
protected class CloseTabButton extends JPanel {
private JLabel titleLabel;
protected CloseTabButton(JTabbedPane aTabbedPane,
final AbstractObservableObjectJPanel<M> aDetailPanel,
String aTitle, Icon anIcon) {
setOpaque(false);
titleLabel = new JLabel(aTitle, anIcon, JLabel.LEFT);
add(titleLabel);
ImageIcon closeImage = new ImageIcon(
CloseTabButton.class.getResource("/images/icon_normal.png"));
Image img = closeImage.getImage();
Image newimg = img.getScaledInstance(16, 16,
java.awt.Image.SCALE_SMOOTH);
ImageIcon closeIcon = new ImageIcon(newimg);
ImageIcon closeImageRollover = new ImageIcon(
CloseTabButton.class.getResource("/images/icon_roll.png"));
Image imgRoll = closeImageRollover.getImage();
Image newimgRoll = imgRoll.getScaledInstance(16, 16,
java.awt.Image.SCALE_SMOOTH);
ImageIcon closeIconRoll = new ImageIcon(newimgRoll);
JButton btClose = new JButton(closeIcon);
btClose.setPreferredSize(new Dimension(15, 15));
add(btClose);
btClose.setOpaque(false);
btClose.setContentAreaFilled(false);
btClose.setBorderPainted(false);
btClose.setRolloverIcon(closeIconRoll);
btClose.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent aE) {
closeTab(aDetailPanel);
}
});
aTabbedPane.setTabComponentAt(
aTabbedPane.indexOfComponent(aDetailPanel), this);
}
public JLabel getTitleLabel() {
return titleLabel;
}
}
To add keyboard shortcuts you can add them to the input map via
KeyStroke ctrlW = KeyStroke.getKeyStroke(KeyEvent.VK_W,
InputEvent.CTRL_DOWN_MASK);
getRootPane()
.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
.put(ctrlW, disposeAction.getValue(Action.NAME));
DisposeAction is just an Action that also calls closeTab(…)
i've got a problem with my program. I'm finishing to develop Pacman for my exam but I've a trouble when I change Panel from "menu" to "game" Panel.
Precisely in the main frame I've added two Panel : the "menu" panel and the "game " one. If I don't add the menu panel , the game (and above all, the listener) works; but if I also add the menu panel, when I press the "start game" button in it , the "setVisible" property of menu will sets up to false and the new thread will starts. In this case the problem is that the listener doesn't work .
public static void main(String[] args) {
boolean gameOver = false;
MainFrame mf = new MainFrame(FRAME_WIDTH, FRAME_HEIGHT, TITLE);
// board is a logic map; gb is the graphic map of the game
Board board = new Board();
GraphicBoard gb = new GraphicBoard(mf, board);
gb.setLocation(150, 0);
Background background = new Background(mf.getWidth(), mf.getHeight(), BACKGROUND_COLOR);
background.add(gb);
PacmanThread thread = new PacmanThread(gb, gameOver);
Menu menu = new Menu(mf.getWidth(), mf.getHeight(), thread);
mf.add(menu);
mf.add(background);
mf.setResizable(false);
mf.setVisible(true);
}
In menu.java is present a subclass, so
public class Menu extends JPanel{
public Menu(int pWidth, int pHeight, PacmanThread pThread) {
this.setLayout(null);
this.setSize(pWidth, pHeight);
this.thread = pThread;
initComponent();
}
private class StartGame implements ActionListener {
private Menu menu;
public StartGame(Menu pMenu) {
this.menu = pMenu;
}
#Override
public void actionPerformed(ActionEvent e) { //<--- is the problem here? what I should add?
menu.setVisible(false);
thread.start();
}
}
}
Finally tha actionListener is present in a class called Pacman.java
i'm trying to make it display one JPanel and remove the other depended on which timer is off in java. here is the snip it of code. mainMenu and pongField are the JPanels and this is inside a timer that acts every millisecond;
if (SwingUtilities.getAncestorOfClass(JFrame.class, pongField) != null){
if (!pongField.getTimer().isRunning())
{
mainMenu = new MainMenu(screenX, screenY, myColor, background, contentPane);
contentPane.remove(pongField);
}}
if (SwingUtilities.getAncestorOfClass(JFrame.class, mainMenu) != null){
if (!mainMenu.getTimer().isRunning())
{
switch(mainMenu.getButton())
{
case 1:
pongField = new PongField(screenX, screenY, 0, moderator, player1UpControl, player2UpControl, player1DownControl, player2DownControl, myColor, background, contentPane, speed);
mainMenu.setButton(0);
contentPane.remove(mainMenu);
break;
case 2:
break;
case 3:
break;
case 4:
break;
}
}}
let me be a little more specific.
I am trying to make Pong, and one JPanel is the actual game while the other is the main menu. if the game ends the timer stops and if i press a button on the main Menu the timer stops and set getButton() to a number depending on which is pressed. for example if button single player is pressed it creates the game and gets rid of the current Jpanel. but if the game ends it get rid of the current JPanel and displays the main menu. Also each JPanel when created set it's own bounds, background color, and adds itself to the JFrame.
The problem is when i run it and the game ends it doesn't change back unless i minimize it and bring it back up but then the button won't work. here is the code for MainMenu. PongField's code is too long but i think i summed up what it did good enough but i can post it too if you want.
public class MainMenu extends JPanel
{
private static final long serialVersionUID = 1L;
//JButtons
private JButton singleJButton, multiJButton, settingsJButton, exitJButton;
//timer if game is still on
private Timer runningTimer;
//JLabel for title
private JLabel titleJLabel;
//screen size
int screenX, screenY;
//color of text and background
private Color myColor, background;
//Container of the JFrame
private Container contentPane;
//which button was clicked
private int buttonPressed;
public MainMenu(int x, int y, Color myC, Color backg, Container contentP)
{
super();
//Initialize all given variables
screenX = x;
screenY = y;
myColor = myC;
background = backg;
contentPane = contentP;
setBounds(0,0,screenX,screenY);
setBackground(background);
contentPane.add(this);
setFocusable(true);
requestFocusInWindow();
singleJButton = new JButton();
singleJButton.setBounds(100, 100, 100, 50);
singleJButton.setText("Single Player");
singleJButton.setFocusable(false);
contentPane.add(singleJButton);
singleJButton.addActionListener(new ActionListener()
{public void actionPerformed( ActionEvent event )
{singleJButtonAction(event);}});
runningTimer = new Timer( 30, new ActionListener()
{public void actionPerformed( ActionEvent event )
{}});
runningTimer.start();
}
public Timer getTimer()
{
return runningTimer;
}
public int getButton()
{
return buttonPressed;
}
public void setButton(int val)
{
buttonPressed = val;
}
private void singleJButtonAction(ActionEvent e)
{
runningTimer.stop();
buttonPressed = 1;
}
//make this panel have focus
public void focus()
{
requestFocusInWindow();
}
What, is it not working? I'm just guessing because you don't say.
The first thing I see if you're creating and removing panes dynamically. It would be more efficient to just hide them. Of course, I don't know what your layout looks like. The other option is to just construct them on startup with all your other GUI stuff and go ahead and add and remove them, but don't recreate them based on the timer. Just show them and hide them as needed. Like this:
mainMenu.setVisible( true );
mainMenu.setVisible( false );
Every millisecond if also pretty fast. I look at ratcheting down, to say one second or so. Of course, it depends on what you're doing.
HTH
I still have problem understanding what your question is?
One thing that seems to be missing in the code is where you add the mainMenu and pongField you create. You remove them from the contentPane by contentPane.remove(pongField); and contentPane.remove(mainMenu); is that your problem?