Archive for June, 2014

SnapCode is the first and only pure JavaFX IDE

June 11, 2014

SnapCode is now the first and only pure JavaFX IDE! This allows SnapCode to easily implement page transitions, mouse over effects, animations and the SnapCode visual code editing that give SnapCode the feel of rich modern applications.

For the last two weeks we’ve been converting the remaining Swing parts of SnapCode to pure JavaFX. This includes the code editor, the runtime browser/player, the welcome panel and much more. We had some secret sauce in there that made the port happen remarkably quick.

This is just our first preview of the pure JavaFX world. We promise a balance of tasteful modern UI effects as we continue to polish SnapCode. But for now, we couldn’t resist some gratuitous effects that JavaFX makes so easy.

Check out the video overview and let us know what you think!

YouTube Video Overview: SnapCode is the First pure JavaFX IDE.

 

 

Getting the Visible Bounds of a Node

June 11, 2014

Today I set out to optimize a custom text node I have (for code editing) to only create nodes for lines of text that are visible – much like ListView only create cell nodes for visible items.

So the crux of this is that I needed a method similar to Swing’s JComponent.getVisibleRect() to tell me the visible bounds for my text node, which is usually in a ScrollPane. It seems there is currently no Node method for this and I couldn’t find previous work online, so I thought I would post my solution.

Here is a method that was sufficient for my purposes – it returns the visible bounds for a node based on the first ancestor clip (which could be from a ScrollPane, SplitPane or any Parent that clips its children):

/**
 * Returns the visible bounds for a node based on first ancestor clip
 * (or null if no clipping found).
 */
public static Bounds getVisibleBounds(Node aNode)
{
    // If node not visible, return empty bounds
    if(!aNode.isVisible()) return new BoundingBox(0,0,-1,-1);
    
    // If node has clip, return clip bounds in node coords
    if(aNode.getClip()!=null) return aNode.getClip().getBoundsInParent();
    
    // If node has parent, get parent visible bounds in node coords
    Bounds bounds = aNode.getParent()!=null? getVisibleBounds(aNode.getParent()) : null;
    if(bounds!=null && !bounds.isEmpty()) bounds = aNode.parentToLocal(bounds);
    return bounds;
}

I also wrote a version called getVisibleBoundsScene(node) which intersects the combined clips of every ancestor node all the way up to the scene. This version was only slightly more complicated, but it turned out to be overkill. In fact, if you have animations where panels are sliding in and out, of Parent containers, using the cumulative intersection of visible bounds can significantly slow things down.

So after I had my VisibleBounds solution, I had one more problem: if I only add child nodes for the visible bounds, I also need to rebuild when those visible bounds change. I achieved this by listening for bounds changes in ancestors up to the clip, a list that is updated every time the node is rebuilt. Below is the code I used for that.

/**
 * Sets listeners for visible bounds.
 */
void setVisibleBoundsListeners()
{
    // Get list of ancestors up to first clip (just return if same as last time)
    List pars = new ArrayList();
    for(Node p=this; p!=null && p.getClip()==null; p=p.getParent()) pars.add(p);
    if(pars.equals(_pars)) return;
    
    // Stop listening to old ancestors, start listening to new
    for(Node par : _pars) par.boundsInParentProperty().removeListener(_bcl);
    _pars = pars;
    for(Node par : _pars) par.boundsInParentProperty().addListener(_bcl);
}

// Listeners
List  _pars = new ArrayList();
ChangeListener _bcl = new ChangeListener() {
    public void changed(ObservableValue<? extends Bounds> observable, Bounds oldValue, Bounds newValue) {
        resetChildNodes(); }}; 

Make ScrollPane Content fill Viewport Bounds

June 3, 2014

One of the most common issues I find when using ScrollPanes in JavaFX is that I need the Content to fill the width/height when the ScrollPane.ViewportBounds is larger than the Content.PrefWidth/PrefHeight.

My first thought was that the ScrollPane properties FitToWidth and FitToHeight were meant to solve this – but they force the Content to ViewportBounds even when the Content has a larger PrefWidth/PrefHeight (this is probably useful for things like a wrapped text view). I suppose it should be possible to create a conditional binding for these properties, but I haven’t looked into this.

My second thought was that maybe I could call HBox.setHgrow(content, Priority.Always) and VBox.setVGrow(content, Priority.Always), but that was ineffective.

The solution I settled on was to add a ChangeListener to ScrollPane.ViewportBoundsProperty to reset ScrollPane.FitToWidth/FitToHeight dynamically:

// Create ScrollPane
final ScrollPane spane = new ScrollPane(aTextArea);

// Add listener to set ScrollPane FitToWidth/FitToHeight when viewport bounds changes
spane.viewportBoundsProperty().addListener(new ChangeListener() {
  public void changed(ObservableValue<? extends Bounds> arg0, Bounds arg1, Bounds arg2) {
      Node content = spane.getContent();
      spane.setFitToWidth(content.prefWidth(-1)<arg2.getWidth());
      spane.setFitToHeight(content.prefHeight(-1)<arg2.getHeight());
    }}});

Please comment if you have a better solution.