SnapKit - a Java UI toolkit

SnapKit is a modern Java UI library + tools for creating rich Java Client applications that achieve the original promise of Java by running pixel-perfect and native on the desktop and in the browser (WORA).

Why do we need another UI kit? Because Swing is out of date, JavaFX missed the boat, and neither run natively in the browser. But Java desktop development is hard to beat: the iterative dev cycle is fast, debugging is powerful, refactoring is easy, and advanced dev tools solve many problems at compile time. The drawback is deployment: browser deployment is impossible to beat. This situation alone has led to the decline of Java Client adoption and prevented it from being a serious contender for most new development. SnapKit is designed to resolve this with no compromises.

Check out demos of SnapKit running in the browser:

SnapKit

Overview

SnapKit runs optimally everywhere via a high level design where low-level functionality (such as painting, user input, system clipboard, drag-and-drop and windowing) is provided via interfaces to the native platform implementation. This makes SnapKit itself comparatively small and simple, light-weight and performant. SnapKit apps run very well in the browser with zero modification with the CheerpJ browser JVM. When compiled to the browser with a transpiler (via TeaVM), many apps are only 1 MB in size.

SnapKit Inspirations

SnapKit is strongly inspired by both Swing and JavaFX. Swing is still a favorite with Java desktop developers, despite its age and lack of recent updates. There are still many things developers love:

When JavaFX was introduced it rewrote the rulebook for Java UI with dramatic changes, often for the better:

SnapKit Advantages

SnapKit is the right blend of modern and conventional. SnapKit tries to be more of a "Swing 2.0". More precisely, it keeps the basic simplicity and standard conventions of Swing while adding the visual richness of JavaFX and bringing the whole thing to the browser:

The ViewOwner

The one thing that may have hurt Swing and JavaFX the most is that there is no standard class to manage the basics of UI management: Create, Init, Reset, Respond (otherwise known as the "Controller" in MVC).

This resulted in confusing controller code, with UI controls often having code for all four functions in the same place. Initially this can be deceptively simple and attractive, but falls apart when dozens of inter-dependent controls are present and order-dependent updates are necessary.

Here is a simple Swing example that quickly gets out of control when extended to many properties and controls:

// Create UI
_textField = new JTextField();

// Init UI
_textField.setText("Initial Value");

// Respond UI
_textField.addActionListener(event -> {
    _myModel.updatePropertyForTextField(_textField.getText());
    SwingUtilities.invokeLater(this::updateUI);
});

// Update UI
public void updateUI()
{
    _textField.setText(_myModel.getPropertyForTextField());
});

Here is the same thing with a ViewOwner:

/** Create UI. */
public View createUI()
{
    _textField = new TextField();
    return _textField;
}

/** Initialize UI. */
public void initUI()
{
    _textField.setText("Initial Value");
}

/** Reset UI. */
public void resetUI()
{
    // Update TextField from Model
    _textField.setText(_myModel.getPropertyForTextField());
}

/** Respond UI. */
public void respondUI(ViewEvent anEvent)
{
    // Update Model from TextField
    if (anEvent.equals(_textField))
        _myModel.updatePropertyForTextField(_textField.getText());
}

Some things to note:

CreateUI() is usually handled automatically by loading a '.snp' UI file created in SnapBuilder.

InitUI() is also usually not needed, because UI is configured in createUI() and updated in ResetUI().

ResetUI() updates are deferred, coalesced and "protected" (will not cause respondUI() side effects) and is called automatically when the user interacts with any UI (or explicitly via resetLater()).

RespondUI() is called automatically by controls (they are preconfigured to do this by default)

ResetUI() and RespondUI() make tracking UI interactions convenient and easy by providing a consistent place to look for all get/set code between controls and the model.

ViewOwner "Universal Accessors"

As a convenience, ViewOwner will let you get/set values using standard methods and support all controls, which avoids having to lookup or remember specific get/set methods for controls. It also provides common type conversions to avoid tedious conversions to/from String/number/boolean formats.

public void resetUI()
{
    // Update MyTextField, MySlider, ...
    setViewValue("MyTextField", _myModel.getPropertyForTextField());
    setViewValue("MySlider", _myModel.getPropertyForSlider());
    ...
}

The same applies to ViewEvent (the sole parameter to respondUI()):

public void respondUI(ViewEvent anEvent)
{
    // Handle MyTextField, MySlider, ...
    if (anEvent.equals("MyTextField"))
        _myModel.updatePropertyForTextField(anEvent.getStringValue());
    if (anEvent.equals("MySlider"))
        _myModel.updatePropertyForSlider(anEvent.getFloatValue());
    ...
}

In addition to get/setViewValue(), there are methods for get/set other View properties: Enabled, Visible, Text, SelectedItem, SelectedIndex.

The Graphics Package

One of the great aspects of Swing is the separation and synergy between the core graphics layer (Java2D) and the UI layer. SnapKit provides this same separation with the snap.gfx package that contains:

The View Package

The essentail part of a good UI kit is the set of classes that model the scene graph and standard UI controls.

The Text Package

The snap.text package provides all the classes necessary to efficiently display and edit large text files and rich text files. There is even fundamental support for text with 'tokens' (like source code text files), to efficiently support working with large source files and providing syntax coloring and symbol highlighting.

The Parser Package

The snap.parse package dynamically generates parsers based on conventional grammar files combined with a rule handler class. Separating the grammar from the handler code makes the parser much easier to read, write and maintain.

Several parsers are included in SnapKit to parse JSON, XML and Java expressions. Other parsers based on this package to parse PDF and Java are available in separate SnapKit dependent projects.

See SnapCode and SnapPDF.

The Properties Package

The snap.props package provides an easy way to serialize Java objects and provides automatic support for read/write (JSON/XML), copy/paste, undo/redo and more. Specifically the props support provides the following:

This serialization is done by simply defining each serializable property of an object in this fashion:

public static String Name_Prop = "Name";

addPropNamed(Name_Prop, <PropClass>, <DefaultValue>)

if (aPropValue == Name_Prop) return getName();

if (aPropValue == Name_Prop) setName((String) aValue);

Here is an example class that can automatically read/write sparse JSON/XML, handle clipboard copy/pase and handle undo:

/**
 * This class supports automatic read/write (JSON/XML), clipboard copy/pase, user
 * undo/redo and automatic clone()/equals()/hashCode()/toString() implementations.
 *
 *  To XML: String xmlString = new PropArchiverXML().writeToXML(new MyClass()).getString();
 *
 *  To JSON: String jsonString = new PropArchiverJS().writeToJSON(new MyClass()).getString();
 *
 *  Clone: MyClass myClone = new PropArchiver().copy(new MyClass());
 */
public class MyClass extends PropObject {

    // Serialzable Name property
    private String  _name;

    // Constants for properties
    public static final String Name_Prop = "Name";

    // Constants for property defaults
    public static final String DEFAULT_NAME = "John Doe";

    /**
     * Constructor.
     */
    public MyClass()
    {
        _name = DEFAULT_NAME;
    }

    /**
     * Override to configure properties for this class.
     */
    protected void initProps(PropSet aPropSet)
    {
        super.initProps(aPropSet);

        aPropSet.addPropForName(Name_Prop, String.class, DEFAULT_NAME);
    }

    /**
     * Override to return propery values for this class.
     */
    public Object getPropValue(String aPropName)
    {
        switch (aPropName) {
            case Name_Prop: return getName();
            default: return super.getPropValue(aPropName);
        }
    }
    
    /**
     * Override to set property values for this class.
     */
    public void setPropValue(String aPropName, Object aValue)
    {
        switch (aPropName) {
            case Name_Prop: setName((String) aValue); break;
            default: super.setPropValue(aPropName, aValue); break;
        }
    }
}

The 3D Graphics Package

The snap.gfx3d package provides a elegant 3D api based on OpenGL that uses JOGL on the desktop and WebGL in the browser. This allows for write-once-run-anywhere 3D. There is also a simple built-in renderer that renders 3D using standard 2D graphics (this avoids unnecessary external JOGL dependencies when 3D isn't really needed and can actually look better in PDF, SVG or print).

The 3D package has:

Sample 3D

The Web Package

The snap.web package provides a set of classes to abstract and unify interactions with URLs, files and sites (file systems).

These classes make it easy to ambiguously work with files regardless of whether they are from the local file system or from a remote HTTP site or even from a Zip archive file, Github repository or DropBox account. The package provides unique shared instances of WebFile and WebSite to make it easy to track changes across different modules of code that work with the same files and sites.

The SnapBuilder UI Builder

Because the best line of code is the one you don't have to write, UI is almost always created using the UI builder and stored in simple XML files ('.snp' files). Simply create/save a .snp file with the same name as your custom ViewOwner class, and the default ViewOwner.createUI() method will load it.

As a bonus, you can run SnapBuilder in the browser and even open any UI file from a running application using the "Developer Tools Console", also available in any running app (see below).

SnapBuilder

Integrated Runtime Developer Tools

If you double-tap the control key in any SnapKit app, a developer console will appear. There are many features here to make it easier to debug visual layouts and explore new or large code bases:

Including SnapKit with Gradle and Maven

SnapKit can easily be included with build tools like Gradle by referencing the maven package:

repositories {

    // Maven package repo at reportmill.com
    maven { url 'https://reportmill.com/maven' }
}

dependencies {

    // Latest release: https://github.com/reportmill/SnapKit/releases
    implementation 'com.reportmill:snapkit:2024.10'
}

Join libs.tech

...and unlock some superpowers

GitHub

We won't share your data with anyone else.