JavaFX: taskbar-less undecorated window
Applications that are launched in Windows OS are by default listed on taskbar. For a utility software like The Console it is not the case. It shouldn’t pollute our task bar or even tray. It should be available under hotkey and that’s it.
Problem is, JavaFX won’t allow us. Previously presented line stage.initStyle(StageStyle.UNDECORATED) gives us undecorated window but collides with stage.initStyle(StageStyle.UTILITY) which makes window’s task not visible in taskbar but window itself is then decorated.
Between workarounds I’ve found a solution. TL;DR initialize window using Swing.
Investigating StageStyle.UTILITY
Let’s start with JavaFX internals. So, basically WindowStage class hides this switch case inside:
case UTILITY:
windowMask |= Window.TITLED | Window.UTILITY | Window.CLOSABLE;
Setting Window.UTILITY alone wouldn’t be enough because window border and top bar (title bar without title) would still be visible. By looking deeper at the same switch block we can see this:
if (isPopupStage) { // TODO: make it a stage style?
windowMask |= Window.POPUP;
if (style == StageStyle.TRANSPARENT) {
windowMask |= Window.TRANSPARENT;
}
focusable = false;
} else ... //our UTILITY is here
Window.POPUP ? Sounds interesting:
/**
* A popup window.
*
* Used to display popups (tooltips, popup menus, etc.) Note that by
* default it may display a task-bar button. To hide it the window must be
* owned.
*/
public static final int POPUP = 1 << 3;
To achieve POPUP we could actually use Popup class which extends PopupWindow. Unfortunately, I didn’t make this working properly. When popup once loses focus it can’t regain it.
I’ve found few similiar suggestions over the internet but none workaround did cover my case.
Swing > JavaFX ?
This StackOverflow answer suggests using Swing’s JFrame which provides this:
<code><span class="pln">setType</span><span class="pun">(</span><span class="typ">Type</span><span class="pun">.</span><span class="pln">UTILITY</span><span class="pun">);</span><span class="pln">
setUndecorated</span><span class="pun">(</span><span class="kwd">true</span><span class="pun">);</span></code>
and of course setAlwaysOnTop(true) is also possible.
Here goes the solution - use Swing instead of JavaFX to create window and initialize it’s style:
import javafx.embed.swing.JFXPanel
import javafx.scene.Scene
import javax.swing.JFrame
import javafx.stage.Screen
class UndecoratedUtilityWindow extends JFrame {
JFXPanel fxContainer
new(Scene scene) {
fxContainer = new JFXPanel()
fxContainer.setScene(scene)
getContentPane().add(fxContainer)
val primaryScreen = Screen.primary
val width = primaryScreen.bounds.width as int
var height = (primaryScreen.bounds.height/2) as int
setSize(width, height)
type = Type.UTILITY
undecorated = true
alwaysOnTop = true
defaultCloseOperation = EXIT_ON_CLOSE
}
}
And instead of initializing app using javafx.application.Application simply go with this:
def public static main(String[] args) {
SwingUtilities.invokeLater [
// initialize JavaFX, this hack was found here:
// http://stackoverflow.com/questions/11273773/javafx-2-1-toolkit-not-initialized
new JFXPanel()
Platform.runLater [
initScene()
val hostWindow = new UndecoratedUtilityWindow(scene)
hostWindow.setVisible(true)
]
]
}
def static initScene() {
val root = new BorderPane()
scene = new Scene(root)
// TODO your scene initialization
}
Summary
JavaFX is great but it seems I can’t leave Swing. It’s much understendable why JavaFX creators decided to support JavaFX-Swing Interoperability but this case just adds another reason - not really about legacy code but lack of features.
JFrame#setUndecorated() was all I actually needed. This feature should be available out of the box in JavaFX but it’s not. The situation was already covered by bug report JDK-8091566: Taskbar-less Undecorated Transparent Window in 2011.
Some guys say that we should Not mix JavaFX with Swing and I’ll try to keep it that way - besides the window which - unfortunately - covers whole application.