Tuesday 15 February 2011

Ace Editor meets GWT

Recently came across Ace and it looks like a pretty cool project.

Since I'm not really a Javascript developer, but I do dabble in some GWT now and then, I hacked together a minimal GWT wrapper around Ace for my own use.  Here it is:


import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.Widget;

public class AceEditor extends Widget {

    private JavaScriptObject editor;

    public AceEditor() {
        Element el = DOM.createDiv();
        el.setId(DOM.createUniqueId());
        setElement(el);
    }

    public void initialise() {
        initialise(null);
    }

    public void initialise(String mode) {
        if (editor == null) {
            editor = __edit(getElement().getId(), mode);
        }
    }

    public String getText() {
        return __getText(editor);
    }

    public void setText(String text) {
        __setText(editor, (text == null) ? "" : text);
    }

    @Override
    public void setWidth(String width) {
        super.setWidth(width);
        resize();
    }

    @Override
    public void setHeight(String height) {
        super.setHeight(height);
        resize();
    }

    @Override
    public void setSize(String width, String height) {
        super.setWidth(width);
        super.setHeight(height);
        resize();
    }

    public void resize() {
        __resize(editor);
    }

    private static native JavaScriptObject __edit(String id, String mode) /*-{
        var editor = $wnd.ace.edit(id), EditMode;
        if (mode) {
            EditMode = $wnd.require(mode).Mode;
            editor.getSession().setMode(new EditMode());
        }
        return editor;
    }-*/;

    private static native String __getText(JavaScriptObject editor) /*-{
        return editor.getSession().getValue();
    }-*/;

    private static native void __setText(JavaScriptObject editor, String text) /*-{
        editor.getSession().setValue(text);
    }-*/;

    private static native void __resize(JavaScriptObject editor) /*-{
        editor.resize();
    }-*/;
}

Just be sure to include the Ace js files along with your GWT module code, and you're away...

To use this, I found it easiest to instantiate the AceEditor class, and then call the initialise() method in a deferred command (I guess once the DOM is ready properly).  Otherwise it doesn't work as well:

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.user.client.ui.RootPanel;

public class MyEntryPoint implements EntryPoint {

    private AceEditor editor;

    public void onModuleLoad() {
        editor = new AceEditor();

        RootPanel.get("main").add(editor);

        Scheduler.get().scheduleDeferred(new ScheduledCommand() {
            @Override
            public void execute() {
                editor.initialise("ace/mode/javascript");
            }
        });
    }
}

This could probably be improved a bit more, and extended to incorporate more of the Ace API, but it's a start.

Cheers,
Jesse.