Tuesday Jun 29, 2010

Don't Use Implicit Return Types


JavaFX, like Scala, is a fully statically typed language. However, unlike Java, it allows you to omit type declarations in many places, since it can infer it. Coupled with the fact that you can use expressions as statements, and that the last expression in a function will be the return value, this lets you write really simple and clear code.


def s = "Hello";
def len = s.length();
function product(a: Number, b: Number) {
a \* b
}

The function above shows an "Implicit Return Type": The compiler figures out the return type, which in this case will be a Number, and that is the signature computed for the function.



Unfortunately, this can sometimes lead to trouble! Here's a real-world example:


public function clearSelection() {
selected = null;
}

This function clears the selection in a node. The intention of the developer was probably for this function to have a "void" return type. But that is not the return type! The last statement is an expression, an assignment, which has the type of the left hand side, which is actually a node.
So the return type here is a Node! And it could easily have leaked an implementation class too.



This may not seem like a big deal here, but what if a subclass wanted to override this function? It would try this:


override function clearSelection() {
super.clearSelection();
moreStuff();
}

And this would fail compilation, with a surprising error message:


File.fx:1: incompatible types
found : void
required: Node
override function clearSelection() {
1 error

That's right, your subclasses will now need to deal with this return type too! If it's an implementation class, they're in trouble!



There are other reasons implicit return types are bad too. Here's a function specifying a desired padding for a node:


public function padding() {
5
}

The user may have intended for this to be a Number, but since the default is "5" instead of "5.0", the implied type here is Integer, which means that subclasses will need to round their desired padding to an integer. And this is not always a good thing - see the pixel considerations article.



For all of the above reasons, our Coding Conventions Document states that you should not use implicit types, at least not for any non-private API. That includes package, protected and public functions. However, it's a simple mistake to make. We've all made it! For that reason, to help track these down, I've created a quickfix for NetBeans which identifies and fixes these problems. Here's what happens if you look at a function like the one described above:







The light bulb and underline warns you that there is a problem here. Clicking on the light bulb, or pressing Alt+Enter when the caret is on that line, shows this info:







It's telling you that you have an implicit return for a public function, and it's offering to insert the actual, current return type of the function. It's also offering to make the function Void.



Pressing return will fix the function:







There are certain return types you cannot express in JavaFX script - such as returns including generics signatures. The quickfix won't warn about those since there is nothing you can do.




You can find the module on the Plugin Center here. Hopefully it will be as useful to you as it has been to me!



P.S. We discussed this topic (implicit return types in JavaFX and Scala) for a while on the latest
Java Posse Podcast episode.

Monday May 10, 2010

Pixel Considerations


Antialiasing makes lines and shapes look smooth - though sometimes at the expense of sharpness. What if you're trying to draw a horizontal or vertical line where you don't need antialiasing? You might be under the impression that if you position your shapes at round integer positions, you will avoid antialiasing.
But it's not quite that simple - so avoid trying to be smart with layout code like this:


...
label.layoutX = (width - labelWidth) / 2 as Integer;
....

The idea here is that when you're placing something in the middle you might end up with a fractional value, say 42.5, and so you added the "as Integer" to round things to a whole number to avoid introducing antialiasing.



Well, this may have exactly the opposite effect! Take a look at the following picture, which shows two rectangles. Both rectangles have a stroke width of 1.0, and one of them is positioned at a round integer, and the other one is positioned at 0.5.







Here's a zoomed in view which makes things clearer:







Obviously, the rectangle on the left is blurry because antialiasing is attempting to show the line as being somewhere in the middle between them. The rectangle
on the right on the other hand is clear and crisp because the lines overlap EXACTLY with the pixel grid the line is rendered into.



Here's the thing though: The rectangle on the left is the one that was positioned at round integers, and the rectangle on the right is the one positioned
at round integer + 0.5 !



So should you run out and ensure that all your horizontal and vertical edges are positioned at 0.5? No. The key here is the stroke width. Take a look at the following figure, where I have position rectangles with different stroke widths (1, 2, 3) and different pixel positions (0, 0.25, 0.5).







Zoomed in:







As you can see, whether you match the pixel grid perfectly depends on the stroke width and the pixel positions. This actually makes sense. Think of your pixel grid as having x coordinates at the boundaries of each pixel. In other words, "0.0" is the left edge of the first pixel, and 1.0 is the right edge of the first pixel. The line position has to be the center of the stroke. So if you want to have a line of thickness 1, then that line will run exactly through the pixel, so we must position its center at x=0.5. When the stroke width increases to 2 however, the center will be in the middle (e.g. 1), and so we should position it at a round number. And so on.



When you're dealing with large shapes this isn't a big deal. But if you're trying to paint a grid (like the one below), a pattern, or small controls (like disclosure arrows - which is how I came to look into this), it can pay off.







By the way -- on OSX there's a nice screen zoom (hold the Option key and then do a two-fingered drag on the trackpad up or down) which makes it easy to zoom in and look at the pixels for anything on the screen. But unfortunately it doesn't show pixels as square, it does more blending, so it's much harder to tell what's going on at the individual pixel level. Get an image editor which lets you zoom in with clear pixel boundaries, or even a screen magnifying lens. Here's how the builtin screen zoom looks - as you can see it's not as clear as the pixel zooms above:







UPDATE: Marius taught me in the comments that you can turn off the OSX zoom smoothing in the Universal Access options. Sweet! I can now instantly check the pixels without going to an intermediate screenshot! Thanks!



Finally: Jonathan Giles from the JavaFX controls team has been doing a great job aggregating and summarizing interesting FX articles each week -- highly recommended if you're doing anything with JavaFX.



Thursday Mar 25, 2010

Transparent windows on Linux


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.

Sunday Nov 08, 2009

JavaFX Coding Conventions


I've been writing a lot of JavaFX code over the last year. After some tweaking I've arrived at a style that I like a lot. I notice that even on my team there are some variations in how people format their code, so I thought I would document what I like in case this helps others get started. (I did a quick google search and didn't find any JavaFX coding convention documents anywhere. The closest thing I found was a blog entry, but while it contains a lot of good advice, the style it recommends does not match the practice of the JavaFX team (or the Sun Java style) either). Therefore, I thought I would document what I consider good JavaFX coding conventions.



Rather than post them here in a blog entry, I placed them in a Wiki page such that they can easily be improved and kept up to date as I get feedback and in case I change my mind :)



You can find the coding conventions document here:


http://wikis.sun.com/display/JavaFxCodeConv/Home



P.S. I will be speaking at Devoxx in Antwerp, Belgium next week! Hope to meet some of you there!

Monday Jun 08, 2009

The Authoring Tool


We lifted the veil on the new designer tool for JavaFX last week at the JavaOne 2009 conference. Here's a screenshot:







The tool made a number of appearances:


  • First in the Tuesday keynote where my boss Nandini showed the basics of the tool.
    Video,
    starts at 23:08.

  • Then in the mobile keynote where Eric Klein showed the multiscreen support and mobile deployment.
    Video,
    starts at the beginning.

  • And finally in the Friday keynote where I got to do a longer 10 minute segment on the tool.
    Video,
    starts at 10:25.

The above video links just point to small chapters of each keynote; for full video replay go to
the keynote replay page.



I've scanned the blogosphere and twitter for comments on the tool and demos and the feedback is very positive.
Here's
a particularly detailed blog post with pictures and video snippets detailing the Friday demo.
Now we just need to finish the feature set, fix the bugs and polish everything! It's been a sprint for the whole team to get to this point. But we're not building a demo! We're building a product! So we're not getting much of a rest, it's right back to work to finish this thing!








(Photo by Balz Schreier)



P.S. In case you missed it, Larry Ellison from Oracle went on stage and made several comments regarding JavaFX in case the acquisition should happen - here's one article, there are many others.



P.S.2. We had our fourth annual Java Posse BOF live recording session last week. It was a blast. Dick stayed up editing and releasing
the podcast
the same night. If you're wondering what happened in the middle of the episode, where there's not much audio and a lot of laughing, that's me nearly drowning. I took a big swig of beer just as Joe made a joke; the beer went down the wrong tube, and then I was laughing so hard I couldn't breathe. My eyes were runny and I had beer all over my face and chest. Pretty embarrassing but reportedly also entertaining for others! Here's a photo from our get-together at a bar afterwards:







(Photo by
Toni Epple)

Wednesday Apr 29, 2009

JavaFX editing tips!


The NetBeans support for editing JavaFX isn't as mature as for other languages. After working with it for a little bit I've figured out a few things you might find helpful:



  • One thing I do a lot when experimenting with FX is trying to comment out parts of the scenegraph to try different things.
    The way I comment/uncomment
    in NetBeans is using the Toggle Comment action -- Ctrl-/ or Cmd-/ depending on your platform. But in JavaFX that keystroke does nothing!
    It turns out they have implemented Comment and Uncomment, just not toggle. So you can just use those actions instead (they're in
    the editor toolbar on the right.)



    Or, if you're like me, you really want Toggle Comment. Especially because it has a keybinding. In that case you can install
    this plugin which adds toggle comment
    support for .fx files. It's a trivial module (just two small files (1,2)) so I'm hoping this will be
    included in the next version.



  • The second issue I ran into is that the editor sometimes tells me my source code is wrong - and I'll stare at it without figuring out
    the problem. Turns out - I'm often right. The code is okay, and the background parser is confused. At the
    roundup a number of other people ran into this bug.



    Fortunately, there's a simple workaround for this - just select all (Ctrl-A), hit delete to wipe the file clean,
    and then undo (Ctrl-Z). You'll get your source
    file back, and the file should be (re)parsed correctly. It looks like there is some kind of incremental parsing bug. If you disagree
    with what the IDE error message is telling you, go ahead and try this workaround.



    Here's an example. The editor is telling me I have an invalid assignment -- huh? There's no assignment there!







    If I select all, delete, paste, I get this - all better:







    An annoying bug, but once you know about it it's pretty trivial to work around it.



  • At first I thought code completion was really broken. Let's say I wanted to insert a DropShadow. Not knowing
    where it lives I would just type DropS to get NetBeans to show it to me and also import it for me (and advantage
    of using code completion instead of just typing the name). But that just didn't work - after typing DropShadow and
    attempting code completion it wouldn't show me any matches!



    Turns out there's another workaround for this. Just hit Ctrl-Space again! In the Java editor, we distinguish between
    completing only the imported classes and all available classes. If you for example have imported a class named "Video"
    in your class and you code complete on "V", it will not list "Vector" as a match (unless that class is also already imported) -
    it will only show the imported classes that match the V prefix. If you press Ctrl-Space a second time, you get to see all
    V matches. Of course, the code completion dialog tells you this - it says "Imported Items; Press Ctrl-Space Again for All Items" right
    there in the list.



    However, this never seems to bite me when coding Java, because it does something else: If there is no match, it will proceed to do
    the all-items completion on its own - so most people don't have to think about it. The JavaFX editor on the other hand does not do this
    so you end up typing a prefix you know exist, complete, and - nothing.







    The workaround is simple - just press Ctrl-Space a second time when this happens, and voila - the class shows up and gets imported
    as well. Of course - the message at the bottom of the semi-empty code completion window says as much, but since you don't usually
    have to pay attention to this in Java (because it auto imports when there isn't a prefix match) you might have missed it:







  • Import Management. At first I would try to just type "DropShadow { }" - and the editor would complain that it doesn't
    know what I'm talking about. Alt-Shift-i, which imports the symbol under the caret doesn't do anything.
    But it turns out that Cmd-Shift-i, Fix All Imports, DOES work. So use that one instead. It doesn't give you the dialog
    you get in Java showing all the matches; instead it works its way sequentially through all missing symbols.







    P.S. Fix Imports also cleans up unused imports.



  • The Preview Panel seems to be a bit hit or miss. I loved using it, but I would occasionally get lots of exceptions from it,
    so now I only use it for simple scenegraph experiments and tend to just run instead to test stuff - it's been fast enough.



  • The main thing I'm missing is keyboard navigation among occurrences. As you (hopefully) know, Ctrl-Up and Ctrl-Down lets you
    cycle through the yellow occurrences of a symbol when you're editing Java, JavaScript, Ruby, etc. This is really handy since
    it gives you instant "find usages" within a file - just click on a symbol and ctrl-down to cycle through the references.
    JavaFX has mark occurrences - but unfortunately they're missing the small code to iterate this through the keyboard.
    I thought I could just add that trivially along with the toggle-comment code plugin above, but unlike toggle comment which
    took 3 minutes to write, I couldn't see a simple (30 minutes or less) way to get access to the occurrences highlights from
    the outside, so I instead fired
    off an e-mail to the FX editor team - I'm crossing my fingers that they can get this in the next version!




Tuesday Apr 07, 2009

Add Effects to the right Container!


The Flubber application I described earlier has an old stop-watch style timer with second and minute hands:







Making the clock hand move was trivial using value binding and a rotation transform on the hand graphics object:


transforms: Rotate {
angle: bind (360.0 / 60 \* minutes) - 90.0
}

Since we are using a binding expression, setting the minutes variable anywhere (and yes, assigning to it!) will cause the hand angle to be recomputed and the graphics updated.



Anyway - once we had the clock moving, the first thing we wanted to do was improve the look of the clock by adding a drop
shadow. That was trivial; all we had to add was this:


effect: DropShadow {
offsetX: 5
offsetY: 5
color: Color.BLACK
radius: 10
}

That looked pretty good:






But look what happened when we let the timer run... Where is the light source?






What's happening here is that the drop shadow is rotating with the hand. Not what we want.
We were all learning JavaFX that day, so we hacked it, using the following code:


effect: DropShadow {
offsetX: bind Math.sin(Math.toRadians(handAngle + 45)) \* 5.0
offsetY: bind Math.cos(Math.toRadians(handAngle + 45)) \* 5.0
color: Color.BLACK
radius: 10
}

Now, the drop shadow offset moves along with the rotation such that the shadow always appears in the right place.
It worked, and we moved on.



Now that I understand JavaFX and the scenegraph a bit better I realize that this approach is wrong - there's a much simpler solution!
We don't want to apply the drop shadow to the Path object directly, since the path is what we are rotating with our minute angle.
Instead, we should simply move the effect out to the surrounding parent. That will apply the drop shadow after the shape
has been rotated.
Doing that, we can set our drop shadow initialization back to the original simple 5,5 delta, and we get exactly the result we want:







So, think about which node you apply effects to! And by the way, the effects framework is really fun to play with - you should definitely
explore javafx.scene.effect.\* !

Monday Mar 23, 2009

JavaFX Text Component


At the Roundup a few weeks ago we rewrote the old Swing JFlubber application to JavaFX. We had a brand new graphical design done in PhotoShop, and we hooked up the functionality with some swanky effects. I might get into that in another blog entry.



But one challenge we ran into was that of displaying text. In the Flubber application we need to have an editable text area where flub times are automatically inserted and the user can add additional comments. JavaFX makes it easy to throw some simple text in a design, but edit, scroll, and so on? Not yet.



Proper GUI components for JavaFX is coming. But in the mean time, there are some tricks you can use. Put simply, you can use a Swing text area, but without looking like Swing. Here's how. First, create a JavaFX scenegraph node for the Swing text component:


var textPane = new JTextPane();
var scrollPane = new JScrollPane(textPane);
var text: SwingComponent = SwingComponent.wrap(scrollPane);

Let's see how that looks:






Ugh!! Not what we want. Obviously, we don't want the text area to look like Swing... we have a nice background as part of the PhotoShop design that we want to use under below the text. But that's not hard to fix. Java colors have an alpha channel, for opacity. We can use a completely transparent background on the text area to make it just pass through its background (and we have to make the scroll pane nonopaque as well):


textPane.setBackground(new java.awt.Color(0,0,0,0)); // 0 opacity => 100% transparent!
textPane.setForeground(java.awt.Color.WHITE);
textPane.setCaretColor(java.awt.Color.WHITE);
scrollPane.setOpaque(false);

Let's try again:






Much better! But there is still a gray rectangle on top of the graphics, placed there by the border of the scrollpane. We can get rid of that too:


scrollPane.setBorder(new EmptyBorder(new Insets(0,0,0,0)));







Now we're talking! But the font is too small. Dick told me he wants to use a Courier font. So let's try this one:


textPane.setFont(new java.awt.Font("American Typewriter", 0, 14));

Here's what we get:






Ewwwwww! The font is not antialiased. I tried a bunch of things to fix this -- for example setting antialias rendering hints on the component itself, setting the aa system property, etc. It looks like there is a bug here in that the rendering hints aren't picked up properly for the wrapped Swing components. But I did find a workaround that works: We can set the antialiasing rendering hints directly on the graphics object ourselves. That means we need to get involved in the painting of the component itself, but luckily that's trivial. Instead of creating a new JTextPane, we'll create a simple subclass of it where we override the paint method to add the rendering hints:


import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.JTextPane;

public class AntiAliasedTextArea extends JTextPane
{
@Override
public void paint(Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
super.paint(g);
}
}

Now we can just change


var textPane = new JTextPane();

to

var textPane = new AntiAliasedTextPane();

and we get this:






Ta-da!



But there is one more problem... What if we add a lot of text:






Scrollbars! We don't want those. This text pane won't be used for much text, so you can just move the caret around like in a text field to navigate the overflow content. So let's turn off the scrollbars:


scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);

and with that, the text pane behaves exactly the way we want. Now you can manipulate text in the usual way: textPane.setText(string), textPane.getText(), etc.



While we're at it, let's fix the selection colors as well such that when you select text you don't get the telltale Swing light blue:


textPane.setSelectionColor(new Color(255, 255, 255, 50));
textPane.setSelectedTextColor(new Color(255, 255, 255, 255));

Note again how we can use transparency on this colors to give a nice glass effect on the selection (I also bumped up the font size):







Hopefully these tricks will help you with your text needs until we have real JavaFX components...


Wednesday Mar 11, 2009

New Job


Apologies for the recent blog silence for me. It's not that I've been slacking off. Quite to the contrary!
The biggest news is that I've changed teams: I'm now working on JavaFX! And more specifically, the designer tool.




If you don't know what JavaFX is, you're probably reading my blog because you're interested in Ruby, Python or the various other languages for NetBeans I've been involved with. If so, fear not - NetBeans language support is in great hands - Erno just improved the Rails dynamic finders code completion for Ruby for example. Of course, as an open source project we can always use more contributors! I've received a bunch of patches for the Python support recently which I've been happy to apply immediately. Keep 'em coming!



Briefly, JavaFX is a new UI stack for Java which makes it trivial to build pretty, dynamic and interactive UIs. While Java has always made it easy to target multiple operating systems, JavaFX makes it easy to target the completely separate platforms of desktop, web and mobile. If you haven't looked into it, you should.



JavaFX has its own language, JavaFX Script. It has some language level features which makes it really suited to building UI applications - from something simple like a literal for durations (so you can write 2m or 5s or 250ms in your source code), to something advanced like variable bindings which give you all of the power of traditional property change listeners, but without the pain (and it's more powerful than that - you can for example bind the rotation angle of a clock hand to an expression which computes the angle from the current time, and whenever the time variable changes the clock hand rotation is updated.) As a Java developer (and a Ruby and Python and JavaScript coder as well) I can truly say I've seen some things in JavaFX Script that I would like to see in other languages. I might talk about that in an upcoming blog entry.



If you're one of the alternate-languages-on-the-JVM people, I think you should seriously look into adding a binding layer for the JavaFX platform to your language. Libraries like Groovy Swingbuilders (and similar libraries for Ruby etc) have made Swing more accessible, but at the end of the day you're still dealing with Swing. If you want to add drop shadows, transition animations etc. a JavaFX DSL would be a much better approach. And besides, we're firing on all 256 cylinders on the JavaFX platform -- from the base Java platform (adding modularity so we can provide a barebones FX runtime), to the graphics system, to components, to the language, and up to the designer tool! I think it's going to shake up the RIA space - which is why I wanted to be part of it.



I promise my next blog entry won't be long in waiting, I've got a bunch of things queued up!

About

Tor Norbye

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today