NanoUI

From Paradise Station Wiki
Jump to: navigation, search

NanoUI Basics

The DM Code

We're going to create a basic NanoUI, to give you an understanding of how the library works.

For this example we're going to create a UI for a fictional object, called a "womdinger".

Here is the basic womdinger.dm file:

   /obj/item/device/wombdinger
       name = "womdinger"
       desc = "It's some kind of crude alien device."
       icon = 'icons/obj/wombdinger.dmi'
       icon_state = "0"
       
   /obj/item/device/wombdinger/attack_self(mob/user as mob)
       // attack_self is currently empty

In order to open our NanoUI we'll need to use the ui_interact proc.

Failure to use the ui_interact proc for opening NanoUIs will prevent the UI from receiving updates.

NanoUI adds the ui_interact proc to all objs and mobs; we will override this in our womdinger.dm file and call it from the attack_self proc:

   /obj/item/device/wombdinger
       name = "womdinger"
       desc = "It's some kind of crude alien device."
       icon = 'icons/obj/wombdinger.dmi'
       icon_state = "0"
       
   /obj/item/device/wombdinger/attack_self(mob/user as mob)
       ui_interact(user)
   
   /obj/item/device/wombdinger/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
       // NanoUI code goes here

Note: We won't go into the parameters of ui_interact here, that has been documented in the code (see \code\modules\nano\nanoui.dm).

Now we need to put something into ui_interact; the data to be passed to the UI and the UI create/update logic:

   /obj/item/device/wombdinger/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
       
       // this is the data which will be sent to the ui, it must be a list
       var/list/data = list()
       
       // we'll add some simple data here as an example
       data["myName"] = name
       data["myDesc"] = desc
       data["someString"] = "I am a string."
       data["aNumber"] = 123
       
       data["assocList"] = list("key1" = "Value1", "key2" = "Value2")
       
       // the backslash tells the compiler to ignore the carriage return, treating the easy-to-read format as a single line.
       data["arrayOfAssocLists"] = list(\ 
           list("key1" = "ValueA1", "key2" = "ValueA2"),\
           list("key1" = "ValueB1", "key2" = "ValueB2"),\
           list("key1" = "ValueC1", "key2" = "ValueC2")
       )
       
       data["emptyArray"] = list()
       
       // update the ui with data if it exists, returns null if no ui is passed/found or if force_open is 1/true
       ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)	
       if (!ui)
           // the ui does not exist, so we'll create a new() one
           // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
           ui = new(user, src, ui_key, "womdinger.tmpl", "Womdinger UI", 520, 410)
           // when the ui is first opened this is the data it will use
           ui.set_initial_data(data)		
           // open the new ui window
           ui.open()

The two important elements to focus on in the code above are the data, which is a simple associative list, and the nanoui creation ("ui = new(user, src, ui_key, "womdinger.tmpl", "Womdinger UI", 520, 410)");

The first three parameters (user, src and ui_key) of the nanoui new proc never change.

The next four parameters are template, title, width and height:

template should be the name of the template file in the \nano\templates\ folder.

title is the title of the ui window.

width is the width, in pixels, of the ui window.

height is the height, in pixels, of the ui window.

That's all the code we need in the DM file, next we need to create the womdinger template.

Creating a Template

NanoUI separates the display code from the logic (DM files) and puts it into template (.tmpl) files.

Template files are basically just HTML documents, with markup added to for dynamic elements.

In the previous section we set up our womdinger.dm file. In that file, under the ui_interact proc, a "womdinger.tmpl" file is referenced. We need to create that file in the \nano\templates\ folder.

Note: As of the latest update to the NanoUI library it is no longer necessary to add template files to the client send_resources proc as this is handled automatically.

Now the basic template code for womdinger.tmpl:

   <div class="item">
        <div class="itemLabel">
            <b>Object Name</b>:
        </div>
        <div class="itemContent">
            {{:data.myName}}
        </div>
    </div>
    <div class="item">
        <div class="itemLabel">
            <b>Object Description</b>:
        </div>
        <div class="itemContent">
            {{:data.myDesc}}
        </div>
    </div>
    <div class="item">
        <div class="itemLabel">
            <b>Some String</b>:
        </div>
        <div class="itemContent">
            {{:data.someString}}
        </div>
    </div>
    <div class="item">
        <div class="itemLabel">
            <b>A Number</b>:
        </div>
        <div class="itemContent">
            {{:data.aNumber}}
        </div>
    </div>
    <div class="item">
        <div class="itemLabel">
            <b>Associated list values</b>:
        </div>
        <div class="itemContent">
            {{:data.assocList.key1}} and {{:data.assocList.key2}}
        </div>
    </div>

The template is standard HTML4 with markup tags (surrounded by curly braces {{...}}) for dynamic content (print, conditional statements, loops etc.).

The default styling for the template is provided by the shared.css file in the /nano/css folder.

That's it, the most basic UI that you can create in NanoUI. In the next section we'll cover using markup tags for dynamic content.

Template Markup Tags

Markup tags are used to add dynamic content to the template.

Print Tag

Format:

   {{:data.variable}}

The print tag outputs variable as text to the UI.

Example 1: Top level data

In womdinger.dm, under the ui_interact proc, we set some text to the "someString" key:

   data["someString"] = "I am a string"

And in the womdinger.tmpl template we access that data with the following tag:

   {{:data.someString}}

When the UI is rendered the tag will have been replaced with:

   I am a string.

Example 2: Going deeper

In womdinger.dm, under the ui_interact proc, we set an associative list to the "assocList" key:

   data["assocList"] = list("key1" = "Value1", "key2" = "Value2")

You can access the values of associated lists using the dot (.) notation:

   {{:data.assocList.key1}} and {{:data.assocList.key2}}

When the UI is rendered these tag will have been replaced with:

   Value1 and Value2

If Tag

Format:

   {{if expression}} <expression true content> {{/if}}
   {{if expression}} <expression true content> {{else}} <expression false content> {{/if}}
   {{if expression1}} <expression1 true content> {{else expression2}} <expression2 true content> {{/if}}

The if tag displays content conditionally based on the provided expression being true.

When combined with the else tag the if tag can also show content if the provided expression is false.

The else tag can optionally have an expression provided (e.g. "{{else expression2}}"), giving it "elseif" functionality.

Example 1: A simple if tag

Template example:

    {{if data.aNumber}}
        data.aNumber is set (not null) and is positive
    {{/if}    

When the UI is rendered the tag will have been replaced with:

   data.aNumber is set (not null) and is positive

Because data.aNumber is set (not null) and is positive (it's value is 123).

Example 2: If and else tags combined

Template example:

    {{if data.someString == "Who goes there?"}}
        Some string is "Who goes there?"
    {{else}}
        Some string is not "Who goes there?"
    {{/if}   

When the UI is rendered the tag will have been replaced with:

   Some string is not "Who goes there?"

Because the expression provided (someString == "Who goes there?") is not true.

Example 3: If and multiple else tags combined

Template example:

    {{if data.aNumber < 50}}
        data.aNumber is less than 50
    {{else data.aNumber < 100}}
        data.aNumber is less than 100
    {{else data.aNumber < 150}}
        data.aNumber is less than 150
    {{else}}
        None of the expressions above are true.
    {{/if}    

When the UI is rendered the tag will have been replaced with:

   data.aNumber is less than 150

Because the expression provided to the second else tag (data.aNumber < 150) is true.

For Tag

Format:

   {{for array}} <list entry content> {{/for}} 
   {{for array}} <list entry content> {{empty}} <empty list content> {{/for}}

Loop through entries in an array (an array is a list with a numeric index (it does not use strings as keys).

Each time the for tag iterates though the array it sets a variable (default "value") to the data of the current entry (another variable, default "index", contains the index).

And example of this is using the print tag to print the contents (e.g. {{:value.key1}} and {{:value.key2}}).

If combined with an empty tag the for tag can display content when the array is empty.

Example 1: Basic usage

Template example:

    {{for data.arrayOfAssocLists}}
        {{:index}} -> {{:value.key1}} and {{:value.key2}}<br>
    {{/for}    

When the UI is rendered the tag will have been replaced with:

   0 -> ValueA1 and ValueA2
   1 -> ValueB1 and ValueB2
   2 -> ValueC1 and ValueC2

Example 2: Using the empty tag

Template example:

    {{for data.emptyArray}}
        {{:value.key}}
    {{empty}}
        This list is empty.
    {{/for}    

When the UI is rendered the tag will have been replaced with:

   This list is empty.

Template Helpers

Helpers are functions written in JavaScript which can be called within templates. They are used within the above markup tags, most commonly the print tag.

Helpers are accessed via the "helper" object.

Link Helper

Format:

   {{:helper.link(text, icon, parameters, status, elementClass, elementId)}}

The link helper generates a link (which by default is styled as a button). When clicked the button will send the supplied parameters to the server.

Parameters:

  • text - string - The text which appears on the link.
  • icon - string - An icon to show on the link. Icon names can be found at http://fontawesome.io/icons/. Examples: 'refresh', 'shuffle', 'radiation', 'power'. * parameters - JSON - Parameters to send to the server when the link is clicked. The "src" parameter is added automatically, do not add it here.
  • status - optional null/string - Optional status of the link. Can be null for active, 'disabled' for disabled or 'selected' for selected. Defaults to active if nothing is passed.
  • elementClass - optional string - If provided this string will be added to the link as a class (used for custom styling in CSS or as a JavaScript selector).
  • elementId - optional string - If provided this string will be added to the link as an id (used for custom styling in CSS or as a JavaScript selector).

Example 1: Basic usage

   {{:helper.link('Toggle Power', 'power', { 'option' : 'togglePower' })}}

If clicked the receiving object's Topic proc's href_list will have the following entries (src is added automatically and should not be changed):

   list("src" = "\ref[src]", "option" = "togglePower")

Example 2: Multiple parameters

   {{:helper.link('Cut Red Wire', 'scissors', { 'wireColor' : 'red', 'action' : 'cut' })}}

If clicked the receiving object's Topic proc's href_list will have the following entries (src is added automatically and should not be changed):

   list("src" = "\ref[src]", "wireColor" = "red", "action" = "cut")

Bar Helper

Format:

   {{:helper.displayBar(value, rangeMin, rangeMax, styleClass, styleClass, showText)}}

This helper creates a bar.

Parameters:

  • value - number - Current value of progress bar
  • rangeMin - number - Lower bound of value
  • rangeMax - number - Upper bound of value
  • styleClass - optional string - List of styles to apply, seperated by spaces. Acceptable styles include alignRight, good, notgood, average, bad, and highlight.
  • showText - optional string - The text that is shown. Do not include the number itself.

Example 1: Health

    {{if data.occupant.health >= 0}}
        {{:helper.displayBar(data.occupant.health, 0, data.occupant.maxHealth, 'good')}}
    {{else}}
        {{:helper.displayBar(data.occupant.health, 0, data.occupant.minHealth, 'average alignRight')}}
    {{/if}}