Updated Swing menu item behavior to permit customization.

This commit is contained in:
nathan 2017-02-17 15:50:42 +01:00
parent 455642caf0
commit 0e5d52352b
19 changed files with 628 additions and 140 deletions

View File

@ -22,11 +22,12 @@ This library provides **OS Native** menus and **Swing/AWT** menus, depending on
The following unique problems are also solved by this library:
1. *Sun/Oracle* system-tray icons on gnu/linux **do not** support images with transparent backgrounds
2. *Sun/Oracle* system-tray and *SWT* system-tray implementations **do not** support app-indicators, which are necessary on different distributions of gnu/linux and unix.
2. *Sun/Oracle* system-tray and *SWT* system-tray implementations **do not** support app-indicators, which are necessary on different distributions of gnu/linux and unix
3. *Sun/Oracle* system-tray menus on Windows **look absolutely horrid**
4. *Sun/Oracle* system-tray icons on Windows are **hard-coded** to a max size of 24x24 (it was last updated in *2006*)
5. *Sun/Oracle* system-tray menus on MacOS **do not** always respond to both mouse buttons, where Apple menus do
6. MacOS and Windows *native* menus **do not** support images attached to menu entries
7. Windows menus **do not** support a different L&F from applications
@ -50,7 +51,7 @@ Problems and Restrictions
- **ToolTips** The maximum length is 64 characters long, and it is not supported on all Operating Systems and Desktop Environments. Please note that **Ubuntu** does not always support this!
- **Linux/Unix Menus** Some linux environments only support right-click to display the menu, and it is not possible to change the behavior.
- **Linux/Unix Menus** Some linux environments only support right-click to display the menu, and it is not possible to change the behavior.
Compatibility Matrix
------------------
@ -134,27 +135,31 @@ SystemTray.ENABLE_SHUTDOWN_HOOK (type boolean, default value 'true')
SystemTray.AUTO_FIX_INCONSISTENCIES (type boolean, default value 'true')
- Allows the SystemTray logic to resolve various OS inconsistencies for the SystemTray in different combinations
- Allows the SystemTray logic to resolve various OS inconsistencies for the SystemTray in different combinations
SystemTray.SWING_UI (type SwingUIFactory, default value 'null')
- Allows the developer to provide a custom look and feel for the Swing UI, if defined. See the test example for specific use.
SystemTray.DEBUG (type boolean, default value 'false')
- This property is provided for debugging any errors in the logic used to determine the system-tray type and initialization feedback.
- This property is provided for debugging any errors in the logic used to determine the system-tray type and initialization feedback.
Extension.ENABLE_EXTENSION_INSTALL (type boolean, default value 'true')
- Permit the StatusTray icon to be displayed next to the clock by installing an extension. By default, gnome
places the icon in the "notification drawer", which is a collapsible menu (usually) at the bottom left corner
of the screen. This should be set to false if you want to preserve the default Desktop Environment UI preferences.
Additionally, Arch Linux is the only exception to this rule where it does not install the extension, so TopIcons is
necessary for placing the icon near the clock.
- Permit the StatusTray icon to be displayed next to the clock by installing an extension. By default, gnome
places the icon in the "notification drawer", which is a collapsible menu (usually) at the bottom left corner
of the screen. This should be set to false if you want to preserve the default Desktop Environment UI preferences.
Additionally, Arch Linux is the only exception to this rule where it does not install the extension, so TopIcons is
necessary for placing the icon near the clock.
Extension.ENABLE_SHELL_RESTART (type boolean, default value 'true')
- Permit the gnome-shell to be restarted when the extension is installed.
- Permit the gnome-shell to be restarted when the extension is installed.
Extension.SHELL_RESTART_COMMAND (type String, default value 'nome-shell --replace &')
- Command to restart the gnome-shell. It is recommended to start it in the background (hence '&')
- Command to restart the gnome-shell. It is recommended to start it in the background (hence '&')
```
@ -163,26 +168,30 @@ Extension.SHELL_RESTART_COMMAND (type String, default value 'nome-shell --rep
The test application is [on GitHub](https://github.com/dorkbox/SystemTray/blob/master/test/dorkbox/TestTray.java), and a *simple* example is as follows:
```
this.systemTray = SystemTray.get();
if (systemTray == null) {
throw new RuntimeException("Unable to load SystemTray!");
}
SystemTray.SWING_UI = new CustomSwingUI();
try {
this.systemTray.setImage("grey_icon.png");
} catch (IOException e) {
e.printStackTrace();
}
this.systemTray.setStatus("Not Running");
this.systemTray.addEntry("Quit", new SystemTrayMenuAction() {
SystemTray systemTray = SystemTray.get();
if (systemTray == null) {
throw new RuntimeException("Unable to load SystemTray!");
}
try {
systemTray.setImage("grey_icon.png");
} catch (IOException e) {
e.printStackTrace();
}
systemTray.setStatus("Not Running");
systemTray.getMenu().add(new MenuItem("Quit", new ActionListener() {
@Override
public
void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) {
System.exit(0);
void actionPerformed(final ActionEvent e) {
systemTray.shutdown();
//System.exit(0); not necessary if all non-daemon threads have stopped.
}
});
})).setShortcut('q'); // case does not matter
```
 
 
@ -229,18 +238,21 @@ ISSUES:
 
 
<h4>We now release to maven!</h4>
<h4>Release Notes</h4>
This project **includes** some utility classes, which are an extremely small subset of a much larger library; including only what is *necessary* for this particular project to function. Additionally this project is **kept in sync** with the utilities library, so "jar hell" is not an issue, and the latest release will always include the same utility files as all other projects in the dorkbox repository at that time.
Please note that the utility classes have their source code included in the release, and eventually the entire utility library will be provided as a dorkbox repository.
```
Please note that the utility classes have their source code included in the release, and also on [GitHub](https://github.com/dorkbox/Utilities) repository.
<h5>Maven Info</h5>
````
<dependency>
<groupId>com.dorkbox</groupId>
<artifactId>SystemTray</artifactId>
<version>2.20</version>
</dependency>
```
````
Or if you don't want to use Maven, you can access the latest files and source-code directly from here:

View File

@ -28,10 +28,9 @@
</content>
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="jdk" jdkName="1.7" jdkType="JavaSDK" />
<orderEntry type="module" module-name="Utilities" />
<orderEntry type="library" name="logging slf4j-api" level="application" />
<orderEntry type="library" name="logging logback" level="application" />
<orderEntry type="module" module-name="Dorkbox-Util" />
<orderEntry type="module" module-name="JavaLauncher-Util" />
<orderEntry type="library" name="jna" level="application" />
<orderEntry type="library" name="SWT" level="project" />
<orderEntry type="library" name="asm" level="application" />

View File

@ -45,6 +45,7 @@ import dorkbox.systemTray.nativeUI.NativeUI;
import dorkbox.systemTray.nativeUI._AppIndicatorNativeTray;
import dorkbox.systemTray.nativeUI._AwtTray;
import dorkbox.systemTray.nativeUI._GtkStatusIconNativeTray;
import dorkbox.systemTray.swingUI.SwingUIFactory;
import dorkbox.systemTray.swingUI._SwingTray;
import dorkbox.systemTray.util.ImageUtils;
import dorkbox.systemTray.util.JavaFX;
@ -127,7 +128,7 @@ class SystemTray {
* <p>
* This is an advanced feature, and it is recommended to leave at AutoDetect.
*/
public static TrayType FORCE_TRAY_TYPE = TrayType.AutoDetect;
public static TrayType FORCE_TRAY_TYPE = TrayType.Swing;
@Property
/**
@ -146,6 +147,12 @@ class SystemTray {
*/
public static boolean AUTO_FIX_INCONSISTENCIES = true;
@Property
/**
* Allows the developer to provide a custom look and feel for the Swing UI, if defined. See the test example for specific use.
*/
public static SwingUIFactory SWING_UI = null;
@Property
/**
* This property is provided for debugging any errors in the logic used to determine the system-tray type.

View File

@ -1,47 +0,0 @@
/*
* Copyright 2016 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.swingUI;
import java.awt.Insets;
import javax.swing.JMenu;
import javax.swing.JPopupMenu;
import javax.swing.border.EmptyBorder;
import dorkbox.systemTray.util.ImageUtils;
class AdjustedJMenu extends JMenu {
AdjustedJMenu() {
super();
setFont(ImageUtils.ENTRY_FONT);
}
@Override
public
Insets getMargin() {
Insets margin = super.getMargin();
if (margin != null) {
margin.set(2, -2, 2, 4);
// makes sure the popup menu is created.
JPopupMenu popupMenu = getPopupMenu();
// now adjust the border of it
popupMenu.setBorder(new EmptyBorder(1, 1, 1, 1));
}
return margin;
}
}

View File

@ -1,40 +0,0 @@
/*
* Copyright 2014 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.swingUI;
import java.awt.Insets;
import javax.swing.JMenuItem;
import dorkbox.systemTray.util.ImageUtils;
class AdjustedJMenuItem extends JMenuItem {
AdjustedJMenuItem() {
super();
setFont(ImageUtils.ENTRY_FONT);
}
@Override
public
Insets getMargin() {
Insets margin = super.getMargin();
if (margin != null) {
margin.set(2, -2, 2, 4);
}
return margin;
}
}

View File

@ -0,0 +1,109 @@
/*
* Copyright 2017 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.swingUI;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.accessibility.Accessible;
import javax.swing.JComponent;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.MenuItemUI;
public
class DefaultMenuItemUI extends MenuItemUI {
private final ComponentUI ui;
public
DefaultMenuItemUI(final JComponent jComponent) {
this.ui = UIManager.getDefaults()
.getUI(jComponent);
}
@Override
public
void installUI(final JComponent c) {
ui.installUI(c);
}
@Override
public
void uninstallUI(final JComponent c) {
ui.uninstallUI(c);
}
@Override
public
void paint(final Graphics g, final JComponent c) {
ui.paint(g, c);
}
@Override
public
void update(final Graphics g, final JComponent c) {
ui.update(g, c);
}
@Override
public
Dimension getPreferredSize(final JComponent c) {
return ui.getPreferredSize(c);
}
@Override
public
Dimension getMinimumSize(final JComponent c) {
return ui.getMinimumSize(c);
}
@Override
public
Dimension getMaximumSize(final JComponent c) {
return ui.getMaximumSize(c);
}
@Override
public
boolean contains(final JComponent c, final int x, final int y) {
return ui.contains(c, x, y);
}
@Override
public
int getBaseline(final JComponent c, final int width, final int height) {
return ui.getBaseline(c, width, height);
}
@Override
public
Component.BaselineResizeBehavior getBaselineResizeBehavior(final JComponent c) {
return ui.getBaselineResizeBehavior(c);
}
@Override
public
int getAccessibleChildrenCount(final JComponent c) {
return ui.getAccessibleChildrenCount(c);
}
@Override
public
Accessible getAccessibleChild(final JComponent c, final int i) {
return ui.getAccessibleChild(c, i);
}
}

View File

@ -0,0 +1,109 @@
/*
* Copyright 2017 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.swingUI;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.accessibility.Accessible;
import javax.swing.JComponent;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.PopupMenuUI;
public
class DefaultPopupMenuUI extends PopupMenuUI {
private final ComponentUI ui;
public
DefaultPopupMenuUI(final JComponent jComponent) {
this.ui = UIManager.getDefaults()
.getUI(jComponent);
}
@Override
public
void installUI(final JComponent c) {
ui.installUI(c);
}
@Override
public
void uninstallUI(final JComponent c) {
ui.uninstallUI(c);
}
@Override
public
void paint(final Graphics g, final JComponent c) {
ui.paint(g, c);
}
@Override
public
void update(final Graphics g, final JComponent c) {
ui.update(g, c);
}
@Override
public
Dimension getPreferredSize(final JComponent c) {
return ui.getPreferredSize(c);
}
@Override
public
Dimension getMinimumSize(final JComponent c) {
return ui.getMinimumSize(c);
}
@Override
public
Dimension getMaximumSize(final JComponent c) {
return ui.getMaximumSize(c);
}
@Override
public
boolean contains(final JComponent c, final int x, final int y) {
return ui.contains(c, x, y);
}
@Override
public
int getBaseline(final JComponent c, final int width, final int height) {
return ui.getBaseline(c, width, height);
}
@Override
public
Component.BaselineResizeBehavior getBaselineResizeBehavior(final JComponent c) {
return ui.getBaselineResizeBehavior(c);
}
@Override
public
int getAccessibleChildrenCount(final JComponent c) {
return ui.getAccessibleChildrenCount(c);
}
@Override
public
Accessible getAccessibleChild(final JComponent c, final int i) {
return ui.getAccessibleChild(c, i);
}
}

View File

@ -0,0 +1,109 @@
/*
* Copyright 2017 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.swingUI;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.accessibility.Accessible;
import javax.swing.JComponent;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.SeparatorUI;
public
class DefaultSeparatorUI extends SeparatorUI {
private final ComponentUI ui;
public
DefaultSeparatorUI(final JComponent jComponent) {
this.ui = UIManager.getDefaults()
.getUI(jComponent);
}
@Override
public
void installUI(final JComponent c) {
ui.installUI(c);
}
@Override
public
void uninstallUI(final JComponent c) {
ui.uninstallUI(c);
}
@Override
public
void paint(final Graphics g, final JComponent c) {
ui.paint(g, c);
}
@Override
public
void update(final Graphics g, final JComponent c) {
ui.update(g, c);
}
@Override
public
Dimension getPreferredSize(final JComponent c) {
return ui.getPreferredSize(c);
}
@Override
public
Dimension getMinimumSize(final JComponent c) {
return ui.getMinimumSize(c);
}
@Override
public
Dimension getMaximumSize(final JComponent c) {
return ui.getMaximumSize(c);
}
@Override
public
boolean contains(final JComponent c, final int x, final int y) {
return ui.contains(c, x, y);
}
@Override
public
int getBaseline(final JComponent c, final int width, final int height) {
return ui.getBaseline(c, width, height);
}
@Override
public
Component.BaselineResizeBehavior getBaselineResizeBehavior(final JComponent c) {
return ui.getBaselineResizeBehavior(c);
}
@Override
public
int getAccessibleChildrenCount(final JComponent c) {
return ui.getAccessibleChildrenCount(c);
}
@Override
public
Accessible getAccessibleChild(final JComponent c, final int i) {
return ui.getAccessibleChild(c, i);
}
}

View File

@ -19,6 +19,8 @@ import java.io.File;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.JPopupMenu;
import dorkbox.systemTray.Checkbox;
import dorkbox.systemTray.Entry;
@ -26,7 +28,9 @@ import dorkbox.systemTray.Menu;
import dorkbox.systemTray.MenuItem;
import dorkbox.systemTray.Separator;
import dorkbox.systemTray.Status;
import dorkbox.systemTray.SystemTray;
import dorkbox.systemTray.peer.MenuPeer;
import dorkbox.systemTray.util.ImageUtils;
import dorkbox.util.SwingUtil;
// this is a weird composite class, because it must be a Menu, but ALSO a Entry -- so it has both (and duplicate code)
@ -38,15 +42,33 @@ class SwingMenu implements MenuPeer {
// This is NOT a copy constructor!
@SuppressWarnings("IncompleteCopyConstructor")
SwingMenu(final SwingMenu parent) {
SwingMenu(final SwingMenu parent, final Menu entry) {
this.parent = parent;
if (parent == null) {
this._native = new TrayPopup();
TrayPopup trayPopup = new TrayPopup();
// this is before setUI, so that users can customize the font if they want
if (ImageUtils.ENTRY_FONT != null) {
trayPopup.setFont(ImageUtils.ENTRY_FONT);
}
if (SystemTray.SWING_UI != null) {
trayPopup.setUI(SystemTray.SWING_UI.getMenuUI(trayPopup, null));
}
this._native = trayPopup;
}
else {
this._native = new AdjustedJMenu();
parent._native.add(this._native);
JMenu jMenu = new JMenu();
JPopupMenu popupMenu = jMenu.getPopupMenu(); // ensure the popup menu is created
// this is before setUI, so that users can customize the font if they want
jMenu.setFont(ImageUtils.ENTRY_FONT);
if (SystemTray.SWING_UI != null) {
jMenu.setUI(SystemTray.SWING_UI.getItemUI(jMenu, entry));
popupMenu.setUI(SystemTray.SWING_UI.getMenuUI(popupMenu, entry));
}
this._native = jMenu;
parent._native.add(jMenu);
}
}
@ -59,7 +81,7 @@ class SwingMenu implements MenuPeer {
public
void run() {
if (entry instanceof Menu) {
SwingMenu swingMenu = new SwingMenu(SwingMenu.this);
SwingMenu swingMenu = new SwingMenu(SwingMenu.this, (Menu) entry);
((Menu) entry).bind(swingMenu, parentMenu, parentMenu.getSystemTray());
}
else if (entry instanceof Separator) {
@ -67,15 +89,15 @@ class SwingMenu implements MenuPeer {
entry.bind(item, parentMenu, parentMenu.getSystemTray());
}
else if (entry instanceof Checkbox) {
SwingMenuItemCheckbox item = new SwingMenuItemCheckbox(SwingMenu.this);
SwingMenuItemCheckbox item = new SwingMenuItemCheckbox(SwingMenu.this, entry);
((Checkbox) entry).bind(item, parentMenu, parentMenu.getSystemTray());
}
else if (entry instanceof Status) {
SwingMenuItemStatus item = new SwingMenuItemStatus(SwingMenu.this);
SwingMenuItemStatus item = new SwingMenuItemStatus(SwingMenu.this, entry);
((Status) entry).bind(item, parentMenu, parentMenu.getSystemTray());
}
else if (entry instanceof MenuItem) {
SwingMenuItem item = new SwingMenuItem(SwingMenu.this);
SwingMenuItem item = new SwingMenuItem(SwingMenu.this, entry);
((MenuItem) entry).bind(item, parentMenu, parentMenu.getSystemTray());
}
}
@ -93,10 +115,10 @@ class SwingMenu implements MenuPeer {
File imageFile = menuItem.getImage();
if (imageFile != null) {
ImageIcon origIcon = new ImageIcon(imageFile.getAbsolutePath());
((AdjustedJMenu) _native).setIcon(origIcon);
((JMenu) _native).setIcon(origIcon);
}
else {
((AdjustedJMenu) _native).setIcon(null);
((JMenu) _native).setIcon(null);
}
}
});
@ -124,7 +146,7 @@ class SwingMenu implements MenuPeer {
@Override
public
void run() {
((AdjustedJMenu) _native).setText(menuItem.getText());
((JMenu) _native).setText(menuItem.getText());
}
});
}
@ -147,7 +169,7 @@ class SwingMenu implements MenuPeer {
@Override
public
void run() {
((AdjustedJMenu) _native).setMnemonic(vKey);
((JMenu) _native).setMnemonic(vKey);
}
});
}

View File

@ -22,6 +22,7 @@ import java.io.File;
import javax.swing.ImageIcon;
import javax.swing.JMenuItem;
import dorkbox.systemTray.Entry;
import dorkbox.systemTray.MenuItem;
import dorkbox.systemTray.SystemTray;
import dorkbox.systemTray.peer.MenuItemPeer;
@ -34,14 +35,23 @@ class SwingMenuItem implements MenuItemPeer {
private static ImageIcon transparentIcon;
private final SwingMenu parent;
private final JMenuItem _native = new AdjustedJMenuItem();
private final JMenuItem _native = new JMenuItem();
private volatile ActionListener swingCallback;
// this is ALWAYS called on the EDT.
SwingMenuItem(final SwingMenu parent) {
SwingMenuItem(final SwingMenu parent, Entry entry) {
this.parent = parent;
// this is before setUI, so that users can customize the font if they want
if (ImageUtils.ENTRY_FONT != null) {
_native.setFont(ImageUtils.ENTRY_FONT);
}
if (SystemTray.SWING_UI != null) {
_native.setUI(SystemTray.SWING_UI.getItemUI(_native, entry));
}
parent._native.add(_native);
if (transparentIcon == null) {

View File

@ -23,6 +23,7 @@ import javax.swing.ImageIcon;
import javax.swing.JMenuItem;
import dorkbox.systemTray.Checkbox;
import dorkbox.systemTray.Entry;
import dorkbox.systemTray.SystemTray;
import dorkbox.systemTray.peer.CheckboxPeer;
import dorkbox.systemTray.util.ImageUtils;
@ -31,7 +32,7 @@ import dorkbox.util.SwingUtil;
class SwingMenuItemCheckbox implements CheckboxPeer {
private final SwingMenu parent;
private final JMenuItem _native = new AdjustedJMenuItem();
private final JMenuItem _native = new JMenuItem();
// these have to be volatile, because they can be changed from any thread
private volatile ActionListener callback;
@ -41,8 +42,17 @@ class SwingMenuItemCheckbox implements CheckboxPeer {
private static ImageIcon uncheckedIcon;
// this is ALWAYS called on the EDT.
SwingMenuItemCheckbox(final SwingMenu parent) {
SwingMenuItemCheckbox(final SwingMenu parent, final Entry entry) {
this.parent = parent;
// this is before setUI, so that users can customize the font if they want
if (ImageUtils.ENTRY_FONT != null) {
_native.setFont(ImageUtils.ENTRY_FONT);
}
if (SystemTray.SWING_UI != null) {
_native.setUI(SystemTray.SWING_UI.getItemUI(_native, entry));
}
parent._native.add(_native);
if (checkedIcon == null) {

View File

@ -17,6 +17,7 @@ package dorkbox.systemTray.swingUI;
import javax.swing.JSeparator;
import dorkbox.systemTray.SystemTray;
import dorkbox.systemTray.peer.EntryPeer;
import dorkbox.util.SwingUtil;
@ -28,6 +29,11 @@ class SwingMenuItemSeparator implements EntryPeer {
// this is ALWAYS called on the EDT.
SwingMenuItemSeparator(final SwingMenu parent) {
this.parent = parent;
if (SystemTray.SWING_UI != null) {
_native.setUI(SystemTray.SWING_UI.getSeparatorUI(_native));
}
parent._native.add(_native);
}

View File

@ -19,19 +19,30 @@ import java.awt.Font;
import javax.swing.JMenuItem;
import dorkbox.systemTray.Entry;
import dorkbox.systemTray.Status;
import dorkbox.systemTray.SystemTray;
import dorkbox.systemTray.peer.StatusPeer;
import dorkbox.systemTray.util.ImageUtils;
import dorkbox.util.SwingUtil;
class SwingMenuItemStatus implements StatusPeer {
private final SwingMenu parent;
private final JMenuItem _native = new AdjustedJMenuItem();
private final JMenuItem _native = new JMenuItem();
// this is ALWAYS called on the EDT.
SwingMenuItemStatus(final SwingMenu parent) {
SwingMenuItemStatus(final SwingMenu parent, final Entry entry) {
this.parent = parent;
// this is before setUI, so that users can customize the font if they want
if (ImageUtils.ENTRY_FONT != null) {
_native.setFont(ImageUtils.ENTRY_FONT);
}
if (SystemTray.SWING_UI != null) {
_native.setUI(SystemTray.SWING_UI.getItemUI(_native, entry));
}
// status is ALWAYS at 0 index...
parent._native.add(_native, 0);

View File

@ -0,0 +1,62 @@
/*
* Copyright 2017 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.swingUI;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import javax.swing.plaf.MenuItemUI;
import javax.swing.plaf.PopupMenuUI;
import javax.swing.plaf.SeparatorUI;
import dorkbox.systemTray.Entry;
import dorkbox.systemTray.Menu;
/**
* Factory to allow for Look & Feel of the Swing UI components in the SystemTray. Provide an implementation to customize the Swing L&F.
*/
public
interface SwingUIFactory {
/**
* Allows one to specify the Look & Feel of the menus (The main SystemTray and sub-menus)
*
* @param jPopupMenu the swing JPopupMenu that is displayed when one clicks on the System Tray icon
* @param entry the entry which is bound to the menu, or null if it is the main SystemTray menu.
*
* @return the UI used to customize the Look & Feel of the SystemTray menu + sub-menus
*/
PopupMenuUI getMenuUI(JPopupMenu jPopupMenu, Menu entry);
/**
* Allows one to specify the Look & Feel of a menu entry
*
* @param jMenuItem the swing JMenuItem that is displayed in the menu
* @param entry the entry which is bound to the JMenuItem.
*
* @return the UI used to customize the Look & Feel of the menu entry
*/
MenuItemUI getItemUI(JMenuItem jMenuItem, Entry entry);
/**
* Allows one to specify the Look & Feel of a menu separator entry
*
* @param jSeparator the swing JSeparator that is displayed in the menu
*
* @return the UI used to customize the Look & Feel of a menu separator entry
*/
SeparatorUI getSeparatorUI(JSeparator jSeparator);
}

View File

@ -62,7 +62,7 @@ class _SwingTray extends Tray implements SwingUI {
_SwingTray.this.tray = SystemTray.getSystemTray();
// we override various methods, because each tray implementation is SLIGHTLY different. This allows us customization.
final SwingMenu swingMenu = new SwingMenu(null) {
final SwingMenu swingMenu = new SwingMenu(null, null) {
@Override
public
void setEnabled(final MenuItem menuItem) {

View File

@ -0,0 +1,104 @@
/*
* Copyright 2017 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;
import java.awt.Insets;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import javax.swing.border.EmptyBorder;
import javax.swing.plaf.MenuItemUI;
import javax.swing.plaf.PopupMenuUI;
import javax.swing.plaf.SeparatorUI;
import dorkbox.systemTray.Entry;
import dorkbox.systemTray.Menu;
import dorkbox.systemTray.swingUI.DefaultMenuItemUI;
import dorkbox.systemTray.swingUI.DefaultPopupMenuUI;
import dorkbox.systemTray.swingUI.DefaultSeparatorUI;
import dorkbox.systemTray.swingUI.SwingUIFactory;
/**
* Factory to allow for Look & Feel of the Swing UI components in the SystemTray.
*
* This implementation is provided as an example of what looks reasonable on our systems for Nimbus. Naturally, everyone will have
* different systems and thus will want to change this based on their own, specified Swing L&F.
*/
public
class CustomSwingUI implements SwingUIFactory {
/**
* Allows one to specify the Look & Feel of the menus (The main SystemTray and sub-menus)
*
* @param jPopupMenu the swing JPopupMenu that is displayed when one clicks on the System Tray icon
* @param entry the entry which is bound to the menu, or null if it is the main SystemTray menu.
*
* @return the UI used to customize the Look & Feel of the SystemTray menu + sub-menus
*/
@Override
public
PopupMenuUI getMenuUI(final JPopupMenu jPopupMenu, final Menu entry) {
return new DefaultPopupMenuUI(jPopupMenu) {
@Override
public
void installUI(final JComponent c) {
super.installUI(c);
// borderUI resource border type will get changed internally!
// setBorder(new BorderUIResource.EmptyBorderUIResource(0, 0, 0, 0));
c.setBorder(new EmptyBorder(1, 1, 1, 1));
}
};
}
/**
* Allows one to specify the Look & Feel of a menu entry
*
* @param jMenuItem the swing JMenuItem that is displayed in the menu
* @param entry the entry which is bound to the JMenuItem.
*
* @return the UI used to customize the Look & Feel of the menu entry
*/
@Override
public
MenuItemUI getItemUI(final JMenuItem jMenuItem, final Entry entry) {
return new DefaultMenuItemUI(jMenuItem) {
@Override
public
void installUI(final JComponent c) {
super.installUI(c);
((JMenuItem) c).setMargin(new Insets(2, -2, 2, 4));
c.setBorder(new EmptyBorder(1, 1, 1, 1));
}
};
}
/**
* Allows one to specify the Look & Feel of a menu separator entry
*
* @param jSeparator the swing JSeparator that is displayed in the menu
*
* @return the UI used to customize the Look & Feel of a menu separator entry
*/
@Override
public
SeparatorUI getSeparatorUI(final JSeparator jSeparator) {
return new DefaultSeparatorUI(jSeparator);
}
}

View File

@ -56,6 +56,8 @@ class TestTray {
public
TestTray() {
SystemTray.SWING_UI = new CustomSwingUI();
this.systemTray = SystemTray.get();
if (systemTray == null) {
throw new RuntimeException("Unable to load SystemTray!");

View File

@ -114,6 +114,8 @@ class TestTrayJavaFX {
primaryStage.show();
SystemTray.SWING_UI = new CustomSwingUI();
this.systemTray = SystemTray.get();
if (systemTray == null) {
throw new RuntimeException("Unable to load SystemTray!");

View File

@ -72,6 +72,7 @@ class TestTraySwt {
helloWorldTest.setText("Hello World SWT ................. ");
helloWorldTest.pack();
SystemTray.SWING_UI = new CustomSwingUI();
this.systemTray = SystemTray.get();
if (systemTray == null) {