SystemTray/src/dorkbox/systemTray/ui/swing/TrayPopup.java

163 lines
5.1 KiB
Java

/*
* Copyright 2021 dorkbox, llc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dorkbox.systemTray.ui.swing;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JDialog;
import javax.swing.JPopupMenu;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import dorkbox.systemTray.SystemTray;
import dorkbox.util.ScreenUtil;
/**
* This custom popup is required if we want to be able to show images on the menu,
*
* This is our "golden standard" since we have 100% control over it on all platforms
*/
public
class TrayPopup extends JPopupMenu {
private static final long serialVersionUID = 1L;
// This lets us click OFF the menu to hide the menu. This will always show an entry in the taskbar
// for java1.6 on linux (possibly others)
private final JDialog hiddenDialog;
private volatile File iconFile;
@SuppressWarnings("unchecked")
public
TrayPopup() {
super();
setFocusable(true);
// Initialize the hidden dialog as a headless, title-less dialog window
hiddenDialog = new JDialog((Frame)null, "Tray menu");
hiddenDialog.setUndecorated(true);
hiddenDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
hiddenDialog.setAlwaysOnTop(true);
// on Linux, the following two entries will **MOST OF THE TIME** prevent the hidden dialog from showing in the task-bar
hiddenDialog.getContentPane().setLayout(null);
hiddenDialog.setType(Window.Type.POPUP);
hiddenDialog.pack();
hiddenDialog.setBounds(0,0,0,0);
addPopupMenuListener(new PopupMenuListener() {
@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
}
@Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
hiddenDialog.setVisible(false);
hiddenDialog.toBack();
}
@Override
public void popupMenuCanceled(PopupMenuEvent e) {
}
});
}
/**
* Sets the image for the title-bar, so IF it shows in the task-bar, it will have the corresponding image as the SystemTray image
*/
public
void setTitleBarImage(final File imageFile) {
if (this.iconFile == null || !this.iconFile.equals(imageFile)) {
this.iconFile = imageFile;
try {
if (imageFile != null) {
Image image = new ImageIcon(ImageIO.read(imageFile)).getImage();
image.flush();
// we set the dialog window to have the same icon as what is on the system tray
hiddenDialog.setIconImage(image);
}
} catch (IOException e) {
SystemTray.logger.error("Error setting the title-bar image for the popup menu task tray dialog");
}
}
}
void close() {
hiddenDialog.setVisible(false);
hiddenDialog.dispatchEvent(new WindowEvent(hiddenDialog, WindowEvent.WINDOW_CLOSING));
}
public
void doShow(final Point point, int offset) {
Dimension size = getPreferredSize();
Rectangle bounds = ScreenUtil.getScreenBoundsAt(point);
int x = point.x;
int y = point.y;
if (y < bounds.y) {
y = bounds.y;
}
else if (y + size.height > bounds.y + bounds.height) {
// our menu cannot have the top-edge snap to the mouse
// so we make the bottom-edge snap to the mouse
y -= size.height; // snap to edge of mouse
}
if (x < bounds.x) {
x = bounds.x;
}
else if (x + size.width > bounds.x + bounds.width) {
// our menu cannot have the left-edge snap to the mouse so we make the right-edge snap to the mouse
x -= size.width; // snap right edge of menu to mouse
offset = -offset; // flip offset
}
// display over the AppIndicator menu (which has to show, then we remove. THIS IS A HACK!)
x -= offset;
// System.err.println("show " + x + "," + y);
// critical to get the keyboard listeners working for the popup menu
setInvoker(hiddenDialog.getContentPane());
hiddenDialog.setLocation(x, y);
hiddenDialog.setVisible(true);
setLocation(x, y);
setVisible(true);
requestFocusInWindow();
}
}