I'm trying to implement a "iconify on quit" behavior in my Java JFrame app, like most native macOS apps have, but I'm quite stumped.
I've tried doing
Window.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent Event) {
System.out.println("Closed on macOS, iconifiying");
Window.setExtendedState(Frame.ICONIFIED);
}
});
and closing the window on quit with (as well as adding a window listener that calls setVisible(false))
Window.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
The former didn't work because it looks like it's minimized and creates 2 separate icons. The latter didn't because I couldn't find a way to detect when the dock icon is clicked to unhide the window. I'd prefer this method if I could figure out how to do so. Does anybody know how to?
Oh, I forgot to really specify what the behavior I'm trying to mimic really was. When you press the quit button on macOS, the window is made invisible. When you click the app's icon in the dock the window should be made visible again.
You really should read the JavaDocs, that and little bit of Googling brought me around to this simple example...
import java.awt.Desktop;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.desktop.AppReopenedEvent;
import java.awt.desktop.AppReopenedListener;
import java.awt.desktop.QuitStrategy;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
public class Test {
public static void main(String[] args) {
new Test();
Desktop.getDesktop().setQuitStrategy(QuitStrategy.CLOSE_ALL_WINDOWS);
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Desktop.getDesktop().addAppEventListener(new AppReopenedListener() {
#Override
public void appReopened(AppReopenedEvent e) {
frame.setVisible(true);
}
});
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setBorder(new EmptyBorder(32, 32, 32, 32));
setLayout(new GridBagLayout());
add(new JLabel("Now you see me"));
}
}
}
Related
I want to create a Java application that displays text on the screen, in such a way that it is not contained in any form or canvas, at least not any visible one, but rather just display it on top of the thing that is currently displaying on the computer.
I'd prefer to use it in pure Java, but I guess if there is some third party library or compiled C API's then that's fine too.
If possible then I'd also like something like Ubuntu's Always on top feature, so that if I click some other window the text still is displayed on top of it.
Is there any way to make JFrames transparent?
This is kind of what I'm aiming for, only doing it in Java
http://2.bp.blogspot.com/-QkxT8pC17nw/T1n_rlr20aI/AAAAAAAAAj0/xbJjYObc4Bw/s1600/screenCaptureRainmeter.png
Conceptually, you problem basically boils down to making the window transparent. To that end you should start by looking at How to Create Translucent and Shaped Windows
The next thing to keep in mind is the fact that most Swing component's are opaque by default, so just beware of that
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setUndecorated(true);
frame.setBackground(new Color(0, 0, 0, 0));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setOpaque(false);
setLayout(new GridBagLayout());
JLabel label = new JLabel("I'm on top");
label.setFont(label.getFont().deriveFont(Font.BOLD, 64));
label.setForeground(Color.WHITE);
add(label);
}
}
}
You may also want to have a look at Window#setAlwaysOnTop
I have a JPanel in a JScrollPane.
The JPanel contains multiple JTextAreas vertically.
I like to keep the scroll of the scrollpane to the top whenever the page is refreshed.
Currently, the scroll always starts from the bottom.
this is my current code and it doesn't work..
panel.invalidate();
panel.revalidate();
panel.repaint();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
((JPanel) panel).setLocation(new Point(0, 0));
}
});
I've also tried adding this code below to scrollpane, but it doesn't work..
scrollPanel.getViewport().setViewPosition( new Point(0, 0) );
I've looked into other stackoverflow questions and they use Jtextarea inside Jscrollpane (they solved it using setCaretPosition(0), however I can't use the same function to the panel). In my case, there is an extra layer.
How can I solve this..?
EDIT**
Based on advice from Pavlo Viazovskyy, I've also tried this below and it still doesn't work for me.. :(
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
scrollPane.getVerticalScrollBar().setValue(0);
}
});
Thank you very much for all the comments.
sorry I didn't give a full proper example in the question as there were too many different classes involved..
In my case, textareas inside Panel inside ScrollPane, I made the scroll to the top by default by using setViewPosition method to scrollPane in the invokelater method.
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
scrollPane.getViewport().setViewPosition( new Point(0, 0) );
}
});
For when you don't have direct access to the JScrollPane, you can simply use JComponent#scrollRectToVisible
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
public class ScrollTest {
public static void main(String[] args) {
new ScrollTest();
}
public ScrollTest() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Test");
frame.add(new JScrollPane(new BigPane()));
frame.setSize(200, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class BigPane extends JPanel {
public BigPane() {
setLayout(new BorderLayout());
JButton scroll = new JButton("Scroll to top");
add(scroll, BorderLayout.SOUTH);
scroll.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
scrollRectToVisible(new Rectangle(0, 0, 1, 1));
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
}
}
Yes, you could walk the component hierarchy till you found a JViewport, but this method does it for you.
Just remember though, the Rectangle is relative to the component which called the method, so if I used the JButton instead, it would try and make the JButton visible, not the panel
I want to show JLabel but want to hide JFrame border and other lower level containers like JPanel.
It just JLabel displayed on the screen.
I tried window transparency but following piece of code hides everything if trying to work with window opacity.
On decreasing windowOpacity , even JLabel becomes blurred. I tried with JPanel as well but couldn't get exact output.
I want this behaviour in jdk1.6 only
I want the JLabel content to be visible properly without any opacity impact but backbround must be purely transparent.
public class TEST {
public static void main(String[] args) {
JFrame frame = new JFrame("Sanjaal Corps - Windows On Top Demo!");
frame.setSize(400, 100);
frame.setLocation(100, 150);
com.sun.awt.AWTUtilities.setWindowOpacity(frame,0.4f);
frame.setUndecorated(true);
frame.add(new JLabel("TESTING"));
frame.setAlwaysOnTop(true);
frame.setVisible(true);
}
}
I tried with solution provided
http://www.dreamincode.net/forums/topic/140041-make-a-jpanel-transparent-to-see-the-desktop-behind/
But the problem here is if we minimize or maximize the window , then a constant color being set, So found its not the best solution or may say the Perfect one.
Assuming I understand your requirements correctly...
I typically add a transparent panel to the Window. This means that, generally, the transparency properties of the Window don't then effect the child components, for example...
Generally speaking, there are now two ways to make a window transparent.
Under Java 7, you simply make it's background color transparent.
Under Java 6 (update 10+), you need to use the unofficial com.sun.AWTUtilities class
...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Window;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.lang.reflect.Method;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.LineBorder;
public class TransparentWindow02 {
public static void main(String[] args) {
new TransparentWindow02();
}
public TransparentWindow02() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setUndecorated(true);
setOpaque(frame, false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setOpaque(false);
setLayout(new BorderLayout());
setBorder(new LineBorder(Color.RED));
JLabel label = new JLabel("Click me if you can see me");
label.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
SwingUtilities.windowForComponent(TestPane.this).dispose();
}
});
add(label);
}
}
public static void setOpaque(Window window, boolean opaque) {
String version = System.getProperty("java.runtime.version");
if (version.startsWith("1.7")) {
window.setBackground(new Color(0, 0, 0, 0));
} else {
try {
Class<?> awtUtilsClass = Class.forName("com.sun.awt.AWTUtilities");
if (awtUtilsClass != null) {
Method method = awtUtilsClass.getMethod("setWindowOpaque", Window.class, boolean.class);
method.invoke(null, window, opaque);
}
} catch (Exception exp) {
exp.printStackTrace();
}
}
}
}
Assuming you want to show the foreground, of the label (nothing else) that is its text/icon, you would set the frame's opacity to false:
com.sun.awt.AWTUtilities.setWindowOpaque(frame, false);
The usual caveat against using com.sun.** classes, which unfortunately is the only way to reach transparent windows prior to java7
In Swing, there are several ways to capture the event of minimizing a frame (iconifying), but the event happens when the frame is ICONIFIED which means after the frame becomes invisible from the screen.
Now I wish to run some code before disappearance of the frame -- immediately when I click the taskbar button.
In other words, do something when the JFrame is "about to" (NOT AFTER) be minimized. Is it possible to do this?
Use WindowStateListener, and call WindowEvent#getNewState() and check against Frame.ICONIFIED.
Here is an example:
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Test {
public Test() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
};
frame.add(panel);
frame.addWindowStateListener(new WindowAdapter() {
#Override
public void windowStateChanged(WindowEvent we) {
if (we.getNewState() == Frame.ICONIFIED) {
System.out.println("Here");
}
}
});
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Test();
}
});
}
}
Create your own JFrame and override setExtendedState method.
public class MyFrame extends JFrame{
....
setExtendedState(JFrame.ICONIFIED);
....
#Override
public void setExtendedState(int state) {
// your code
super.setExtendedState(state);
};
}
Answer to the question "Is it possible to perform some action BEFORE a JFrame is minimized?"
I would say no unfortunately, I checked the native code for openjdk (windows) for frame and window that sends these events to java-space. And as I understand it, it is a callback from the windows API VM_SIZE message. And the SIZE_MINIMIZED is sent when "The window has been minimized" and is not getting any messages before the actual minimization.
I just wrote this test code in my CustomUIPanel class:
public static void main(String[] args) {
final JDialog dialog = CustomUIPanel.createDialog(null,
CustomUIPanel.selectFile());
dialog.addWindowListener(new WindowAdapter() {
#Override public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
It works correctly if CustomUIPanel.main() is the program's entry point, but it makes me wonder something: what if another class called CustomUIPanel.main() for testing? Then my call to System.exit(0) is incorrect.
Is there a way to tell the Swing event dispatch thread to exit automatically if there are no top-level windows?
If not, what's the right thing for a JDialog/JFrame to do upon closing if the goal is for the program to exit when all the top level windows are closed?
You can use the setDefaultCloseOperation() method of JDialog, specifying DISPOSE_ON_CLOSE:
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
See also 12.8 Program Exit.
Addendum: Incorporating #camickr's helpful answer, this example exits when either the window is closed or the close button is pressed.
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.WindowEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
/** #see http://stackoverflow.com/questions/5540354 */
public class DialogClose extends JDialog {
public DialogClose() {
this.setLayout(new GridLayout(0, 1));
this.add(new JLabel("Dialog close test.", JLabel.CENTER));
this.add(new JButton(new AbstractAction("Close") {
#Override
public void actionPerformed(ActionEvent e) {
DialogClose.this.setVisible(false);
DialogClose.this.dispatchEvent(new WindowEvent(
DialogClose.this, WindowEvent.WINDOW_CLOSING));
}
}));
}
private void display() {
this.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
this.pack();
this.setLocationRelativeTo(null);
this.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new DialogClose().display();
}
});
}
}
Not sure about when using a JDialog.
But when using a JFrame you should use frame.dispose(). If the frame is the last open frame then the VM will exit.
Note a dialog does not have an EXIT_ON_CLOSE option since it should not generally exit the VM.
When closing the dialog you could always get the dialogs parent frame. Then you could dispatch an event to the frame to tell it to close itself. Something like:
WindowEvent windowClosing = new WindowEvent(frame, WindowEvent.WINDOW_CLOSING);
//Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(windowClosing);
frame.dispatchEvent(windowClosing);
Use
this.dispose();
It should work.
Well,
You could use a JFrame instead. JDialog is supposed to be used as popup of an application that runs in an JFrame to catch the users attention and to pause the main application.
If the JFrame is closed, you can call System.exit(0)
dialog has a getParent() method, which I guess, is set to null in your case here CustomUIPanel.createDialog(null,
you can use that to exit conditionally.
Here is what I would recommend : dialog.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);