Open goxr3plus opened 5 years ago
Third party version, see https://git.dorkbox.com/dorkbox/SystemTray
Question was : if JavaFX doesn't support it...
I found just this : https://bugs.openjdk.java.net/browse/JDK-8092115 It seems like development about SystemTray in JavaFX is stopped.
Anyone some good new about this ? :) Thanks !!!
In my opinion, if the third party version is good enough for use, it should not waste the precious open source community labor on it ( By the way, there should be a Gitter or Discord channel to discuss something like this)
@XQDD It is not JavaFX implementation though ... it's using Swing/AWT.
@goxr3plus All that's required is for someone to step up and start the process of adding this functionality to OpenJFX. A new API is possible to add but it takes a commitment of work and time by a volunteer. In other words, just posting an issue on this Github repo will almost surely not make it so an Oracle dev implements this feature - we need the communities support!
@brcolow Yes i understand, it's just i have no clue how to start implementing this :'(
How to start:
A public API needs to be defined. Most likely it would be a SystemTray
class that has various methods for setting menu items, icons, separators, etc. The public API would need to be platform-agnostic but encompass enough of the shared feature set between platforms.
Once the public API is defined you would create a SystemTrayPeer
class in the private API that defines the native methods.
Then you would implement the native methods for Windows, macOS, and GTK (Linux) to create the native system trays for each platform.
Would definitely like to see a JavaFX SystemTray
.
For Linux it needs implementation at least for both KDE and GTK. The two dominant desktop environments.
A public API could be created for JavaFX. The implementations could be made as third party dependencies for KDE, GNOME, and all the others in Linux, Windows and Mac.
I noticed the dorkbox library was mentioned and wanted to share my experiences. The lack of a reliable library on all Linux desktops makes it impossible to support in a product. I've done a workaround -- inspired in part by Steam -- to just not use the tray area, but instead draw a menu near the dock. Quoting an Ubuntu bug report on the dorkbox repo:
@dorkbox FYI, I've found a reasonable way to draw the menu at the task bar using a minimize/restore listener on a 1px x 1px window, wanted to share. (Pure Java, no JNI).
I'd be happy to share my technique for drawing the menu at the mouse location. It works pretty well with Gnome, Pantheon and KDE by drawing a menu at the mouse. The code is currently Swing-based (not JavaFX) but a similar technique should be possible. If someone finds this useful and needs to use the code under a different license, let me know.
Sorry if this is off-topic.
Please share it with us, i will try to implement in JavaFx
@tresf
@goxr3plus sure. Apologies as the example's for Swing, hopefully it's of use...
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import java.awt.*;
import java.awt.event.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
public class TaskTray extends JFrame implements WindowListener {
private JPopupMenu menu;
private ActionListener exitListener;
public TaskTray(String title, JPopupMenu menu, Image image, ActionListener exitListener) {
this.menu = menu;
this.exitListener = exitListener;
this.addListeners();
this.setUndecorated(true);
this.setTitle(title);
this.setSize(0,0);
this.getContentPane().setBackground(Color.BLACK);
this.setIconImage(image);
this.setResizable(false);
this.addWindowListener(this);
this.pack();
this.setVisible(true);
}
private void addListeners() {
this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
if (exitListener != null) {
exitListener.actionPerformed(new ActionEvent(e.getComponent(), e.getID(), "Exit"));
}
}
});
menu.addPopupMenuListener(new PopupMenuListener() {
@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {}
@Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
TaskTray.this.setState(JFrame.ICONIFIED);
}
@Override
public void popupMenuCanceled(PopupMenuEvent e) {
setState(JFrame.ICONIFIED);
}
});
menu.addKeyListener(new KeyListener() {
@Override
public void keyTyped(KeyEvent e) {}
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
menu.setVisible(false);
}
}
@Override
public void keyReleased(KeyEvent e) {}
});
}
@Override
public void setTitle(String title) {
super.setTitle(title);
TaskTray.setTaskBarTitle(title);
}
@Override
public void windowOpened(WindowEvent windowEvent) {}
@Override
public void windowClosing(WindowEvent windowEvent) {}
@Override
public void windowClosed(WindowEvent windowEvent) {}
@Override
public void windowIconified(WindowEvent windowEvent) {}
@Override
public void windowActivated(WindowEvent windowEvent) {}
@Override
public void windowDeactivated(WindowEvent windowEvent) {
menu.setVisible(false);
this.setState(JFrame.ICONIFIED);
}
@Override
public void windowDeiconified(WindowEvent e) {
Point p = MouseInfo.getPointerInfo().getLocation();
this.setLocation(p);
// call show against parent to prevent un-clickable state
this.menu.show(this, 0,0);
// move to mouse cursor; adjusting for screen boundaries
Point before = this.menu.getLocationOnScreen();
Point after = new Point();
after.setLocation(before.x < p.x ? p.x - this.menu.getWidth() : p.x, before.y < p.y ? p.y - this.menu.getHeight() : p.y);
this.menu.setLocation(after);
}
// Fixes Linux taskbar title per http://hg.netbeans.org/core-main/rev/5832261b8434
public static void setTaskBarTitle(String title) {
try {
Class<?> toolkit = Toolkit.getDefaultToolkit().getClass();
if (toolkit.getName().equals("sun.awt.X11.XToolkit")) {
final Field awtAppClassName = toolkit.getDeclaredField("awtAppClassName");
awtAppClassName.setAccessible(true);
awtAppClassName.set(null, title);
}
} catch(Exception ignore) {}
}
public static void main(String ... args) throws InterruptedException, InvocationTargetException {
SwingUtilities.invokeAndWait(() -> {
// Build a menu
JMenuItem hello = new JMenuItem("Hello");
hello.addActionListener(a -> {
JOptionPane.showMessageDialog(null, "You opened a new window!");
});
JMenuItem exit = new JMenuItem("Exit");
exit.addActionListener(a -> System.exit(0));
JPopupMenu menu = new JPopupMenu();
menu.add(hello);
menu.add(exit);
// Handle someone closing it manually, set to exit.getActionListeners()[0] if needed
ActionListener windowClosing = e -> System.out.println("I detected that you exited!");
// Title
String title = "MENU";
// Get an icon from the filesystem
Image image = null;
try {
image = ImageIO.read(new URL("https://via.placeholder.com/40/878f99/FFFFFF/?text=" + title));
} catch(Exception e) {
e.printStackTrace();
}
// Create a taskbar item that can draw a menu
new TaskTray(title, menu, image, windowClosing);
});
}
}
@tresf Thank you :) 🥇
I am thinking how to make a solution for Windows and Mac now .
Any updates?
Long ago i was using Swing which was providing SystemTray https://docs.oracle.com/javase/tutorial/uiswing/misc/systemtray.html
There is nothing like that in JavaFX as i have seen 6 years now .
Can we implement it ?