Thursday, March 25, 2010

Transparent windows on Linux

WARNING: This blog entry was imported from my old blog on blogs.sun.com (which used different blogging software), so formatting and links may not be correct.


JavaFX makes it easy to create non-rectangular windows -- just set a StageStyle.TRANSPARENT on your stage.style.
We use rounded corners on our popup menus, and we also have a non-rectangular and alpha-blended splash screen.

The other day I was checking how the cursors were looking on different platforms, and imagine my horror when I discovered how our new splash screen and rounded menus looked on Linux! Here they are -- as you can see you end up with white rectangles around the supposed-to-be-blended areas:





That looks.... craptastic!

Historically, the JDK didn't support alpha blended windows on Linux. However, that was added a while ago (I'm not sure exactly which version, but I think it was JDK 6 update 14). It turns out that the FX code which initializes the native frame for the stage does not do conditional checking for this; it simply turns off transparency on Linux. Fortunately, there's a System property you can set to force it to respect the transparency flag. You would obviously only do this if you know you are running on a JDK which supports transparency. And that's easy!

In your startup code, do something like this:


if (Utils.IS_LINUX and Utils.jdkAtLeast(1, 6, 0, 14)) {
java.lang.System.setProperty("javafx.allowTransparentStage", "true");
}

There are a couple of utility methods here that are simple - just looking at some system properties to determine whether we're on Linux and whether we're on a particular version of the JDK or higher (if you run on non-Sun/Oracle JDKs you may want to check for that as well).


public def IS_LINUX = osName.contains("linux");
public def IS_MAC = osName.contains("mac");
public def IS_WINDOWS = osName.contains("windows");
public def IS_SOLARIS = osName.contains("solaris");

/**
* Is the version of the running JDK at least major.minor.micro_update?
* In 1.6.0_18 macro=1,minor=6,micro=0,update=18
*/
public function jdkAtLeast(macro: Integer, minor: Integer, micro: Integer, update: Integer): Boolean {
def runtimeVersion = java.lang.System.getProperty("java.runtime.version");
def pattern = java.util.regex.Pattern.compile("^(\\d)\\.(\\d)\\.(\\d)_(\\d+)-");
def matcher = pattern.matcher(runtimeVersion);
if (matcher.find()) {
def currentMacro = Integer.valueOf(matcher.group(1));
def currentMinor = Integer.valueOf(matcher.group(2));
def currentMicro = Integer.valueOf(matcher.group(3));
def currentUpdate = Integer.valueOf(matcher.group(4));
if (currentMacro < macro or currentMinor < minor or
currentMicro < micro or currentUpdate < update) {
return false;
}
}

true
}

With that modification at startup, we get much nicer results:





P.S. Yes, there's a JIRA issue tracking this to be automatically done by the platform, RT-4797.

P.S.2. This is for desktop; I'm not sure this code is mobile-safe.


No comments:

Post a Comment