AntOSDK tutorial: Developing a simple text editor

AntOS API in a nutshell

AntOS provides an abstract API for application development. The core API contains three main elements: the UI API, the VFS API and the VDB API, as shown in the following graph:

The UI API defines the basic UI elements such as Window, List, Tree, Dialogs, etc, and provides a generic interface for application and dialog UI development. UI design consists two steps: (1) the first step is to layout UI elements using antOS' scheme syntax (in XML format); (2) the second step is to handle user interaction using the coffeescript/ Javascript API.

The VFS API defines an abstract and generic file system interface. On one hand, it specifies an unique and independent virtual file system interface to AntOS applications and on the other hand, it allows to adapt different kinds of file system to AntOS VFS interface. This mechanism decouples the applications from underneath file system, thus applications can work flexibly with any file systems that has a compatible AntOS VFS interface adapter. For example, an AntOS application can access Google Drive and Drobox files as the same way as the local files.

The VDB API has the same principle as the VFS API, but it applies on the relational database accessing. VDB API is useful for applications that require to store more complex date structure (than files) on the server. On the application side, different kinds of database can be accessed using an unique interface. On the low-level API side, an adapter interface is available for different database sources.

Using AntOSDK

AntOSDK is an AntOS application intending to provide an Integrated Development Environment (IDE) to applications development using AntOS API. Application can be developed, built and tested directly from browser. Basic features:

  • Automatically generate project template and files
  • In browser Coffeescript to javascript compiler
  • Automatically build, run and test project directly from browser
  • Editor powered by ACE editor library
  • Automatically generate release package in zip format that can be put on MarketPlace

This post serves as a beginning guild to AntOSDK and AntOS application development. Readers can follow the steps in this tutorial by accessing to the AntOSDK IDE available on the demo version of AntOS at : https://os.lxsang.me/ (user and password: demo). The IDE is also available on the MatketPlace. The complete source code of the project can be found on this repository https://github.com/lxsang/antosdk-apps

When launching, the application requires to open or create new project. Let's create a new project named TinyEditor in home://workspace:

The IDE will then generate all necessary files and open the development window as follow:

The development window is simple, the project files can be accessed via the file panel on the left. The main big area of the window is the editor and below it is the output panel which logs the build result of the project.
By the IDE convention, the generated project files has the following structure:

  • assets: contains all project asset files such as images, schemes, etc. The generated scheme.html is the main UI scheme of the application.
  • build: the built code/application will be stored in the debug folder, the release folder is where the IDE put the release Zip package of the application.
  • coffees: contains all coffee script files, the generated main.coffee is the main application code.
  • css: all application stylesheet (CSS) should be put here
  • javascripts: additional Javascript libraries go here
  • package.json: application metadata
  • project.apj: the project file, used by AntOSDK to configure project location and build options.
  • README.md : project documentation

The project meta-data

Project meta-data can be configured in the package.json file, its basic format is as follow:

{
    "app":"TinyEditor",
    "name":"TinyEditor",
    "description":"Simple text editor",
    "info":{
        "author": "",
        "email": ""
    },
    "version":"0.0.1-a",
    "category":"Other",
    "iconclass":"fa fa-adn",
    "mimes":["text/.*"]
}

Application icon can be configured using the icon or iconclass attribute. The mimes is an important attribute configuring the file types that the application can open. This information is useful since it allows the system to identify which application can open a file when user double click on it. For the purpose of this tutorial, as we want to develop a simple text editor, we can configure this attribute with the value "text/.*", which means that the application can open any file whose mime type start with "text".

Now build and run the application by accessing the menu Build>Build and Run or hit CTRL-R. You should see some thing like this in the output panel:

INFO: Verifying home://workspace/TinyEditor/coffees/main.coffee
SUCCESS: Compiled successful
SUCCESS: Generated home://workspace/TinyEditor/build/debug/main.js
INFO: Copied home://workspace/TinyEditor/assets/scheme.html -> home://workspace/TinyEditor/build/debug
INFO: Copied home://workspace/TinyEditor/package.json -> home://workspace/TinyEditor/build/debug
INFO: Copied home://workspace/TinyEditor/README.md -> home://workspace/TinyEditor/build/debug
SUCCESS: Build done
INFO: Metadata found...
INFO: Installing...
INFO: Running TinyEditor...

An empty window appears since we didn't add anything to the application code. Close that window, open the File application and double click on a file whose mime start with "text". Thanks to the application meta-data, you should see the TinyEditor application in the list of the OpenWith dialog.

Select TinyEditor and click OK will run the application (Just an empty window).

The UI scheme design

AntOS provides a XML-like scheme syntax that allows to layout the UI widgets inside an application window. Open the assets/scheme.html file, and modify it as follow:

<afx-app-window apptitle="__(Text Editor)" width="500" height="400" data-id="TinyEditor">
    <afx-vbox >
        <textarea data-id='editor'></textarea>
    </afx-vbox>
</afx-app-window>

The scheme is pretty simple, it contains a window widget with a textarea inside an afx-vbox. AntOS' scheme use afx-vbox and afx-hbox as main layout elements to organize other UI widgets. The textarea is just a normal HTML textarea element. Now build and run the application, you should see the textarea inside the window.

Styling the UI scheme

Styling the UI scheme is done via CSS, create a main.css inside the css folder (in the project tree), then add the following CSS snippet:

afx-app-window[data-id="TinyEditor"] textarea[data-id="editor"]
{
    background-color: #272822;
    color:white;
    margin: 0;
    padding:10px;
    border: 0;
}

This sets the textarea color and background-color as well as some padding and margin.

By default, the project's build target doesn't contain any CSS file, one need to add the newly created CSS file to the build target. Open the build option dialog by accessing to Build>Build Options or hit ALT-Y, in the CSS list, add the main.css file then click Save:

Rebuild the project and you should see the CSS file is logged in the output panel:

INFO: Verifying home://workspace/TinyEditor/coffees/main.coffee
SUCCESS: Compiled successful
SUCCESS: Generated home://workspace/TinyEditor/build/debug/main.js
SUCCESS: Generated home://workspace/TinyEditor/build/debug/main.css
...

NOTE: When one want to incorporate any additional files into the project, such as CSS, asset files, javascript libraries, etc., these files should be added to the project's build target (using the build option dialog) to take effect.

Application Code development

Application code is developed using Coffeescript. Readers should have basic knowledge about this language or refer to this link first: https://coffeescript.org/. The generated coffees/main.coffee file contains the base class of the application:

class TinyEditor extends this.OS.GUI.BaseApplication
    constructor: ( args ) ->
        super "TinyEditor", args

    main: () ->
        # your application code here

this.OS.register "TinyEditor", Tinytditor

All AntOS application classes should extend the BaseApplication class provided by the core API. The main() method at line 5 will be called automatically whenever the application run, subclasses should implement this method to specify the application behaviors. The code in line 8 is necessary to register the application classs (TinyEditor) to the system.

For this application, the main method is implemented as follow:

main: () ->
    me = @
    @editor = @find "editor"
    @bindKey "ALT-N", () -> me.newFile()
    @bindKey "ALT-O", () -> me.openFile()
    @bindKey "CTRL-S", () -> me.saveFile()
    @filehandler = if @args and @args.length > 0 then @args[0].asFileHandler() else null
    $(@editor).on 'input', (e) ->
        return if me.filehandler.dirty is true
        me.filehandler.dirty = true
        me.scheme.set "apptitle", "#{me.filehandler.path}*"
    @read()

In line 3 we access to the UI element (the textarea) defined in the scheme file above using its data-id attribute. Line 4,5, and 6 bind keyboard shortcuts to the application for three operations: create new file, open an existing file and save a file
Line 7 initializes an instance variable (filehandler) which holds a file handle using the VFS interface. Since our application manipulates a file, all read/write from/to the file will be performed via this file handle.
Lines 8-11 handle the input event of the textarea. Basically, when user inputs data to the textarea, the application will mark the filehandler as dirty (need to be saved later) and change the title of the application window.
Lastly, we call the read operation to read the file content via the file handle.

So, there are some missing methods need to be implemented:

  • newFile: User create a new empty file
  • openFile : User open an existing file
  • saveFile: User save the file
  • read: actual read of a file via the VFS interface
  • write: actual write to a file via the VFS interface

The first three methods newFile, openFile and saveFile are pretty simple:

newFile: () ->
    @filehandler = null
    @read()

openFile: () ->
    me = @
    @openDialog "FileDiaLog", ( dir, fname, d ) ->
        me.filehandler = "#{dir}/#{fname}".asFileHandler()
        me.read()
    , __("Open file")

saveFile: () ->
    me = @
    @filehandler.cache = @editor.value
    return @write() unless @filehandler.path is "Untitled"
    @openDialog "FileDiaLog", (dir, fname, d) ->
        me.filehandler.setPath "#{dir}/#{fname}"
        me.write()
    , __("Save as"), { file: me.filehandler }

The newFile method just sets the file handle variable to null, then calls the read operation, which in turn creates an empty file handle.
The openFile method opens a FileDialog then refers the file handle variable to the selected file before calling the read operation.
The saveFile method first sets the internal cache of the file handle variable to the editor value. It then rewrites the file using the write operation if the file already exists, otherwise it opens a FileDialog for file selection before calling the write operation.

The read and write operations are implemented as follow:

 read: () ->
    me = @
    @editor.value = ""
    if @filehandler is null
        @filehandler = "Untitled".asFileHandler()
        @scheme.set "apptitle", "Untitled"
        return
    @filehandler.read (d) ->
        me.scheme.set "apptitle", me.filehandler.path
        me.editor.value = d

write: () ->
    me = @
    @filehandler.write "text/plain", (d) ->
        return me.error __("Error saving file {0}", me.filehandler.path) if d.error
        me.filehandler.dirty = false
        me.scheme.set "apptitle", "#{me.filehandler.path}"

The read method checks if the file handle is null, it will create an new empty file handle if it is the case, otherwise, it reads and puts the file content to the editor (textarea).
The write method writes the internal cache content of the file handle to the actual file and reset the dirty flag of the file handle.

Until now, all the basic operations of the application are implemented. The application can be built an run, user can hit ALT-N to create new file, ALT-O to open a file and CTRL-S to save the file.
A text file can be opened directly by double clicking on it from the File application, then select TinyEditor from the OpenWith dialog.

Adding File menu to the system panel

In AntOS, when an application is selected, its menu will appear in the system panel, to define this menu, one need to implement the menu method in the application class, the following snippet shows an example of menu for the text editor:

menu: () ->
    me = @
    m = [ 
        {
            text: "__(File)",
            child: [
                { text: "__(New)", dataid :"new", shortcut: 'A-N' },
                { text: "__(Open)", dataid :"open", shortcut: 'A-O' },
                { text: "__(Save)", dataid :"save", shortcut: 'C-S'  }
            ],
            onmenuselect: (e) ->
                switch e.item.data.dataid
                    when "new" then me.newFile()
                    when "open" then me.openFile()
                    when "save" then me.saveFile()
        }
    ]
    m

This is how the final application and its menu look like:

Release the project

To release a project, go to menu Build > Build release or hit ALT-P, you should see some thing like this in the log output:

INFO: Verifying home://workspace/TinyEditor/coffees/main.coffee
SUCCESS: Compiled successful
SUCCESS: Generated home://workspace/TinyEditor/build/debug/main.js
SUCCESS: Generated home://workspace/TinyEditor/build/debug/main.css
INFO: Copied home://workspace/TinyEditor/assets/scheme.html -> home://workspace/TinyEditor/build/debug
INFO: Copied home://workspace/TinyEditor/package.json -> home://workspace/TinyEditor/build/debug
INFO: Copied home://workspace/TinyEditor/README.md -> home://workspace/TinyEditor/build/debug
SUCCESS: Build done
INFO: Preparing for release
INFO: add main.js to zip
INFO: add README.md to zip
INFO: add main.css to zip
INFO: add package.json to zip
INFO: add scheme.html to zip
SUCCESS: zip file generated in release folder

The IDE will package the built application into a Zip file and put it to the build/release folder, this zip file is ready to publish on the MarketPlace.

Related posts

Powered by antd server, (c) 2017 - 2018 Xuan Sang LE