Java CLI in the Browser not as Stupid as it Sounds

October 27, 2016

Stay with me here, this may sound silly unless you are a teacher or student, but a standard part of many intro programming classes is learning the traditional Command Line Interface with simple tools like System.out.println() and java.util.Scanner. This lets you learn the basics of input, output and data processing with only a few lines of code:

System.out.println("Enter two numbers:");
Scanner scan = new Scanner(System.in);
int val1 = scan.nextInt();
int val2 = scan.nextInt();
System.out.println("Total value is: " + (val1 + val2));

With just these basics you can write many interesting programs:

  • Calculators, formula solvers, quizzes, surveys
  • String manipulation: word counting, palindromes, anagrams, etc.
  • Text based adventure games, digital psychologist, etc.

This was the only way to learn when I started on the TRS-80 and Apple II, and it’s still a great way to learn today, without introducing too many concepts that overwhelm new students. The downside is that these programs tend to be locked in the IDE or a terminal.

However, using a class called SnapPane, via one extra line of code, the same app that runs at the console pops up a window instead and runs on the desktop. And with SnapTea, and another line of code, you can then run in the browser and share with the world. Here’s a command line calculator running in the browser (notice, also, a tab to view the source code):

Simple Calculator using println and Scanner

SnapCode is a free IDE for education. Please like the blog, video, tweet (and retweet!) and join the discussion group if you’d like to hear more.

snapcode-cli

(video demo)

Java UI Builder for the Browser and Desktop

October 13, 2016

Last week I showed an example of writing just a few lines of UI code and running it on the desktop and in the browser, using SnapCode, SnapKit and TeaVM.

Well that was downright barbaric. There is a better way to design UI than code, compile and iterate: it’s a UI builder. This week I get rid of those extra lines of code by generating the UI in SnapCode’s UI builder. Much more civilized – and perhaps the most high-level “Hello World” ever seen.

This is the system I wish Flash had been: Visual designer, code editing, project file management, native browser (no plugin), native desktop and support for the most popular programming language on the planet.

SnapCode is a free IDE for education. I’d love to hear feedback. Please like the blog, video, tweet (and retweet!) if you’d like to see more.

snapcodequick

Quickest way to Java in the Browser

October 4, 2016

This week SnapCode was updated to automatically convert SnapKit Java apps to JavaScript and launch in the browser when it detects an app is linked against TeaVM (an incredible Java-to-JavaScript transpiler). This finally makes it trivial to write a Java UI app and immediately run in the browser.

I’m not a fan of demos with coding, but this is a UI version of “Hello World” (it’s only a few lines of code). The UI builder version of this demo will be available in a few weeks.

As a Java developer, I’m very excited. I have jumped through many hoops over the years with Applets, Web Start and native app packaging, and still I don’t have the ability to share and distribute even basic apps without a disproportionate amount of work. Until now.

SnapCode is a free IDE for education. I hope people check it out.

snapcodequick

If you want more – please like/retweet the blog, video, tweet. (tweet is here)!

Rich Java in the Browser with SnapKit and TeaVM

September 20, 2016

I’ve been playing around more with my SnapTea project. SnapKit is my Java client library for writing rich apps on top of either Swing or JavaFX. TeaVM is a remarkable bytecode to JavaScript transpiler. SnapTea is SnapKit with a TeaVM adapter – so I can compile and run my SnapKit Java desktop apps in the browser.

With SnapKit, I can easily write a rich Java desktop app in the conventional fashion. I build UIs as a hierarchy of Views containing text, images, graphics, buttons, sliders and more Views. Once I’m done, I run TeaVM from the command line and it generates a compact Classes.js file that opens in my browser.

So my project this week was to write a simple showcase application: BusyBox. This app contains many of the controls found in SnapKit. This is just a first version, with just a few hours of development, so check back later for updates!

BusyBox.png

Java Desktop apps to JavaScript

July 13, 2016

My ideal dev platform would be a Java environment that lets me write fully functional, rich native desktop apps that I can also build/run natively for the browser, even if in limited fashion (but without plugin).

So two years ago I started writing my own UI kit, Snapkit, that has the modern features of JavaFX, but adheres more to Java conventions like Swing. My big cheat (and epiphany) was to implement the bottom most system interaction (graphics, input events, clipboard) as an adapter. So Snapkit can easily run on top of either Swing or JavaFX, in any Swing Component or JavaFX Node. It really only relies on a Painter, supplied through the adapter (Graphics2D or JFX Canvas). All of the graphics primitives (Shape, Rect, Path, Color, Font, Transform, etc.) and all of the components (Button, Slider, TextField, List/Table/TreeView, etc.) are implemented in Snapkit.

So since this adapter model eliminates most system dependent code, I was able to reasonably embark on an adapter to run a Snapkit desktop app in the browser. This is when I found TeaVM – a remarkable system that compiles Java bytecode to JavaScript that supports a good subset of the standard Java class library. It compiles only referenced code, so the resulting JavaScript is amazingly small.

So far my two test cases are a (1) SimpleTest that has button, slider, label, images, vector graphics, text and animation and (2) Greenfoot Asteroids, an asteroids implementation written in Greenfoot (with a simple glue library I wrote to emulate Greenfoot in Snapkit).

The interesting thing about these two apps is that I can change one line of code to run them as native desktop applications, with access to full desktop features and performance (and a 40mb download). Or I can deploy in a much more accessible fashion to the browser, where there is no download step to scare potential users away (but with some missing features). Eventually, I could add a third option where the app runs client/server: still no download, and more functionality, but slower runtime performance (due to networked graphics). Those are options I can live with!

I still need time to properly integrate this into SnapCode, and SnapCode itself still needs some work. But here are the two apps:

SimpleTestAsteroids

I’d love to hear any feedback people have. I’ve been aching for a solution along these lines for years – I wish Oracle would do this with JavaFX and/or Swing. With WebAssembly I hope we’ll see more of this. Feel free to comment or send me a note at jeff at reportmill.

Automatically Update your Javapackager Applications

December 4, 2014

For years we shipped our desktop applications, ReportMill and SnapCode, as Java Web Start applications, enjoying the built-in auto-update feature it provided. This year we began shipping our apps as native app packages using Javapackager, which makes it easy to generate a exe/app for Windows/MacOS. This has been a huge improvement – we no longer worry what JRE is installed, whether it is broken/missing or whether it will change. The JRE is entirely under our control and hidden from the user. The fact that we use Java at all is now just an “implementation detail”.

The only concern we had when switching was that we still want users to automatically get the latest update without having to re-install the latest version. Fortunately, we found a simple solution: We put the core of the app in a single compact jar file (.pack.gz – like Web Start) on our website and we have an AppLoader main class that does the following:

  1. Check for new version of jar file on web site
  2. If found, download and alert user “New Update Available on Relaunch”
  3. If update from above step is in App-Support directory, move into place
  4. Create URLClassLoader for latest jar
  5. Get main class from URLClassLoader, and invoke real app main()

We were able to do this in a single AppLoader.java file in 250 lines of code that only uses JRE classes. To use this in your application, add AppLoader.java to your project, make it the main class and customize the string constants at the top of the file for your AppName, JarName, JarURL and MainClass.

Thanks to Jim Weaver for inspiring us to share this. Hopefully this works well for you, too!

Source code:  AppLoader.java

SnapCode Interactive Exercises

October 24, 2014

At JavaOne we presented a new feature of SnapCode we have been working on to provide interactive exercises to learn Java. The exercises come in the form of a puzzle game, which has the following parts:

  • Instructions for a task
  • A set of puzzle pieces to complete the task
  • A skeleton class to add the pieces to

The user is expected to drag in the puzzle pieces in the right order and run the app. When the task completes an automatic checker (unit test) grades you to see how well you did. The first exercise we have is called “Teach Sparky to Fetch”.

SparkyFetch

I’ve posted a short, updated SnapCode overview video that shows this in action (starting at the 4m 30s mark).

Video: SnapCode Overview with Exercises
YouTube-Watch-Button


SnapCode Debugger Improvements

August 9, 2014

I’ve spent the last few weeks boning up on the Java Debug Interface (JDI) API and improving SnapCode’s debugger. The debugger is now more accessible with a prominent “Debug” button in the Processes pane, easy viewing of debug threads and stack frames, and easy access to pause, continue, and step into/over/out of the current line of code.

Additionally, there is a new “Variables” tab to show the variables in the currently selected stack frame, and a “Debug Console” pane to allow you to type any valid Java expression and have it evaluated on the spot.

I still have a few debugger bugs to squash, and next week I need to change the Variables table to be a JavaFX TreeTable, but the debugger is very usable now. Here is a snapshot of the bottom part of the project window showing a process being debugged with the Variables pane selected in the Support tray.

SnapCodeDebugger

 

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(); }};