From 09143f76301d9db6165fb3533408e16a96d0dec5 Mon Sep 17 00:00:00 2001 From: nathan Date: Mon, 10 Oct 2016 22:40:39 +0200 Subject: [PATCH] API cleanup, WIP adding widgets to the menu (on pause because of problems with the enter key not responding after this). Updated test examples. --- ...{SystemTrayMenuAction.java => Action.java} | 4 +- src/dorkbox/systemTray/Entry.java | 2 +- src/dorkbox/systemTray/Menu.java | 26 ++- src/dorkbox/systemTray/SystemTray.java | 30 ++-- src/dorkbox/systemTray/swing/EntryImpl.java | 24 +-- src/dorkbox/systemTray/swing/EntryItem.java | 8 +- .../systemTray/swing/EntrySeparator.java | 4 +- src/dorkbox/systemTray/swing/EntryStatus.java | 4 +- src/dorkbox/systemTray/swing/EntryWidget.java | 62 +++++++ src/dorkbox/systemTray/swing/MenuImpl.java | 154 +++++++++++++----- src/dorkbox/systemTray/swing/TrayPopup.java | 14 +- .../systemTray/swing/_AppIndicatorTray.java | 6 +- .../systemTray/swing/_GtkStatusIconTray.java | 6 +- src/dorkbox/systemTray/swing/_SwingTray.java | 4 +- test/dorkbox/TestTray.java | 54 ++++-- test/dorkbox/TestTrayJavaFX.java | 54 ++++-- test/dorkbox/TestTraySwt.java | 54 ++++-- .../accommodation_camping.glow.0092DA.32.png | Bin 0 -> 1779 bytes .../amenity_firestation.p.000000.32.png | Bin 0 -> 636 bytes test/dorkbox/amenity_post_box.p.000000.32.png | Bin 0 -> 474 bytes test/dorkbox/amenity_post_box.p.39AC39.32.png | Bin 0 -> 669 bytes .../transport_train_station.p.000000.32.png | Bin 0 -> 623 bytes .../transport_train_station.p.666666.32.png | Bin 0 -> 937 bytes 23 files changed, 361 insertions(+), 149 deletions(-) rename src/dorkbox/systemTray/{SystemTrayMenuAction.java => Action.java} (86%) create mode 100644 src/dorkbox/systemTray/swing/EntryWidget.java create mode 100644 test/dorkbox/accommodation_camping.glow.0092DA.32.png create mode 100644 test/dorkbox/amenity_firestation.p.000000.32.png create mode 100644 test/dorkbox/amenity_post_box.p.000000.32.png create mode 100644 test/dorkbox/amenity_post_box.p.39AC39.32.png create mode 100644 test/dorkbox/transport_train_station.p.000000.32.png create mode 100644 test/dorkbox/transport_train_station.p.666666.32.png diff --git a/src/dorkbox/systemTray/SystemTrayMenuAction.java b/src/dorkbox/systemTray/Action.java similarity index 86% rename from src/dorkbox/systemTray/SystemTrayMenuAction.java rename to src/dorkbox/systemTray/Action.java index 7227f81..28bd48c 100644 --- a/src/dorkbox/systemTray/SystemTrayMenuAction.java +++ b/src/dorkbox/systemTray/Action.java @@ -16,9 +16,9 @@ package dorkbox.systemTray; public -interface SystemTrayMenuAction { +interface Action { /** - * This method will ALWAYS be called in the swing EDT + * This method will ALWAYS be called in the swing EDT. If there is work conducted in this method, it will slow-down the GUI. * * @param systemTray this is the parent, system tray object * @param parent this is the parent menu of this menu entry diff --git a/src/dorkbox/systemTray/Entry.java b/src/dorkbox/systemTray/Entry.java index 2f03688..389bd55 100644 --- a/src/dorkbox/systemTray/Entry.java +++ b/src/dorkbox/systemTray/Entry.java @@ -97,7 +97,7 @@ interface Entry { * * @param callback the callback to set. If null, the callback is safely removed. */ - void setCallback(SystemTrayMenuAction callback); + void setCallback(Action callback); /** * Sets a menu entry shortcut key (Mnemonic) so that menu entry can be "selected" via the keyboard while the menu is displayed. diff --git a/src/dorkbox/systemTray/Menu.java b/src/dorkbox/systemTray/Menu.java index 68e8833..143d365 100644 --- a/src/dorkbox/systemTray/Menu.java +++ b/src/dorkbox/systemTray/Menu.java @@ -52,7 +52,7 @@ interface Menu extends Entry { /** * This removes al menu entries from this menu */ - void clear(); + void removeAll(); /** * Gets the menu entry for a specified text @@ -62,17 +62,17 @@ interface Menu extends Entry { Entry get(final String menuText); /** - * Gets the first menu entry or sub-menu, ignoring status and spacers + * Gets the first menu entry or sub-menu, ignoring status and separators */ Entry getFirst(); /** - * Gets the last menu entry or sub-menu, ignoring status and spacers + * Gets the last menu entry or sub-menu, ignoring status and separators */ Entry getLast(); /** - * Gets the menu entry or sub-menu for a specified index (zero-index), ignoring status and spacers + * Gets the menu entry or sub-menu for a specified index (zero-index), ignoring status and separators * * @param menuIndex the menu entry index to use to retrieve the menu entry. */ @@ -86,7 +86,7 @@ interface Menu extends Entry { * @param menuText string of the text you want to appear * @param callback callback that will be executed when this menu entry is clicked */ - Entry addEntry(String menuText, SystemTrayMenuAction callback); + Entry addEntry(String menuText, Action callback); /** * Adds a menu entry with text + image @@ -95,7 +95,7 @@ interface Menu extends Entry { * @param imagePath the image (full path required) to use. If null, no image will be used * @param callback callback that will be executed when this menu entry is clicked */ - Entry addEntry(String menuText, String imagePath, SystemTrayMenuAction callback); + Entry addEntry(String menuText, String imagePath, Action callback); /** * Adds a menu entry with text + image @@ -104,7 +104,7 @@ interface Menu extends Entry { * @param imageUrl the URL of the image to use. If null, no image will be used * @param callback callback that will be executed when this menu entry is clicked */ - Entry addEntry(String menuText, URL imageUrl, SystemTrayMenuAction callback); + Entry addEntry(String menuText, URL imageUrl, Action callback); /** * Adds a menu entry with text + image @@ -114,7 +114,7 @@ interface Menu extends Entry { * @param imageStream the InputStream of the image to use. If null, no image will be used * @param callback callback that will be executed when this menu entry is clicked */ - Entry addEntry(String menuText, String cacheName, InputStream imageStream, SystemTrayMenuAction callback); + Entry addEntry(String menuText, String cacheName, InputStream imageStream, Action callback); /** * Adds a menu entry with text + image @@ -123,7 +123,7 @@ interface Menu extends Entry { * @param imageStream the InputStream of the image to use. If null, no image will be used * @param callback callback that will be executed when this menu entry is clicked */ - Entry addEntry(String menuText, InputStream imageStream, SystemTrayMenuAction callback); + Entry addEntry(String menuText, InputStream imageStream, Action callback); @@ -168,6 +168,14 @@ interface Menu extends Entry { */ Menu addMenu(String menuText, InputStream imageStream); + /** + * Adds a swing widget as a menu entry. + * + * @param widget the JComponent that is to be added as an entry + */ +// TODO: buggy. The menu will **sometimes** stop responding to the "enter" key after this. Mnemonics still work however. +// Entry addWidget(JComponent widget); + /** * This removes a menu entry from the dropdown menu. diff --git a/src/dorkbox/systemTray/SystemTray.java b/src/dorkbox/systemTray/SystemTray.java index be2b84d..17081a2 100644 --- a/src/dorkbox/systemTray/SystemTray.java +++ b/src/dorkbox/systemTray/SystemTray.java @@ -562,10 +562,7 @@ class SystemTray implements Menu { /* * appIndicator/gtk require strings (which is the path) * swing version loads as an image (which can be stream or path, we use path) - * - * For KDE4, it must also be unique across runs */ - CacheUtil.setUniqueCachePerRun = isKDE; CacheUtil.tempDir = "SysTray"; try { @@ -883,7 +880,7 @@ class SystemTray implements Menu { */ @Override public - void setCallback(final SystemTrayMenuAction callback) { + void setCallback(final Action callback) { // NO OP. } @@ -951,7 +948,7 @@ class SystemTray implements Menu { * @param callback callback that will be executed when this menu entry is clicked */ public final - Entry addEntry(String menuText, SystemTrayMenuAction callback) { + Entry addEntry(String menuText, Action callback) { return addEntry(menuText, (String) null, callback); } @@ -963,7 +960,7 @@ class SystemTray implements Menu { * @param callback callback that will be executed when this menu entry is clicked */ public final - Entry addEntry(String menuText, String imagePath, SystemTrayMenuAction callback) { + Entry addEntry(String menuText, String imagePath, Action callback) { return systemTrayMenu.addEntry(menuText, imagePath, callback); } @@ -975,7 +972,7 @@ class SystemTray implements Menu { * @param callback callback that will be executed when this menu entry is clicked */ public final - Entry addEntry(String menuText, URL imageUrl, SystemTrayMenuAction callback) { + Entry addEntry(String menuText, URL imageUrl, Action callback) { return systemTrayMenu.addEntry(menuText, imageUrl, callback); } @@ -988,7 +985,7 @@ class SystemTray implements Menu { * @param callback callback that will be executed when this menu entry is clicked */ public - Entry addEntry(String menuText, String cacheName, InputStream imageStream, SystemTrayMenuAction callback) { + Entry addEntry(String menuText, String cacheName, InputStream imageStream, Action callback) { return systemTrayMenu.addEntry(menuText, cacheName, imageStream, callback); } @@ -1000,7 +997,7 @@ class SystemTray implements Menu { * @param callback callback that will be executed when this menu entry is clicked */ public final - Entry addEntry(String menuText, InputStream imageStream, SystemTrayMenuAction callback) { + Entry addEntry(String menuText, InputStream imageStream, Action callback) { return systemTrayMenu.addEntry(menuText, imageStream, callback); } @@ -1063,6 +1060,17 @@ class SystemTray implements Menu { return systemTrayMenu.addMenu(menuText, imageStream); } + /** + * Adds a swing widget as a menu entry. + * + * @param widget the JComponent that is to be added as an entry + */ +// TODO: buggy. The menu will **sometimes** stop responding to the "enter" key after this. Mnemonics still work however. +// @Override +// public +// Entry addWidget(final JComponent widget) { +// return systemTrayMenu.addWidget(widget); +// } @@ -1092,8 +1100,8 @@ class SystemTray implements Menu { */ @Override public final - void clear() { - systemTrayMenu.clear(); + void removeAll() { + systemTrayMenu.removeAll(); } /** diff --git a/src/dorkbox/systemTray/swing/EntryImpl.java b/src/dorkbox/systemTray/swing/EntryImpl.java index d63b837..027dc9a 100644 --- a/src/dorkbox/systemTray/swing/EntryImpl.java +++ b/src/dorkbox/systemTray/swing/EntryImpl.java @@ -25,9 +25,7 @@ import javax.swing.JMenuItem; import dorkbox.systemTray.Entry; import dorkbox.systemTray.Menu; -import dorkbox.systemTray.SystemTray; import dorkbox.systemTray.util.ImageUtils; -import dorkbox.util.SwingUtil; abstract class EntryImpl implements Entry { @@ -102,7 +100,7 @@ class EntryImpl implements Entry { void setText(final String newText) { this.text = newText; - SwingUtil.invokeLater(new Runnable() { + parent.dispatch(new Runnable() { @Override public void run() { @@ -169,18 +167,14 @@ class EntryImpl implements Entry { @Override public final void remove() { - try { - SwingUtil.invokeAndWait(new Runnable() { - @Override - public - void run() { - removePrivate(); - parent._native.remove(_native); - } - }); - } catch (Exception e) { - SystemTray.logger.error("Error processing event on the dispatch thread.", e); - } + parent.dispatchAndWait(new Runnable() { + @Override + public + void run() { + removePrivate(); + parent._native.remove(_native); + } + }); } // called when this item is removed. Necessary to cleanup/remove itself diff --git a/src/dorkbox/systemTray/swing/EntryItem.java b/src/dorkbox/systemTray/swing/EntryItem.java index 10c483a..9ffb51f 100644 --- a/src/dorkbox/systemTray/swing/EntryItem.java +++ b/src/dorkbox/systemTray/swing/EntryItem.java @@ -22,7 +22,7 @@ import java.io.File; import javax.swing.ImageIcon; import javax.swing.JMenuItem; -import dorkbox.systemTray.SystemTrayMenuAction; +import dorkbox.systemTray.Action; import dorkbox.util.SwingUtil; class EntryItem extends EntryImpl { @@ -30,10 +30,10 @@ class EntryItem extends EntryImpl { private final ActionListener swingCallback; private volatile boolean hasLegitIcon = false; - private volatile SystemTrayMenuAction callback; + private volatile Action callback; // this is ALWAYS called on the EDT. - EntryItem(final MenuImpl parent, final SystemTrayMenuAction callback) { + EntryItem(final MenuImpl parent, final Action callback) { super(parent, new AdjustedJMenuItem()); this.callback = callback; @@ -58,7 +58,7 @@ class EntryItem extends EntryImpl { @Override public - void setCallback(final SystemTrayMenuAction callback) { + void setCallback(final Action callback) { this.callback = callback; } diff --git a/src/dorkbox/systemTray/swing/EntrySeparator.java b/src/dorkbox/systemTray/swing/EntrySeparator.java index 1dc3f46..508b39b 100644 --- a/src/dorkbox/systemTray/swing/EntrySeparator.java +++ b/src/dorkbox/systemTray/swing/EntrySeparator.java @@ -19,7 +19,7 @@ import java.io.File; import javax.swing.JSeparator; -import dorkbox.systemTray.SystemTrayMenuAction; +import dorkbox.systemTray.Action; class EntrySeparator extends EntryImpl implements dorkbox.systemTray.Separator { @@ -54,6 +54,6 @@ class EntrySeparator extends EntryImpl implements dorkbox.systemTray.Separator { @Override public - void setCallback(final SystemTrayMenuAction callback) { + void setCallback(final Action callback) { } } diff --git a/src/dorkbox/systemTray/swing/EntryStatus.java b/src/dorkbox/systemTray/swing/EntryStatus.java index 65e598c..94ae2a5 100644 --- a/src/dorkbox/systemTray/swing/EntryStatus.java +++ b/src/dorkbox/systemTray/swing/EntryStatus.java @@ -20,8 +20,8 @@ import java.io.File; import javax.swing.JMenuItem; +import dorkbox.systemTray.Action; import dorkbox.systemTray.Status; -import dorkbox.systemTray.SystemTrayMenuAction; class EntryStatus extends EntryImpl implements Status { @@ -64,7 +64,7 @@ class EntryStatus extends EntryImpl implements Status { @Override public - void setCallback(final SystemTrayMenuAction callback) { + void setCallback(final Action callback) { } } diff --git a/src/dorkbox/systemTray/swing/EntryWidget.java b/src/dorkbox/systemTray/swing/EntryWidget.java new file mode 100644 index 0000000..d9399ff --- /dev/null +++ b/src/dorkbox/systemTray/swing/EntryWidget.java @@ -0,0 +1,62 @@ +/* + * 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.swing; + +import java.io.File; + +import javax.swing.JComponent; + +import dorkbox.systemTray.Action; + +// TODO: buggy. The menu will **sometimes** stop responding to the "enter" key after this. Mnemonics still work however. +class EntryWidget extends EntryImpl implements dorkbox.systemTray.Separator { + + // this is ALWAYS called on the EDT. + EntryWidget(final MenuImpl parent, JComponent widget) { + super(parent, widget); + + _native.setEnabled(true); + } + + // called in the EDT thread + @Override + void renderText(final String text) { + } + + @Override + void setImage_(final File imageFile) { + } + + @Override + void removePrivate() { + } + + @Override + public + void setShortcut(final char key) { + } + + @Override + public + boolean hasImage() { + return false; + } + + @Override + public + void setCallback(final Action callback) { + } +} diff --git a/src/dorkbox/systemTray/swing/MenuImpl.java b/src/dorkbox/systemTray/swing/MenuImpl.java index 501c646..d17e4e5 100644 --- a/src/dorkbox/systemTray/swing/MenuImpl.java +++ b/src/dorkbox/systemTray/swing/MenuImpl.java @@ -30,20 +30,21 @@ import javax.swing.ImageIcon; import javax.swing.JComponent; import javax.swing.JMenuItem; +import dorkbox.systemTray.Action; import dorkbox.systemTray.Entry; import dorkbox.systemTray.Menu; import dorkbox.systemTray.Status; import dorkbox.systemTray.SystemTray; -import dorkbox.systemTray.SystemTrayMenuAction; 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 +@SuppressWarnings("ForLoopReplaceableByForEach") class MenuImpl implements Menu { - public static final AtomicInteger MENU_ID_COUNTER = new AtomicInteger(); + static final AtomicInteger MENU_ID_COUNTER = new AtomicInteger(); private final int id = MenuImpl.MENU_ID_COUNTER.getAndIncrement(); - protected final java.util.List menuEntries = new ArrayList(); + private final java.util.List menuEntries = new ArrayList(); private final SystemTray systemTray; private final Menu parent; @@ -69,13 +70,11 @@ class MenuImpl implements Menu { this._native = _native; } - protected void dispatch(final Runnable runnable) { // this will properly check if we are running on the EDT SwingUtil.invokeLater(runnable); } - protected void dispatchAndWait(final Runnable runnable) { // this will properly check if we are running on the EDT try { @@ -98,7 +97,7 @@ class MenuImpl implements Menu { * NOT ALWAYS CALLED ON EDT */ private - Entry addEntry_(final String menuText, final File imagePath, final SystemTrayMenuAction callback) { + Entry addEntry_(final String menuText, final File imagePath, final Action callback) { if (menuText == null) { throw new NullPointerException("Menu text cannot be null"); } @@ -154,7 +153,7 @@ class MenuImpl implements Menu { if (entry == null) { // must always be called on the EDT entry = new MenuImpl(getSystemTray(), MenuImpl.this, new AdjustedJMenu()); - _native.add(((MenuImpl) entry)._native); + _native.add(((MenuImpl) entry)._native); // have to add it separately entry.setText(menuText); entry.setImage(imagePath); @@ -256,6 +255,31 @@ class MenuImpl implements Menu { }); } +// TODO: buggy. The menu will **sometimes** stop responding to the "enter" key after this. Mnemonics still work however. +// public +// Entry addWidget(final JComponent widget) { +// if (widget == null) { +// throw new NullPointerException("Widget cannot be null"); +// } +// +// final AtomicReference value = new AtomicReference(); +// +// dispatchAndWait(new Runnable() { +// @Override +// public +// void run() { +// synchronized (menuEntries) { +// // must always be called on the EDT +// Entry entry = new EntryWidget(MenuImpl.this, widget); +// value.set(entry); +// menuEntries.add(entry); +// } +// } +// }); +// +// return value.get(); +// } + public Entry get(final String menuText) { @@ -265,7 +289,8 @@ class MenuImpl implements Menu { // Must be wrapped in a synchronized block for object visibility synchronized (menuEntries) { - for (Entry entry : menuEntries) { + for (int i = 0, menuEntriesSize = menuEntries.size(); i < menuEntriesSize; i++) { + final Entry entry = menuEntries.get(i); String text = entry.getText(); // text can be null @@ -278,23 +303,25 @@ class MenuImpl implements Menu { return null; } + // ignores status + separators public Entry getFirst() { return get(0); } + // ignores status + separators public Entry getLast() { // Must be wrapped in a synchronized block for object visibility synchronized (menuEntries) { if (!menuEntries.isEmpty()) { - Entry entry = null; - for (int i = 0, menuEntriesSize = menuEntries.size(); i < menuEntriesSize; i++) { + Entry entry; + for (int i = menuEntries.size()-1; i >= 0; i--) { entry = menuEntries.get(i); - } - if (!(entry instanceof dorkbox.systemTray.Separator || entry instanceof Status)) { - return entry; + if (!(entry instanceof dorkbox.systemTray.Separator || entry instanceof Status)) { + return entry; + } } } } @@ -302,6 +329,7 @@ class MenuImpl implements Menu { return null; } + // ignores status + separators public Entry get(final int menuIndex) { if (menuIndex < 0) { @@ -331,12 +359,12 @@ class MenuImpl implements Menu { public - Entry addEntry(String menuText, SystemTrayMenuAction callback) { + Entry addEntry(String menuText, Action callback) { return addEntry(menuText, (String) null, callback); } public - Entry addEntry(String menuText, String imagePath, SystemTrayMenuAction callback) { + Entry addEntry(String menuText, String imagePath, Action callback) { if (imagePath == null) { return addEntry_(menuText, null, callback); } @@ -346,7 +374,7 @@ class MenuImpl implements Menu { } public - Entry addEntry(String menuText, URL imageUrl, SystemTrayMenuAction callback) { + Entry addEntry(String menuText, URL imageUrl, Action callback) { if (imageUrl == null) { return addEntry_(menuText, null, callback); } @@ -356,7 +384,7 @@ class MenuImpl implements Menu { } public - Entry addEntry(String menuText, String cacheName, InputStream imageStream, SystemTrayMenuAction callback) { + Entry addEntry(String menuText, String cacheName, InputStream imageStream, Action callback) { if (imageStream == null) { return addEntry_(menuText, null, callback); } @@ -366,7 +394,7 @@ class MenuImpl implements Menu { } public - Entry addEntry(String menuText, InputStream imageStream, SystemTrayMenuAction callback) { + Entry addEntry(String menuText, InputStream imageStream, Action callback) { if (imageStream == null) { return addEntry_(menuText, null, callback); } @@ -483,29 +511,54 @@ class MenuImpl implements Menu { } } + public + String getStatus() { + synchronized (menuEntries) { + Entry entry = menuEntries.get(0); + if (entry instanceof EntryStatus) { + return entry.getText(); + } + } + return null; + } + public + void setStatus(final String statusText) { + final MenuImpl _this = this; + dispatchAndWait(new Runnable() { + @Override + public + void run() { + synchronized (menuEntries) { + // status is ALWAYS at 0 index... + EntryImpl menuEntry = null; + if (!menuEntries.isEmpty()) { + menuEntry = (EntryImpl) menuEntries.get(0); + } + if (menuEntry instanceof EntryStatus) { + // set the text or delete... + if (statusText == null) { + // delete + remove(menuEntry); + } + else { + // set text + menuEntry.setText(statusText); + } - - - - - - - - - - - - - - - - - - + } else { + // create a new one + menuEntry = new EntryStatus(_this, statusText); + // status is ALWAYS at 0 index... + menuEntries.add(0, menuEntry); + } + } + } + }); + } @@ -531,7 +584,7 @@ class MenuImpl implements Menu { @Override public - void setCallback(final SystemTrayMenuAction callback) { + void setCallback(final Action callback) { } @Override @@ -702,7 +755,7 @@ class MenuImpl implements Menu { @Override public - void clear() { + void removeAll() { dispatch(new Runnable() { @Override public @@ -713,8 +766,33 @@ class MenuImpl implements Menu { for (Entry entry : menuEntriesCopy) { entry.remove(); } + menuEntries.clear(); } } }); } + + @Override + public final + int hashCode() { + return id; + } + + + @Override + public final + boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + + MenuImpl other = (MenuImpl) obj; + return this.id == other.id; + } } diff --git a/src/dorkbox/systemTray/swing/TrayPopup.java b/src/dorkbox/systemTray/swing/TrayPopup.java index 0907335..096eb04 100644 --- a/src/dorkbox/systemTray/swing/TrayPopup.java +++ b/src/dorkbox/systemTray/swing/TrayPopup.java @@ -140,12 +140,7 @@ class TrayPopup extends JPopupMenu { hiddenDialog.dispatchEvent(new WindowEvent(hiddenDialog, WindowEvent.WINDOW_CLOSING)); } - void doShow(final Point point, int offset) { - // when the menu entries are changed, this makes sure to correctly show them - invalidate(); - revalidate(); - doLayout(); - + void doShow(final MenuImpl systemTray, final Point point, int offset) { Dimension size = getPreferredSize(); Rectangle bounds = ScreenUtil.getScreenBoundsAt(point); @@ -185,6 +180,13 @@ class TrayPopup extends JPopupMenu { setLocation(x, y); setVisible(true); + // when the menu entries are changed, this makes sure to correctly show them + invalidate(); + revalidate(); + doLayout(); + requestFocusInWindow(); + + ((EntryImpl) systemTray.getFirst())._native.requestFocusInWindow(); } } diff --git a/src/dorkbox/systemTray/swing/_AppIndicatorTray.java b/src/dorkbox/systemTray/swing/_AppIndicatorTray.java index 337846d..184faac 100644 --- a/src/dorkbox/systemTray/swing/_AppIndicatorTray.java +++ b/src/dorkbox/systemTray/swing/_AppIndicatorTray.java @@ -145,7 +145,7 @@ class _AppIndicatorTray extends MenuImpl { .getLocation(); TrayPopup popupMenu = (TrayPopup) _native; - popupMenu.doShow(point, SystemTray.DEFAULT_TRAY_SIZE); + popupMenu.doShow(_AppIndicatorTray.this, point, SystemTray.DEFAULT_TRAY_SIZE); } }; @@ -219,8 +219,8 @@ class _AppIndicatorTray extends MenuImpl { Gtk.shutdownGui(); // uses EDT - clear(); - remove(); + removeAll(); + remove(); // remove ourselves from our parent } } diff --git a/src/dorkbox/systemTray/swing/_GtkStatusIconTray.java b/src/dorkbox/systemTray/swing/_GtkStatusIconTray.java index 3368a25..76426ad 100644 --- a/src/dorkbox/systemTray/swing/_GtkStatusIconTray.java +++ b/src/dorkbox/systemTray/swing/_GtkStatusIconTray.java @@ -81,7 +81,7 @@ class _GtkStatusIconTray extends MenuImpl { .getLocation(); TrayPopup popupMenu = (TrayPopup) _native; - popupMenu.doShow(point, 0); + popupMenu.doShow(_GtkStatusIconTray.this, point, 0); } }; @@ -171,8 +171,8 @@ class _GtkStatusIconTray extends MenuImpl { Gtk.shutdownGui(); // uses EDT - clear(); - remove(); + removeAll(); + remove(); // remove ourselves from our parent } } diff --git a/src/dorkbox/systemTray/swing/_SwingTray.java b/src/dorkbox/systemTray/swing/_SwingTray.java index d6d089c..3f39461 100644 --- a/src/dorkbox/systemTray/swing/_SwingTray.java +++ b/src/dorkbox/systemTray/swing/_SwingTray.java @@ -67,7 +67,7 @@ class _SwingTray extends MenuImpl { void run() { tray.remove(trayIcon); - clear(); + removeAll(); remove(); } }); @@ -100,7 +100,7 @@ class _SwingTray extends MenuImpl { public void mousePressed(MouseEvent e) { TrayPopup popupMenu = (TrayPopup) _native; - popupMenu.doShow(e.getPoint(), 0); + popupMenu.doShow(_SwingTray.this, e.getPoint(), 0); } }); diff --git a/test/dorkbox/TestTray.java b/test/dorkbox/TestTray.java index 4d93647..fba4200 100644 --- a/test/dorkbox/TestTray.java +++ b/test/dorkbox/TestTray.java @@ -18,10 +18,10 @@ package dorkbox; import java.net.URL; +import dorkbox.systemTray.Action; import dorkbox.systemTray.Entry; import dorkbox.systemTray.Menu; import dorkbox.systemTray.SystemTray; -import dorkbox.systemTray.SystemTrayMenuAction; /** * Icons from 'SJJB Icons', public domain/CC0 icon set @@ -29,9 +29,18 @@ import dorkbox.systemTray.SystemTrayMenuAction; public class TestTray { - public static final URL BLACK_MAIL = TestTray.class.getResource("transport_bus_station.p.000000.32.png"); - public static final URL GREEN_MAIL = TestTray.class.getResource("transport_bus_station.p.39AC39.32.png"); - public static final URL LT_GRAY_MAIL = TestTray.class.getResource("transport_bus_station.p.999999.32.png"); + public static final URL BLUE_CAMPING = TestTray.class.getResource("accommodation_camping.glow.0092DA.32.png"); + public static final URL BLACK_FIRE = TestTray.class.getResource("amenity_firestation.p.000000.32.png"); + + public static final URL BLACK_MAIL = TestTray.class.getResource("amenity_post_box.p.000000.32.png"); + public static final URL GREEN_MAIL = TestTray.class.getResource("amenity_post_box.p.39AC39.32.png"); + + public static final URL BLACK_BUS = TestTray.class.getResource("transport_bus_station.p.000000.32.png"); + public static final URL LT_GRAY_BUS = TestTray.class.getResource("transport_bus_station.p.999999.32.png"); + + public static final URL BLACK_TRAIN = TestTray.class.getResource("transport_train_station.p.000000.32.png"); + public static final URL GREEN_TRAIN = TestTray.class.getResource("transport_train_station.p.39AC39.32.png"); + public static final URL LT_GRAY_TRAIN = TestTray.class.getResource("transport_train_station.p.666666.32.png"); public static void main(String[] args) { @@ -40,8 +49,8 @@ class TestTray { } private SystemTray systemTray; - private SystemTrayMenuAction callbackGreen; - private SystemTrayMenuAction callbackGray; + private Action callbackGreen; + private Action callbackGray; public TestTray() { @@ -50,15 +59,15 @@ class TestTray { throw new RuntimeException("Unable to load SystemTray!"); } - systemTray.setImage(LT_GRAY_MAIL); + systemTray.setImage(LT_GRAY_TRAIN); systemTray.setStatus("No Mail"); - callbackGreen = new SystemTrayMenuAction() { + callbackGreen = new Action() { @Override public void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) { systemTray.setStatus("Some Mail!"); - systemTray.setImage(GREEN_MAIL); + systemTray.setImage(GREEN_TRAIN); entry.setCallback(callbackGray); entry.setImage(BLACK_MAIL); @@ -67,12 +76,12 @@ class TestTray { } }; - callbackGray = new SystemTrayMenuAction() { + callbackGray = new Action() { @Override public void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) { systemTray.setStatus(null); - systemTray.setImage(BLACK_MAIL); + systemTray.setImage(BLACK_TRAIN); entry.setCallback(null); // systemTray.setStatus("Mail Empty"); @@ -87,23 +96,34 @@ class TestTray { this.systemTray.addSeparator(); - final Menu submenu = this.systemTray.addMenu("Options", BLACK_MAIL); - submenu.addEntry("Disable menu", LT_GRAY_MAIL, new SystemTrayMenuAction() { + final Menu submenu = this.systemTray.addMenu("Options", BLUE_CAMPING); + submenu.setShortcut('t'); + submenu.addEntry("Disable menu", BLACK_BUS, new Action() { @Override public void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) { submenu.setEnabled(false); } }); - submenu.addEntry("Hide tray", BLACK_MAIL, new SystemTrayMenuAction() { +// TODO: buggy. The menu will **sometimes** stop responding to the "enter" key after this. Mnemonics still work however. +// submenu.addEntry("Add widget", GREEN_BUS, new Action() { +// @Override +// public +// void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) { +// JProgressBar progressBar = new JProgressBar(0, 100); +// progressBar.setValue(new Random().nextInt(101)); +// progressBar.setStringPainted(true); +// systemTray.addWidget(progressBar); +// } +// }); + submenu.addEntry("Hide tray", LT_GRAY_BUS, new Action() { @Override public void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) { systemTray.setEnabled(false); } }); - - submenu.addEntry("Remove menu", GREEN_MAIL, new SystemTrayMenuAction() { + submenu.addEntry("Remove menu", BLACK_FIRE, new Action() { @Override public void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) { @@ -112,7 +132,7 @@ class TestTray { }); - systemTray.addEntry("Quit", new SystemTrayMenuAction() { + systemTray.addEntry("Quit", new Action() { @Override public void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) { diff --git a/test/dorkbox/TestTrayJavaFX.java b/test/dorkbox/TestTrayJavaFX.java index 6d38253..1f89bd2 100644 --- a/test/dorkbox/TestTrayJavaFX.java +++ b/test/dorkbox/TestTrayJavaFX.java @@ -18,10 +18,10 @@ package dorkbox; import java.net.URL; +import dorkbox.systemTray.Action; import dorkbox.systemTray.Entry; import dorkbox.systemTray.Menu; import dorkbox.systemTray.SystemTray; -import dorkbox.systemTray.SystemTrayMenuAction; import javafx.application.Application; import javafx.application.Platform; import javafx.event.ActionEvent; @@ -39,9 +39,18 @@ import javafx.stage.Stage; public class TestTrayJavaFX extends Application { - public static final URL BLACK_MAIL = TestTray.class.getResource("transport_bus_station.p.000000.32.png"); - public static final URL GREEN_MAIL = TestTray.class.getResource("transport_bus_station.p.39AC39.32.png"); - public static final URL LT_GRAY_MAIL = TestTray.class.getResource("transport_bus_station.p.999999.32.png"); + public static final URL BLUE_CAMPING = TestTray.class.getResource("accommodation_camping.glow.0092DA.32.png"); + public static final URL BLACK_FIRE = TestTray.class.getResource("amenity_firestation.p.000000.32.png"); + + public static final URL BLACK_MAIL = TestTray.class.getResource("amenity_post_box.p.000000.32.png"); + public static final URL GREEN_MAIL = TestTray.class.getResource("amenity_post_box.p.39AC39.32.png"); + + public static final URL BLACK_BUS = TestTray.class.getResource("transport_bus_station.p.000000.32.png"); + public static final URL LT_GRAY_BUS = TestTray.class.getResource("transport_bus_station.p.999999.32.png"); + + public static final URL BLACK_TRAIN = TestTray.class.getResource("transport_train_station.p.000000.32.png"); + public static final URL GREEN_TRAIN = TestTray.class.getResource("transport_train_station.p.39AC39.32.png"); + public static final URL LT_GRAY_TRAIN = TestTray.class.getResource("transport_train_station.p.666666.32.png"); public static void main(String[] args) { @@ -50,8 +59,8 @@ class TestTrayJavaFX extends Application { } private SystemTray systemTray; - private SystemTrayMenuAction callbackGreen; - private SystemTrayMenuAction callbackGray; + private Action callbackGreen; + private Action callbackGray; public TestTrayJavaFX() { @@ -83,16 +92,15 @@ class TestTrayJavaFX extends Application { throw new RuntimeException("Unable to load SystemTray!"); } - this.systemTray.setImage(LT_GRAY_MAIL); - + systemTray.setImage(LT_GRAY_TRAIN); systemTray.setStatus("No Mail"); - callbackGreen = new SystemTrayMenuAction() { + callbackGreen = new Action() { @Override public void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) { - systemTray.setImage(GREEN_MAIL); systemTray.setStatus("Some Mail!"); + systemTray.setImage(GREEN_TRAIN); entry.setCallback(callbackGray); entry.setImage(BLACK_MAIL); @@ -101,12 +109,12 @@ class TestTrayJavaFX extends Application { } }; - callbackGray = new SystemTrayMenuAction() { + callbackGray = new Action() { @Override public void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) { systemTray.setStatus(null); - systemTray.setImage(BLACK_MAIL); + systemTray.setImage(BLACK_TRAIN); entry.setCallback(null); // systemTray.setStatus("Mail Empty"); @@ -121,22 +129,34 @@ class TestTrayJavaFX extends Application { this.systemTray.addSeparator(); - final Menu submenu = this.systemTray.addMenu("Options", BLACK_MAIL); - submenu.addEntry("Disable menu", LT_GRAY_MAIL, new SystemTrayMenuAction() { + final Menu submenu = this.systemTray.addMenu("Options", BLUE_CAMPING); + submenu.setShortcut('t'); + submenu.addEntry("Disable menu", BLACK_BUS, new Action() { @Override public void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) { submenu.setEnabled(false); } }); - submenu.addEntry("Hide tray", BLACK_MAIL, new SystemTrayMenuAction() { +// TODO: buggy. The menu will **sometimes** stop responding to the "enter" key after this. Mnemonics still work however. +// submenu.addEntry("Add widget", GREEN_BUS, new Action() { +// @Override +// public +// void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) { +// JProgressBar progressBar = new JProgressBar(0, 100); +// progressBar.setValue(new Random().nextInt(101)); +// progressBar.setStringPainted(true); +// systemTray.addWidget(progressBar); +// } +// }); + submenu.addEntry("Hide tray", LT_GRAY_BUS, new Action() { @Override public void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) { systemTray.setEnabled(false); } }); - submenu.addEntry("Remove menu", GREEN_MAIL, new SystemTrayMenuAction() { + submenu.addEntry("Remove menu", BLACK_FIRE, new Action() { @Override public void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) { @@ -144,7 +164,7 @@ class TestTrayJavaFX extends Application { } }); - systemTray.addEntry("Quit", new SystemTrayMenuAction() { + systemTray.addEntry("Quit", new Action() { @Override public void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) { diff --git a/test/dorkbox/TestTraySwt.java b/test/dorkbox/TestTraySwt.java index 327810f..1cf0a16 100644 --- a/test/dorkbox/TestTraySwt.java +++ b/test/dorkbox/TestTraySwt.java @@ -23,10 +23,10 @@ import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; +import dorkbox.systemTray.Action; import dorkbox.systemTray.Entry; import dorkbox.systemTray.Menu; import dorkbox.systemTray.SystemTray; -import dorkbox.systemTray.SystemTrayMenuAction; /** * Icons from 'SJJB Icons', public domain/CC0 icon set @@ -36,9 +36,18 @@ import dorkbox.systemTray.SystemTrayMenuAction; public class TestTraySwt { - public static final URL BLACK_MAIL = TestTray.class.getResource("transport_bus_station.p.000000.32.png"); - public static final URL GREEN_MAIL = TestTray.class.getResource("transport_bus_station.p.39AC39.32.png"); - public static final URL LT_GRAY_MAIL = TestTray.class.getResource("transport_bus_station.p.999999.32.png"); + public static final URL BLUE_CAMPING = TestTray.class.getResource("accommodation_camping.glow.0092DA.32.png"); + public static final URL BLACK_FIRE = TestTray.class.getResource("amenity_firestation.p.000000.32.png"); + + public static final URL BLACK_MAIL = TestTray.class.getResource("amenity_post_box.p.000000.32.png"); + public static final URL GREEN_MAIL = TestTray.class.getResource("amenity_post_box.p.39AC39.32.png"); + + public static final URL BLACK_BUS = TestTray.class.getResource("transport_bus_station.p.000000.32.png"); + public static final URL LT_GRAY_BUS = TestTray.class.getResource("transport_bus_station.p.999999.32.png"); + + public static final URL BLACK_TRAIN = TestTray.class.getResource("transport_train_station.p.000000.32.png"); + public static final URL GREEN_TRAIN = TestTray.class.getResource("transport_train_station.p.39AC39.32.png"); + public static final URL LT_GRAY_TRAIN = TestTray.class.getResource("transport_train_station.p.666666.32.png"); public static void main(String[] args) { @@ -49,8 +58,8 @@ class TestTraySwt { } private SystemTray systemTray; - private SystemTrayMenuAction callbackGreen; - private SystemTrayMenuAction callbackGray; + private Action callbackGreen; + private Action callbackGray; public TestTraySwt() { @@ -67,16 +76,15 @@ class TestTraySwt { throw new RuntimeException("Unable to load SystemTray!"); } - this.systemTray.setImage(LT_GRAY_MAIL); - + systemTray.setImage(LT_GRAY_TRAIN); systemTray.setStatus("No Mail"); - callbackGreen = new SystemTrayMenuAction() { + callbackGreen = new Action() { @Override public void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) { systemTray.setStatus("Some Mail!"); - systemTray.setImage(GREEN_MAIL); + systemTray.setImage(GREEN_TRAIN); entry.setCallback(callbackGray); entry.setImage(BLACK_MAIL); @@ -85,12 +93,12 @@ class TestTraySwt { } }; - callbackGray = new SystemTrayMenuAction() { + callbackGray = new Action() { @Override public void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) { systemTray.setStatus(null); - systemTray.setImage(BLACK_MAIL); + systemTray.setImage(BLACK_TRAIN); entry.setCallback(null); // systemTray.setStatus("Mail Empty"); @@ -105,22 +113,34 @@ class TestTraySwt { this.systemTray.addSeparator(); - final Menu submenu = this.systemTray.addMenu("Options", BLACK_MAIL); - submenu.addEntry("Disable menu", LT_GRAY_MAIL, new SystemTrayMenuAction() { + final Menu submenu = this.systemTray.addMenu("Options", BLUE_CAMPING); + submenu.setShortcut('t'); + submenu.addEntry("Disable menu", BLACK_BUS, new Action() { @Override public void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) { submenu.setEnabled(false); } }); - submenu.addEntry("Hide tray", BLACK_MAIL, new SystemTrayMenuAction() { +// TODO: buggy. The menu will **sometimes** stop responding to the "enter" key after this. Mnemonics still work however. +// submenu.addEntry("Add widget", GREEN_BUS, new Action() { +// @Override +// public +// void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) { +// JProgressBar progressBar = new JProgressBar(0, 100); +// progressBar.setValue(new Random().nextInt(101)); +// progressBar.setStringPainted(true); +// systemTray.addWidget(progressBar); +// } +// }); + submenu.addEntry("Hide tray", LT_GRAY_BUS, new Action() { @Override public void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) { systemTray.setEnabled(false); } }); - submenu.addEntry("Remove menu", GREEN_MAIL, new SystemTrayMenuAction() { + submenu.addEntry("Remove menu", BLACK_FIRE, new Action() { @Override public void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) { @@ -128,7 +148,7 @@ class TestTraySwt { } }); - systemTray.addEntry("Quit", new SystemTrayMenuAction() { + systemTray.addEntry("Quit", new Action() { @Override public void onClick(final SystemTray systemTray, final Menu parent, final Entry entry) { diff --git a/test/dorkbox/accommodation_camping.glow.0092DA.32.png b/test/dorkbox/accommodation_camping.glow.0092DA.32.png new file mode 100644 index 0000000000000000000000000000000000000000..79f0c2a209032034b57c690e290d3c8cd54ca562 GIT binary patch literal 1779 zcmZ|QXHXMp8o==+5LQYQLQEhK1z8j@2nirR#L|M` zAW-GcIVU36S*KkT0V3Yk<^+L)_sz67L4atOg-fJBi@(O~Ww6no#|L5f0D*t?fM;O^ zUj?bWB?134d8EI@Jp|?i8&8PxFWw!G??`iK{|W@Z(;^U+kDh=;FPpK@YblDqSdloo z(l#WEXeCUajAz#R&{qYFeWsLl5TnJ!yAm!5BhG@vVOFj^Z$hI2J{)QCXBQWjTidir z*u~!7&85SEv&g+}zC8Y7PC+YDCslIj{vM($2rrU%187?yD>Q-Kz$siWLJQdrq*IO5 zcWPUzta6_!NE87A>h(>;st`|v$lD#(82!5vN3RZ(%Y^4aIzb_7vT_qZ7cT;XVw_*t zOjb`xpKSm`anMY)ToqiP7mmKr*qI_Tlb&o;I@An$)x3MW@kI+ z)+rO0k|!F+1Kr|7Q$!?z-o+UQhlV#UG#SLG8|sBv9{w?~bvq-Xb&rt-Nl=6Le~$44 z1m#|p{C00~o(-;eppkG~TsFldycjn7vf(#sS~Fm0zH(x)cDmOb{nRhui?o3w@@o1nQmqZdV46KI11{3!AgOWj9XF zmI$G`rG-P=MT&(>U+1{H`{^IB-M0FqSLVX$vYaa4ZPg{si)}JAqKMBo4IqhC-sw8f zuZbsUUp$1N|oXV0c zHFvFZ|HM0Q1W+dzPfxO>>vl}n9{atnf3`!9zk3fqR(bKBGkV*R>D{XgzV1hzYMGA} zmD%P*Nk2A(gV5$kxdGb-p=vd_PZp)*SMq;o%EX=fZpg=RJsJzi!sa%iNYI>}S7ARj z4y)DVzmpN>gTW7y(a`#WeiTZV3;ejI1tDYl*|9AhY;2k{+~Ogb?-Z005vc_9X~}bf zwM6l}{ntl_+@2MyzDb&HtK6EV%(OSe=WnMTcj&%}jn_P@%^3aFrv>*!IPPerW-%C4 z8WrW{xnEH`20JM1e8pr?W~QVZVWc0EiU+JZZjV&j?_Ibss2RGX4RAEl)9T{Ij%CMC zN3J>;t=@^!Lc(vo%$)h|W=*M3?L!E^T%~ubKB`;S$Gf2UHT=E?;M+mpC-0ZpES*#E z;<=XK_!AfhLK|SIgijW&OeF(=yW@=%vB^sNv5u8eQ)*V-K5d0;RM4j6@kK&N|GLzr zSDIR{!92jNUCNBHzw(Gsx+ECwf$H3-_+I8ApGx#8U%fy5n{X*Add+KLP5czBf8P7} zgn;0PyE#(#x#Q3pc~x;2;V&ngPpdNQlmQ=XqZsLorRExWQj)9ZjC|6|=>RtYy5?*5Z#?G`HB z+qk+l(rRWPGQQ7H7GpkNxm|##m&?A))NiGG^kqP=y?EO8ycpxm3q~LcMOW(CamoX8 z!{qRHlUM7K-KE!iTqZQmrnKqdifywIReshX<|tG;t^}mzo0%(_DrF(Bnz~uuDIeHq z6!D(fv|tpSJM2)2#?=YaJe8^V4|lFsWR|{tFhMx?h?7WQ{1#Y`Ii`TwMYBKHl~fOE zL!??!5zozc4HhzAgjcmQ!`QsdOJkHASO;;h+4;&3u{oV)OJ1Q~=$7r~=5RUFn6Xkq zwnnz;Bwew>1KtJU)kOg&);dYFvZ_q{cfHAv%|}LJJV*J7*wOQ8l$+EN8Q6ki&!ICvCS^xzfNpqKa;#&`@sZLED<{%}aWFSDJRNspqoB&z};0I(6Jkm^JY3R>cKD1)qj8 zjYy{X-lh2&_(b>#0AMvS7)BM+7cQ~ypsP10bU|~ literal 0 HcmV?d00001 diff --git a/test/dorkbox/amenity_firestation.p.000000.32.png b/test/dorkbox/amenity_firestation.p.000000.32.png new file mode 100644 index 0000000000000000000000000000000000000000..dc0342c5c0bc19b7342651393126bc1ad9d5af94 GIT binary patch literal 636 zcmV-?0)zdDP);H3zI2&7JW z_0J_lPf7>9)TxW2h!^oD?#-KrpaMaH@Sq!A1PR=-lvw&a%)9J3eBZJ=^XWb?F!RjJ z`#d}E%=<04p~P`NmmK%@6w^Qx&{<3aEkIW>4OF30&{Qk~mr<8+%Ef>UN`N}x0pLk{ za*+n&zyK-*f5gCnm?{`HDTo6PffO(=TrDsy{Rq%tVg?MXD9bZ=GRNE@swRBQGu9bIIh)5l{PX)9 zIP&4Ypq}7{jd%u`ikAe20u0;$mVNj^R3&u!D2+<6dDIut6xoq=uMPe;a2<65r7i{{ zKmr(-{!c?_Le||k_zyrM@X|#*gEF95`iq)K8n`Wdi^d-TH&G|x^y~c)2aZK3q4AxX zf$^|KO^k`qh{in=ZX38#NU(^7fmzgl!XFyfih3!JfD?>oSAdZ@1Aq+9cpT7{xMx= z*l(fdoO5ksCH3P5ex396x+qb1f|!^1oT|Kq+jxNsRUI4nDU_^<`yC{|gYVdgF$ybC zrtAR2F~9-_D*kMaIt6i`UJ(yU`tSjpQ&5%(}BEMOF0%RWx&O%)OSc#p&Av)nFt z>TqNtew2uKfOpu(NJ{c50x}U3*u)!u4rX7p9b+;zIceK4&m~;Mb)nR+aD*vb!wxQ2 zj9Ewndo8wJtYJxL!AwkkjCl;h3CD72k|Oq<^tPiC=E&mg{$-q+nwtNV-{#J4v^)xh Q#{d8T07*qoM6N<$g4RsdF#rGn literal 0 HcmV?d00001 diff --git a/test/dorkbox/amenity_post_box.p.39AC39.32.png b/test/dorkbox/amenity_post_box.p.39AC39.32.png new file mode 100644 index 0000000000000000000000000000000000000000..d6e56841895ab8d9f0c97f87630738820b227412 GIT binary patch literal 669 zcmV;O0%HA%P)YygMORY2&#cC02$>Ja2 z;NaroDAG~z&ybjOOT~&Ml9Z&C4s{SJf}*I@B5l#3_PqCVsDY}z^il$XW z%hW?6au+aBHO&M9;2pws$2e!Jqt%uqk`u|pPUzeRzKDpZ>b7-}WyX1pMtP1|)dVcfz;!Ic6%3`YUxlXld=5LR zA(3DndT}a)&1`fDS?E$_780Dmr|2{bZYq=_3;91#hjv`6!giNXt`WS$;w;9KDr9Jw7 zfLqvJw9g}5-NFJ~8Mq$J^ z3#HqH0nANOkT09)@u6N}styV*I$f9%OQQZ~viU-WH>-twi?TW)Fh@98|B}RaG$keI z!8T!rH>%xSsDxqu{vw)zBSrcgu9h)w!qRaP7{Jja0}F)fbQ<;+ZE72Zz(#CI8K_g} z{fBrgTvi{268y$VwXJU}4oZ=wBm*rKccn)k&f-~({7v9Lz%Mg~B5#5)X@vj)002ov JPDHLkV1l2bBzgb< literal 0 HcmV?d00001 diff --git a/test/dorkbox/transport_train_station.p.666666.32.png b/test/dorkbox/transport_train_station.p.666666.32.png new file mode 100644 index 0000000000000000000000000000000000000000..2137293949d46e658330b040d7175debe94f36c7 GIT binary patch literal 937 zcmV;a16KTrP)}>Dv|tol1&$O6S5E&Zp12;NUcJVN|D-1eZ{60>Oz~B-1%JO zy^_4P$$hq6wI2*KckX=WeD}CN{8Af1_uXUD~jT2ASuBwfCH6E<=x@o;lD0g zz}Br>9}OW)0&6=lV*B9y{QRbgiHVs`1*D4~k|cRM#&{-#Fape#aJ&K7DCw?BrScK* zVy6PmMxejHzdK2ipF#*zqobpF8S>!Z;7zutZTCnz(r7du>@*6YHAvHRha^kdUY0yE zGI9d=4ZwC!nx^k};(W0HN$LakH;tBt?cI{*0YV5*3=Iw4-U(j-0|NsOBuTPI(#Ii$ zS|i{o&p+F;WlPUF0wlGB92y%N>jS=A&b#fS zV`F2FNZPr?SO{zKJbxpE&?@nBwh!8#0e-RFSE*E{vn=~6&+`|m)oLn$EX#h7)C(K~ z_5pVTcPyjfBrpy10PEY{Mc}lg!uDoKneAu0y1Ldk8jYHy`z8Hp`#wpV(`E!(TX0LW zt%S9}lNWdwfE#Q_NmI=PS5Xu#AID>iJ0wloKGF(RQNn4b2)1u)jvFz?J(8}oeNxgo zfFw!Iv~u9r%OGGeD!`pUx1@W44*}U;7n(|51%AKGA{Jv6kl0Sn-UqTQdsxz^z|FQV zRb5yPF@*5=k}FxB=c_{q@7WfB?U@+k7wt^j8`Csh?v$*EqA32Jot^z41sE6@xK+|x zN!P|0Us<&70C0|gBuO5#{Y*(;hiy+w`d-q0+kXJZYqi=x(EJ@IN%9tu+ioY{6GGV6 z;m5&3jhM7OH9kJxUM-iPRR_K~kMsY7Q?_@FkB|R+DU4mkm4W{N<5Er>T}Ymc00000 LNkvXXu0mjfk2JSM literal 0 HcmV?d00001