Good day,
I am having issues with my app's custom minimize and close buttons working inconsistently. I would like to fix this before advancing this project and making it more complicated.
The app is an undecorated JavaFX stage that contains a WebView
that loads a JSP hosted by an embedded Tomcat server. The minimize and close buttons are HTML within the JSP and have onclick
listeners that call Java methods to minimize and close the app via a JavaScript-to-Java interface class, AppHandle
, and a delegate-class, AppUtility
.
The buttons work, but work inconsistently. Also, whenever the buttons do fail at random, they fail together.
E.g.
JavaFX Components
Stage
|
\-- StackPane
|
\-- Browser (contains the WebView/WebEngine)
The Browser
class extends Region
and houses the WebView
and its WebEngine
. This is where the AppHandle
class is bound to the "app
" label within the JSP.
public Browser(String page, final AppUtility utility) {
browser.setFontSmoothingType(FontSmoothingType.GRAY);
browser.setContextMenuEnabled(false);
webEngine.getLoadWorker().stateProperty()
.addListener(
new ChangeListener<State>() {
@Override
public void changed(ObservableValue<? extends State> ov,
State oldState, State newState) {
if (newState == State.SUCCEEDED) {
JSObject win = (JSObject) webEngine.executeScript("window");
win.setMember("app", new AppHandle(utility));
}
}
}
);
webEngine.load(page);
getChildren().add(browser);
}
Here is the relevant portion of the JSP where the buttons call the AppHandler
methods onclick
.
<div class="container-app">
<div class="text-title">myApp</div>
<div class="icon utility minimize" onclick="app.hide()">
<i class="fa fa-window-minimize" aria-hidden="true"></i>
</div>
<div class="icon utility close" onclick="app.exit()">
<i class="fa fa-times" aria-hidden="true"></i>
</div>
</div>
This is the AppHandle
class, its methods are called from the onclick
events in the JSP. If I put print
statements in the exit()
and hide()
classes, they will not execute when the buttons fail. Which leads me to believe something is failing with the JSObject
.
public class AppHandle {
private AppUtility utility;
public AppHandle(AppUtility utility) {
this.utility = utility;
}
public void exit() {
utility.exit();
}
public void hide() {
utility.hide();
}
}
The AppUtility
methods act on the JavaFX Stage
to hide and close the JavaFX Application
. I originally thought something might be wrong with my show/hide/inconified
logic, but this code doesn't even get executed when the buttons fail.
public void exit() {
if (SystemTray.isSupported())
SystemTray.getSystemTray().remove(app.getTrayIcon());
Platform.exit();
System.exit(0);
}
public void hide() {
if (SystemTray.isSupported()) {
try {
SystemTray.getSystemTray().add(app.getTrayIcon());
displayIconDialogue();
} catch (AWTException e) {
e.printStackTrace();
}
}
app.getWindow().setIconified(true);
app.getWindow().hide();
}
public void show() {
if (SystemTray.isSupported())
SystemTray.getSystemTray().remove(app.getTrayIcon());
app.getWindow().setIconified(false);
app.getWindow().show();
}
How can I assess the "health" of my JSObject during run-time to see if it is the culprit?
Is there anything here that would explicitly break the connection between the JSP and the AppHandle
on launch? On minimize
? On show
?
This would be easier if it just didn't work at all, I'm not sure where the inconsistency is coming from.
Any help would be appreciated,
Thank you.
Bah, I solved it. Let this be a lesson to me to search for questions better.
This question clued me in to the problem.
I am creating the AppHandle
locally within the JSObject
. I guess at some point the local AppHandle
gets dereferenced and with it goes my JSP-to-Java connection.
I now make the AppHandle
in the AppClient
and pass it into the Browser
. That way the app maintains a reference to the handle. The Browser
constructor was changed to the following:
public Browser(String page, AppHandle handle) {
browser.setFontSmoothingType(FontSmoothingType.GRAY);
browser.setContextMenuEnabled(false);
webEngine.getLoadWorker().stateProperty()
.addListener(
new ChangeListener<State>() {
@Override
public void changed(ObservableValue<? extends State> ov,
State oldState, State newState) {
if (newState == State.SUCCEEDED) {
JSObject win = (JSObject) webEngine.executeScript("window");
win.setMember("app", handle);
}
}
}
);
webEngine.load(page);
getChildren().add(browser);
}