setUndecorated(true) messing with toFront() - java

I have these two JFrames that work just fine when setUndecorated is set to false, but it doesn't work consistently when it's set to true;
frame = new JFrame("Name of the frame"); // main frame
frame.setAlwaysOnTop(false);
frame.setSize(width, height);
frame.add(canvas);
frame.setUndecorated(true);
frame.setVisible(true);
menu = new MenuUI(); // this is also a undecorated JFrame
menu is called by a mouse event, but it sometimes shows up in the front and sometimes it shows in the back of the main frame...
public static void checkMove(int action, MouseEvent e) {
int x = e.getX();
if(x == 0){
menu.setVisible(true);
menu.toFront();
}else{
menu.setVisible(false);
}
I've tried to do frame.toBack(); but it sends the frame behind everything else as well...
How can I bring undecorated JFrame in front of another undecorated JFrame and make it consistent?
EDIT: the menu frame disappears on mouse event
contentPane.addMouseListener(new MouseAdapter() {
#Override
public void mouseExited(MouseEvent e) {
setVisible(false);
}
});
EDIT2: after mKorbel's answer, I've tried to create a JDialog, but I get the same result as with JFrame. How can I set the parent if I extend a JDialog?
package menu;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.JDialog;
import openGL.main.openGLMain;
#SuppressWarnings("serial")
public class menuTest extends JDialog {
public menuTest() {
setUndecorated(true);
setBounds(0, 0, 250, frame.getHeight());
GroupLayout groupLayout = new GroupLayout(getContentPane());
groupLayout.setHorizontalGroup(
groupLayout.createParallelGroup(Alignment.LEADING)
.addGap(0, 450, Short.MAX_VALUE)
);
groupLayout.setVerticalGroup(
groupLayout.createParallelGroup(Alignment.LEADING)
.addGap(0, 700, Short.MAX_VALUE)
);
getContentPane().setLayout(groupLayout);
getContentPane().addMouseListener(new MouseAdapter() {
#Override
public void mouseExited(MouseEvent e) {
setVisible(false);
}
});
}
}

JFrame by default never react to AlwaysOnTop or toFront correctly, isn't main functionality for primary Top-Level Container (without iconify and deiconify, but then flashing on the screen)
you have to use JDialog for this job, set for parent and modality (if needed)

JFrame by default never react to AlwaysOnTop or toFront correctly, isn't main functionality for primary Top-Level Container (without iconify and deiconify, but then flashing on the screen)
you have to use JDialog for this job, set for parent and modality (if needed)
exactly and also you can use JinternalFrame that makes you have some children frame in your main Jframe and you can iconify and deiconify or close which is don't needed and also make them resizable if you need.
and if you want make them in front or back to others,you can make it easily by Drag&Drop that Frame Children!

Related

How to fix KeyAdapter no longer working once window loses focus and gains it again

I'm making a simple game in Java for a school project, and I'm using a JFrame in conjunction with a class extending KeyAdapter in order to listen for inputs. However, once I change focus to another window (such as Snipping Tool) then back to the game window, the KeyAdapter no longer seems to recognise key presses.
I've tried running this code once compiled, and in Eclipse, experiencing the same issue both times. I'm running Windows 10, if that makes a difference, and using version 4.11.0 of Eclipse with the latest Java package available. I have not used JFrames, etc. in the past and have found very little content describing their use in a beginner-friendly fashion.
import java.awt.*;
import javax.swing.*;
public class GWindow extends Canvas {
public GWindow() {
frame = new JFrame("Dungeon120");
frame.addKeyListener(new GInputs());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(1000, 650);
frame.add(this);
frame.pack();
frame.setVisible(true);
this.setBackground(Color.black);
}
}
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
public class GInputs extends KeyAdapter {
public void keyPressed(KeyEvent event) {
if (event.getKeyCode() == KeyEvent.VK_SPACE) {
Main.MainGame.TurnQueue.nextGUnit();
System.out.println("SPACE KEY PRESSED");
}
}
}
When the program is first run, it runs as expected, with space presses printing "SPACE KEY PRESSED" and executing the nextGUnit() method. However, nothing happens once any key press once the program loses and regains focus.
You should try to avoid mixing AWT components with Swing components (in your case, Canvas with JFrame).
Moving on, in my past experience, using KeyListener/KeyAdapter with JFrame and JPanel does not work. Instead, to listen for inputs, you can position an offscreen JTextField to listen for keys for you.
import java.awt.*;
import javax.swing.*;
public class GWindow extends Canvas {
public GWindow() {
frame = new JFrame("Dungeon120");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(1000, 650);
frame.add(this);
JTextField listener = new JTextField();
listener.addKeyListener(new GInputs());
//add it to the frame using any layout of your choice
this.add(listener);
frame.pack();
frame.setVisible(true);
//set focus to JTextField
listener.requestFocusInWindow();
this.setBackground(Color.black);
}
}
If you are not familiar with layouts, here is a code excerpt:
import javax.swing.*;
import java.awt.*;
public class GWindow extends Canvas {
public GWindow() {
JFrame frame = new JFrame("Dungeon120");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//set layout to SpringLayout
SpringLayout layout = new SpringLayout();
frame.setLayout(new SpringLayout());
this.setSize(1000, 650);
frame.add(this);
JTextField listener = new JTextField();
listener.addKeyListener(new GInputs());
//position it offscreen
layout.putConstraint(SpringLayout.SOUTH, listener, 0, SpringLayout.NORTH, frame.getContentPane());
frame.add(listener);
frame.pack();
frame.setVisible(true);
//set focus to JTextField
listener.requestFocusInWindow();
this.setBackground(Color.black);
}
}
Also, try to make your JFrame outside of the GWindow constructor.

How to set dismiss delay on JButton's rollover effect?

By analogy with ToolTipManager setDismissDelay(int milliseconds) method, i would like to implement a dismiss delay for the rollover effect on a JButton.
In my swing application i have set different icons for my JButtons (setIcon, setPressedIcon and setRolloverIcon methods), but i'm trying to solve an issue occurring when a particular JButton, which should open a modal dialog, is pressed.
When the button is pressed and the modal dialog is shown, the jbutton still shows the Rollover icon, even if i passed the "normal" icon to setPressedIcon method.
Also, the rollover icon won't disappear until the cursor returns to main frame, also if the jdialog has been closed.
I made an example to show what i mean. I placed only two buttons into main frame, each button has a green square icon as "normal" icon, and a red icon for rollover effect.
As i sayed, i would like the buttons to show again the green icon when they are pressed. The first button will behave "wrongly", since the red icon is visible after the jdialog creation.
For the second button i solved this issue overriding isPressed () method (in its DefaultButtonModel), by calling setRollover (false) when the button is pressed.
I don't think this is the best solution, i would prefer not to act directly on ButtonModel.
So i would like to know if you have a better idea, maybe something similar to a setDismissDelay method, as i sayd before. Thanks !
Here there's an SSCE :
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.DefaultButtonModel;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class SSCE
{
public static void main (String[] a) {
SwingUtilities.invokeLater (new Runnable () {
public void run () {
JFrame frame = new JFrame ("Icon Test");
frame.setContentPane (new MainPanel (frame));
frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
frame.setResizable (false);
frame.pack ();
frame.setLocationRelativeTo (null);
frame.setVisible (true);
}
});
}
}
class MainPanel extends JPanel
{
public MainPanel (JFrame parent) {
JButton firstButton = createButton (createButtonImage (Color.GREEN), createButtonImage (Color.RED), parent);
JButton secondButton = createButton (createButtonImage (Color.GREEN), createButtonImage (Color.RED), parent);
secondButton.setModel (new DefaultButtonModel () {
#Override public boolean isPressed () {
boolean isPressed = super.isPressed ();
if (isPressed) setRollover (false);
return isPressed;
}
});
add (firstButton);
add (secondButton);
}
private JButton createButton (BufferedImage normalImage, BufferedImage rolloverImage, final JFrame parent) {
ImageIcon normalIcon = new ImageIcon (normalImage), rolloverIcon = new ImageIcon (rolloverImage);
JButton button = new JButton (new AbstractAction () {
public void actionPerformed (ActionEvent e) {
JDialog dialog = new JDialog (parent, "Test Dialog",true);
dialog.setSize (400, 400);
dialog.setLocationRelativeTo (parent);
dialog.setVisible (true);
}
});
button.setBorderPainted (false);
button.setCursor (Cursor.getPredefinedCursor (Cursor.HAND_CURSOR));
button.setFocusPainted (false);
button.setContentAreaFilled (false);
button.setIcon (normalIcon);
button.setPressedIcon (normalIcon);
button.setRolloverEnabled (true);
button.setRolloverIcon (rolloverIcon);
return button;
}
private BufferedImage createButtonImage (Color color) {
BufferedImage image = new BufferedImage (20, 20, BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics ();
g.setColor (color);
g.fillRect (0, 0, 20, 20);
g.dispose ();
return image;
}
}
EDIT :
As #camickr suggested, i tried to wrap the ActionListener code in a SwingUtilities.invokeLater ().
I won't repost the full code, i have only replaced those lines :
JButton button = new JButton (new AbstractAction () {
public void actionPerformed (ActionEvent e) {
JDialog dialog = new JDialog (parent, "Test Dialog",true);
dialog.setSize (400, 400);
dialog.setLocationRelativeTo (parent);
dialog.setVisible (true);
}
});
with :
JButton button = new JButton ();
button.addActionListener (new ActionListener () {
public void actionPerformed (ActionEvent e) {
SwingUtilities.invokeLater (new Runnable () {
public void run () {
JDialog dialog = new JDialog (parent, "Test Dialog",true);
dialog.setSize (400, 400);
dialog.setLocationRelativeTo (parent);
dialog.setVisible (true);
}
});
}
});
However, this doesn't solve my problem, the red icon is still visible when the dialog is created.
I tried some small adjustments, with addActionListener or setAction, also only calling setVisible into the invokeLater call, but it still doesn't work.
Also, how could i use a Timer without using the same code on ButtonModel which i am using now ?
I already tried some "hacks" by setting "normal icon" inside the actionPerformed and then invoking the other Action with a "custom" ActionEvent, but i would like to have a "clean" solution.
All code in a listener executes on the Event Dispatch Thread (EDT).
The problem is that the state of the button is not changed before the ActionListener code is invoked. Once the modal dialog is displayed, the button state change code isn't executed until the dialog is closed.
Wrap the code in the ActionListener in a SwingUtilities.invokeLater(). This code will be added to the end of the EDT allowing normal button processing to finish before the dialog is displayed.
Read the section from the Swing tutorial on Concurrency in Swing for more information about the EDT.
Edit:
i would prefer not to act directly on ButtonModel
Spend more time playing with the code. The problem is that there is no mouseExited that is generated when the dialog is displayed so the state of the ButtonModel is never updated.
Another option might be to manually generate a MouseEvent for the mouseExited event and dispatch the event to the button before the dialog is displayed.
Although this approach would also be considered a hack.
how could i use a Timer
Again, the problem is the state of the button. Even if you use a Timer you would manually need to reset the state of the model.
Your current solution seems reasonable since all the logic is located in a class that customizes the behaviour.

how to put my background image at the bottom

I want to put my background image at the very bottom in this frame, and the button on top. However the code I wrote below doesn't work. Can anyone see where the problems are?
Another thing is that even though I set the location for my button, it keep showing at the top center on the frame.
Please ignore the comment lines. (I was just guessing, and hoping them will work, but they don't apparently.)
public class Menu extends JFrame{
private JLayeredPane pane;
private JLayeredPane pane2;
public Menu(){
final JFrame f = new JFrame("Chinese Chess");
JButton play = new JButton("Play vs. AI");
f.setLayout(new FlowLayout());
f.setLocationRelativeTo(null);
f.setSize(800, 800);
f.setVisible(true);
f.setResizable(false);
//f.pack();
pane = new JLayeredPane();
pane2 = new JLayeredPane();
f.add(pane);
f.add(pane2);
//background image
JLabel background = new JLabel(new ImageIcon("res/img/background.png"));
background.setLocation(0, 0);
background.setSize(800, 800);
pane.add(background, JLayeredPane.FRAME_CONTENT_LAYER);
pane2.add(play, JLayeredPane.DEFAULT_LAYER);
//pane.moveToBack();
//button PlayAI
play.setLocation(500,500);
play.setPreferredSize(new Dimension(100,50));
//f.setLayout(new FlowLayout());
//frame menu
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//f.getContentPane().add(play);
play.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
new PlayAI();
}
});
}
public static void main(String[] args){
new Menu();
}
Problems/Solutions:
setLocation(...) and setBounds(...) types of calls are ignored by most layout managers. The only way to use them is to set the layout of the container to null via .setLayout(null);
But having said that, while null layouts and setBounds() might seem to Swing newbies like the easiest and best way to create complex GUI's, the more Swing GUI'S you create the more serious difficulties you will run into when using them. They won't resize your components when the GUI resizes, they are a royal witch to enhance or maintain, they fail completely when placed in scrollpanes, they look gawd-awful when viewed on all platforms or screen resolutions that are different from the original one.
So in sum -- don't do this, don't use null layouts or setBounds, but rather nest JPanels, each using its own layout manager, and thereby create easy to maintain and decent GUI's.
If you want an image to be in the background, then draw it in a JPanel that you use as a container for your GUI components by drawing it in the JPanel's paintComponent(Graphics g) method as has been demonstrated in many many similar questions on this site -- I'll find you some of mine in a second.
If you add any JPanels on top of this image drawing JPanel, be sure that you can see through them by calling setOpaque(false) on these overlying JPanels. Otherwise you'll cover up the image.
Your code has two JFrames when only one is needed. Get rid of the one you don't use.
You call setVisible(true) too early on the JFrame, before components have been added to the GUI -- don't. Call it only after adding everything to the GUI so all display OK.
You're creating two JLayedPanes, and completely covering one by the other by adding them to the JFrame without understanding how the JFrame's BorderLayout handles added components.
I suggest that you not even use one JLayeredPane but instead draw in the JPanel as noted above, and use that as your container.
Your code looks to be opening a completely new GUI window when the play button is pressed, and if so, this can get annoying to the user fast. Consider swapping views instead with a CardLayout.
For example:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
// extend JPanel so you can draw to its background
#SuppressWarnings("serial")
public class Menu2 extends JPanel {
private BufferedImage bgImage = null; // our background image
private JButton playButton = new JButton(new PlayVsAiAction("Play Vs. AI", KeyEvent.VK_P));
public Menu2(BufferedImage bgImage) {
this.bgImage = bgImage;
setLayout(new GridBagLayout()); // center our button
add(playButton);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (bgImage != null) {
g.drawImage(bgImage, 0, 0, this);
}
}
// to size our GUI to match a constant or the image.
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
// if you want to size it based on the image
if (bgImage != null) {
int width = bgImage.getWidth();
int height = bgImage.getHeight();
return new Dimension(width, height);
} else {
return super.getPreferredSize();
}
// if you want to size the GUI with constants:
// return new Dimension(PREF_W, PREF_H);
}
private class PlayVsAiAction extends AbstractAction {
public PlayVsAiAction(String name, int mnemonic) {
super(name); // have our button display this name
putValue(MNEMONIC_KEY, mnemonic); // alt-key to press button
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO code to start program
}
}
private static void createAndShowGui() {
BufferedImage img = null;
String imagePath = "res/img/background.png";
try {
// TODO: fix this -- use class resources to get image, not File
img = ImageIO.read(new File(imagePath));
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
Menu2 mainPanel = new Menu2(img);
JFrame frame = new JFrame("Chinese Chess");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
createAndShowGui();
});
}
}
Apart from the solution above... you should create and launch your swing application this way:
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();
}
});
}
private static void createAndShowGUI() {
// Instantiate your JFrame and show it
}

Make JWindow retain focus when children are focused, including another window as child

I have created JWindow, which has several JTextInput fields. This JWindow parent is JFrame. Now the situation - if text fields are clicked - they get the focus BUT JWindow also stays focused. That is good. But if I create another JWindow (child of first JWindow), and set it visible, then focus on first JWindow is lost, and the child JWindow is focused. This is bad. Is there some way to retain focus on the parent JWindow, and to make the child JWindow get the focus like JTextInput fields?
JWindow without parent never will be focusable, carefully then JTextComponents never will be editable
for example
import java.awt.*;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
public class WindowTest {
private JFrame frame;
private boolean bol = true;
public WindowTest() {
frame = new JFrame("Window Test");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setSize(300, 300);
frame.setVisible(true);
JWindow win = new JWindow(frame);
win.setLayout(new GridLayout(0, 1));
JTextField text = new JTextField("Show Window");
text.getDocument().addDocumentListener(new DocumentListener() {
#Override
public void insertUpdate(DocumentEvent e) {
if (!bol) {
JWindow win = new JWindow();
win.setLayout(new GridLayout(0, 1));
win.add(new JTextField("JTextField"));
win.add(new JTextField("JTextField"));
win.add(new JLabel("<html> Concurency Issues in Swing<br>"
+ " never to use Thread.sleep(int) <br>"
+ " durring EDT, simple to freeze GUI </html>"));
win.pack();
win.setLocation(350, 150);
win.setVisible(true);
bol = true;
}
}
#Override
public void removeUpdate(DocumentEvent e) {
}
#Override
public void changedUpdate(DocumentEvent e) {
}
});
win.add(text);
win.add(new JTextField("JTextField"));
win.add(new JTextField("JTextField"));
win.pack();
win.setLocation(250, 150);
win.setVisible(true);
bol = false;
}
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new WindowTest();
}
});
}
}
Before setting the child window visible, add a WindowListener. After you call setVisible on the child window, you'll receive the windowActivated callback. At that point call toFront on your parent window to bring it forward. Given the quirkiness of OS z-order handling, you might need/want to call toBack on the child window first, as well as calling requestFocus on the parent window or one of its focusable children afterward.

I can't see my JPanel and its components in the JApplet

I want to put a JPanel in a JApplet, the problem is that I can't see it :( I've overridden the paintComponent of my JPanel in order to have a background image, but I can't see anything. When I remove the paintComponenet method that I had overriden, and set a color to the background of this panel, it seems that JPanel fills the JApplet and still no component is visible :-S I've tried different layouts. I also put my panel in the center of another panel which fills my JApplet but nothing changed, and still no component and no background image is visible :(
import java.awt.BorderLayout;
import java.awt.Graphics;
import javax.swing.ImageIcon;
import javax.swing.JApplet;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
public class Main extends JApplet implements Runnable{
private JTextArea display;
private Thread outputThread;
JPanel boardPanel;
private ClientViewManager view;
#Override
public void init() {
try {
javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
createGUI();
}
});
} catch (Exception e) {
System.err.println("createGUI didn't successfully complete");
}
}
private void createGUI() {
display = new JTextArea(4, 30);
display.setEditable(false);
getContentPane().add(new JScrollPane(display), BorderLayout.SOUTH);
setFocusable(true);
setVisible(true);
setName("CE Tanks");
setSize(600, 600);
setLocation(100, 100);
boardPanel = new JPanel();
boardPanel.setLayout(null);
boardPanel.setBackground(new java.awt.Color(128, 255, 255));
getContentPane().add(boardPanel, BorderLayout.CENTER);
}
public void start() {
outputThread = new Thread(this);
outputThread.start();
}
public void run() {
view = new ClientViewManager();
boardPanel.add(view);
boardPanel.repaint();
repaint();
}
}
class ClientViewManager extends JPanel {
private int rows=8;
private int columns=8;
public ClientViewManager() {
super(null);
JLabel lb= new JLabel("lb.jpg");
lb.setLocation(10, 10);
lb.setSize(50, 50);
lb.setOpaque(false);
lb.setVisible(true);
this.add(lb);
}
public void paintComponent(Graphics g) {
g.drawImage(new ImageIcon("ground.jpg").getImage(), 0, 0, columns * 50,
rows * 50, this);
}
}
The code above can be compiled. I cant even add Keylistener to neither my JPanel nor to my JApplet. I used java.awt.KeyEventDispatcher and in dispatchKeyEvent(KeyEvent e) I printed something in console but, it was printed 3 times. :(
I've overridden the paintComponent of my JPanel inorder to have a background image,
But you didn't add the custom component to your applet:
//boardPanel = new JPanel();
boardPanel = new ClientViewManager();
Also:
get rid of setVisible(). This is not required for any of the controls in your program. By default all components except top level Container (Jframe, JDialog etc) are already visible. In the case of JApplet, you don't need to make it visible as this is part of the process of displaying an applet.
get rid of setSize() and setLocation() you can't control the position of the applet this way.
Don't read the image file in the paintComponent() method. This is not efficient as this method is invoked whenever Swing determines the component needs to be repainted.
JLabels are opaque by default so there is not need to invoke the setOpaque method.
When doing custom painting you should also override the getPreferredSize() method of the component to return the proper size of the custom painting so layout managers can use this information. It works in this case because you added the panel to the CENTER of the BorderLayout. Try adding the panel to the NORTH to see what happens.
Edit:
Now I see where you are adding the ClientViewManager. I'm not sure why you are trying to do this with a Thread but once again there are several problems.
When you add/remove components from a visble GUI then the basic code is:
panel.add(...);
panel.revalidate();
panel.repaint();
However this still won't work because you are using a null layout and the size of the panel is 0. Use a proper layout manager and implement the getPreferredSize() method as suggest above and the component will be displayed properly.
I recommend you to use the GUI Builder of Netbeans to build a GUI like that, and then compare the generated code to your code. Netbeans results really useful to help you create swing code.

Categories